8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / rcm_daemon / common / rcm_script.c
blob094bc6288ed947ce4338c7d086aecb63e5408167
1 /*
2 * CDDL HEADER START
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
7 * with the License.
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]
20 * CDDL HEADER END
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
34 * interfaces.
36 * Entry points:
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.
66 #include "rcm_impl.h"
67 #include "rcm_script_impl.h"
68 #include <sys/resource.h>
69 #include <procfs.h>
70 #include <sys/proc.h>
71 #include <ctype.h>
74 * All rcm scripting commands are enumerated here.
75 * NOTE: command positions in script_cmd_id_t and script_cmd_name must match.
77 typedef enum {
78 C_SCRIPTINFO,
79 C_RESOURCEINFO,
80 C_REGISTER,
81 C_QUERYREMOVE,
82 C_PREREMOVE,
83 C_POSTREMOVE,
84 C_UNDOREMOVE,
85 C_QUERYCAPACITY,
86 C_PRECAPACITY,
87 C_POSTCAPACITY,
88 C_QUERYSUSPEND,
89 C_PRESUSPEND,
90 C_POSTRESUME,
91 C_CANCELSUSPEND
92 } script_cmd_id_t;
94 /* NOTE: command positions in script_cmd_id_t and script_cmd_name must match */
95 static char *script_cmd_name[] = {
96 "scriptinfo",
97 "resourceinfo",
98 "register",
99 "queryremove",
100 "preremove",
101 "postremove",
102 "undoremove",
103 "querycapacity",
104 "precapacity",
105 "postcapacity",
106 "querysuspend",
107 "presuspend",
108 "postresume",
109 "cancelsuspend",
110 NULL
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.
118 typedef enum {
119 D_SCRIPT_VERSION,
120 D_SCRIPT_FUNC_INFO,
121 D_CMD_TIMEOUT,
122 D_RESOURCE_NAME,
123 D_RESOURCE_USAGE_INFO,
124 D_FAILURE_REASON,
125 D_LOG_ERR,
126 D_LOG_WARN,
127 D_LOG_INFO,
128 D_LOG_DEBUG
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",
138 "rcm_cmd_timeout",
139 "rcm_resource_name",
140 "rcm_resource_usage_info",
141 "rcm_failure_reason",
142 "rcm_log_err",
143 "rcm_log_warn",
144 "rcm_log_info",
145 "rcm_log_debug",
146 NULL
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,
194 uint32_t);
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 =
254 RCM_MOD_OPS_VERSION,
255 script_register_interest, /* register */
256 script_register_interest, /* unregister */
257 script_get_info,
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,
265 NULL
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")
279 #define MF_NV_ERR \
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")
299 #define MS_LOG_MSG \
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 */
313 build_env();
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,
322 USYNC_THREAD, NULL);
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,
332 PS_STATE_FILE_VER);
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);
345 return (0);
349 * Do any cleanup.
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);
357 return (0);
361 * Initialize the given rcm script.
362 * module->name contains the name of the rcm script.
364 struct rcm_mod_ops *
365 script_init(module_t *module)
367 script_info_t *rsi;
368 size_t len;
369 char *script_path;
371 rcm_log_message(RSCR_TRACE, "script_init: script name = %s\n",
372 module->name);
374 module->rsi = NULL;
376 if ((script_path = rcm_get_script_dir(module->name)) == NULL)
377 return (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,
391 module->name);
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
400 * default
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);
411 module->rsi = rsi;
412 return (&script_ops);
415 (void) mutex_unlock(&rsi->channel_lock);
417 free(rsi->script_full_name);
418 free(rsi);
419 return (NULL);
423 * Returns a string describing the script's functionality.
424 * module->name contains the name of the rcm script for which information
425 * is requested.
427 char *
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",
433 rsi->script_name);
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.
440 * Do any cleanup.
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",
448 rsi->script_name);
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);
461 free(rsi);
463 module->rsi = NULL;
465 return (RCM_SUCCESS);
468 /* build base environment for scripts */
469 static void
470 build_env(void)
472 const char *env_list[] = { "LANG", "LC_COLLATE", "LC_CTYPE",
473 "LC_MESSAGES", "LC_MONETARY", "LC_NUMERIC", "LC_TIME",
474 "LC_ALL", "TZ", NULL };
475 char *x;
476 int len;
477 int i, j = 0;
478 int d;
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]);
485 if (x) {
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",
490 env_list[i], x);
494 len = strlen("RCM_ENV_DEBUG_LEVEL") + 3;
495 script_env[j] = (char *)rcmscript_malloc(len);
497 if (debug_level < 0)
498 d = 0;
499 else if (debug_level > 9)
500 d = 9;
501 else
502 d = debug_level;
504 (void) snprintf(script_env[j++], len, "RCM_ENV_DEBUG_LEVEL=%d", d);
506 script_env[j] = NULL;
509 static void
510 copy_env(char *src[], char *dst[])
512 int i;
514 for (i = 0; src[i] != NULL; i++)
515 dst[i] = src[i];
517 dst[i] = NULL;
521 * Open (or create if the file does not exist) the given state file
522 * and mmap it.
524 static void
525 open_state_file(const char *filename,
526 state_file_descr_t *statefd,
527 size_t element_size,
528 int chunk_size,
529 uint32_t version)
531 struct stat stats;
532 int error_num;
534 if ((statefd->fd = open(filename, O_CREAT|O_RDWR, 0600)) ==
535 -1) {
536 error_num = errno;
537 rcm_log_message(RCM_ERROR, MF_STATE_FILE_ERR,
538 "open", strerror(error_num));
539 rcmd_exit(error_num);
540 /*NOTREACHED*/
543 if (fstat(statefd->fd, &stats) != 0) {
544 error_num = errno;
545 rcm_log_message(RCM_ERROR, MF_STATE_FILE_ERR,
546 "fstat", strerror(error_num));
547 rcmd_exit(error_num);
548 /*NOTREACHED*/
551 if (stats.st_size != 0) {
552 /* LINTED */
553 statefd->state_file = (state_file_t *)mmap(NULL,
554 stats.st_size, PROT_READ|PROT_WRITE, MAP_SHARED,
555 statefd->fd, 0);
557 if (statefd->state_file == MAP_FAILED) {
558 error_num = errno;
559 rcm_log_message(RCM_ERROR, MF_STATE_FILE_ERR,
560 "mmap", strerror(error_num));
561 rcmd_exit(error_num);
562 /*NOTREACHED*/
565 if (statefd->state_file->version != version) {
566 (void) munmap((void *)statefd->state_file,
567 stats.st_size);
568 statefd->state_file = NULL;
569 (void) ftruncate(statefd->fd, 0);
571 } else {
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;
579 statefd->index = 0;
582 static void
583 truncate_state_file(state_file_descr_t *statefd)
585 size_t size;
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);
597 static void
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
607 * and mmap it.
609 static void
610 grow_state_file(state_file_descr_t *statefd)
612 size_t size;
613 int max_elements;
614 int error_num;
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) {
624 error_num = errno;
625 rcm_log_message(RCM_ERROR, MF_STATE_FILE_ERR,
626 "ftruncate", strerror(error_num));
627 rcmd_exit(error_num);
628 /*NOTREACHED*/
631 /* LINTED */
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) {
636 error_num = errno;
637 rcm_log_message(RCM_ERROR, MF_STATE_FILE_ERR,
638 "mmap", strerror(error_num));
639 rcmd_exit(error_num);
640 /*NOTREACHED*/
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
650 * state element.
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.
655 static void *
656 get_state_element(state_file_descr_t *statefd, int index, int *flag)
658 char *ptr;
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;
667 if (flag) {
668 *flag = (((state_element_t *)((void *)ptr))->flags &
669 STATE_ELEMENT_IN_USE) ? 1 : 0;
672 ptr += sizeof (state_element_t);
673 } else
674 ptr = NULL;
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.
685 static void *
686 allocate_state_element(state_file_descr_t *statefd, int *index)
688 void *x;
689 int i;
690 int flag;
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,
696 &flag);
697 assert(x != NULL);
699 if (flag == 0)
700 /* entry is free */
701 break;
703 statefd->index++;
704 if (statefd->index >= statefd->state_file->max_elements)
705 statefd->index = 0;
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);
715 assert(flag == 0);
718 if (index != NULL)
719 *index = statefd->index;
721 statefd->index++;
722 if (statefd->index >= statefd->state_file->max_elements)
723 statefd->index = 0;
725 ((state_element_t *)x - 1)->flags |= STATE_ELEMENT_IN_USE;
726 return (x);
729 static void
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.
738 static void
739 script_ps_state_file_kill_pids(void)
741 ps_state_element_t *x;
742 char procfile[80];
743 psinfo_t psi;
744 int fd, i, flag;
746 /* LINTED */
747 for (i = 0; 1; i++) {
748 if ((x = (ps_state_element_t *)get_state_element(
749 &script_ps_statefd, i, &flag)) == NULL)
750 break;
752 if (flag == 1) { /* the entry is in use */
753 (void) snprintf(procfile, 80, "/proc/%ld/psinfo",
754 (long)x->pid);
755 if ((fd = open(procfile, O_RDONLY)) != -1 &&
756 read(fd, &psi, sizeof (psi)) == sizeof (psi) &&
757 strcmp(psi.pr_fname,
758 x->script_name) == 0) {
760 (void) close(fd);
763 * just a safety check to not to blow up
764 * system processes if the file is ever corrupt
766 if (x->pid > 1) {
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);
775 } else {
776 if (fd != -1)
777 (void) close(fd);
779 free_state_element((void *)x);
785 * Add a state element entry to ps state file.
787 static void
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);
797 x->pid = pid;
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
807 * ps state file.
809 static void
810 script_ps_state_file_remove_entry(pid_t pid)
812 ps_state_element_t *x;
813 int flag, i;
815 (void) mutex_lock(&script_lock);
817 /* LINTED */
818 for (i = 0; 1; i++) {
819 if ((x = (ps_state_element_t *)get_state_element(
820 &script_ps_statefd, i, &flag)) == NULL)
821 break;
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);
826 break;
830 (void) mutex_unlock(&script_lock);
834 * Get data item id given data item name
836 static int
837 dname_to_id(char *dname)
839 int i;
841 for (i = 0; script_data_item_name[i] != NULL; i++) {
842 if (strcmp(dname, script_data_item_name[i]) == 0)
843 return (i);
846 return (-1);
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.
854 static void
855 script_process_sema_wait(void)
857 int error_num;
859 /* LINTED */
860 while (1) {
861 if (sema_wait(&script_process_sema) == 0)
862 return;
864 if (errno != EINTR && errno != EAGAIN) {
865 error_num = errno;
866 rcm_log_message(RCM_ERROR, MF_FUNC_CALL_ERR,
867 "sema_wait", strerror(error_num));
868 rcmd_exit(error_num);
869 /*NOTREACHED*/
873 /*NOTREACHED*/
877 * Fork and execute the script.
879 static int
880 run_script(script_info_t *rsi, char *argv[], char *envp[], char **errmsg)
882 int i, p1 = -1, p2 = -1;
883 struct rlimit rlp;
884 struct stat stats;
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",
891 i, argv[i]);
893 *errmsg = NULL;
895 /* check that the script exists */
896 if (stat(rsi->script_full_name, &stats) != 0)
897 goto error;
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
902 * open file count.
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
914 * openlog
915 * Some files which are opened briefly and closed such as
916 * directory files.
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
920 * parallel.
922 if ((p1 = pipe(rsi->pipe1)) == -1 || (p2 = pipe(rsi->pipe2)) == -1) {
923 if ((errno == EMFILE) &&
924 (getrlimit(RLIMIT_NOFILE, &rlp) == 0)) {
926 rlp.rlim_cur += 16;
927 if (rlp.rlim_max < rlp.rlim_cur)
928 rlp.rlim_max = rlp.rlim_cur;
929 (void) setrlimit(RLIMIT_NOFILE, &rlp);
931 if (p1 == -1) {
932 if ((p1 = pipe(rsi->pipe1)) == -1)
933 goto error;
935 if ((p2 = pipe(rsi->pipe2)) == -1)
936 goto error;
937 } else
938 goto error;
941 forkagain:
942 if ((rsi->pid = fork1()) == (pid_t)-1) {
943 if (errno == EINTR || errno == EAGAIN)
944 goto forkagain;
946 goto error;
949 if (rsi->pid == 0) {
950 /* child process */
952 (void) setsid();
954 /* close stdin, stdout and stderr */
955 (void) close(0);
956 (void) close(1);
957 (void) close(2);
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 */
967 closefrom(3);
969 /* restore original file limit */
970 (void) setrlimit(RLIMIT_NOFILE, &file_limit);
972 /* set current working dir */
973 if (stats.st_uid == 0) {
974 /* root */
975 if (chdir("/var/run") == -1)
976 _exit(127);
977 } else {
978 if (chdir("/tmp") == -1)
979 _exit(127);
983 * setuid sets real, effective and saved user ids to the
984 * given id.
985 * setgid sets real, effective and saved group ids to the
986 * given id.
988 (void) setgid(stats.st_gid);
989 (void) setuid(stats.st_uid);
991 (void) execve(rsi->script_full_name, argv, envp);
992 _exit(127);
993 /*NOTREACHED*/
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);
1001 return (0);
1003 error:
1004 *errmsg = dup_err(RCM_ERROR, MS_SCRIPT_ERR,
1005 rsi->script_name, strerror(errno));
1007 if (p1 != -1) {
1008 (void) close(rsi->pipe1[PARENT_END_OF_PIPE]);
1009 (void) close(rsi->pipe1[CHILD_END_OF_PIPE]);
1012 if (p2 != -1) {
1013 (void) close(rsi->pipe2[PARENT_END_OF_PIPE]);
1014 (void) close(rsi->pipe2[CHILD_END_OF_PIPE]);
1017 return (-1);
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.
1029 * Return values:
1030 * 0 success
1031 * -1 an error occured
1032 * -2 timeout occurred
1033 * -3 script exited
1035 static int
1036 get_line(int fd,
1037 char *fdname,
1038 char *buf,
1039 int maxbuflen,
1040 size_t *buflen,
1041 time_t timeoutval,
1042 int *error_num)
1044 char c = '\0';
1045 struct pollfd fds[1];
1046 int x;
1047 size_t len = 0;
1048 char *ptr;
1049 int timeit;
1050 time_t deadline;
1051 int rval = 0;
1053 if (timeoutval) {
1054 timeit = TRUE;
1055 deadline = time(NULL) + timeoutval;
1056 fds[0].fd = fd;
1057 fds[0].events = POLLIN;
1058 } else
1059 timeit = FALSE;
1061 ptr = buf;
1063 while (c != '\n' && len < (maxbuflen -1)) {
1064 if (timeit) {
1065 pollagain:
1066 fds[0].revents = 0;
1067 timeoutval = deadline - time(NULL);
1068 if (timeoutval <= 0) {
1069 rval = -2;
1070 break;
1072 x = poll(fds, 1, timeoutval*1000);
1073 if (x <= 0) {
1074 if (x == 0)
1075 /* poll timedout */
1076 rval = -2;
1077 else {
1078 if (errno == EINTR || errno == EAGAIN)
1079 goto pollagain;
1080 *error_num = errno;
1081 rval = -1;
1083 break;
1086 readagain:
1087 if ((x = read(fd, &c, 1)) != 1) {
1088 if (x == 0)
1090 * Script exited. Or more specifically the
1091 * script has closed its end of the pipe.
1093 rval = -3;
1094 else {
1095 if (errno == EINTR || errno == EAGAIN)
1096 goto readagain;
1097 *error_num = errno;
1098 rval = -1;
1100 break;
1103 *ptr++ = c;
1104 len++;
1107 *ptr = '\0';
1108 *buflen = len;
1110 rcm_log_message(RSCR_TRACE,
1111 "get_line(%s): rval = %d buflen = %d line = %s\n",
1112 fdname, rval, *buflen, buf);
1113 return (rval);
1116 static void
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);
1132 rsi->pid = 0;
1133 (void) sema_post(&script_process_sema);
1137 * Kill the specified process group
1139 static int
1140 kill_pid(pid_t pid)
1142 time_t deadline, timeleft;
1143 int child_status;
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;
1150 do {
1151 if (waitpid(pid, &child_status, WNOHANG) == pid)
1152 return (0);
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 */
1161 return (-1);
1165 * Kill the specified script.
1167 static void
1168 kill_script(script_info_t *rsi)
1170 if (rsi->pid > 1) {
1171 (void) kill_pid(rsi->pid);
1172 script_exited(rsi);
1173 remove_drreq_all(rsi);
1178 * Convert rcm flags parameter to a string.
1179 * Used for debug prints.
1181 static char *
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" : "");
1188 return (buf);
1191 static void
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;
1198 argv[3] = NULL;
1199 } else
1200 argv[2] = NULL;
1204 * stderr thread:
1205 * Reads stderr and logs to syslog.
1206 * Runs as a separate thread.
1208 static void *
1209 read_stderr(script_info_t *rsi)
1211 char buf[MAX_LINE_LEN];
1212 size_t buflen;
1213 int error_num;
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);
1220 if (buflen)
1221 log_msg(rsi, RCM_ERROR, buf);
1223 return (NULL);
1226 /* process return data items passed by scripts to the framework */
1227 static int
1228 process_dataitem(script_info_t *rsi, int token, char *value, char **errmsg)
1230 char *ptr;
1231 int status;
1233 *errmsg = NULL;
1235 if (*value == '\0')
1236 goto error;
1238 switch (token) {
1239 case D_SCRIPT_VERSION:
1240 if (rsi->cmd != C_SCRIPTINFO)
1241 goto error;
1243 /* check that value contains only digits */
1244 for (ptr = value; *ptr != '\0'; ptr++)
1245 if (isdigit((int)(*ptr)) == 0)
1246 break;
1248 if (*ptr == '\0')
1249 rsi->ver = atoi(value);
1250 else
1251 goto error;
1253 break;
1255 case D_SCRIPT_FUNC_INFO:
1256 if (rsi->cmd != C_SCRIPTINFO)
1257 goto error;
1259 rcmscript_snprintf(&rsi->func_info_buf,
1260 &rsi->func_info_buf_len,
1261 &rsi->func_info_buf_curptr,
1262 "%s", value);
1263 break;
1265 case D_CMD_TIMEOUT:
1266 if (rsi->cmd != C_SCRIPTINFO)
1267 goto error;
1269 /* check that value contains only digits */
1270 for (ptr = value; *ptr != '\0'; ptr++)
1271 if (isdigit((int)(*ptr)) == 0)
1272 break;
1274 if (*ptr == '\0')
1275 rsi->cmd_timeout = atoi(value);
1276 else
1277 goto error;
1278 break;
1280 case D_RESOURCE_NAME:
1281 if (rsi->cmd != C_REGISTER)
1282 goto error;
1284 if (get_capacity_descr(value) != NULL)
1285 status = rcm_register_capacity(rsi->hdl, value,
1286 0, NULL);
1287 else
1288 status = rcm_register_interest(rsi->hdl, value, 0,
1289 NULL);
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);
1300 break;
1302 case D_RESOURCE_USAGE_INFO:
1303 if (rsi->cmd != C_RESOURCEINFO)
1304 goto error;
1306 rcmscript_snprintf(&rsi->resource_usage_info_buf,
1307 &rsi->resource_usage_info_buf_len,
1308 &rsi->resource_usage_info_buf_curptr,
1309 "%s", value);
1310 break;
1312 case D_FAILURE_REASON:
1313 rcmscript_snprintf(&rsi->failure_reason_buf,
1314 &rsi->failure_reason_buf_len,
1315 &rsi->failure_reason_buf_curptr,
1316 "%s", value);
1317 break;
1319 default:
1320 goto error;
1323 return (0);
1325 error:
1326 *errmsg = dup_err(RCM_ERROR, MS_PROTOCOL_ERR, rsi->script_name);
1327 return (-1);
1330 /* Send the given command to the script and process return data */
1331 static int
1332 do_cmd(script_info_t *rsi, char *argv[], char *envp[], char **errmsg)
1334 char buf[MAX_LINE_LEN];
1335 size_t buflen;
1336 int loglevel = -1, continuelog = 0;
1337 char *ptr, *dname, *value;
1338 time_t maxsecs;
1339 time_t deadline;
1340 int sigaborted = 0;
1341 int rval, child_status, token;
1342 int error_num;
1343 int cmd_timeout = rsi->cmd_timeout;
1345 *errmsg = NULL;
1347 script_process_sema_wait();
1349 if (run_script(rsi, argv, envp, errmsg) == -1) {
1350 (void) sema_post(&script_process_sema);
1351 goto error2;
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));
1361 goto error1;
1363 rsi->flags |= STDERR_THREAD_CREATED;
1365 /* LINTED */
1366 while (1) {
1367 if (cmd_timeout > 0) {
1368 maxsecs = deadline - time(NULL);
1369 if (maxsecs <= 0)
1370 goto timedout;
1371 } else
1372 maxsecs = 0;
1374 rval = get_line(rsi->pipe1[PARENT_END_OF_PIPE],
1375 "stdout", buf, MAX_LINE_LEN, &buflen,
1376 maxsecs, &error_num);
1378 if (buflen) {
1379 if (continuelog)
1380 log_msg(rsi, loglevel, buf);
1381 else {
1382 if ((ptr = strchr(buf, '=')) == NULL)
1383 goto error;
1385 *ptr = '\0';
1386 dname = buf;
1387 value = ptr + 1;
1388 if ((token = dname_to_id(dname)) == -1)
1389 goto error;
1391 switch (token) {
1392 case D_LOG_ERR:
1393 loglevel = RCM_ERROR;
1394 break;
1396 case D_LOG_WARN:
1397 loglevel = RCM_WARNING;
1398 break;
1400 case D_LOG_INFO:
1401 loglevel = RCM_INFO;
1402 break;
1404 case D_LOG_DEBUG:
1405 loglevel = RCM_DEBUG;
1406 break;
1408 default:
1409 loglevel = -1;
1410 break;
1413 if (loglevel != -1) {
1414 log_msg(rsi, loglevel, value);
1415 if (buf[buflen - 1] == '\n')
1416 continuelog = 0;
1417 else
1418 continuelog = 1;
1419 } else {
1420 if (buf[buflen - 1] != '\n')
1421 goto error;
1423 buf[buflen - 1] = '\0';
1424 if (process_dataitem(rsi, token,
1425 value, errmsg) != 0)
1426 goto error1;
1431 if (rval == -3) {
1432 /* script exited */
1433 waitagain:
1434 if (waitpid(rsi->pid, &child_status, 0)
1435 != rsi->pid) {
1436 if (errno == EINTR || errno == EAGAIN)
1437 goto waitagain;
1438 *errmsg = dup_err(RCM_ERROR, MS_SCRIPT_ERR,
1439 rsi->script_name, strerror(errno));
1440 goto error1;
1443 if (WIFEXITED(child_status)) {
1444 script_exited(rsi);
1445 rsi->exit_status = WEXITSTATUS(child_status);
1446 } else {
1447 if (sigaborted)
1448 *errmsg = dup_err(RCM_ERROR,
1449 MS_TIMEOUT_ERR, rsi->script_name);
1450 else
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);
1456 script_exited(rsi);
1457 goto error2;
1460 break;
1463 if (rval == -1) {
1464 *errmsg = dup_err(RCM_ERROR, MS_SCRIPT_ERR,
1465 rsi->script_name, strerror(errno));
1466 goto error1;
1469 if (rval == -2) {
1470 timedout:
1471 /* timeout occurred */
1472 if (sigaborted == 0) {
1473 (void) kill(rsi->pid, SIGABRT);
1474 sigaborted = 1;
1475 /* extend deadline */
1476 deadline += SCRIPT_ABORT_TIMEOUT;
1477 } else {
1478 *errmsg = dup_err(RCM_ERROR,
1479 MS_TIMEOUT_ERR, rsi->script_name);
1480 goto error1;
1485 return (0);
1487 error:
1488 *errmsg = dup_err(RCM_ERROR, MS_PROTOCOL_ERR, rsi->script_name);
1490 error1:
1491 kill_script(rsi);
1493 error2:
1494 return (-1);
1497 static int
1498 do_script_info(script_info_t *rsi)
1500 char *argv[MAX_ARGS];
1501 int status = RCM_FAILURE;
1502 int err = 0;
1503 char *errmsg = NULL;
1505 rcm_log_message(RSCR_TRACE, "do_script_info: script name = %s\n",
1506 rsi->script_name);
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) {
1515 case E_SUCCESS:
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;
1522 else
1523 rcm_log_message(RCM_ERROR,
1524 MS_UNSUPPORTED_VER, rsi->script_name,
1525 rsi->ver);
1526 } else
1527 err = 1;
1528 break;
1530 case E_FAILURE:
1531 if (rsi->failure_reason_buf != NULL) {
1532 rcm_log_message(RCM_ERROR, MS_SCRIPTINFO_ERR,
1533 rsi->script_name,
1534 rsi->failure_reason_buf);
1535 } else
1536 err = 1;
1537 break;
1539 default:
1540 err = 1;
1541 break;
1543 if (err)
1544 rcm_log_message(RCM_ERROR, MS_PROTOCOL_ERR,
1545 rsi->script_name);
1546 } else if (errmsg)
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);
1555 return (status);
1558 static int
1559 do_dr(script_info_t *rsi, char *argv[], char *envp[], char **info)
1561 int status = RCM_FAILURE;
1562 int err = 0;
1564 rsi->failure_reason_buf = NULL;
1566 if (do_cmd(rsi, argv, envp, info) == 0) {
1567 switch (rsi->exit_status) {
1568 case E_SUCCESS:
1569 case E_UNSUPPORTED_CMD:
1570 if (rsi->failure_reason_buf == NULL)
1571 status = RCM_SUCCESS;
1572 else
1573 err = 1;
1574 break;
1576 case E_FAILURE:
1577 case E_REFUSE:
1578 if (rsi->failure_reason_buf != NULL) {
1579 *info = rsi->failure_reason_buf;
1580 rsi->failure_reason_buf = NULL;
1581 } else
1582 err = 1;
1583 break;
1585 default:
1586 err = 1;
1587 break;
1590 if (err)
1591 *info = dup_err(RCM_ERROR, MS_PROTOCOL_ERR,
1592 rsi->script_name);
1595 if (rsi->failure_reason_buf)
1596 free(rsi->failure_reason_buf);
1598 return (status);
1602 * get_info entry point
1604 /* ARGSUSED */
1605 static int
1606 script_get_info(rcm_handle_t *hdl,
1607 char *resource_name,
1608 pid_t pid,
1609 uint_t flag,
1610 char **info,
1611 char **error,
1612 nvlist_t *props,
1613 rcm_info_t **dependent_info)
1615 script_info_t *rsi = hdl->module->rsi;
1616 char *argv[MAX_ARGS];
1617 int status = RCM_FAILURE;
1618 int err = 0;
1620 rcm_log_message(RSCR_TRACE, "script_get_info: resource = %s\n",
1621 resource_name);
1623 *info = NULL;
1624 *error = NULL;
1626 (void) mutex_lock(&rsi->channel_lock);
1628 rsi->hdl = hdl;
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) {
1636 case E_SUCCESS:
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;
1643 } else
1644 err = 1;
1645 break;
1647 case E_FAILURE:
1648 if (rsi->failure_reason_buf != NULL) {
1649 *error = rsi->failure_reason_buf;
1650 rsi->failure_reason_buf = NULL;
1651 } else
1652 err = 1;
1653 break;
1655 default:
1656 err = 1;
1657 break;
1659 if (err)
1660 *error = dup_err(RCM_ERROR, MS_PROTOCOL_ERR,
1661 rsi->script_name);
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);
1672 return (status);
1675 static void
1676 add_for_unregister(script_info_t *rsi)
1678 module_t *module = rsi->module;
1679 client_t *client;
1680 rcm_queue_t *head;
1681 rcm_queue_t *q;
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);
1695 static void
1696 remove_from_unregister(script_info_t *rsi, char *resource_name)
1698 module_t *module = rsi->module;
1699 client_t *client;
1700 rcm_queue_t *head;
1701 rcm_queue_t *q;
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;
1711 break;
1715 (void) mutex_unlock(&rcm_req_lock);
1718 static void
1719 complete_unregister(script_info_t *rsi)
1721 module_t *module = rsi->module;
1722 client_t *client;
1723 rcm_queue_t *head;
1724 rcm_queue_t *q;
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
1744 static int
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;
1750 int err = 0;
1751 char *errmsg = NULL;
1753 rcm_log_message(RSCR_TRACE,
1754 "script_register_interest: script name = %s\n",
1755 rsi->script_name);
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);
1765 rsi->hdl = hdl;
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) {
1774 case E_SUCCESS:
1775 status = RCM_SUCCESS;
1776 break;
1778 case E_FAILURE:
1779 if (rsi->failure_reason_buf != NULL) {
1780 rcm_log_message(RCM_ERROR, MS_REGISTER_ERR,
1781 rsi->script_name,
1782 rsi->failure_reason_buf);
1783 } else
1784 err = 1;
1785 break;
1787 default:
1788 err = 1;
1789 break;
1791 if (err)
1792 rcm_log_message(RCM_ERROR, MS_PROTOCOL_ERR,
1793 rsi->script_name);
1794 } else if (errmsg)
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);
1804 return (status);
1808 * Add the specified resource name to the drreq_q.
1810 static void
1811 add_drreq(script_info_t *rsi, char *resource_name)
1813 rcm_queue_t *head = &rsi->drreq_q;
1814 rcm_queue_t *q;
1815 drreq_t *drreq;
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 */
1822 return;
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.
1834 static void
1835 remove_drreq(script_info_t *rsi, char *resource_name)
1837 rcm_queue_t *head = &rsi->drreq_q;
1838 rcm_queue_t *q;
1839 drreq_t *drreq;
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)
1845 break;
1848 if (q != head) {
1849 /* found drreq on the queue */
1850 rcm_dequeue(&drreq->queue);
1851 free(drreq->resource_name);
1852 free(drreq);
1857 * Remove all dr req's.
1859 static void
1860 remove_drreq_all(script_info_t *rsi)
1862 drreq_t *drreq;
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
1874 /* ARGSUSED */
1875 static int
1876 script_request_offline(rcm_handle_t *hdl,
1877 char *resource_name,
1878 pid_t pid,
1879 uint_t flag,
1880 char **info,
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];
1887 int status;
1888 int i;
1890 rcm_log_message(RSCR_TRACE,
1891 "script_request_offline: resource = %s flags = %s\n",
1892 resource_name,
1893 flags_to_name(flag, flags_name, MAX_FLAGS_NAME_LEN));
1895 *info = NULL;
1897 (void) mutex_lock(&rsi->channel_lock);
1899 rsi->hdl = hdl;
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;
1910 envp[i] = NULL;
1912 status = do_dr(rsi, argv, envp, info);
1914 (void) mutex_unlock(&rsi->channel_lock);
1915 return (status);
1919 * notify_online entry point
1921 /* ARGSUSED */
1922 static int
1923 script_notify_online(rcm_handle_t *hdl,
1924 char *resource_name,
1925 pid_t pid,
1926 uint_t flag,
1927 char **info,
1928 rcm_info_t **dependent_info)
1930 script_info_t *rsi = hdl->module->rsi;
1931 char *argv[MAX_ARGS];
1932 int status;
1934 rcm_log_message(RSCR_TRACE, "script_notify_online: resource = %s\n",
1935 resource_name);
1937 *info = NULL;
1939 (void) mutex_lock(&rsi->channel_lock);
1941 rsi->hdl = hdl;
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);
1950 return (status);
1954 * notify_remove entry point
1956 /* ARGSUSED */
1957 static int
1958 script_notify_remove(rcm_handle_t *hdl,
1959 char *resource_name,
1960 pid_t pid,
1961 uint_t flag,
1962 char **info,
1963 rcm_info_t **dependent_info)
1965 script_info_t *rsi = hdl->module->rsi;
1966 char *argv[MAX_ARGS];
1967 int status;
1969 rcm_log_message(RSCR_TRACE, "script_notify_remove: resource = %s\n",
1970 resource_name);
1972 *info = NULL;
1974 (void) mutex_lock(&rsi->channel_lock);
1976 rsi->hdl = hdl;
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);
1985 return (status);
1989 * request_suspend entry point
1991 /* ARGSUSED */
1992 static int
1993 script_request_suspend(rcm_handle_t *hdl,
1994 char *resource_name,
1995 pid_t pid,
1996 timespec_t *interval,
1997 uint_t flag,
1998 char **info,
1999 rcm_info_t **dependent_info)
2001 script_info_t *rsi = hdl->module->rsi;
2002 char *buf = NULL;
2003 char *curptr = NULL;
2004 char *argv[MAX_ARGS];
2005 char *envp[MAX_ENV_PARAMS];
2006 char flags_name[MAX_FLAGS_NAME_LEN];
2007 int buflen = 0;
2008 long seconds;
2009 int status;
2010 int i;
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));
2016 *info = NULL;
2018 (void) mutex_lock(&rsi->channel_lock);
2020 rsi->hdl = hdl;
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;
2033 if (interval) {
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,
2042 seconds);
2043 envp[i++] = buf;
2046 envp[i] = NULL;
2048 status = do_dr(rsi, argv, envp, info);
2050 (void) mutex_unlock(&rsi->channel_lock);
2051 if (buf)
2052 free(buf);
2053 return (status);
2057 * notify_resume entry point
2059 /* ARGSUSED */
2060 static int
2061 script_notify_resume(rcm_handle_t *hdl,
2062 char *resource_name,
2063 pid_t pid,
2064 uint_t flag,
2065 char **info,
2066 rcm_info_t **dependent_info)
2068 script_info_t *rsi = hdl->module->rsi;
2069 char *argv[MAX_ARGS];
2070 int status;
2072 rcm_log_message(RSCR_TRACE, "script_notify_resume: resource = %s\n",
2073 resource_name);
2075 *info = NULL;
2077 (void) mutex_lock(&rsi->channel_lock);
2079 rsi->hdl = hdl;
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);
2088 return (status);
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",
2095 "", ""},
2096 { "SUNW_cpu", MATCH_EXACT,
2097 "new_total", "RCM_ENV_CAPACITY",
2098 "new_cpu_list", "RCM_ENV_CPU_IDS",
2099 "", ""},
2100 { "SUNW_cpu/set", MATCH_PREFIX,
2101 "new_total", "RCM_ENV_CAPACITY",
2102 "new_cpu_list", "RCM_ENV_CPU_IDS",
2103 "", ""},
2104 { "", MATCH_INVALID, "", "" }
2107 static capacity_descr_t *
2108 get_capacity_descr(char *resource_name)
2110 int i;
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,
2118 resource_name,
2119 strlen(capacity_type[i].resource_name)) == 0))
2121 return (&capacity_type[i]);
2124 return (NULL);
2127 static int
2128 build_env_for_capacity(script_info_t *rsi,
2129 char *resource_name,
2130 uint_t flag,
2131 nvlist_t *capacity_info,
2132 char *envp[],
2133 int *dynamic_env_index,
2134 char **errmsg)
2136 int p, i;
2137 capacity_descr_t *capa = NULL;
2138 nvpair_t *nvpair;
2139 char *buf;
2140 char *curptr;
2141 int buflen;
2142 int error;
2143 uint_t n;
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 :
2151 script_env_noforce;
2153 envp[p] = NULL;
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);
2159 return (-1);
2162 for (i = 0; *capa->param[i].nvname != '\0'; i++) {
2163 nvpair = NULL;
2164 while ((nvpair = nvlist_next_nvpair(capacity_info, nvpair))
2165 != NULL) {
2166 if (strcmp(nvpair_name(nvpair),
2167 capa->param[i].nvname) == 0)
2168 break;
2171 if (nvpair == NULL) {
2172 *errmsg = dup_err(RCM_ERROR, MF_NV_ERR,
2173 rsi->script_name);
2174 return (-1);
2177 error = 0;
2178 buf = NULL;
2180 rcmscript_snprintf(&buf, &buflen, &curptr, "%s=",
2181 capa->param[i].envname);
2183 switch (nvpair_type(nvpair)) {
2184 case DATA_TYPE_INT16:
2186 int16_t x;
2188 if (nvpair_value_int16(nvpair, &x) == 0) {
2189 rcmscript_snprintf(&buf, &buflen, &curptr,
2190 "%hd", (short)x);
2191 } else
2192 error = 1;
2193 break;
2196 case DATA_TYPE_UINT16:
2198 uint16_t x;
2200 if (nvpair_value_uint16(nvpair, &x) == 0) {
2201 rcmscript_snprintf(&buf, &buflen, &curptr,
2202 "%hu", (unsigned short)x);
2203 } else
2204 error = 1;
2205 break;
2208 case DATA_TYPE_INT32:
2210 int32_t x;
2212 if (nvpair_value_int32(nvpair, &x) == 0) {
2213 rcmscript_snprintf(&buf, &buflen, &curptr,
2214 "%d", (int)x);
2215 } else
2216 error = 1;
2217 break;
2220 case DATA_TYPE_UINT32:
2222 uint32_t x;
2224 if (nvpair_value_uint32(nvpair, &x) == 0) {
2225 rcmscript_snprintf(&buf, &buflen, &curptr,
2226 "%u", (uint_t)x);
2227 } else
2228 error = 1;
2229 break;
2232 case DATA_TYPE_INT64:
2234 int64_t x;
2236 if (nvpair_value_int64(nvpair, &x) == 0) {
2237 rcmscript_snprintf(&buf, &buflen, &curptr,
2238 "%lld", (long long)x);
2239 } else
2240 error = 1;
2241 break;
2244 case DATA_TYPE_UINT64:
2246 uint64_t x;
2248 if (nvpair_value_uint64(nvpair, &x) == 0) {
2249 rcmscript_snprintf(&buf, &buflen, &curptr,
2250 "%llu", (unsigned long long)x);
2251 } else
2252 error = 1;
2253 break;
2256 case DATA_TYPE_INT16_ARRAY:
2258 int16_t *x;
2260 if (nvpair_value_int16_array(nvpair, &x, &n) == 0) {
2261 while (n--) {
2262 rcmscript_snprintf(&buf, &buflen,
2263 &curptr, "%hd%s",
2264 (short)(*x),
2265 (n == 0) ? "" : " ");
2266 x++;
2268 } else
2269 error = 1;
2270 break;
2273 case DATA_TYPE_UINT16_ARRAY:
2275 uint16_t *x;
2277 if (nvpair_value_uint16_array(nvpair, &x, &n) == 0) {
2278 while (n--) {
2279 rcmscript_snprintf(&buf, &buflen,
2280 &curptr, "%hu%s",
2281 (unsigned short)(*x),
2282 (n == 0) ? "" : " ");
2283 x++;
2285 } else
2286 error = 1;
2287 break;
2290 case DATA_TYPE_INT32_ARRAY:
2292 int32_t *x;
2294 if (nvpair_value_int32_array(nvpair, &x, &n) == 0) {
2295 while (n--) {
2296 rcmscript_snprintf(&buf, &buflen,
2297 &curptr, "%d%s",
2298 (int)(*x),
2299 (n == 0) ? "" : " ");
2300 x++;
2302 } else
2303 error = 1;
2304 break;
2307 case DATA_TYPE_UINT32_ARRAY:
2309 uint32_t *x;
2311 if (nvpair_value_uint32_array(nvpair, &x, &n) == 0) {
2312 while (n--) {
2313 rcmscript_snprintf(&buf, &buflen,
2314 &curptr, "%u%s",
2315 (uint_t)(*x),
2316 (n == 0) ? "" : " ");
2317 x++;
2319 } else
2320 error = 1;
2321 break;
2324 case DATA_TYPE_INT64_ARRAY:
2326 int64_t *x;
2328 if (nvpair_value_int64_array(nvpair, &x, &n) == 0) {
2329 while (n--) {
2330 rcmscript_snprintf(&buf, &buflen,
2331 &curptr, "%lld%s",
2332 (long long)(*x),
2333 (n == 0) ? "" : " ");
2334 x++;
2336 } else
2337 error = 1;
2338 break;
2341 case DATA_TYPE_UINT64_ARRAY:
2343 uint64_t *x;
2345 if (nvpair_value_uint64_array(nvpair, &x, &n) == 0) {
2346 while (n--) {
2347 rcmscript_snprintf(&buf, &buflen,
2348 &curptr, "%llu%s",
2349 (unsigned long long)(*x),
2350 (n == 0) ? "" : " ");
2351 x++;
2353 } else
2354 error = 1;
2355 break;
2358 case DATA_TYPE_STRING:
2360 char *x;
2362 if (nvpair_value_string(nvpair, &x) == 0) {
2363 rcmscript_snprintf(&buf, &buflen, &curptr,
2364 "%s", x);
2365 } else
2366 error = 1;
2367 break;
2371 default:
2372 error = 1;
2373 break;
2376 envp[p++] = buf;
2378 if (error) {
2379 envp[p] = NULL;
2380 for (p = *dynamic_env_index; envp[p] != NULL; p++)
2381 free(envp[p]);
2382 *errmsg = dup_err(RCM_ERROR, MF_NV_ERR,
2383 rsi->script_name);
2384 return (-1);
2388 envp[p] = NULL;
2390 return (0);
2394 * request_capacity_change entry point
2396 /* ARGSUSED */
2397 static int
2398 script_request_capacity_change(rcm_handle_t *hdl,
2399 char *resource_name,
2400 pid_t pid,
2401 uint_t flag,
2402 nvlist_t *capacity_info,
2403 char **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];
2410 int status;
2411 int dynamic_env_index;
2413 rcm_log_message(RSCR_TRACE,
2414 "script_request_capacity_change: resource = %s flags = %s\n",
2415 resource_name,
2416 flags_to_name(flag, flags_name, MAX_FLAGS_NAME_LEN));
2418 *info = NULL;
2420 (void) mutex_lock(&rsi->channel_lock);
2422 rsi->hdl = hdl;
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++;
2435 } else
2436 status = RCM_FAILURE;
2438 (void) mutex_unlock(&rsi->channel_lock);
2439 return (status);
2443 * notify_capacity_change entry point
2445 /* ARGSUSED */
2446 static int
2447 script_notify_capacity_change(rcm_handle_t *hdl,
2448 char *resource_name,
2449 pid_t pid,
2450 uint_t flag,
2451 nvlist_t *capacity_info,
2452 char **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];
2458 int status;
2459 int dynamic_env_index;
2461 rcm_log_message(RSCR_TRACE,
2462 "script_notify_capacity_change: resource = %s\n", resource_name);
2464 *info = NULL;
2466 (void) mutex_lock(&rsi->channel_lock);
2468 rsi->hdl = hdl;
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++;
2481 } else
2482 status = RCM_FAILURE;
2484 (void) mutex_unlock(&rsi->channel_lock);
2485 return (status);
2488 /* Log the message to syslog */
2489 static void
2490 log_msg(script_info_t *rsi, int level, char *msg)
2492 rcm_log_msg(level, MS_LOG_MSG, rsi->script_name, msg);
2495 /*PRINTFLIKE2*/
2496 static char *
2497 dup_err(int level, char *format, ...)
2499 va_list ap;
2500 char buf1[1];
2501 char *buf2;
2502 int n;
2504 va_start(ap, format);
2505 n = vsnprintf(buf1, 1, format, ap);
2506 va_end(ap);
2508 if (n > 0) {
2509 n++;
2510 if (buf2 = (char *)malloc(n)) {
2511 va_start(ap, format);
2512 n = vsnprintf(buf2, n, format, ap);
2513 va_end(ap);
2514 if (n > 0) {
2515 if (level != -1)
2516 rcm_log_message(level, buf2);
2517 return (buf2);
2519 free(buf2);
2523 return (NULL);
2526 /*PRINTFLIKE4*/
2527 static void
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
2534 va_list ap;
2535 int offset, bytesneeded, bytesleft, error_num;
2537 if (*buf == NULL) {
2538 *buflen = 0;
2539 *curptr = NULL;
2542 offset = *curptr - *buf;
2543 bytesneeded = SPRINTF_MIN_CHUNK_LEN;
2544 bytesleft = *buflen - offset;
2546 /* LINTED */
2547 while (1) {
2548 if (bytesneeded > bytesleft) {
2549 *buflen += RSCR_ROUNDUP(bytesneeded - bytesleft,
2550 SPRINTF_CHUNK_LEN);
2551 if ((*buf = (char *)realloc(*buf, *buflen)) == NULL) {
2552 error_num = errno;
2553 rcm_log_message(RCM_ERROR,
2554 MF_MEMORY_ALLOCATION_ERR,
2555 strerror(error_num));
2556 rcmd_exit(error_num);
2557 /*NOTREACHED*/
2559 *curptr = *buf + offset;
2560 bytesleft = *buflen - offset;
2563 va_start(ap, format);
2564 bytesneeded = vsnprintf(*curptr, bytesleft, format, ap);
2565 va_end(ap);
2567 if (bytesneeded < 0) {
2568 /* vsnprintf encountered an error */
2569 error_num = errno;
2570 rcm_log_message(RCM_ERROR, MF_FUNC_CALL_ERR,
2571 "vsnprintf", strerror(error_num));
2572 rcmd_exit(error_num);
2573 /*NOTREACHED*/
2575 } else if (bytesneeded < bytesleft) {
2576 /* vsnprintf succeeded */
2577 *curptr += bytesneeded;
2578 return;
2580 } else {
2581 bytesneeded++; /* to account for storage for '\0' */
2586 static char *
2587 rcmscript_strdup(char *str)
2589 char *dupstr;
2591 if ((dupstr = strdup(str)) == NULL) {
2592 rcm_log_message(RCM_ERROR, MF_MEMORY_ALLOCATION_ERR,
2593 strerror(errno));
2594 rcmd_exit(errno);
2595 /*NOTREACHED*/
2598 return (dupstr);
2601 static void *
2602 rcmscript_malloc(size_t len)
2604 void *ptr;
2606 if ((ptr = malloc(len)) == NULL) {
2607 rcm_log_message(RCM_ERROR, MF_MEMORY_ALLOCATION_ERR,
2608 strerror(errno));
2609 rcmd_exit(errno);
2610 /*NOTREACHED*/
2613 return (ptr);
2616 static void *
2617 rcmscript_calloc(size_t nelem, size_t elsize)
2619 void *ptr;
2621 if ((ptr = calloc(nelem, elsize)) == NULL) {
2622 rcm_log_message(RCM_ERROR, MF_MEMORY_ALLOCATION_ERR,
2623 strerror(errno));
2624 rcmd_exit(errno);
2625 /*NOTREACHED*/
2628 return (ptr);