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.
28 * rcm scripting module:
30 * This module implements rcm scripting interfaces.
31 * It translates rcm module based interfaces to rcm script based
36 * int script_main_init()
37 * Initialize the rcm scripting framework.
38 * Called during the rcm daemon initialization
40 * int script_main_fini()
41 * Called at the time of the rcm daemon exit.
43 * struct rcm_mod_ops *script_init(module_t *module)
44 * Initialize the given script.
45 * module->name contains the name of the script.
46 * Called at the time of loading scripts.
47 * Semantics are similar to module init.
49 * char *script_info(module_t *module)
50 * Called when the rcm daemon wishes to get the script information.
51 * module->name contains the name of the script.
52 * Semantics are similar to module info.
54 * int script_fini(module_t *module)
55 * Called before removing the script.
56 * module->name contains the name of the script.
57 * Semantics are similar to module fini.
59 * In addition to the above entry points rcm_mod_ops structure contains
60 * the other entry points. A pointer to this structure is returned when
61 * script_init() is called.
65 #include "rcm_script_impl.h"
66 #include <sys/resource.h>
72 * All rcm scripting commands are enumerated here.
73 * NOTE: command positions in script_cmd_id_t and script_cmd_name must match.
92 /* NOTE: command positions in script_cmd_id_t and script_cmd_name must match */
93 static char *script_cmd_name
[] = {
112 * All rcm scripting data items are enumerated here.
113 * NOTE: data item positions in script_data_item_id_t and
114 * script_data_item_name must match.
121 D_RESOURCE_USAGE_INFO
,
127 } script_data_item_id_t
;
130 * NOTE: data item positions in script_data_item_id_t and
131 * script_data_item_name must match.
133 static const char *script_data_item_name
[] = {
134 "rcm_script_version",
135 "rcm_script_func_info",
138 "rcm_resource_usage_info",
139 "rcm_failure_reason",
148 * Maximum number of rcm scripts that can run in parallel.
149 * RCM daemon has no limit on the number of scripts supported. But
150 * at most it runs script_max_parallelism number of scripts in parallel.
151 * For each running script rcm daemon consumes two file descriptors
152 * in order to communicate with the script via pipes.
153 * So maximum number of file descriptor entries consumed by rcm daemon
154 * on behalf of rcm scripts is "script_max_parallelism * 2"
156 static const int script_max_parallelism
= 64;
159 * semaphore to limit the number of rcm script processes running in
160 * parallel to script_max_parallelism.
162 static sema_t script_process_sema
;
164 /* mutex to protect the any global data */
165 static mutex_t script_lock
;
167 /* contains head to a queue of script_info structures */
168 static rcm_queue_t script_info_q
;
171 * This mmapped state file is used to store the process id and
172 * rcm script name of all currently running rcm scripts.
174 static const char *script_ps_state_file
= "/var/run/rcm_script_state";
175 static state_file_descr_t script_ps_statefd
;
177 static char *script_env_noforce
= "RCM_ENV_FORCE=FALSE";
178 static char *script_env_force
= "RCM_ENV_FORCE=TRUE";
179 static char *script_env_interval
= "RCM_ENV_INTERVAL=%ld";
181 #define RSCR_TRACE RCM_TRACE1
183 /* rcm script base environment */
184 static char *script_env
[MAX_ENV_PARAMS
];
186 struct rlimit file_limit
;
188 /* function prototypes */
189 static void build_env(void);
190 static void copy_env(char *[], char *[]);
191 static void open_state_file(const char *, state_file_descr_t
*, size_t, int,
193 static void truncate_state_file(state_file_descr_t
*);
194 static void close_state_file(const char *, state_file_descr_t
*);
195 static void grow_state_file(state_file_descr_t
*);
196 static void *get_state_element(state_file_descr_t
*, int, int *);
197 static void *allocate_state_element(state_file_descr_t
*, int *);
198 static void free_state_element(void *);
199 static void script_ps_state_file_kill_pids(void);
200 static void script_ps_state_file_add_entry(pid_t
, char *);
201 static void script_ps_state_file_remove_entry(pid_t
);
202 static int dname_to_id(char *);
203 static void script_process_sema_wait(void);
204 static int run_script(script_info_t
*, char *[], char *[], char **);
205 static int get_line(int fd
, char *, char *, int, size_t *, time_t, int *);
206 static void script_exited(script_info_t
*);
207 static int kill_pid(pid_t
);
208 static void kill_script(script_info_t
*);
209 static char *flags_to_name(int, char *, int);
210 static void fill_argv(script_info_t
*, char *[], char *);
211 static void *read_stderr(script_info_t
*);
212 static int process_dataitem(script_info_t
*, int, char *, char **);
213 static int do_cmd(script_info_t
*, char *[], char *[], char **);
214 static int do_script_info(script_info_t
*);
215 static int do_dr(script_info_t
*, char *[], char *[], char **);
216 static int script_get_info(rcm_handle_t
*, char *, pid_t
, uint_t
, char **,
217 char **, nvlist_t
*, rcm_info_t
**);
218 static void add_for_unregister(script_info_t
*);
219 static void remove_from_unregister(script_info_t
*, char *);
220 static void complete_unregister(script_info_t
*);
221 static int script_register_interest(rcm_handle_t
*);
222 static void add_drreq(script_info_t
*, char *);
223 static void remove_drreq(script_info_t
*, char *);
224 static void remove_drreq_all(script_info_t
*);
225 static int script_request_offline(rcm_handle_t
*, char *, pid_t
, uint_t
,
226 char **, rcm_info_t
**);
227 static int script_notify_online(rcm_handle_t
*, char *, pid_t
, uint_t
,
228 char **, rcm_info_t
**);
229 static int script_notify_remove(rcm_handle_t
*, char *, pid_t
, uint_t
,
230 char **, rcm_info_t
**);
231 static int script_request_suspend(rcm_handle_t
*, char *, pid_t
, timespec_t
*,
232 uint_t
, char **, rcm_info_t
**);
233 static int script_notify_resume(rcm_handle_t
*, char *, pid_t
, uint_t
,
234 char **, rcm_info_t
**);
235 static capacity_descr_t
*get_capacity_descr(char *);
236 static int build_env_for_capacity(script_info_t
*, char *, uint_t
, nvlist_t
*,
237 char *[], int *, char **);
238 static int script_request_capacity_change(rcm_handle_t
*, char *, pid_t
,
239 uint_t
, nvlist_t
*, char **, rcm_info_t
**);
240 static int script_notify_capacity_change(rcm_handle_t
*, char *, pid_t
,
241 uint_t
, nvlist_t
*, char **, rcm_info_t
**);
242 static void log_msg(script_info_t
*, int, char *);
243 static char *dup_err(int, char *, ...);
244 static void rcmscript_snprintf(char **, int *, char **, char *, ...);
245 static char *rcmscript_strdup(char *);
246 static void *rcmscript_malloc(size_t);
247 static void *rcmscript_calloc(size_t, size_t);
250 static struct rcm_mod_ops script_ops
=
253 script_register_interest
, /* register */
254 script_register_interest
, /* unregister */
256 script_request_suspend
,
257 script_notify_resume
,
258 script_request_offline
,
259 script_notify_online
,
260 script_notify_remove
,
261 script_request_capacity_change
,
262 script_notify_capacity_change
,
267 * Messages fall into two categories:
268 * framework messages (MF_..)
269 * errors directly attributable to scripts (MS_..)
271 #define MF_MEMORY_ALLOCATION_ERR \
272 gettext("rcm: failed to allocate memory: %1$s\n")
273 #define MF_STATE_FILE_ERR \
274 gettext("rcm: state file error: %1$s: %2$s\n")
275 #define MF_FUNC_CALL_ERR \
276 gettext("rcm: %1$s: %2$s\n")
278 gettext("rcm: required name-value parameters missing (%1$s)\n")
279 #define MF_UNKNOWN_RSRC_ERR \
280 gettext("rcm: unknown resource name %1$s (%2$s)\n")
281 #define MS_REGISTER_RSRC_ERR \
282 gettext("rcm script %1$s: failed to register %2$s\n")
283 #define MS_REGISTER_ERR \
284 gettext("rcm script %1$s: register: %2$s\n")
285 #define MS_SCRIPTINFO_ERR \
286 gettext("rcm script %1$s: scriptinfo: %2$s\n")
287 #define MS_PROTOCOL_ERR \
288 gettext("rcm script %1$s: scripting protocol error\n")
289 #define MS_TIMEOUT_ERR \
290 gettext("rcm script %1$s: timeout error\n")
291 #define MS_UNSUPPORTED_VER \
292 gettext("rcm script %1$s: unsupported version %2$d\n")
293 #define MS_SCRIPT_ERR \
294 gettext("rcm script %1$s: error: %2$s\n")
295 #define MS_UNKNOWN_ERR \
296 gettext("rcm script %1$s: unknown error\n")
298 gettext("rcm script %1$s: %2$s\n")
302 * Initialize rcm scripting framework.
303 * Called during initialization of rcm daemon.
306 script_main_init(void)
308 #define PS_STATE_FILE_CHUNK_SIZE 32
310 /* set base script environment */
313 rcm_init_queue(&script_info_q
);
316 * Initialize the semaphore to limit the number of rcm script
317 * process running in parallel to script_max_parallelism.
319 (void) sema_init(&script_process_sema
, script_max_parallelism
,
322 (void) mutex_init(&script_lock
, USYNC_THREAD
, NULL
);
324 /* save original file limit */
325 (void) getrlimit(RLIMIT_NOFILE
, &file_limit
);
327 open_state_file(script_ps_state_file
, &script_ps_statefd
,
328 sizeof (ps_state_element_t
),
329 PS_STATE_FILE_CHUNK_SIZE
,
333 * If any pids exist in the ps state file since the last incarnation of
334 * the rcm daemon, kill the pids.
335 * On a normal daemon exit no pids should exist in the ps state file.
336 * But on an abnormal daemon exit pids may exist in the ps state file.
338 if (script_ps_statefd
.state_file
) {
339 script_ps_state_file_kill_pids();
340 truncate_state_file(&script_ps_statefd
);
348 * Called at the time of normal rcm daemon exit.
351 script_main_fini(void)
353 script_ps_state_file_kill_pids();
354 close_state_file(script_ps_state_file
, &script_ps_statefd
);
359 * Initialize the given rcm script.
360 * module->name contains the name of the rcm script.
363 script_init(module_t
*module
)
369 rcm_log_message(RSCR_TRACE
, "script_init: script name = %s\n",
374 if ((script_path
= rcm_get_script_dir(module
->name
)) == NULL
)
377 len
= strlen(script_path
) + strlen(module
->name
) + 2;
379 /* calloc also zeros the contents */
380 rsi
= (script_info_t
*)rcmscript_calloc(1, sizeof (script_info_t
));
381 rsi
->script_full_name
= (char *)rcmscript_calloc(1, len
);
383 rsi
->module
= module
;
384 rcm_init_queue(&rsi
->drreq_q
);
386 (void) mutex_init(&rsi
->channel_lock
, USYNC_THREAD
, NULL
);
388 (void) snprintf(rsi
->script_full_name
, len
, "%s%s", script_path
,
390 rsi
->script_name
= strrchr(rsi
->script_full_name
, '/') + 1;
392 (void) mutex_lock(&rsi
->channel_lock
);
394 rsi
->cmd_timeout
= -1; /* don't time scriptinfo command */
395 if (do_script_info(rsi
) == RCM_SUCCESS
) {
397 * if the script hasn't specified a timeout value set it to
400 if (rsi
->cmd_timeout
== -1)
401 rsi
->cmd_timeout
= SCRIPT_CMD_TIMEOUT
;
402 (void) mutex_unlock(&rsi
->channel_lock
);
404 /* put rsi on script_info_q */
405 (void) mutex_lock(&script_lock
);
406 rcm_enqueue_tail(&script_info_q
, &rsi
->queue
);
407 (void) mutex_unlock(&script_lock
);
410 return (&script_ops
);
413 (void) mutex_unlock(&rsi
->channel_lock
);
415 free(rsi
->script_full_name
);
421 * Returns a string describing the script's functionality.
422 * module->name contains the name of the rcm script for which information
426 script_info(module_t
*module
)
428 script_info_t
*rsi
= module
->rsi
;
430 rcm_log_message(RSCR_TRACE
, "script_info: script name = %s\n",
432 return (rsi
->func_info_buf
);
436 * Called before unloading the script.
437 * module->name contains the name of the rcm script which is being unloaded.
441 script_fini(module_t
*module
)
443 script_info_t
*rsi
= module
->rsi
;
445 rcm_log_message(RSCR_TRACE
, "script_fini: script name = %s\n",
448 /* remove rsi from script_info_q */
449 (void) mutex_lock(&script_lock
);
450 rcm_dequeue(&rsi
->queue
);
451 (void) mutex_unlock(&script_lock
);
453 remove_drreq_all(rsi
);
455 free(rsi
->func_info_buf
);
457 free(rsi
->script_full_name
);
462 return (RCM_SUCCESS
);
465 /* build base environment for scripts */
469 const char *env_list
[] = { "LANG", "LC_COLLATE", "LC_CTYPE",
470 "LC_MESSAGES", "LC_MONETARY", "LC_NUMERIC", "LC_TIME",
471 "LC_ALL", "TZ", NULL
};
476 extern int debug_level
;
478 script_env
[j
++] = rcmscript_strdup("PATH=/usr/sbin:/usr/bin");
480 for (i
= 0; env_list
[i
] != NULL
; i
++) {
481 x
= getenv(env_list
[i
]);
483 len
= strlen(env_list
[i
]) + strlen(x
) + 2;
484 script_env
[j
] = (char *)rcmscript_malloc(len
);
486 (void) snprintf(script_env
[j
++], len
, "%s=%s",
491 len
= strlen("RCM_ENV_DEBUG_LEVEL") + 3;
492 script_env
[j
] = (char *)rcmscript_malloc(len
);
496 else if (debug_level
> 9)
501 (void) snprintf(script_env
[j
++], len
, "RCM_ENV_DEBUG_LEVEL=%d", d
);
503 script_env
[j
] = NULL
;
507 copy_env(char *src
[], char *dst
[])
511 for (i
= 0; src
[i
] != NULL
; i
++)
518 * Open (or create if the file does not exist) the given state file
522 open_state_file(const char *filename
,
523 state_file_descr_t
*statefd
,
531 if ((statefd
->fd
= open(filename
, O_CREAT
|O_RDWR
, 0600)) ==
534 rcm_log_message(RCM_ERROR
, MF_STATE_FILE_ERR
,
535 "open", strerror(error_num
));
536 rcmd_exit(error_num
);
540 if (fstat(statefd
->fd
, &stats
) != 0) {
542 rcm_log_message(RCM_ERROR
, MF_STATE_FILE_ERR
,
543 "fstat", strerror(error_num
));
544 rcmd_exit(error_num
);
548 if (stats
.st_size
!= 0) {
550 statefd
->state_file
= mmap(NULL
,
551 stats
.st_size
, PROT_READ
|PROT_WRITE
, MAP_SHARED
,
554 if (statefd
->state_file
== MAP_FAILED
) {
556 rcm_log_message(RCM_ERROR
, MF_STATE_FILE_ERR
,
557 "mmap", strerror(error_num
));
558 rcmd_exit(error_num
);
562 if (statefd
->state_file
->version
!= version
) {
563 (void) munmap((void *)statefd
->state_file
,
565 statefd
->state_file
= NULL
;
566 (void) ftruncate(statefd
->fd
, 0);
569 statefd
->state_file
= NULL
;
572 statefd
->version
= version
;
573 statefd
->element_size
= sizeof (state_element_t
) +
574 RSCR_ROUNDUP(element_size
, 8);
575 statefd
->chunk_size
= chunk_size
;
580 truncate_state_file(state_file_descr_t
*statefd
)
584 if (statefd
->state_file
) {
585 size
= sizeof (state_file_t
) + statefd
->element_size
*
586 statefd
->state_file
->max_elements
;
588 (void) munmap((void *)statefd
->state_file
, size
);
589 statefd
->state_file
= NULL
;
591 (void) ftruncate(statefd
->fd
, 0);
595 close_state_file(const char *filename
, state_file_descr_t
*statefd
)
597 truncate_state_file(statefd
);
598 (void) close(statefd
->fd
);
599 (void) unlink(filename
);
603 * Grow the state file by the chunk size specified in statefd
607 grow_state_file(state_file_descr_t
*statefd
)
613 max_elements
= statefd
->chunk_size
;
614 if (statefd
->state_file
)
615 max_elements
+= statefd
->state_file
->max_elements
;
617 size
= sizeof (state_file_t
) +
618 statefd
->element_size
* max_elements
;
620 if (ftruncate(statefd
->fd
, size
) != 0) {
622 rcm_log_message(RCM_ERROR
, MF_STATE_FILE_ERR
,
623 "ftruncate", strerror(error_num
));
624 rcmd_exit(error_num
);
629 statefd
->state_file
= mmap(NULL
, size
,
630 PROT_READ
|PROT_WRITE
, MAP_SHARED
, statefd
->fd
, 0);
632 if (statefd
->state_file
== MAP_FAILED
) {
634 rcm_log_message(RCM_ERROR
, MF_STATE_FILE_ERR
,
635 "mmap", strerror(error_num
));
636 rcmd_exit(error_num
);
640 statefd
->index
= statefd
->state_file
->max_elements
;
641 statefd
->state_file
->max_elements
= max_elements
;
642 statefd
->state_file
->version
= statefd
->version
;
646 * Given index into state element array, get the pointer to the actual
648 * If flag is non-null set *flag to
649 * TRUE if the state element is currently is use.
650 * FALSE if the state element is free.
653 get_state_element(state_file_descr_t
*statefd
, int index
, int *flag
)
657 if (statefd
->state_file
&&
658 (index
< statefd
->state_file
->max_elements
)) {
660 ptr
= (char *)(statefd
->state_file
);
661 ptr
+= sizeof (state_file_t
) +
662 index
* statefd
->element_size
;
665 *flag
= (((state_element_t
*)((void *)ptr
))->flags
&
666 STATE_ELEMENT_IN_USE
) ? 1 : 0;
669 ptr
+= sizeof (state_element_t
);
673 return ((void *)ptr
);
677 * Allocate a state element entry in the state file and return a pointer
678 * to the allocated entry.
679 * If index is non-null set *index to index into the state element array
680 * of the allocated entry.
683 allocate_state_element(state_file_descr_t
*statefd
, int *index
)
689 if (statefd
->state_file
) {
690 /* find an empty slot */
691 for (i
= 0; i
< statefd
->state_file
->max_elements
; i
++) {
692 x
= get_state_element(statefd
, statefd
->index
,
701 if (statefd
->index
>= statefd
->state_file
->max_elements
)
706 if (statefd
->state_file
== NULL
||
707 i
== statefd
->state_file
->max_elements
) {
709 /* All entries are in use. Grow the list */
710 grow_state_file(statefd
);
711 x
= get_state_element(statefd
, statefd
->index
, &flag
);
716 *index
= statefd
->index
;
719 if (statefd
->index
>= statefd
->state_file
->max_elements
)
722 ((state_element_t
*)x
- 1)->flags
|= STATE_ELEMENT_IN_USE
;
727 free_state_element(void *x
)
729 ((state_element_t
*)x
- 1)->flags
&= ~STATE_ELEMENT_IN_USE
;
733 * Kill the pids contained in ps state file.
736 script_ps_state_file_kill_pids(void)
738 ps_state_element_t
*x
;
744 for (i
= 0; 1; i
++) {
745 if ((x
= (ps_state_element_t
*)get_state_element(
746 &script_ps_statefd
, i
, &flag
)) == NULL
)
749 if (flag
== 1) { /* the entry is in use */
750 (void) snprintf(procfile
, 80, "/proc/%ld/psinfo",
752 if ((fd
= open(procfile
, O_RDONLY
)) != -1 &&
753 read(fd
, &psi
, sizeof (psi
)) == sizeof (psi
) &&
755 x
->script_name
) == 0) {
760 * just a safety check to not to blow up
761 * system processes if the file is ever corrupt
764 rcm_log_message(RCM_DEBUG
,
765 "script_ps_state_file_kill_pids: "
766 "killing script_name = %s pid = %ld\n",
767 x
->script_name
, x
->pid
);
769 /* kill the process group */
770 (void) kill(-(x
->pid
), SIGKILL
);
776 free_state_element((void *)x
);
782 * Add a state element entry to ps state file.
785 script_ps_state_file_add_entry(pid_t pid
, char *script_name
)
787 ps_state_element_t
*x
;
789 (void) mutex_lock(&script_lock
);
791 x
= (ps_state_element_t
*)allocate_state_element(
792 &script_ps_statefd
, NULL
);
795 (void) strlcpy(x
->script_name
, script_name
, MAXNAMELEN
);
797 (void) fsync(script_ps_statefd
.fd
);
799 (void) mutex_unlock(&script_lock
);
803 * Remove the state element entry corresponding to pid from the
807 script_ps_state_file_remove_entry(pid_t pid
)
809 ps_state_element_t
*x
;
812 (void) mutex_lock(&script_lock
);
815 for (i
= 0; 1; i
++) {
816 if ((x
= (ps_state_element_t
*)get_state_element(
817 &script_ps_statefd
, i
, &flag
)) == NULL
)
820 /* if the state element entry is in use and pid matches */
821 if (flag
== 1 && x
->pid
== pid
) {
822 free_state_element((void *)x
);
827 (void) mutex_unlock(&script_lock
);
831 * Get data item id given data item name
834 dname_to_id(char *dname
)
838 for (i
= 0; script_data_item_name
[i
] != NULL
; i
++) {
839 if (strcmp(dname
, script_data_item_name
[i
]) == 0)
847 * Called before running any script.
848 * This routine waits until the number of script processes running in
849 * parallel drops down below to script_max_parallelism.
852 script_process_sema_wait(void)
858 if (sema_wait(&script_process_sema
) == 0)
861 if (errno
!= EINTR
&& errno
!= EAGAIN
) {
863 rcm_log_message(RCM_ERROR
, MF_FUNC_CALL_ERR
,
864 "sema_wait", strerror(error_num
));
865 rcmd_exit(error_num
);
874 * Fork and execute the script.
877 run_script(script_info_t
*rsi
, char *argv
[], char *envp
[], char **errmsg
)
879 int i
, p1
= -1, p2
= -1;
883 rcm_log_message(RSCR_TRACE
, "run_script: script name = %s\n",
884 rsi
->script_full_name
);
886 for (i
= 0; argv
[i
] != NULL
; i
++)
887 rcm_log_message(RSCR_TRACE
, "run_script: argv[%d] = %s\n",
892 /* check that the script exists */
893 if (stat(rsi
->script_full_name
, &stats
) != 0)
897 * If the syscall pipe fails because of reaching the max open file
898 * count per process then dynamically increase the limit on the max
901 * At present the rcm_daemon consumes file descriptor
902 * entries for the following files.
903 * RCM_STATE_FILE - /var/run/rcm_daemon_state
904 * DAEMON_LOCK_FILE - /var/run/rcm_daemon_lock
905 * RCM_SERVICE_DOOR - /var/run/rcm_daemon_door
906 * proc files in the format "/proc/pid/as" for each pid
907 * communicating with the rcm_daemon via doors
908 * dlopen for each rcm module
909 * When in daemon mode stdin, stdout and stderr are closed;
910 * /dev/null opened and duped to stdout, and stderr
912 * Some files which are opened briefly and closed such as
914 * Two file descriptors for each script in running state.
915 * Note that the constant script_max_parallelism sets an
916 * upper cap on how many rcm scripts can run in
919 if ((p1
= pipe(rsi
->pipe1
)) == -1 || (p2
= pipe(rsi
->pipe2
)) == -1) {
920 if ((errno
== EMFILE
) &&
921 (getrlimit(RLIMIT_NOFILE
, &rlp
) == 0)) {
924 if (rlp
.rlim_max
< rlp
.rlim_cur
)
925 rlp
.rlim_max
= rlp
.rlim_cur
;
926 (void) setrlimit(RLIMIT_NOFILE
, &rlp
);
929 if ((p1
= pipe(rsi
->pipe1
)) == -1)
932 if ((p2
= pipe(rsi
->pipe2
)) == -1)
939 if ((rsi
->pid
= fork1()) == (pid_t
)-1) {
940 if (errno
== EINTR
|| errno
== EAGAIN
)
951 /* close stdin, stdout and stderr */
956 /* set stdin to /dev/null */
957 (void) open("/dev/null", O_RDWR
, 0);
959 /* redirect stdout and stderr to pipe */
960 (void) dup2(rsi
->pipe1
[CHILD_END_OF_PIPE
], 1);
961 (void) dup2(rsi
->pipe2
[CHILD_END_OF_PIPE
], 2);
963 /* close all other file descriptors */
966 /* restore original file limit */
967 (void) setrlimit(RLIMIT_NOFILE
, &file_limit
);
969 /* set current working dir */
970 if (stats
.st_uid
== 0) {
972 if (chdir("/var/run") == -1)
975 if (chdir("/tmp") == -1)
980 * setuid sets real, effective and saved user ids to the
982 * setgid sets real, effective and saved group ids to the
985 (void) setgid(stats
.st_gid
);
986 (void) setuid(stats
.st_uid
);
988 (void) execve(rsi
->script_full_name
, argv
, envp
);
993 (void) close(rsi
->pipe1
[CHILD_END_OF_PIPE
]);
994 (void) close(rsi
->pipe2
[CHILD_END_OF_PIPE
]);
996 script_ps_state_file_add_entry(rsi
->pid
, rsi
->script_name
);
1001 *errmsg
= dup_err(RCM_ERROR
, MS_SCRIPT_ERR
,
1002 rsi
->script_name
, strerror(errno
));
1005 (void) close(rsi
->pipe1
[PARENT_END_OF_PIPE
]);
1006 (void) close(rsi
->pipe1
[CHILD_END_OF_PIPE
]);
1010 (void) close(rsi
->pipe2
[PARENT_END_OF_PIPE
]);
1011 (void) close(rsi
->pipe2
[CHILD_END_OF_PIPE
]);
1018 * Reads one line of input (including the newline character) from the
1019 * given file descriptor "fd" to buf.
1020 * maxbuflen specifies the size of memory allocated for buf.
1021 * Timeoutval is the max timeout value in seconds for the script to supply
1022 * input. A timeoutval of 0 implies no timeout.
1024 * Upon return *buflen contains the number of bytes read.
1028 * -1 an error occured
1029 * -2 timeout occurred
1042 struct pollfd fds
[1];
1052 deadline
= time(NULL
) + timeoutval
;
1054 fds
[0].events
= POLLIN
;
1060 while (c
!= '\n' && len
< (maxbuflen
-1)) {
1064 timeoutval
= deadline
- time(NULL
);
1065 if (timeoutval
<= 0) {
1069 x
= poll(fds
, 1, timeoutval
*1000);
1075 if (errno
== EINTR
|| errno
== EAGAIN
)
1084 if ((x
= read(fd
, &c
, 1)) != 1) {
1087 * Script exited. Or more specifically the
1088 * script has closed its end of the pipe.
1092 if (errno
== EINTR
|| errno
== EAGAIN
)
1107 rcm_log_message(RSCR_TRACE
,
1108 "get_line(%s): rval = %d buflen = %d line = %s\n",
1109 fdname
, rval
, *buflen
, buf
);
1114 script_exited(script_info_t
*rsi
)
1116 if (rsi
->flags
& STDERR_THREAD_CREATED
) {
1117 rcm_log_message(RSCR_TRACE
,
1118 "script_exited: doing thr_join (%s)\n", rsi
->script_name
);
1119 (void) thr_join(rsi
->tid
, NULL
, NULL
);
1120 rsi
->flags
&= ~STDERR_THREAD_CREATED
;
1123 (void) close(rsi
->pipe1
[PARENT_END_OF_PIPE
]);
1124 (void) close(rsi
->pipe2
[PARENT_END_OF_PIPE
]);
1125 rsi
->pipe1
[PARENT_END_OF_PIPE
] = -1;
1126 rsi
->pipe2
[PARENT_END_OF_PIPE
] = -1;
1128 script_ps_state_file_remove_entry(rsi
->pid
);
1130 (void) sema_post(&script_process_sema
);
1134 * Kill the specified process group
1139 time_t deadline
, timeleft
;
1142 /* kill the entire process group */
1143 (void) kill(-(pid
), SIGKILL
);
1145 /* give some time for the script to be killed */
1146 deadline
= time(NULL
) + SCRIPT_KILL_TIMEOUT
;
1148 if (waitpid(pid
, &child_status
, WNOHANG
) == pid
)
1151 /* wait for 100 ms */
1152 (void) poll(NULL
, 0, 100);
1154 timeleft
= deadline
- time(NULL
);
1155 } while (timeleft
> 0);
1157 /* script process was not killed successfully */
1162 * Kill the specified script.
1165 kill_script(script_info_t
*rsi
)
1168 (void) kill_pid(rsi
->pid
);
1170 remove_drreq_all(rsi
);
1175 * Convert rcm flags parameter to a string.
1176 * Used for debug prints.
1179 flags_to_name(int flags
, char *buf
, int maxbuflen
)
1181 (void) snprintf(buf
, maxbuflen
, "%s%s",
1182 (flags
& RCM_QUERY
) ? "RCM_QUERY " : "",
1183 (flags
& RCM_FORCE
) ? "RCM_FORCE" : "");
1189 fill_argv(script_info_t
*rsi
, char *argv
[], char *resource_name
)
1191 argv
[0] = rsi
->script_full_name
;
1192 argv
[1] = script_cmd_name
[rsi
->cmd
];
1193 if (resource_name
) {
1194 argv
[2] = resource_name
;
1202 * Reads stderr and logs to syslog.
1203 * Runs as a separate thread.
1206 read_stderr(script_info_t
*rsi
)
1208 char buf
[MAX_LINE_LEN
];
1212 while ((get_line(rsi
->pipe2
[PARENT_END_OF_PIPE
], "stderr",
1213 buf
, MAX_LINE_LEN
, &buflen
, 0, &error_num
)) == 0) {
1214 log_msg(rsi
, RCM_ERROR
, buf
);
1218 log_msg(rsi
, RCM_ERROR
, buf
);
1223 /* process return data items passed by scripts to the framework */
1225 process_dataitem(script_info_t
*rsi
, int token
, char *value
, char **errmsg
)
1236 case D_SCRIPT_VERSION
:
1237 if (rsi
->cmd
!= C_SCRIPTINFO
)
1240 /* check that value contains only digits */
1241 for (ptr
= value
; *ptr
!= '\0'; ptr
++)
1242 if (isdigit((int)(*ptr
)) == 0)
1246 rsi
->ver
= atoi(value
);
1252 case D_SCRIPT_FUNC_INFO
:
1253 if (rsi
->cmd
!= C_SCRIPTINFO
)
1256 rcmscript_snprintf(&rsi
->func_info_buf
,
1257 &rsi
->func_info_buf_len
,
1258 &rsi
->func_info_buf_curptr
,
1263 if (rsi
->cmd
!= C_SCRIPTINFO
)
1266 /* check that value contains only digits */
1267 for (ptr
= value
; *ptr
!= '\0'; ptr
++)
1268 if (isdigit((int)(*ptr
)) == 0)
1272 rsi
->cmd_timeout
= atoi(value
);
1277 case D_RESOURCE_NAME
:
1278 if (rsi
->cmd
!= C_REGISTER
)
1281 if (get_capacity_descr(value
) != NULL
)
1282 status
= rcm_register_capacity(rsi
->hdl
, value
,
1285 status
= rcm_register_interest(rsi
->hdl
, value
, 0,
1288 if (status
== RCM_FAILURE
&& errno
== EALREADY
)
1289 status
= RCM_SUCCESS
;
1291 if (status
!= RCM_SUCCESS
) {
1292 rcm_log_message(RCM_ERROR
, MS_REGISTER_RSRC_ERR
,
1293 rsi
->script_name
, value
);
1296 remove_from_unregister(rsi
, value
);
1299 case D_RESOURCE_USAGE_INFO
:
1300 if (rsi
->cmd
!= C_RESOURCEINFO
)
1303 rcmscript_snprintf(&rsi
->resource_usage_info_buf
,
1304 &rsi
->resource_usage_info_buf_len
,
1305 &rsi
->resource_usage_info_buf_curptr
,
1309 case D_FAILURE_REASON
:
1310 rcmscript_snprintf(&rsi
->failure_reason_buf
,
1311 &rsi
->failure_reason_buf_len
,
1312 &rsi
->failure_reason_buf_curptr
,
1323 *errmsg
= dup_err(RCM_ERROR
, MS_PROTOCOL_ERR
, rsi
->script_name
);
1327 /* Send the given command to the script and process return data */
1329 do_cmd(script_info_t
*rsi
, char *argv
[], char *envp
[], char **errmsg
)
1331 char buf
[MAX_LINE_LEN
];
1333 int loglevel
= -1, continuelog
= 0;
1334 char *ptr
, *dname
, *value
;
1338 int rval
, child_status
, token
;
1340 int cmd_timeout
= rsi
->cmd_timeout
;
1344 script_process_sema_wait();
1346 if (run_script(rsi
, argv
, envp
, errmsg
) == -1) {
1347 (void) sema_post(&script_process_sema
);
1351 (void) time(&rsi
->lastrun
);
1352 deadline
= rsi
->lastrun
+ cmd_timeout
;
1354 if (thr_create(NULL
, 0, (void *(*)(void *))read_stderr
, rsi
,
1355 0, &rsi
->tid
) != 0) {
1356 *errmsg
= dup_err(RCM_ERROR
, MF_FUNC_CALL_ERR
,
1357 "thr_create", strerror(errno
));
1360 rsi
->flags
|= STDERR_THREAD_CREATED
;
1364 if (cmd_timeout
> 0) {
1365 maxsecs
= deadline
- time(NULL
);
1371 rval
= get_line(rsi
->pipe1
[PARENT_END_OF_PIPE
],
1372 "stdout", buf
, MAX_LINE_LEN
, &buflen
,
1373 maxsecs
, &error_num
);
1377 log_msg(rsi
, loglevel
, buf
);
1379 if ((ptr
= strchr(buf
, '=')) == NULL
)
1385 if ((token
= dname_to_id(dname
)) == -1)
1390 loglevel
= RCM_ERROR
;
1394 loglevel
= RCM_WARNING
;
1398 loglevel
= RCM_INFO
;
1402 loglevel
= RCM_DEBUG
;
1410 if (loglevel
!= -1) {
1411 log_msg(rsi
, loglevel
, value
);
1412 if (buf
[buflen
- 1] == '\n')
1417 if (buf
[buflen
- 1] != '\n')
1420 buf
[buflen
- 1] = '\0';
1421 if (process_dataitem(rsi
, token
,
1422 value
, errmsg
) != 0)
1431 if (waitpid(rsi
->pid
, &child_status
, 0)
1433 if (errno
== EINTR
|| errno
== EAGAIN
)
1435 *errmsg
= dup_err(RCM_ERROR
, MS_SCRIPT_ERR
,
1436 rsi
->script_name
, strerror(errno
));
1440 if (WIFEXITED(child_status
)) {
1442 rsi
->exit_status
= WEXITSTATUS(child_status
);
1445 *errmsg
= dup_err(RCM_ERROR
,
1446 MS_TIMEOUT_ERR
, rsi
->script_name
);
1448 *errmsg
= dup_err(RCM_ERROR
,
1449 MS_UNKNOWN_ERR
, rsi
->script_name
);
1451 /* kill any remaining processes in the pgrp */
1452 (void) kill(-(rsi
->pid
), SIGKILL
);
1461 *errmsg
= dup_err(RCM_ERROR
, MS_SCRIPT_ERR
,
1462 rsi
->script_name
, strerror(errno
));
1468 /* timeout occurred */
1469 if (sigaborted
== 0) {
1470 (void) kill(rsi
->pid
, SIGABRT
);
1472 /* extend deadline */
1473 deadline
+= SCRIPT_ABORT_TIMEOUT
;
1475 *errmsg
= dup_err(RCM_ERROR
,
1476 MS_TIMEOUT_ERR
, rsi
->script_name
);
1485 *errmsg
= dup_err(RCM_ERROR
, MS_PROTOCOL_ERR
, rsi
->script_name
);
1495 do_script_info(script_info_t
*rsi
)
1497 char *argv
[MAX_ARGS
];
1498 int status
= RCM_FAILURE
;
1500 char *errmsg
= NULL
;
1502 rcm_log_message(RSCR_TRACE
, "do_script_info: script name = %s\n",
1505 rsi
->cmd
= C_SCRIPTINFO
;
1506 rsi
->func_info_buf
= NULL
;
1507 rsi
->failure_reason_buf
= NULL
;
1508 fill_argv(rsi
, argv
, NULL
);
1510 if (do_cmd(rsi
, argv
, script_env
, &errmsg
) == 0) {
1511 switch (rsi
->exit_status
) {
1513 if (rsi
->func_info_buf
!= NULL
&&
1514 rsi
->failure_reason_buf
== NULL
) {
1516 if (rsi
->ver
>= SCRIPT_API_MIN_VER
&&
1517 rsi
->ver
<= SCRIPT_API_MAX_VER
)
1518 status
= RCM_SUCCESS
;
1520 rcm_log_message(RCM_ERROR
,
1521 MS_UNSUPPORTED_VER
, rsi
->script_name
,
1528 if (rsi
->failure_reason_buf
!= NULL
) {
1529 rcm_log_message(RCM_ERROR
, MS_SCRIPTINFO_ERR
,
1531 rsi
->failure_reason_buf
);
1541 rcm_log_message(RCM_ERROR
, MS_PROTOCOL_ERR
,
1544 (void) free(errmsg
);
1546 if (status
!= RCM_SUCCESS
)
1547 free(rsi
->func_info_buf
);
1549 free(rsi
->failure_reason_buf
);
1555 do_dr(script_info_t
*rsi
, char *argv
[], char *envp
[], char **info
)
1557 int status
= RCM_FAILURE
;
1560 rsi
->failure_reason_buf
= NULL
;
1562 if (do_cmd(rsi
, argv
, envp
, info
) == 0) {
1563 switch (rsi
->exit_status
) {
1565 case E_UNSUPPORTED_CMD
:
1566 if (rsi
->failure_reason_buf
== NULL
)
1567 status
= RCM_SUCCESS
;
1574 if (rsi
->failure_reason_buf
!= NULL
) {
1575 *info
= rsi
->failure_reason_buf
;
1576 rsi
->failure_reason_buf
= NULL
;
1587 *info
= dup_err(RCM_ERROR
, MS_PROTOCOL_ERR
,
1591 free(rsi
->failure_reason_buf
);
1597 * get_info entry point
1601 script_get_info(rcm_handle_t
*hdl
,
1602 char *resource_name
,
1608 rcm_info_t
**dependent_info
)
1610 script_info_t
*rsi
= hdl
->module
->rsi
;
1611 char *argv
[MAX_ARGS
];
1612 int status
= RCM_FAILURE
;
1615 rcm_log_message(RSCR_TRACE
, "script_get_info: resource = %s\n",
1621 (void) mutex_lock(&rsi
->channel_lock
);
1624 rsi
->cmd
= C_RESOURCEINFO
;
1625 rsi
->resource_usage_info_buf
= NULL
;
1626 rsi
->failure_reason_buf
= NULL
;
1627 fill_argv(rsi
, argv
, resource_name
);
1629 if (do_cmd(rsi
, argv
, script_env
, error
) == 0) {
1630 switch (rsi
->exit_status
) {
1632 if (rsi
->resource_usage_info_buf
!= NULL
&&
1633 rsi
->failure_reason_buf
== NULL
) {
1635 *info
= rsi
->resource_usage_info_buf
;
1636 rsi
->resource_usage_info_buf
= NULL
;
1637 status
= RCM_SUCCESS
;
1643 if (rsi
->failure_reason_buf
!= NULL
) {
1644 *error
= rsi
->failure_reason_buf
;
1645 rsi
->failure_reason_buf
= NULL
;
1655 *error
= dup_err(RCM_ERROR
, MS_PROTOCOL_ERR
,
1659 free(rsi
->resource_usage_info_buf
);
1661 free(rsi
->failure_reason_buf
);
1663 (void) mutex_unlock(&rsi
->channel_lock
);
1669 add_for_unregister(script_info_t
*rsi
)
1671 module_t
*module
= rsi
->module
;
1676 (void) mutex_lock(&rcm_req_lock
);
1678 head
= &module
->client_q
;
1680 for (q
= head
->next
; q
!= head
; q
= q
->next
) {
1681 client
= RCM_STRUCT_BASE_ADDR(client_t
, q
, queue
);
1682 client
->prv_flags
|= RCM_NEED_TO_UNREGISTER
;
1685 (void) mutex_unlock(&rcm_req_lock
);
1689 remove_from_unregister(script_info_t
*rsi
, char *resource_name
)
1691 module_t
*module
= rsi
->module
;
1696 (void) mutex_lock(&rcm_req_lock
);
1698 head
= &module
->client_q
;
1700 for (q
= head
->next
; q
!= head
; q
= q
->next
) {
1701 client
= RCM_STRUCT_BASE_ADDR(client_t
, q
, queue
);
1702 if (strcmp(client
->alias
, resource_name
) == 0) {
1703 client
->prv_flags
&= ~RCM_NEED_TO_UNREGISTER
;
1708 (void) mutex_unlock(&rcm_req_lock
);
1712 complete_unregister(script_info_t
*rsi
)
1714 module_t
*module
= rsi
->module
;
1719 (void) mutex_lock(&rcm_req_lock
);
1721 head
= &module
->client_q
;
1723 for (q
= head
->next
; q
!= head
; q
= q
->next
) {
1724 client
= RCM_STRUCT_BASE_ADDR(client_t
, q
, queue
);
1725 if (client
->prv_flags
& RCM_NEED_TO_UNREGISTER
) {
1726 client
->prv_flags
&= ~RCM_NEED_TO_UNREGISTER
;
1727 client
->state
= RCM_STATE_REMOVE
;
1731 (void) mutex_unlock(&rcm_req_lock
);
1735 * register_interest entry point
1738 script_register_interest(rcm_handle_t
*hdl
)
1740 script_info_t
*rsi
= hdl
->module
->rsi
;
1741 char *argv
[MAX_ARGS
];
1742 int status
= RCM_FAILURE
;
1744 char *errmsg
= NULL
;
1746 rcm_log_message(RSCR_TRACE
,
1747 "script_register_interest: script name = %s\n",
1750 (void) mutex_lock(&rsi
->channel_lock
);
1752 if (rsi
->drreq_q
.next
!= &rsi
->drreq_q
) {
1753 /* if DR is already in progress no need to register again */
1754 (void) mutex_unlock(&rsi
->channel_lock
);
1755 return (RCM_SUCCESS
);
1759 rsi
->cmd
= C_REGISTER
;
1760 rsi
->failure_reason_buf
= NULL
;
1761 fill_argv(rsi
, argv
, NULL
);
1763 add_for_unregister(rsi
);
1765 if (do_cmd(rsi
, argv
, script_env
, &errmsg
) == 0) {
1766 switch (rsi
->exit_status
) {
1768 status
= RCM_SUCCESS
;
1772 if (rsi
->failure_reason_buf
!= NULL
) {
1773 rcm_log_message(RCM_ERROR
, MS_REGISTER_ERR
,
1775 rsi
->failure_reason_buf
);
1785 rcm_log_message(RCM_ERROR
, MS_PROTOCOL_ERR
,
1788 (void) free(errmsg
);
1790 complete_unregister(rsi
);
1792 free(rsi
->failure_reason_buf
);
1794 (void) mutex_unlock(&rsi
->channel_lock
);
1800 * Add the specified resource name to the drreq_q.
1803 add_drreq(script_info_t
*rsi
, char *resource_name
)
1805 rcm_queue_t
*head
= &rsi
->drreq_q
;
1809 /* check if the dr req is already in the list */
1810 for (q
= head
->next
; q
!= head
; q
= q
->next
) {
1811 drreq
= RCM_STRUCT_BASE_ADDR(drreq_t
, q
, queue
);
1812 if (strcmp(drreq
->resource_name
, resource_name
) == 0)
1813 /* dr req is already present in the queue */
1817 drreq
= (drreq_t
*)rcmscript_calloc(1, sizeof (drreq_t
));
1818 drreq
->resource_name
= rcmscript_strdup(resource_name
);
1820 rcm_enqueue_tail(&rsi
->drreq_q
, &drreq
->queue
);
1824 * Remove the dr req for the specified resource name from the drreq_q.
1827 remove_drreq(script_info_t
*rsi
, char *resource_name
)
1829 rcm_queue_t
*head
= &rsi
->drreq_q
;
1833 /* search for dr req and remove from the list */
1834 for (q
= head
->next
; q
!= head
; q
= q
->next
) {
1835 drreq
= RCM_STRUCT_BASE_ADDR(drreq_t
, q
, queue
);
1836 if (strcmp(drreq
->resource_name
, resource_name
) == 0)
1841 /* found drreq on the queue */
1842 rcm_dequeue(&drreq
->queue
);
1843 free(drreq
->resource_name
);
1849 * Remove all dr req's.
1852 remove_drreq_all(script_info_t
*rsi
)
1856 while (rsi
->drreq_q
.next
!= &rsi
->drreq_q
) {
1857 drreq
= RCM_STRUCT_BASE_ADDR(drreq_t
,
1858 rsi
->drreq_q
.next
, queue
);
1859 remove_drreq(rsi
, drreq
->resource_name
);
1864 * request_offline entry point
1868 script_request_offline(rcm_handle_t
*hdl
,
1869 char *resource_name
,
1873 rcm_info_t
**dependent_info
)
1875 script_info_t
*rsi
= hdl
->module
->rsi
;
1876 char *argv
[MAX_ARGS
];
1877 char *envp
[MAX_ENV_PARAMS
];
1878 char flags_name
[MAX_FLAGS_NAME_LEN
];
1882 rcm_log_message(RSCR_TRACE
,
1883 "script_request_offline: resource = %s flags = %s\n",
1885 flags_to_name(flag
, flags_name
, MAX_FLAGS_NAME_LEN
));
1889 (void) mutex_lock(&rsi
->channel_lock
);
1892 rsi
->cmd
= (flag
& RCM_QUERY
) ? C_QUERYREMOVE
: C_PREREMOVE
;
1894 if (rsi
->cmd
== C_PREREMOVE
)
1895 add_drreq(rsi
, resource_name
);
1897 fill_argv(rsi
, argv
, resource_name
);
1898 copy_env(script_env
, envp
);
1899 for (i
= 0; envp
[i
] != NULL
; i
++)
1901 envp
[i
++] = (flag
& RCM_FORCE
) ? script_env_force
: script_env_noforce
;
1904 status
= do_dr(rsi
, argv
, envp
, info
);
1906 (void) mutex_unlock(&rsi
->channel_lock
);
1911 * notify_online entry point
1915 script_notify_online(rcm_handle_t
*hdl
,
1916 char *resource_name
,
1920 rcm_info_t
**dependent_info
)
1922 script_info_t
*rsi
= hdl
->module
->rsi
;
1923 char *argv
[MAX_ARGS
];
1926 rcm_log_message(RSCR_TRACE
, "script_notify_online: resource = %s\n",
1931 (void) mutex_lock(&rsi
->channel_lock
);
1934 rsi
->cmd
= C_UNDOREMOVE
;
1935 fill_argv(rsi
, argv
, resource_name
);
1937 status
= do_dr(rsi
, argv
, script_env
, info
);
1939 remove_drreq(rsi
, resource_name
);
1941 (void) mutex_unlock(&rsi
->channel_lock
);
1946 * notify_remove entry point
1950 script_notify_remove(rcm_handle_t
*hdl
,
1951 char *resource_name
,
1955 rcm_info_t
**dependent_info
)
1957 script_info_t
*rsi
= hdl
->module
->rsi
;
1958 char *argv
[MAX_ARGS
];
1961 rcm_log_message(RSCR_TRACE
, "script_notify_remove: resource = %s\n",
1966 (void) mutex_lock(&rsi
->channel_lock
);
1969 rsi
->cmd
= C_POSTREMOVE
;
1970 fill_argv(rsi
, argv
, resource_name
);
1972 status
= do_dr(rsi
, argv
, script_env
, info
);
1974 remove_drreq(rsi
, resource_name
);
1976 (void) mutex_unlock(&rsi
->channel_lock
);
1981 * request_suspend entry point
1985 script_request_suspend(rcm_handle_t
*hdl
,
1986 char *resource_name
,
1988 timespec_t
*interval
,
1991 rcm_info_t
**dependent_info
)
1993 script_info_t
*rsi
= hdl
->module
->rsi
;
1995 char *curptr
= NULL
;
1996 char *argv
[MAX_ARGS
];
1997 char *envp
[MAX_ENV_PARAMS
];
1998 char flags_name
[MAX_FLAGS_NAME_LEN
];
2004 rcm_log_message(RSCR_TRACE
,
2005 "script_request_suspend: resource = %s flags = %s\n", resource_name
,
2006 flags_to_name(flag
, flags_name
, MAX_FLAGS_NAME_LEN
));
2010 (void) mutex_lock(&rsi
->channel_lock
);
2013 rsi
->cmd
= (flag
& RCM_QUERY
) ? C_QUERYSUSPEND
: C_PRESUSPEND
;
2015 if (rsi
->cmd
== C_PRESUSPEND
)
2016 add_drreq(rsi
, resource_name
);
2018 fill_argv(rsi
, argv
, resource_name
);
2020 copy_env(script_env
, envp
);
2021 for (i
= 0; envp
[i
] != NULL
; i
++);
2023 envp
[i
++] = (flag
& RCM_FORCE
) ? script_env_force
: script_env_noforce
;
2027 * Merge the seconds and nanoseconds, rounding up if there
2028 * are any remainder nanoseconds.
2030 seconds
= interval
->tv_sec
+ (interval
->tv_nsec
/ 1000000000L);
2031 if (interval
->tv_nsec
% 1000000000L)
2032 seconds
+= (interval
->tv_sec
> 0) ? 1L : -1L;
2033 rcmscript_snprintf(&buf
, &buflen
, &curptr
, script_env_interval
,
2040 status
= do_dr(rsi
, argv
, envp
, info
);
2042 (void) mutex_unlock(&rsi
->channel_lock
);
2048 * notify_resume entry point
2052 script_notify_resume(rcm_handle_t
*hdl
,
2053 char *resource_name
,
2057 rcm_info_t
**dependent_info
)
2059 script_info_t
*rsi
= hdl
->module
->rsi
;
2060 char *argv
[MAX_ARGS
];
2063 rcm_log_message(RSCR_TRACE
, "script_notify_resume: resource = %s\n",
2068 (void) mutex_lock(&rsi
->channel_lock
);
2071 rsi
->cmd
= (flag
& RCM_SUSPENDED
) ? C_POSTRESUME
: C_CANCELSUSPEND
;
2072 fill_argv(rsi
, argv
, resource_name
);
2074 status
= do_dr(rsi
, argv
, script_env
, info
);
2076 remove_drreq(rsi
, resource_name
);
2078 (void) mutex_unlock(&rsi
->channel_lock
);
2082 static capacity_descr_t capacity_type
[] = {
2083 { "SUNW_memory", MATCH_EXACT
,
2084 "new_pages", "RCM_ENV_CAPACITY",
2085 "page_size", "RCM_ENV_UNIT_SIZE",
2087 { "SUNW_cpu", MATCH_EXACT
,
2088 "new_total", "RCM_ENV_CAPACITY",
2089 "new_cpu_list", "RCM_ENV_CPU_IDS",
2091 { "SUNW_cpu/set", MATCH_PREFIX
,
2092 "new_total", "RCM_ENV_CAPACITY",
2093 "new_cpu_list", "RCM_ENV_CPU_IDS",
2095 { "", MATCH_INVALID
, "", "" }
2098 static capacity_descr_t
*
2099 get_capacity_descr(char *resource_name
)
2103 for (i
= 0; *capacity_type
[i
].resource_name
!= '\0'; i
++) {
2104 if ((capacity_type
[i
].match_type
== MATCH_EXACT
&&
2105 strcmp(capacity_type
[i
].resource_name
,
2106 resource_name
) == 0) ||
2107 (capacity_type
[i
].match_type
== MATCH_PREFIX
&&
2108 strncmp(capacity_type
[i
].resource_name
,
2110 strlen(capacity_type
[i
].resource_name
)) == 0))
2112 return (&capacity_type
[i
]);
2119 build_env_for_capacity(script_info_t
*rsi
,
2120 char *resource_name
,
2122 nvlist_t
*capacity_info
,
2124 int *dynamic_env_index
,
2128 capacity_descr_t
*capa
= NULL
;
2136 copy_env(script_env
, envp
);
2137 for (p
= 0; envp
[p
] != NULL
; p
++)
2140 if (rsi
->cmd
== C_QUERYCAPACITY
|| rsi
->cmd
== C_PRECAPACITY
)
2141 envp
[p
++] = (flag
& RCM_FORCE
) ? script_env_force
:
2145 *dynamic_env_index
= p
;
2147 if ((capa
= get_capacity_descr(resource_name
)) == NULL
) {
2148 *errmsg
= dup_err(RCM_ERROR
, MF_UNKNOWN_RSRC_ERR
,
2149 resource_name
, rsi
->script_name
);
2153 for (i
= 0; *capa
->param
[i
].nvname
!= '\0'; i
++) {
2155 while ((nvpair
= nvlist_next_nvpair(capacity_info
, nvpair
))
2157 if (strcmp(nvpair_name(nvpair
),
2158 capa
->param
[i
].nvname
) == 0)
2162 if (nvpair
== NULL
) {
2163 *errmsg
= dup_err(RCM_ERROR
, MF_NV_ERR
,
2171 rcmscript_snprintf(&buf
, &buflen
, &curptr
, "%s=",
2172 capa
->param
[i
].envname
);
2174 switch (nvpair_type(nvpair
)) {
2175 case DATA_TYPE_INT16
:
2179 if (nvpair_value_int16(nvpair
, &x
) == 0) {
2180 rcmscript_snprintf(&buf
, &buflen
, &curptr
,
2187 case DATA_TYPE_UINT16
:
2191 if (nvpair_value_uint16(nvpair
, &x
) == 0) {
2192 rcmscript_snprintf(&buf
, &buflen
, &curptr
,
2193 "%hu", (unsigned short)x
);
2199 case DATA_TYPE_INT32
:
2203 if (nvpair_value_int32(nvpair
, &x
) == 0) {
2204 rcmscript_snprintf(&buf
, &buflen
, &curptr
,
2211 case DATA_TYPE_UINT32
:
2215 if (nvpair_value_uint32(nvpair
, &x
) == 0) {
2216 rcmscript_snprintf(&buf
, &buflen
, &curptr
,
2223 case DATA_TYPE_INT64
:
2227 if (nvpair_value_int64(nvpair
, &x
) == 0) {
2228 rcmscript_snprintf(&buf
, &buflen
, &curptr
,
2229 "%lld", (long long)x
);
2235 case DATA_TYPE_UINT64
:
2239 if (nvpair_value_uint64(nvpair
, &x
) == 0) {
2240 rcmscript_snprintf(&buf
, &buflen
, &curptr
,
2241 "%llu", (unsigned long long)x
);
2247 case DATA_TYPE_INT16_ARRAY
:
2251 if (nvpair_value_int16_array(nvpair
, &x
, &n
) == 0) {
2253 rcmscript_snprintf(&buf
, &buflen
,
2256 (n
== 0) ? "" : " ");
2264 case DATA_TYPE_UINT16_ARRAY
:
2268 if (nvpair_value_uint16_array(nvpair
, &x
, &n
) == 0) {
2270 rcmscript_snprintf(&buf
, &buflen
,
2272 (unsigned short)(*x
),
2273 (n
== 0) ? "" : " ");
2281 case DATA_TYPE_INT32_ARRAY
:
2285 if (nvpair_value_int32_array(nvpair
, &x
, &n
) == 0) {
2287 rcmscript_snprintf(&buf
, &buflen
,
2290 (n
== 0) ? "" : " ");
2298 case DATA_TYPE_UINT32_ARRAY
:
2302 if (nvpair_value_uint32_array(nvpair
, &x
, &n
) == 0) {
2304 rcmscript_snprintf(&buf
, &buflen
,
2307 (n
== 0) ? "" : " ");
2315 case DATA_TYPE_INT64_ARRAY
:
2319 if (nvpair_value_int64_array(nvpair
, &x
, &n
) == 0) {
2321 rcmscript_snprintf(&buf
, &buflen
,
2324 (n
== 0) ? "" : " ");
2332 case DATA_TYPE_UINT64_ARRAY
:
2336 if (nvpair_value_uint64_array(nvpair
, &x
, &n
) == 0) {
2338 rcmscript_snprintf(&buf
, &buflen
,
2340 (unsigned long long)(*x
),
2341 (n
== 0) ? "" : " ");
2349 case DATA_TYPE_STRING
:
2353 if (nvpair_value_string(nvpair
, &x
) == 0) {
2354 rcmscript_snprintf(&buf
, &buflen
, &curptr
,
2371 for (p
= *dynamic_env_index
; envp
[p
] != NULL
; p
++)
2373 *errmsg
= dup_err(RCM_ERROR
, MF_NV_ERR
,
2385 * request_capacity_change entry point
2389 script_request_capacity_change(rcm_handle_t
*hdl
,
2390 char *resource_name
,
2393 nvlist_t
*capacity_info
,
2395 rcm_info_t
**dependent_info
)
2397 script_info_t
*rsi
= hdl
->module
->rsi
;
2398 char *argv
[MAX_ARGS
];
2399 char *envp
[MAX_ENV_PARAMS
];
2400 char flags_name
[MAX_FLAGS_NAME_LEN
];
2402 int dynamic_env_index
;
2404 rcm_log_message(RSCR_TRACE
,
2405 "script_request_capacity_change: resource = %s flags = %s\n",
2407 flags_to_name(flag
, flags_name
, MAX_FLAGS_NAME_LEN
));
2411 (void) mutex_lock(&rsi
->channel_lock
);
2414 rsi
->cmd
= (flag
& RCM_QUERY
) ? C_QUERYCAPACITY
: C_PRECAPACITY
;
2415 fill_argv(rsi
, argv
, resource_name
);
2417 if (build_env_for_capacity(rsi
, resource_name
, flag
,
2418 capacity_info
, envp
, &dynamic_env_index
, info
) == 0) {
2420 status
= do_dr(rsi
, argv
, envp
, info
);
2422 while (envp
[dynamic_env_index
] != NULL
) {
2423 free(envp
[dynamic_env_index
]);
2424 dynamic_env_index
++;
2427 status
= RCM_FAILURE
;
2429 (void) mutex_unlock(&rsi
->channel_lock
);
2434 * notify_capacity_change entry point
2438 script_notify_capacity_change(rcm_handle_t
*hdl
,
2439 char *resource_name
,
2442 nvlist_t
*capacity_info
,
2444 rcm_info_t
**dependent_info
)
2446 script_info_t
*rsi
= hdl
->module
->rsi
;
2447 char *argv
[MAX_ARGS
];
2448 char *envp
[MAX_ENV_PARAMS
];
2450 int dynamic_env_index
;
2452 rcm_log_message(RSCR_TRACE
,
2453 "script_notify_capacity_change: resource = %s\n", resource_name
);
2457 (void) mutex_lock(&rsi
->channel_lock
);
2460 rsi
->cmd
= C_POSTCAPACITY
;
2461 fill_argv(rsi
, argv
, resource_name
);
2463 if (build_env_for_capacity(rsi
, resource_name
, flag
,
2464 capacity_info
, envp
, &dynamic_env_index
, info
) == 0) {
2466 status
= do_dr(rsi
, argv
, envp
, info
);
2468 while (envp
[dynamic_env_index
] != NULL
) {
2469 free(envp
[dynamic_env_index
]);
2470 dynamic_env_index
++;
2473 status
= RCM_FAILURE
;
2475 (void) mutex_unlock(&rsi
->channel_lock
);
2479 /* Log the message to syslog */
2481 log_msg(script_info_t
*rsi
, int level
, char *msg
)
2483 rcm_log_msg(level
, MS_LOG_MSG
, rsi
->script_name
, msg
);
2488 dup_err(int level
, char *format
, ...)
2495 va_start(ap
, format
);
2496 n
= vsnprintf(buf1
, 1, format
, ap
);
2501 if (buf2
= (char *)malloc(n
)) {
2502 va_start(ap
, format
);
2503 n
= vsnprintf(buf2
, n
, format
, ap
);
2507 rcm_log_message(level
, buf2
);
2519 rcmscript_snprintf(char **buf
, int *buflen
, char **curptr
, char *format
, ...)
2521 /* must be power of 2 otherwise RSCR_ROUNDUP would break */
2522 #define SPRINTF_CHUNK_LEN 512
2523 #define SPRINTF_MIN_CHUNK_LEN 64
2526 int offset
, bytesneeded
, bytesleft
, error_num
;
2533 offset
= *curptr
- *buf
;
2534 bytesneeded
= SPRINTF_MIN_CHUNK_LEN
;
2535 bytesleft
= *buflen
- offset
;
2539 if (bytesneeded
> bytesleft
) {
2540 *buflen
+= RSCR_ROUNDUP(bytesneeded
- bytesleft
,
2542 if ((*buf
= (char *)realloc(*buf
, *buflen
)) == NULL
) {
2544 rcm_log_message(RCM_ERROR
,
2545 MF_MEMORY_ALLOCATION_ERR
,
2546 strerror(error_num
));
2547 rcmd_exit(error_num
);
2550 *curptr
= *buf
+ offset
;
2551 bytesleft
= *buflen
- offset
;
2554 va_start(ap
, format
);
2555 bytesneeded
= vsnprintf(*curptr
, bytesleft
, format
, ap
);
2558 if (bytesneeded
< 0) {
2559 /* vsnprintf encountered an error */
2561 rcm_log_message(RCM_ERROR
, MF_FUNC_CALL_ERR
,
2562 "vsnprintf", strerror(error_num
));
2563 rcmd_exit(error_num
);
2566 } else if (bytesneeded
< bytesleft
) {
2567 /* vsnprintf succeeded */
2568 *curptr
+= bytesneeded
;
2572 bytesneeded
++; /* to account for storage for '\0' */
2578 rcmscript_strdup(char *str
)
2582 if ((dupstr
= strdup(str
)) == NULL
) {
2583 rcm_log_message(RCM_ERROR
, MF_MEMORY_ALLOCATION_ERR
,
2593 rcmscript_malloc(size_t len
)
2597 if ((ptr
= malloc(len
)) == NULL
) {
2598 rcm_log_message(RCM_ERROR
, MF_MEMORY_ALLOCATION_ERR
,
2608 rcmscript_calloc(size_t nelem
, size_t elsize
)
2612 if ((ptr
= calloc(nelem
, elsize
)) == NULL
) {
2613 rcm_log_message(RCM_ERROR
, MF_MEMORY_ALLOCATION_ERR
,