4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
30 * rcm scripting module:
32 * This module implements rcm scripting interfaces.
33 * It translates rcm module based interfaces to rcm script based
38 * int script_main_init()
39 * Initialize the rcm scripting framework.
40 * Called during the rcm daemon initialization
42 * int script_main_fini()
43 * Called at the time of the rcm daemon exit.
45 * struct rcm_mod_ops *script_init(module_t *module)
46 * Initialize the given script.
47 * module->name contains the name of the script.
48 * Called at the time of loading scripts.
49 * Semantics are similar to module init.
51 * char *script_info(module_t *module)
52 * Called when the rcm daemon wishes to get the script information.
53 * module->name contains the name of the script.
54 * Semantics are similar to module info.
56 * int script_fini(module_t *module)
57 * Called before removing the script.
58 * module->name contains the name of the script.
59 * Semantics are similar to module fini.
61 * In addition to the above entry points rcm_mod_ops structure contains
62 * the other entry points. A pointer to this structure is returned when
63 * script_init() is called.
67 #include "rcm_script_impl.h"
68 #include <sys/resource.h>
74 * All rcm scripting commands are enumerated here.
75 * NOTE: command positions in script_cmd_id_t and script_cmd_name must match.
94 /* NOTE: command positions in script_cmd_id_t and script_cmd_name must match */
95 static char *script_cmd_name
[] = {
114 * All rcm scripting data items are enumerated here.
115 * NOTE: data item positions in script_data_item_id_t and
116 * script_data_item_name must match.
123 D_RESOURCE_USAGE_INFO
,
129 } script_data_item_id_t
;
132 * NOTE: data item positions in script_data_item_id_t and
133 * script_data_item_name must match.
135 static const char *script_data_item_name
[] = {
136 "rcm_script_version",
137 "rcm_script_func_info",
140 "rcm_resource_usage_info",
141 "rcm_failure_reason",
150 * Maximum number of rcm scripts that can run in parallel.
151 * RCM daemon has no limit on the number of scripts supported. But
152 * at most it runs script_max_parallelism number of scripts in parallel.
153 * For each running script rcm daemon consumes two file descriptors
154 * in order to communicate with the script via pipes.
155 * So maximum number of file descriptor entries consumed by rcm daemon
156 * on behalf of rcm scripts is "script_max_parallelism * 2"
158 static const int script_max_parallelism
= 64;
161 * semaphore to limit the number of rcm script processes running in
162 * parallel to script_max_parallelism.
164 static sema_t script_process_sema
;
166 /* mutex to protect the any global data */
167 static mutex_t script_lock
;
169 /* contains head to a queue of script_info structures */
170 static rcm_queue_t script_info_q
;
173 * This mmapped state file is used to store the process id and
174 * rcm script name of all currently running rcm scripts.
176 static const char *script_ps_state_file
= "/var/run/rcm_script_state";
177 static state_file_descr_t script_ps_statefd
;
179 static char *script_env_noforce
= "RCM_ENV_FORCE=FALSE";
180 static char *script_env_force
= "RCM_ENV_FORCE=TRUE";
181 static char *script_env_interval
= "RCM_ENV_INTERVAL=%ld";
183 #define RSCR_TRACE RCM_TRACE1
185 /* rcm script base environment */
186 static char *script_env
[MAX_ENV_PARAMS
];
188 struct rlimit file_limit
;
190 /* function prototypes */
191 static void build_env(void);
192 static void copy_env(char *[], char *[]);
193 static void open_state_file(const char *, state_file_descr_t
*, size_t, int,
195 static void truncate_state_file(state_file_descr_t
*);
196 static void close_state_file(const char *, state_file_descr_t
*);
197 static void grow_state_file(state_file_descr_t
*);
198 static void *get_state_element(state_file_descr_t
*, int, int *);
199 static void *allocate_state_element(state_file_descr_t
*, int *);
200 static void free_state_element(void *);
201 static void script_ps_state_file_kill_pids(void);
202 static void script_ps_state_file_add_entry(pid_t
, char *);
203 static void script_ps_state_file_remove_entry(pid_t
);
204 static int dname_to_id(char *);
205 static void script_process_sema_wait(void);
206 static int run_script(script_info_t
*, char *[], char *[], char **);
207 static int get_line(int fd
, char *, char *, int, size_t *, time_t, int *);
208 static void script_exited(script_info_t
*);
209 static int kill_pid(pid_t
);
210 static void kill_script(script_info_t
*);
211 static char *flags_to_name(int, char *, int);
212 static void fill_argv(script_info_t
*, char *[], char *);
213 static void *read_stderr(script_info_t
*);
214 static int process_dataitem(script_info_t
*, int, char *, char **);
215 static int do_cmd(script_info_t
*, char *[], char *[], char **);
216 static int do_script_info(script_info_t
*);
217 static int do_dr(script_info_t
*, char *[], char *[], char **);
218 static int script_get_info(rcm_handle_t
*, char *, pid_t
, uint_t
, char **,
219 char **, nvlist_t
*, rcm_info_t
**);
220 static void add_for_unregister(script_info_t
*);
221 static void remove_from_unregister(script_info_t
*, char *);
222 static void complete_unregister(script_info_t
*);
223 static int script_register_interest(rcm_handle_t
*);
224 static void add_drreq(script_info_t
*, char *);
225 static void remove_drreq(script_info_t
*, char *);
226 static void remove_drreq_all(script_info_t
*);
227 static int script_request_offline(rcm_handle_t
*, char *, pid_t
, uint_t
,
228 char **, rcm_info_t
**);
229 static int script_notify_online(rcm_handle_t
*, char *, pid_t
, uint_t
,
230 char **, rcm_info_t
**);
231 static int script_notify_remove(rcm_handle_t
*, char *, pid_t
, uint_t
,
232 char **, rcm_info_t
**);
233 static int script_request_suspend(rcm_handle_t
*, char *, pid_t
, timespec_t
*,
234 uint_t
, char **, rcm_info_t
**);
235 static int script_notify_resume(rcm_handle_t
*, char *, pid_t
, uint_t
,
236 char **, rcm_info_t
**);
237 static capacity_descr_t
*get_capacity_descr(char *);
238 static int build_env_for_capacity(script_info_t
*, char *, uint_t
, nvlist_t
*,
239 char *[], int *, char **);
240 static int script_request_capacity_change(rcm_handle_t
*, char *, pid_t
,
241 uint_t
, nvlist_t
*, char **, rcm_info_t
**);
242 static int script_notify_capacity_change(rcm_handle_t
*, char *, pid_t
,
243 uint_t
, nvlist_t
*, char **, rcm_info_t
**);
244 static void log_msg(script_info_t
*, int, char *);
245 static char *dup_err(int, char *, ...);
246 static void rcmscript_snprintf(char **, int *, char **, char *, ...);
247 static char *rcmscript_strdup(char *);
248 static void *rcmscript_malloc(size_t);
249 static void *rcmscript_calloc(size_t, size_t);
252 static struct rcm_mod_ops script_ops
=
255 script_register_interest
, /* register */
256 script_register_interest
, /* unregister */
258 script_request_suspend
,
259 script_notify_resume
,
260 script_request_offline
,
261 script_notify_online
,
262 script_notify_remove
,
263 script_request_capacity_change
,
264 script_notify_capacity_change
,
269 * Messages fall into two categories:
270 * framework messages (MF_..)
271 * errors directly attributable to scripts (MS_..)
273 #define MF_MEMORY_ALLOCATION_ERR \
274 gettext("rcm: failed to allocate memory: %1$s\n")
275 #define MF_STATE_FILE_ERR \
276 gettext("rcm: state file error: %1$s: %2$s\n")
277 #define MF_FUNC_CALL_ERR \
278 gettext("rcm: %1$s: %2$s\n")
280 gettext("rcm: required name-value parameters missing (%1$s)\n")
281 #define MF_UNKNOWN_RSRC_ERR \
282 gettext("rcm: unknown resource name %1$s (%2$s)\n")
283 #define MS_REGISTER_RSRC_ERR \
284 gettext("rcm script %1$s: failed to register %2$s\n")
285 #define MS_REGISTER_ERR \
286 gettext("rcm script %1$s: register: %2$s\n")
287 #define MS_SCRIPTINFO_ERR \
288 gettext("rcm script %1$s: scriptinfo: %2$s\n")
289 #define MS_PROTOCOL_ERR \
290 gettext("rcm script %1$s: scripting protocol error\n")
291 #define MS_TIMEOUT_ERR \
292 gettext("rcm script %1$s: timeout error\n")
293 #define MS_UNSUPPORTED_VER \
294 gettext("rcm script %1$s: unsupported version %2$d\n")
295 #define MS_SCRIPT_ERR \
296 gettext("rcm script %1$s: error: %2$s\n")
297 #define MS_UNKNOWN_ERR \
298 gettext("rcm script %1$s: unknown error\n")
300 gettext("rcm script %1$s: %2$s\n")
304 * Initialize rcm scripting framework.
305 * Called during initialization of rcm daemon.
308 script_main_init(void)
310 #define PS_STATE_FILE_CHUNK_SIZE 32
312 /* set base script environment */
315 rcm_init_queue(&script_info_q
);
318 * Initialize the semaphore to limit the number of rcm script
319 * process running in parallel to script_max_parallelism.
321 (void) sema_init(&script_process_sema
, script_max_parallelism
,
324 (void) mutex_init(&script_lock
, USYNC_THREAD
, NULL
);
326 /* save original file limit */
327 (void) getrlimit(RLIMIT_NOFILE
, &file_limit
);
329 open_state_file(script_ps_state_file
, &script_ps_statefd
,
330 sizeof (ps_state_element_t
),
331 PS_STATE_FILE_CHUNK_SIZE
,
335 * If any pids exist in the ps state file since the last incarnation of
336 * the rcm daemon, kill the pids.
337 * On a normal daemon exit no pids should exist in the ps state file.
338 * But on an abnormal daemon exit pids may exist in the ps state file.
340 if (script_ps_statefd
.state_file
) {
341 script_ps_state_file_kill_pids();
342 truncate_state_file(&script_ps_statefd
);
350 * Called at the time of normal rcm daemon exit.
353 script_main_fini(void)
355 script_ps_state_file_kill_pids();
356 close_state_file(script_ps_state_file
, &script_ps_statefd
);
361 * Initialize the given rcm script.
362 * module->name contains the name of the rcm script.
365 script_init(module_t
*module
)
371 rcm_log_message(RSCR_TRACE
, "script_init: script name = %s\n",
376 if ((script_path
= rcm_get_script_dir(module
->name
)) == NULL
)
379 len
= strlen(script_path
) + strlen(module
->name
) + 2;
381 /* calloc also zeros the contents */
382 rsi
= (script_info_t
*)rcmscript_calloc(1, sizeof (script_info_t
));
383 rsi
->script_full_name
= (char *)rcmscript_calloc(1, len
);
385 rsi
->module
= module
;
386 rcm_init_queue(&rsi
->drreq_q
);
388 (void) mutex_init(&rsi
->channel_lock
, USYNC_THREAD
, NULL
);
390 (void) snprintf(rsi
->script_full_name
, len
, "%s%s", script_path
,
392 rsi
->script_name
= strrchr(rsi
->script_full_name
, '/') + 1;
394 (void) mutex_lock(&rsi
->channel_lock
);
396 rsi
->cmd_timeout
= -1; /* don't time scriptinfo command */
397 if (do_script_info(rsi
) == RCM_SUCCESS
) {
399 * if the script hasn't specified a timeout value set it to
402 if (rsi
->cmd_timeout
== -1)
403 rsi
->cmd_timeout
= SCRIPT_CMD_TIMEOUT
;
404 (void) mutex_unlock(&rsi
->channel_lock
);
406 /* put rsi on script_info_q */
407 (void) mutex_lock(&script_lock
);
408 rcm_enqueue_tail(&script_info_q
, &rsi
->queue
);
409 (void) mutex_unlock(&script_lock
);
412 return (&script_ops
);
415 (void) mutex_unlock(&rsi
->channel_lock
);
417 free(rsi
->script_full_name
);
423 * Returns a string describing the script's functionality.
424 * module->name contains the name of the rcm script for which information
428 script_info(module_t
*module
)
430 script_info_t
*rsi
= module
->rsi
;
432 rcm_log_message(RSCR_TRACE
, "script_info: script name = %s\n",
434 return (rsi
->func_info_buf
);
438 * Called before unloading the script.
439 * module->name contains the name of the rcm script which is being unloaded.
443 script_fini(module_t
*module
)
445 script_info_t
*rsi
= module
->rsi
;
447 rcm_log_message(RSCR_TRACE
, "script_fini: script name = %s\n",
450 /* remove rsi from script_info_q */
451 (void) mutex_lock(&script_lock
);
452 rcm_dequeue(&rsi
->queue
);
453 (void) mutex_unlock(&script_lock
);
455 remove_drreq_all(rsi
);
457 if (rsi
->func_info_buf
)
458 free(rsi
->func_info_buf
);
460 free(rsi
->script_full_name
);
465 return (RCM_SUCCESS
);
468 /* build base environment for scripts */
472 const char *env_list
[] = { "LANG", "LC_COLLATE", "LC_CTYPE",
473 "LC_MESSAGES", "LC_MONETARY", "LC_NUMERIC", "LC_TIME",
474 "LC_ALL", "TZ", NULL
};
479 extern int debug_level
;
481 script_env
[j
++] = rcmscript_strdup("PATH=/usr/sbin:/usr/bin");
483 for (i
= 0; env_list
[i
] != NULL
; i
++) {
484 x
= getenv(env_list
[i
]);
486 len
= strlen(env_list
[i
]) + strlen(x
) + 2;
487 script_env
[j
] = (char *)rcmscript_malloc(len
);
489 (void) snprintf(script_env
[j
++], len
, "%s=%s",
494 len
= strlen("RCM_ENV_DEBUG_LEVEL") + 3;
495 script_env
[j
] = (char *)rcmscript_malloc(len
);
499 else if (debug_level
> 9)
504 (void) snprintf(script_env
[j
++], len
, "RCM_ENV_DEBUG_LEVEL=%d", d
);
506 script_env
[j
] = NULL
;
510 copy_env(char *src
[], char *dst
[])
514 for (i
= 0; src
[i
] != NULL
; i
++)
521 * Open (or create if the file does not exist) the given state file
525 open_state_file(const char *filename
,
526 state_file_descr_t
*statefd
,
534 if ((statefd
->fd
= open(filename
, O_CREAT
|O_RDWR
, 0600)) ==
537 rcm_log_message(RCM_ERROR
, MF_STATE_FILE_ERR
,
538 "open", strerror(error_num
));
539 rcmd_exit(error_num
);
543 if (fstat(statefd
->fd
, &stats
) != 0) {
545 rcm_log_message(RCM_ERROR
, MF_STATE_FILE_ERR
,
546 "fstat", strerror(error_num
));
547 rcmd_exit(error_num
);
551 if (stats
.st_size
!= 0) {
553 statefd
->state_file
= (state_file_t
*)mmap(NULL
,
554 stats
.st_size
, PROT_READ
|PROT_WRITE
, MAP_SHARED
,
557 if (statefd
->state_file
== MAP_FAILED
) {
559 rcm_log_message(RCM_ERROR
, MF_STATE_FILE_ERR
,
560 "mmap", strerror(error_num
));
561 rcmd_exit(error_num
);
565 if (statefd
->state_file
->version
!= version
) {
566 (void) munmap((void *)statefd
->state_file
,
568 statefd
->state_file
= NULL
;
569 (void) ftruncate(statefd
->fd
, 0);
572 statefd
->state_file
= NULL
;
575 statefd
->version
= version
;
576 statefd
->element_size
= sizeof (state_element_t
) +
577 RSCR_ROUNDUP(element_size
, 8);
578 statefd
->chunk_size
= chunk_size
;
583 truncate_state_file(state_file_descr_t
*statefd
)
587 if (statefd
->state_file
) {
588 size
= sizeof (state_file_t
) + statefd
->element_size
*
589 statefd
->state_file
->max_elements
;
591 (void) munmap((void *)statefd
->state_file
, size
);
592 statefd
->state_file
= NULL
;
594 (void) ftruncate(statefd
->fd
, 0);
598 close_state_file(const char *filename
, state_file_descr_t
*statefd
)
600 truncate_state_file(statefd
);
601 (void) close(statefd
->fd
);
602 (void) unlink(filename
);
606 * Grow the state file by the chunk size specified in statefd
610 grow_state_file(state_file_descr_t
*statefd
)
616 max_elements
= statefd
->chunk_size
;
617 if (statefd
->state_file
)
618 max_elements
+= statefd
->state_file
->max_elements
;
620 size
= sizeof (state_file_t
) +
621 statefd
->element_size
* max_elements
;
623 if (ftruncate(statefd
->fd
, size
) != 0) {
625 rcm_log_message(RCM_ERROR
, MF_STATE_FILE_ERR
,
626 "ftruncate", strerror(error_num
));
627 rcmd_exit(error_num
);
632 statefd
->state_file
= (state_file_t
*)mmap(NULL
, size
,
633 PROT_READ
|PROT_WRITE
, MAP_SHARED
, statefd
->fd
, 0);
635 if (statefd
->state_file
== MAP_FAILED
) {
637 rcm_log_message(RCM_ERROR
, MF_STATE_FILE_ERR
,
638 "mmap", strerror(error_num
));
639 rcmd_exit(error_num
);
643 statefd
->index
= statefd
->state_file
->max_elements
;
644 statefd
->state_file
->max_elements
= max_elements
;
645 statefd
->state_file
->version
= statefd
->version
;
649 * Given index into state element array, get the pointer to the actual
651 * If flag is non-null set *flag to
652 * TRUE if the state element is currently is use.
653 * FALSE if the state element is free.
656 get_state_element(state_file_descr_t
*statefd
, int index
, int *flag
)
660 if (statefd
->state_file
&&
661 (index
< statefd
->state_file
->max_elements
)) {
663 ptr
= (char *)(statefd
->state_file
);
664 ptr
+= sizeof (state_file_t
) +
665 index
* statefd
->element_size
;
668 *flag
= (((state_element_t
*)((void *)ptr
))->flags
&
669 STATE_ELEMENT_IN_USE
) ? 1 : 0;
672 ptr
+= sizeof (state_element_t
);
676 return ((void *)ptr
);
680 * Allocate a state element entry in the state file and return a pointer
681 * to the allocated entry.
682 * If index is non-null set *index to index into the state element array
683 * of the allocated entry.
686 allocate_state_element(state_file_descr_t
*statefd
, int *index
)
692 if (statefd
->state_file
) {
693 /* find an empty slot */
694 for (i
= 0; i
< statefd
->state_file
->max_elements
; i
++) {
695 x
= get_state_element(statefd
, statefd
->index
,
704 if (statefd
->index
>= statefd
->state_file
->max_elements
)
709 if (statefd
->state_file
== NULL
||
710 i
== statefd
->state_file
->max_elements
) {
712 /* All entries are in use. Grow the list */
713 grow_state_file(statefd
);
714 x
= get_state_element(statefd
, statefd
->index
, &flag
);
719 *index
= statefd
->index
;
722 if (statefd
->index
>= statefd
->state_file
->max_elements
)
725 ((state_element_t
*)x
- 1)->flags
|= STATE_ELEMENT_IN_USE
;
730 free_state_element(void *x
)
732 ((state_element_t
*)x
- 1)->flags
&= ~STATE_ELEMENT_IN_USE
;
736 * Kill the pids contained in ps state file.
739 script_ps_state_file_kill_pids(void)
741 ps_state_element_t
*x
;
747 for (i
= 0; 1; i
++) {
748 if ((x
= (ps_state_element_t
*)get_state_element(
749 &script_ps_statefd
, i
, &flag
)) == NULL
)
752 if (flag
== 1) { /* the entry is in use */
753 (void) snprintf(procfile
, 80, "/proc/%ld/psinfo",
755 if ((fd
= open(procfile
, O_RDONLY
)) != -1 &&
756 read(fd
, &psi
, sizeof (psi
)) == sizeof (psi
) &&
758 x
->script_name
) == 0) {
763 * just a safety check to not to blow up
764 * system processes if the file is ever corrupt
767 rcm_log_message(RCM_DEBUG
,
768 "script_ps_state_file_kill_pids: "
769 "killing script_name = %s pid = %ld\n",
770 x
->script_name
, x
->pid
);
772 /* kill the process group */
773 (void) kill(-(x
->pid
), SIGKILL
);
779 free_state_element((void *)x
);
785 * Add a state element entry to ps state file.
788 script_ps_state_file_add_entry(pid_t pid
, char *script_name
)
790 ps_state_element_t
*x
;
792 (void) mutex_lock(&script_lock
);
794 x
= (ps_state_element_t
*)allocate_state_element(
795 &script_ps_statefd
, NULL
);
798 (void) strlcpy(x
->script_name
, script_name
, MAXNAMELEN
);
800 (void) fsync(script_ps_statefd
.fd
);
802 (void) mutex_unlock(&script_lock
);
806 * Remove the state element entry corresponding to pid from the
810 script_ps_state_file_remove_entry(pid_t pid
)
812 ps_state_element_t
*x
;
815 (void) mutex_lock(&script_lock
);
818 for (i
= 0; 1; i
++) {
819 if ((x
= (ps_state_element_t
*)get_state_element(
820 &script_ps_statefd
, i
, &flag
)) == NULL
)
823 /* if the state element entry is in use and pid matches */
824 if (flag
== 1 && x
->pid
== pid
) {
825 free_state_element((void *)x
);
830 (void) mutex_unlock(&script_lock
);
834 * Get data item id given data item name
837 dname_to_id(char *dname
)
841 for (i
= 0; script_data_item_name
[i
] != NULL
; i
++) {
842 if (strcmp(dname
, script_data_item_name
[i
]) == 0)
850 * Called before running any script.
851 * This routine waits until the number of script processes running in
852 * parallel drops down below to script_max_parallelism.
855 script_process_sema_wait(void)
861 if (sema_wait(&script_process_sema
) == 0)
864 if (errno
!= EINTR
&& errno
!= EAGAIN
) {
866 rcm_log_message(RCM_ERROR
, MF_FUNC_CALL_ERR
,
867 "sema_wait", strerror(error_num
));
868 rcmd_exit(error_num
);
877 * Fork and execute the script.
880 run_script(script_info_t
*rsi
, char *argv
[], char *envp
[], char **errmsg
)
882 int i
, p1
= -1, p2
= -1;
886 rcm_log_message(RSCR_TRACE
, "run_script: script name = %s\n",
887 rsi
->script_full_name
);
889 for (i
= 0; argv
[i
] != NULL
; i
++)
890 rcm_log_message(RSCR_TRACE
, "run_script: argv[%d] = %s\n",
895 /* check that the script exists */
896 if (stat(rsi
->script_full_name
, &stats
) != 0)
900 * If the syscall pipe fails because of reaching the max open file
901 * count per process then dynamically increase the limit on the max
904 * At present the rcm_daemon consumes file descriptor
905 * entries for the following files.
906 * RCM_STATE_FILE - /var/run/rcm_daemon_state
907 * DAEMON_LOCK_FILE - /var/run/rcm_daemon_lock
908 * RCM_SERVICE_DOOR - /var/run/rcm_daemon_door
909 * proc files in the format "/proc/pid/as" for each pid
910 * communicating with the rcm_daemon via doors
911 * dlopen for each rcm module
912 * When in daemon mode stdin, stdout and stderr are closed;
913 * /dev/null opened and duped to stdout, and stderr
915 * Some files which are opened briefly and closed such as
917 * Two file descriptors for each script in running state.
918 * Note that the constant script_max_parallelism sets an
919 * upper cap on how many rcm scripts can run in
922 if ((p1
= pipe(rsi
->pipe1
)) == -1 || (p2
= pipe(rsi
->pipe2
)) == -1) {
923 if ((errno
== EMFILE
) &&
924 (getrlimit(RLIMIT_NOFILE
, &rlp
) == 0)) {
927 if (rlp
.rlim_max
< rlp
.rlim_cur
)
928 rlp
.rlim_max
= rlp
.rlim_cur
;
929 (void) setrlimit(RLIMIT_NOFILE
, &rlp
);
932 if ((p1
= pipe(rsi
->pipe1
)) == -1)
935 if ((p2
= pipe(rsi
->pipe2
)) == -1)
942 if ((rsi
->pid
= fork1()) == (pid_t
)-1) {
943 if (errno
== EINTR
|| errno
== EAGAIN
)
954 /* close stdin, stdout and stderr */
959 /* set stdin to /dev/null */
960 (void) open("/dev/null", O_RDWR
, 0);
962 /* redirect stdout and stderr to pipe */
963 (void) dup2(rsi
->pipe1
[CHILD_END_OF_PIPE
], 1);
964 (void) dup2(rsi
->pipe2
[CHILD_END_OF_PIPE
], 2);
966 /* close all other file descriptors */
969 /* restore original file limit */
970 (void) setrlimit(RLIMIT_NOFILE
, &file_limit
);
972 /* set current working dir */
973 if (stats
.st_uid
== 0) {
975 if (chdir("/var/run") == -1)
978 if (chdir("/tmp") == -1)
983 * setuid sets real, effective and saved user ids to the
985 * setgid sets real, effective and saved group ids to the
988 (void) setgid(stats
.st_gid
);
989 (void) setuid(stats
.st_uid
);
991 (void) execve(rsi
->script_full_name
, argv
, envp
);
996 (void) close(rsi
->pipe1
[CHILD_END_OF_PIPE
]);
997 (void) close(rsi
->pipe2
[CHILD_END_OF_PIPE
]);
999 script_ps_state_file_add_entry(rsi
->pid
, rsi
->script_name
);
1004 *errmsg
= dup_err(RCM_ERROR
, MS_SCRIPT_ERR
,
1005 rsi
->script_name
, strerror(errno
));
1008 (void) close(rsi
->pipe1
[PARENT_END_OF_PIPE
]);
1009 (void) close(rsi
->pipe1
[CHILD_END_OF_PIPE
]);
1013 (void) close(rsi
->pipe2
[PARENT_END_OF_PIPE
]);
1014 (void) close(rsi
->pipe2
[CHILD_END_OF_PIPE
]);
1021 * Reads one line of input (including the newline character) from the
1022 * given file descriptor "fd" to buf.
1023 * maxbuflen specifies the size of memory allocated for buf.
1024 * Timeoutval is the max timeout value in seconds for the script to supply
1025 * input. A timeoutval of 0 implies no timeout.
1027 * Upon return *buflen contains the number of bytes read.
1031 * -1 an error occured
1032 * -2 timeout occurred
1045 struct pollfd fds
[1];
1055 deadline
= time(NULL
) + timeoutval
;
1057 fds
[0].events
= POLLIN
;
1063 while (c
!= '\n' && len
< (maxbuflen
-1)) {
1067 timeoutval
= deadline
- time(NULL
);
1068 if (timeoutval
<= 0) {
1072 x
= poll(fds
, 1, timeoutval
*1000);
1078 if (errno
== EINTR
|| errno
== EAGAIN
)
1087 if ((x
= read(fd
, &c
, 1)) != 1) {
1090 * Script exited. Or more specifically the
1091 * script has closed its end of the pipe.
1095 if (errno
== EINTR
|| errno
== EAGAIN
)
1110 rcm_log_message(RSCR_TRACE
,
1111 "get_line(%s): rval = %d buflen = %d line = %s\n",
1112 fdname
, rval
, *buflen
, buf
);
1117 script_exited(script_info_t
*rsi
)
1119 if (rsi
->flags
& STDERR_THREAD_CREATED
) {
1120 rcm_log_message(RSCR_TRACE
,
1121 "script_exited: doing thr_join (%s)\n", rsi
->script_name
);
1122 (void) thr_join(rsi
->tid
, NULL
, NULL
);
1123 rsi
->flags
&= ~STDERR_THREAD_CREATED
;
1126 (void) close(rsi
->pipe1
[PARENT_END_OF_PIPE
]);
1127 (void) close(rsi
->pipe2
[PARENT_END_OF_PIPE
]);
1128 rsi
->pipe1
[PARENT_END_OF_PIPE
] = -1;
1129 rsi
->pipe2
[PARENT_END_OF_PIPE
] = -1;
1131 script_ps_state_file_remove_entry(rsi
->pid
);
1133 (void) sema_post(&script_process_sema
);
1137 * Kill the specified process group
1142 time_t deadline
, timeleft
;
1145 /* kill the entire process group */
1146 (void) kill(-(pid
), SIGKILL
);
1148 /* give some time for the script to be killed */
1149 deadline
= time(NULL
) + SCRIPT_KILL_TIMEOUT
;
1151 if (waitpid(pid
, &child_status
, WNOHANG
) == pid
)
1154 /* wait for 100 ms */
1155 (void) poll(NULL
, 0, 100);
1157 timeleft
= deadline
- time(NULL
);
1158 } while (timeleft
> 0);
1160 /* script process was not killed successfully */
1165 * Kill the specified script.
1168 kill_script(script_info_t
*rsi
)
1171 (void) kill_pid(rsi
->pid
);
1173 remove_drreq_all(rsi
);
1178 * Convert rcm flags parameter to a string.
1179 * Used for debug prints.
1182 flags_to_name(int flags
, char *buf
, int maxbuflen
)
1184 (void) snprintf(buf
, maxbuflen
, "%s%s",
1185 (flags
& RCM_QUERY
) ? "RCM_QUERY " : "",
1186 (flags
& RCM_FORCE
) ? "RCM_FORCE" : "");
1192 fill_argv(script_info_t
*rsi
, char *argv
[], char *resource_name
)
1194 argv
[0] = rsi
->script_full_name
;
1195 argv
[1] = script_cmd_name
[rsi
->cmd
];
1196 if (resource_name
) {
1197 argv
[2] = resource_name
;
1205 * Reads stderr and logs to syslog.
1206 * Runs as a separate thread.
1209 read_stderr(script_info_t
*rsi
)
1211 char buf
[MAX_LINE_LEN
];
1215 while ((get_line(rsi
->pipe2
[PARENT_END_OF_PIPE
], "stderr",
1216 buf
, MAX_LINE_LEN
, &buflen
, 0, &error_num
)) == 0) {
1217 log_msg(rsi
, RCM_ERROR
, buf
);
1221 log_msg(rsi
, RCM_ERROR
, buf
);
1226 /* process return data items passed by scripts to the framework */
1228 process_dataitem(script_info_t
*rsi
, int token
, char *value
, char **errmsg
)
1239 case D_SCRIPT_VERSION
:
1240 if (rsi
->cmd
!= C_SCRIPTINFO
)
1243 /* check that value contains only digits */
1244 for (ptr
= value
; *ptr
!= '\0'; ptr
++)
1245 if (isdigit((int)(*ptr
)) == 0)
1249 rsi
->ver
= atoi(value
);
1255 case D_SCRIPT_FUNC_INFO
:
1256 if (rsi
->cmd
!= C_SCRIPTINFO
)
1259 rcmscript_snprintf(&rsi
->func_info_buf
,
1260 &rsi
->func_info_buf_len
,
1261 &rsi
->func_info_buf_curptr
,
1266 if (rsi
->cmd
!= C_SCRIPTINFO
)
1269 /* check that value contains only digits */
1270 for (ptr
= value
; *ptr
!= '\0'; ptr
++)
1271 if (isdigit((int)(*ptr
)) == 0)
1275 rsi
->cmd_timeout
= atoi(value
);
1280 case D_RESOURCE_NAME
:
1281 if (rsi
->cmd
!= C_REGISTER
)
1284 if (get_capacity_descr(value
) != NULL
)
1285 status
= rcm_register_capacity(rsi
->hdl
, value
,
1288 status
= rcm_register_interest(rsi
->hdl
, value
, 0,
1291 if (status
== RCM_FAILURE
&& errno
== EALREADY
)
1292 status
= RCM_SUCCESS
;
1294 if (status
!= RCM_SUCCESS
) {
1295 rcm_log_message(RCM_ERROR
, MS_REGISTER_RSRC_ERR
,
1296 rsi
->script_name
, value
);
1299 remove_from_unregister(rsi
, value
);
1302 case D_RESOURCE_USAGE_INFO
:
1303 if (rsi
->cmd
!= C_RESOURCEINFO
)
1306 rcmscript_snprintf(&rsi
->resource_usage_info_buf
,
1307 &rsi
->resource_usage_info_buf_len
,
1308 &rsi
->resource_usage_info_buf_curptr
,
1312 case D_FAILURE_REASON
:
1313 rcmscript_snprintf(&rsi
->failure_reason_buf
,
1314 &rsi
->failure_reason_buf_len
,
1315 &rsi
->failure_reason_buf_curptr
,
1326 *errmsg
= dup_err(RCM_ERROR
, MS_PROTOCOL_ERR
, rsi
->script_name
);
1330 /* Send the given command to the script and process return data */
1332 do_cmd(script_info_t
*rsi
, char *argv
[], char *envp
[], char **errmsg
)
1334 char buf
[MAX_LINE_LEN
];
1336 int loglevel
= -1, continuelog
= 0;
1337 char *ptr
, *dname
, *value
;
1341 int rval
, child_status
, token
;
1343 int cmd_timeout
= rsi
->cmd_timeout
;
1347 script_process_sema_wait();
1349 if (run_script(rsi
, argv
, envp
, errmsg
) == -1) {
1350 (void) sema_post(&script_process_sema
);
1354 (void) time(&rsi
->lastrun
);
1355 deadline
= rsi
->lastrun
+ cmd_timeout
;
1357 if (thr_create(NULL
, 0, (void *(*)(void *))read_stderr
, rsi
,
1358 0, &rsi
->tid
) != 0) {
1359 *errmsg
= dup_err(RCM_ERROR
, MF_FUNC_CALL_ERR
,
1360 "thr_create", strerror(errno
));
1363 rsi
->flags
|= STDERR_THREAD_CREATED
;
1367 if (cmd_timeout
> 0) {
1368 maxsecs
= deadline
- time(NULL
);
1374 rval
= get_line(rsi
->pipe1
[PARENT_END_OF_PIPE
],
1375 "stdout", buf
, MAX_LINE_LEN
, &buflen
,
1376 maxsecs
, &error_num
);
1380 log_msg(rsi
, loglevel
, buf
);
1382 if ((ptr
= strchr(buf
, '=')) == NULL
)
1388 if ((token
= dname_to_id(dname
)) == -1)
1393 loglevel
= RCM_ERROR
;
1397 loglevel
= RCM_WARNING
;
1401 loglevel
= RCM_INFO
;
1405 loglevel
= RCM_DEBUG
;
1413 if (loglevel
!= -1) {
1414 log_msg(rsi
, loglevel
, value
);
1415 if (buf
[buflen
- 1] == '\n')
1420 if (buf
[buflen
- 1] != '\n')
1423 buf
[buflen
- 1] = '\0';
1424 if (process_dataitem(rsi
, token
,
1425 value
, errmsg
) != 0)
1434 if (waitpid(rsi
->pid
, &child_status
, 0)
1436 if (errno
== EINTR
|| errno
== EAGAIN
)
1438 *errmsg
= dup_err(RCM_ERROR
, MS_SCRIPT_ERR
,
1439 rsi
->script_name
, strerror(errno
));
1443 if (WIFEXITED(child_status
)) {
1445 rsi
->exit_status
= WEXITSTATUS(child_status
);
1448 *errmsg
= dup_err(RCM_ERROR
,
1449 MS_TIMEOUT_ERR
, rsi
->script_name
);
1451 *errmsg
= dup_err(RCM_ERROR
,
1452 MS_UNKNOWN_ERR
, rsi
->script_name
);
1454 /* kill any remaining processes in the pgrp */
1455 (void) kill(-(rsi
->pid
), SIGKILL
);
1464 *errmsg
= dup_err(RCM_ERROR
, MS_SCRIPT_ERR
,
1465 rsi
->script_name
, strerror(errno
));
1471 /* timeout occurred */
1472 if (sigaborted
== 0) {
1473 (void) kill(rsi
->pid
, SIGABRT
);
1475 /* extend deadline */
1476 deadline
+= SCRIPT_ABORT_TIMEOUT
;
1478 *errmsg
= dup_err(RCM_ERROR
,
1479 MS_TIMEOUT_ERR
, rsi
->script_name
);
1488 *errmsg
= dup_err(RCM_ERROR
, MS_PROTOCOL_ERR
, rsi
->script_name
);
1498 do_script_info(script_info_t
*rsi
)
1500 char *argv
[MAX_ARGS
];
1501 int status
= RCM_FAILURE
;
1503 char *errmsg
= NULL
;
1505 rcm_log_message(RSCR_TRACE
, "do_script_info: script name = %s\n",
1508 rsi
->cmd
= C_SCRIPTINFO
;
1509 rsi
->func_info_buf
= NULL
;
1510 rsi
->failure_reason_buf
= NULL
;
1511 fill_argv(rsi
, argv
, NULL
);
1513 if (do_cmd(rsi
, argv
, script_env
, &errmsg
) == 0) {
1514 switch (rsi
->exit_status
) {
1516 if (rsi
->func_info_buf
!= NULL
&&
1517 rsi
->failure_reason_buf
== NULL
) {
1519 if (rsi
->ver
>= SCRIPT_API_MIN_VER
&&
1520 rsi
->ver
<= SCRIPT_API_MAX_VER
)
1521 status
= RCM_SUCCESS
;
1523 rcm_log_message(RCM_ERROR
,
1524 MS_UNSUPPORTED_VER
, rsi
->script_name
,
1531 if (rsi
->failure_reason_buf
!= NULL
) {
1532 rcm_log_message(RCM_ERROR
, MS_SCRIPTINFO_ERR
,
1534 rsi
->failure_reason_buf
);
1544 rcm_log_message(RCM_ERROR
, MS_PROTOCOL_ERR
,
1547 (void) free(errmsg
);
1549 if (status
!= RCM_SUCCESS
&& rsi
->func_info_buf
!= NULL
)
1550 free(rsi
->func_info_buf
);
1552 if (rsi
->failure_reason_buf
)
1553 free(rsi
->failure_reason_buf
);
1559 do_dr(script_info_t
*rsi
, char *argv
[], char *envp
[], char **info
)
1561 int status
= RCM_FAILURE
;
1564 rsi
->failure_reason_buf
= NULL
;
1566 if (do_cmd(rsi
, argv
, envp
, info
) == 0) {
1567 switch (rsi
->exit_status
) {
1569 case E_UNSUPPORTED_CMD
:
1570 if (rsi
->failure_reason_buf
== NULL
)
1571 status
= RCM_SUCCESS
;
1578 if (rsi
->failure_reason_buf
!= NULL
) {
1579 *info
= rsi
->failure_reason_buf
;
1580 rsi
->failure_reason_buf
= NULL
;
1591 *info
= dup_err(RCM_ERROR
, MS_PROTOCOL_ERR
,
1595 if (rsi
->failure_reason_buf
)
1596 free(rsi
->failure_reason_buf
);
1602 * get_info entry point
1606 script_get_info(rcm_handle_t
*hdl
,
1607 char *resource_name
,
1613 rcm_info_t
**dependent_info
)
1615 script_info_t
*rsi
= hdl
->module
->rsi
;
1616 char *argv
[MAX_ARGS
];
1617 int status
= RCM_FAILURE
;
1620 rcm_log_message(RSCR_TRACE
, "script_get_info: resource = %s\n",
1626 (void) mutex_lock(&rsi
->channel_lock
);
1629 rsi
->cmd
= C_RESOURCEINFO
;
1630 rsi
->resource_usage_info_buf
= NULL
;
1631 rsi
->failure_reason_buf
= NULL
;
1632 fill_argv(rsi
, argv
, resource_name
);
1634 if (do_cmd(rsi
, argv
, script_env
, error
) == 0) {
1635 switch (rsi
->exit_status
) {
1637 if (rsi
->resource_usage_info_buf
!= NULL
&&
1638 rsi
->failure_reason_buf
== NULL
) {
1640 *info
= rsi
->resource_usage_info_buf
;
1641 rsi
->resource_usage_info_buf
= NULL
;
1642 status
= RCM_SUCCESS
;
1648 if (rsi
->failure_reason_buf
!= NULL
) {
1649 *error
= rsi
->failure_reason_buf
;
1650 rsi
->failure_reason_buf
= NULL
;
1660 *error
= dup_err(RCM_ERROR
, MS_PROTOCOL_ERR
,
1664 if (rsi
->resource_usage_info_buf
)
1665 free(rsi
->resource_usage_info_buf
);
1667 if (rsi
->failure_reason_buf
)
1668 free(rsi
->failure_reason_buf
);
1670 (void) mutex_unlock(&rsi
->channel_lock
);
1676 add_for_unregister(script_info_t
*rsi
)
1678 module_t
*module
= rsi
->module
;
1683 (void) mutex_lock(&rcm_req_lock
);
1685 head
= &module
->client_q
;
1687 for (q
= head
->next
; q
!= head
; q
= q
->next
) {
1688 client
= RCM_STRUCT_BASE_ADDR(client_t
, q
, queue
);
1689 client
->prv_flags
|= RCM_NEED_TO_UNREGISTER
;
1692 (void) mutex_unlock(&rcm_req_lock
);
1696 remove_from_unregister(script_info_t
*rsi
, char *resource_name
)
1698 module_t
*module
= rsi
->module
;
1703 (void) mutex_lock(&rcm_req_lock
);
1705 head
= &module
->client_q
;
1707 for (q
= head
->next
; q
!= head
; q
= q
->next
) {
1708 client
= RCM_STRUCT_BASE_ADDR(client_t
, q
, queue
);
1709 if (strcmp(client
->alias
, resource_name
) == 0) {
1710 client
->prv_flags
&= ~RCM_NEED_TO_UNREGISTER
;
1715 (void) mutex_unlock(&rcm_req_lock
);
1719 complete_unregister(script_info_t
*rsi
)
1721 module_t
*module
= rsi
->module
;
1726 (void) mutex_lock(&rcm_req_lock
);
1728 head
= &module
->client_q
;
1730 for (q
= head
->next
; q
!= head
; q
= q
->next
) {
1731 client
= RCM_STRUCT_BASE_ADDR(client_t
, q
, queue
);
1732 if (client
->prv_flags
& RCM_NEED_TO_UNREGISTER
) {
1733 client
->prv_flags
&= ~RCM_NEED_TO_UNREGISTER
;
1734 client
->state
= RCM_STATE_REMOVE
;
1738 (void) mutex_unlock(&rcm_req_lock
);
1742 * register_interest entry point
1745 script_register_interest(rcm_handle_t
*hdl
)
1747 script_info_t
*rsi
= hdl
->module
->rsi
;
1748 char *argv
[MAX_ARGS
];
1749 int status
= RCM_FAILURE
;
1751 char *errmsg
= NULL
;
1753 rcm_log_message(RSCR_TRACE
,
1754 "script_register_interest: script name = %s\n",
1757 (void) mutex_lock(&rsi
->channel_lock
);
1759 if (rsi
->drreq_q
.next
!= &rsi
->drreq_q
) {
1760 /* if DR is already in progress no need to register again */
1761 (void) mutex_unlock(&rsi
->channel_lock
);
1762 return (RCM_SUCCESS
);
1766 rsi
->cmd
= C_REGISTER
;
1767 rsi
->failure_reason_buf
= NULL
;
1768 fill_argv(rsi
, argv
, NULL
);
1770 add_for_unregister(rsi
);
1772 if (do_cmd(rsi
, argv
, script_env
, &errmsg
) == 0) {
1773 switch (rsi
->exit_status
) {
1775 status
= RCM_SUCCESS
;
1779 if (rsi
->failure_reason_buf
!= NULL
) {
1780 rcm_log_message(RCM_ERROR
, MS_REGISTER_ERR
,
1782 rsi
->failure_reason_buf
);
1792 rcm_log_message(RCM_ERROR
, MS_PROTOCOL_ERR
,
1795 (void) free(errmsg
);
1797 complete_unregister(rsi
);
1799 if (rsi
->failure_reason_buf
)
1800 free(rsi
->failure_reason_buf
);
1802 (void) mutex_unlock(&rsi
->channel_lock
);
1808 * Add the specified resource name to the drreq_q.
1811 add_drreq(script_info_t
*rsi
, char *resource_name
)
1813 rcm_queue_t
*head
= &rsi
->drreq_q
;
1817 /* check if the dr req is already in the list */
1818 for (q
= head
->next
; q
!= head
; q
= q
->next
) {
1819 drreq
= RCM_STRUCT_BASE_ADDR(drreq_t
, q
, queue
);
1820 if (strcmp(drreq
->resource_name
, resource_name
) == 0)
1821 /* dr req is already present in the queue */
1825 drreq
= (drreq_t
*)rcmscript_calloc(1, sizeof (drreq_t
));
1826 drreq
->resource_name
= rcmscript_strdup(resource_name
);
1828 rcm_enqueue_tail(&rsi
->drreq_q
, &drreq
->queue
);
1832 * Remove the dr req for the specified resource name from the drreq_q.
1835 remove_drreq(script_info_t
*rsi
, char *resource_name
)
1837 rcm_queue_t
*head
= &rsi
->drreq_q
;
1841 /* search for dr req and remove from the list */
1842 for (q
= head
->next
; q
!= head
; q
= q
->next
) {
1843 drreq
= RCM_STRUCT_BASE_ADDR(drreq_t
, q
, queue
);
1844 if (strcmp(drreq
->resource_name
, resource_name
) == 0)
1849 /* found drreq on the queue */
1850 rcm_dequeue(&drreq
->queue
);
1851 free(drreq
->resource_name
);
1857 * Remove all dr req's.
1860 remove_drreq_all(script_info_t
*rsi
)
1864 while (rsi
->drreq_q
.next
!= &rsi
->drreq_q
) {
1865 drreq
= RCM_STRUCT_BASE_ADDR(drreq_t
,
1866 rsi
->drreq_q
.next
, queue
);
1867 remove_drreq(rsi
, drreq
->resource_name
);
1872 * request_offline entry point
1876 script_request_offline(rcm_handle_t
*hdl
,
1877 char *resource_name
,
1881 rcm_info_t
**dependent_info
)
1883 script_info_t
*rsi
= hdl
->module
->rsi
;
1884 char *argv
[MAX_ARGS
];
1885 char *envp
[MAX_ENV_PARAMS
];
1886 char flags_name
[MAX_FLAGS_NAME_LEN
];
1890 rcm_log_message(RSCR_TRACE
,
1891 "script_request_offline: resource = %s flags = %s\n",
1893 flags_to_name(flag
, flags_name
, MAX_FLAGS_NAME_LEN
));
1897 (void) mutex_lock(&rsi
->channel_lock
);
1900 rsi
->cmd
= (flag
& RCM_QUERY
) ? C_QUERYREMOVE
: C_PREREMOVE
;
1902 if (rsi
->cmd
== C_PREREMOVE
)
1903 add_drreq(rsi
, resource_name
);
1905 fill_argv(rsi
, argv
, resource_name
);
1906 copy_env(script_env
, envp
);
1907 for (i
= 0; envp
[i
] != NULL
; i
++)
1909 envp
[i
++] = (flag
& RCM_FORCE
) ? script_env_force
: script_env_noforce
;
1912 status
= do_dr(rsi
, argv
, envp
, info
);
1914 (void) mutex_unlock(&rsi
->channel_lock
);
1919 * notify_online entry point
1923 script_notify_online(rcm_handle_t
*hdl
,
1924 char *resource_name
,
1928 rcm_info_t
**dependent_info
)
1930 script_info_t
*rsi
= hdl
->module
->rsi
;
1931 char *argv
[MAX_ARGS
];
1934 rcm_log_message(RSCR_TRACE
, "script_notify_online: resource = %s\n",
1939 (void) mutex_lock(&rsi
->channel_lock
);
1942 rsi
->cmd
= C_UNDOREMOVE
;
1943 fill_argv(rsi
, argv
, resource_name
);
1945 status
= do_dr(rsi
, argv
, script_env
, info
);
1947 remove_drreq(rsi
, resource_name
);
1949 (void) mutex_unlock(&rsi
->channel_lock
);
1954 * notify_remove entry point
1958 script_notify_remove(rcm_handle_t
*hdl
,
1959 char *resource_name
,
1963 rcm_info_t
**dependent_info
)
1965 script_info_t
*rsi
= hdl
->module
->rsi
;
1966 char *argv
[MAX_ARGS
];
1969 rcm_log_message(RSCR_TRACE
, "script_notify_remove: resource = %s\n",
1974 (void) mutex_lock(&rsi
->channel_lock
);
1977 rsi
->cmd
= C_POSTREMOVE
;
1978 fill_argv(rsi
, argv
, resource_name
);
1980 status
= do_dr(rsi
, argv
, script_env
, info
);
1982 remove_drreq(rsi
, resource_name
);
1984 (void) mutex_unlock(&rsi
->channel_lock
);
1989 * request_suspend entry point
1993 script_request_suspend(rcm_handle_t
*hdl
,
1994 char *resource_name
,
1996 timespec_t
*interval
,
1999 rcm_info_t
**dependent_info
)
2001 script_info_t
*rsi
= hdl
->module
->rsi
;
2003 char *curptr
= NULL
;
2004 char *argv
[MAX_ARGS
];
2005 char *envp
[MAX_ENV_PARAMS
];
2006 char flags_name
[MAX_FLAGS_NAME_LEN
];
2012 rcm_log_message(RSCR_TRACE
,
2013 "script_request_suspend: resource = %s flags = %s\n", resource_name
,
2014 flags_to_name(flag
, flags_name
, MAX_FLAGS_NAME_LEN
));
2018 (void) mutex_lock(&rsi
->channel_lock
);
2021 rsi
->cmd
= (flag
& RCM_QUERY
) ? C_QUERYSUSPEND
: C_PRESUSPEND
;
2023 if (rsi
->cmd
== C_PRESUSPEND
)
2024 add_drreq(rsi
, resource_name
);
2026 fill_argv(rsi
, argv
, resource_name
);
2028 copy_env(script_env
, envp
);
2029 for (i
= 0; envp
[i
] != NULL
; i
++);
2031 envp
[i
++] = (flag
& RCM_FORCE
) ? script_env_force
: script_env_noforce
;
2035 * Merge the seconds and nanoseconds, rounding up if there
2036 * are any remainder nanoseconds.
2038 seconds
= interval
->tv_sec
+ (interval
->tv_nsec
/ 1000000000L);
2039 if (interval
->tv_nsec
% 1000000000L)
2040 seconds
+= (interval
->tv_sec
> 0) ? 1L : -1L;
2041 rcmscript_snprintf(&buf
, &buflen
, &curptr
, script_env_interval
,
2048 status
= do_dr(rsi
, argv
, envp
, info
);
2050 (void) mutex_unlock(&rsi
->channel_lock
);
2057 * notify_resume entry point
2061 script_notify_resume(rcm_handle_t
*hdl
,
2062 char *resource_name
,
2066 rcm_info_t
**dependent_info
)
2068 script_info_t
*rsi
= hdl
->module
->rsi
;
2069 char *argv
[MAX_ARGS
];
2072 rcm_log_message(RSCR_TRACE
, "script_notify_resume: resource = %s\n",
2077 (void) mutex_lock(&rsi
->channel_lock
);
2080 rsi
->cmd
= (flag
& RCM_SUSPENDED
) ? C_POSTRESUME
: C_CANCELSUSPEND
;
2081 fill_argv(rsi
, argv
, resource_name
);
2083 status
= do_dr(rsi
, argv
, script_env
, info
);
2085 remove_drreq(rsi
, resource_name
);
2087 (void) mutex_unlock(&rsi
->channel_lock
);
2091 static capacity_descr_t capacity_type
[] = {
2092 { "SUNW_memory", MATCH_EXACT
,
2093 "new_pages", "RCM_ENV_CAPACITY",
2094 "page_size", "RCM_ENV_UNIT_SIZE",
2096 { "SUNW_cpu", MATCH_EXACT
,
2097 "new_total", "RCM_ENV_CAPACITY",
2098 "new_cpu_list", "RCM_ENV_CPU_IDS",
2100 { "SUNW_cpu/set", MATCH_PREFIX
,
2101 "new_total", "RCM_ENV_CAPACITY",
2102 "new_cpu_list", "RCM_ENV_CPU_IDS",
2104 { "", MATCH_INVALID
, "", "" }
2107 static capacity_descr_t
*
2108 get_capacity_descr(char *resource_name
)
2112 for (i
= 0; *capacity_type
[i
].resource_name
!= '\0'; i
++) {
2113 if ((capacity_type
[i
].match_type
== MATCH_EXACT
&&
2114 strcmp(capacity_type
[i
].resource_name
,
2115 resource_name
) == 0) ||
2116 (capacity_type
[i
].match_type
== MATCH_PREFIX
&&
2117 strncmp(capacity_type
[i
].resource_name
,
2119 strlen(capacity_type
[i
].resource_name
)) == 0))
2121 return (&capacity_type
[i
]);
2128 build_env_for_capacity(script_info_t
*rsi
,
2129 char *resource_name
,
2131 nvlist_t
*capacity_info
,
2133 int *dynamic_env_index
,
2137 capacity_descr_t
*capa
= NULL
;
2145 copy_env(script_env
, envp
);
2146 for (p
= 0; envp
[p
] != NULL
; p
++)
2149 if (rsi
->cmd
== C_QUERYCAPACITY
|| rsi
->cmd
== C_PRECAPACITY
)
2150 envp
[p
++] = (flag
& RCM_FORCE
) ? script_env_force
:
2154 *dynamic_env_index
= p
;
2156 if ((capa
= get_capacity_descr(resource_name
)) == NULL
) {
2157 *errmsg
= dup_err(RCM_ERROR
, MF_UNKNOWN_RSRC_ERR
,
2158 resource_name
, rsi
->script_name
);
2162 for (i
= 0; *capa
->param
[i
].nvname
!= '\0'; i
++) {
2164 while ((nvpair
= nvlist_next_nvpair(capacity_info
, nvpair
))
2166 if (strcmp(nvpair_name(nvpair
),
2167 capa
->param
[i
].nvname
) == 0)
2171 if (nvpair
== NULL
) {
2172 *errmsg
= dup_err(RCM_ERROR
, MF_NV_ERR
,
2180 rcmscript_snprintf(&buf
, &buflen
, &curptr
, "%s=",
2181 capa
->param
[i
].envname
);
2183 switch (nvpair_type(nvpair
)) {
2184 case DATA_TYPE_INT16
:
2188 if (nvpair_value_int16(nvpair
, &x
) == 0) {
2189 rcmscript_snprintf(&buf
, &buflen
, &curptr
,
2196 case DATA_TYPE_UINT16
:
2200 if (nvpair_value_uint16(nvpair
, &x
) == 0) {
2201 rcmscript_snprintf(&buf
, &buflen
, &curptr
,
2202 "%hu", (unsigned short)x
);
2208 case DATA_TYPE_INT32
:
2212 if (nvpair_value_int32(nvpair
, &x
) == 0) {
2213 rcmscript_snprintf(&buf
, &buflen
, &curptr
,
2220 case DATA_TYPE_UINT32
:
2224 if (nvpair_value_uint32(nvpair
, &x
) == 0) {
2225 rcmscript_snprintf(&buf
, &buflen
, &curptr
,
2232 case DATA_TYPE_INT64
:
2236 if (nvpair_value_int64(nvpair
, &x
) == 0) {
2237 rcmscript_snprintf(&buf
, &buflen
, &curptr
,
2238 "%lld", (long long)x
);
2244 case DATA_TYPE_UINT64
:
2248 if (nvpair_value_uint64(nvpair
, &x
) == 0) {
2249 rcmscript_snprintf(&buf
, &buflen
, &curptr
,
2250 "%llu", (unsigned long long)x
);
2256 case DATA_TYPE_INT16_ARRAY
:
2260 if (nvpair_value_int16_array(nvpair
, &x
, &n
) == 0) {
2262 rcmscript_snprintf(&buf
, &buflen
,
2265 (n
== 0) ? "" : " ");
2273 case DATA_TYPE_UINT16_ARRAY
:
2277 if (nvpair_value_uint16_array(nvpair
, &x
, &n
) == 0) {
2279 rcmscript_snprintf(&buf
, &buflen
,
2281 (unsigned short)(*x
),
2282 (n
== 0) ? "" : " ");
2290 case DATA_TYPE_INT32_ARRAY
:
2294 if (nvpair_value_int32_array(nvpair
, &x
, &n
) == 0) {
2296 rcmscript_snprintf(&buf
, &buflen
,
2299 (n
== 0) ? "" : " ");
2307 case DATA_TYPE_UINT32_ARRAY
:
2311 if (nvpair_value_uint32_array(nvpair
, &x
, &n
) == 0) {
2313 rcmscript_snprintf(&buf
, &buflen
,
2316 (n
== 0) ? "" : " ");
2324 case DATA_TYPE_INT64_ARRAY
:
2328 if (nvpair_value_int64_array(nvpair
, &x
, &n
) == 0) {
2330 rcmscript_snprintf(&buf
, &buflen
,
2333 (n
== 0) ? "" : " ");
2341 case DATA_TYPE_UINT64_ARRAY
:
2345 if (nvpair_value_uint64_array(nvpair
, &x
, &n
) == 0) {
2347 rcmscript_snprintf(&buf
, &buflen
,
2349 (unsigned long long)(*x
),
2350 (n
== 0) ? "" : " ");
2358 case DATA_TYPE_STRING
:
2362 if (nvpair_value_string(nvpair
, &x
) == 0) {
2363 rcmscript_snprintf(&buf
, &buflen
, &curptr
,
2380 for (p
= *dynamic_env_index
; envp
[p
] != NULL
; p
++)
2382 *errmsg
= dup_err(RCM_ERROR
, MF_NV_ERR
,
2394 * request_capacity_change entry point
2398 script_request_capacity_change(rcm_handle_t
*hdl
,
2399 char *resource_name
,
2402 nvlist_t
*capacity_info
,
2404 rcm_info_t
**dependent_info
)
2406 script_info_t
*rsi
= hdl
->module
->rsi
;
2407 char *argv
[MAX_ARGS
];
2408 char *envp
[MAX_ENV_PARAMS
];
2409 char flags_name
[MAX_FLAGS_NAME_LEN
];
2411 int dynamic_env_index
;
2413 rcm_log_message(RSCR_TRACE
,
2414 "script_request_capacity_change: resource = %s flags = %s\n",
2416 flags_to_name(flag
, flags_name
, MAX_FLAGS_NAME_LEN
));
2420 (void) mutex_lock(&rsi
->channel_lock
);
2423 rsi
->cmd
= (flag
& RCM_QUERY
) ? C_QUERYCAPACITY
: C_PRECAPACITY
;
2424 fill_argv(rsi
, argv
, resource_name
);
2426 if (build_env_for_capacity(rsi
, resource_name
, flag
,
2427 capacity_info
, envp
, &dynamic_env_index
, info
) == 0) {
2429 status
= do_dr(rsi
, argv
, envp
, info
);
2431 while (envp
[dynamic_env_index
] != NULL
) {
2432 free(envp
[dynamic_env_index
]);
2433 dynamic_env_index
++;
2436 status
= RCM_FAILURE
;
2438 (void) mutex_unlock(&rsi
->channel_lock
);
2443 * notify_capacity_change entry point
2447 script_notify_capacity_change(rcm_handle_t
*hdl
,
2448 char *resource_name
,
2451 nvlist_t
*capacity_info
,
2453 rcm_info_t
**dependent_info
)
2455 script_info_t
*rsi
= hdl
->module
->rsi
;
2456 char *argv
[MAX_ARGS
];
2457 char *envp
[MAX_ENV_PARAMS
];
2459 int dynamic_env_index
;
2461 rcm_log_message(RSCR_TRACE
,
2462 "script_notify_capacity_change: resource = %s\n", resource_name
);
2466 (void) mutex_lock(&rsi
->channel_lock
);
2469 rsi
->cmd
= C_POSTCAPACITY
;
2470 fill_argv(rsi
, argv
, resource_name
);
2472 if (build_env_for_capacity(rsi
, resource_name
, flag
,
2473 capacity_info
, envp
, &dynamic_env_index
, info
) == 0) {
2475 status
= do_dr(rsi
, argv
, envp
, info
);
2477 while (envp
[dynamic_env_index
] != NULL
) {
2478 free(envp
[dynamic_env_index
]);
2479 dynamic_env_index
++;
2482 status
= RCM_FAILURE
;
2484 (void) mutex_unlock(&rsi
->channel_lock
);
2488 /* Log the message to syslog */
2490 log_msg(script_info_t
*rsi
, int level
, char *msg
)
2492 rcm_log_msg(level
, MS_LOG_MSG
, rsi
->script_name
, msg
);
2497 dup_err(int level
, char *format
, ...)
2504 va_start(ap
, format
);
2505 n
= vsnprintf(buf1
, 1, format
, ap
);
2510 if (buf2
= (char *)malloc(n
)) {
2511 va_start(ap
, format
);
2512 n
= vsnprintf(buf2
, n
, format
, ap
);
2516 rcm_log_message(level
, buf2
);
2528 rcmscript_snprintf(char **buf
, int *buflen
, char **curptr
, char *format
, ...)
2530 /* must be power of 2 otherwise RSCR_ROUNDUP would break */
2531 #define SPRINTF_CHUNK_LEN 512
2532 #define SPRINTF_MIN_CHUNK_LEN 64
2535 int offset
, bytesneeded
, bytesleft
, error_num
;
2542 offset
= *curptr
- *buf
;
2543 bytesneeded
= SPRINTF_MIN_CHUNK_LEN
;
2544 bytesleft
= *buflen
- offset
;
2548 if (bytesneeded
> bytesleft
) {
2549 *buflen
+= RSCR_ROUNDUP(bytesneeded
- bytesleft
,
2551 if ((*buf
= (char *)realloc(*buf
, *buflen
)) == NULL
) {
2553 rcm_log_message(RCM_ERROR
,
2554 MF_MEMORY_ALLOCATION_ERR
,
2555 strerror(error_num
));
2556 rcmd_exit(error_num
);
2559 *curptr
= *buf
+ offset
;
2560 bytesleft
= *buflen
- offset
;
2563 va_start(ap
, format
);
2564 bytesneeded
= vsnprintf(*curptr
, bytesleft
, format
, ap
);
2567 if (bytesneeded
< 0) {
2568 /* vsnprintf encountered an error */
2570 rcm_log_message(RCM_ERROR
, MF_FUNC_CALL_ERR
,
2571 "vsnprintf", strerror(error_num
));
2572 rcmd_exit(error_num
);
2575 } else if (bytesneeded
< bytesleft
) {
2576 /* vsnprintf succeeded */
2577 *curptr
+= bytesneeded
;
2581 bytesneeded
++; /* to account for storage for '\0' */
2587 rcmscript_strdup(char *str
)
2591 if ((dupstr
= strdup(str
)) == NULL
) {
2592 rcm_log_message(RCM_ERROR
, MF_MEMORY_ALLOCATION_ERR
,
2602 rcmscript_malloc(size_t len
)
2606 if ((ptr
= malloc(len
)) == NULL
) {
2607 rcm_log_message(RCM_ERROR
, MF_MEMORY_ALLOCATION_ERR
,
2617 rcmscript_calloc(size_t nelem
, size_t elsize
)
2621 if ((ptr
= calloc(nelem
, elsize
)) == NULL
) {
2622 rcm_log_message(RCM_ERROR
, MF_MEMORY_ALLOCATION_ERR
,