dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / rcm_daemon / common / rcm_script.c
blob165c946cd832551f994ddcd822eeee956b4f8e21
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.
28 * rcm scripting module:
30 * This module implements rcm scripting interfaces.
31 * It translates rcm module based interfaces to rcm script based
32 * interfaces.
34 * Entry points:
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.
64 #include "rcm_impl.h"
65 #include "rcm_script_impl.h"
66 #include <sys/resource.h>
67 #include <procfs.h>
68 #include <sys/proc.h>
69 #include <ctype.h>
72 * All rcm scripting commands are enumerated here.
73 * NOTE: command positions in script_cmd_id_t and script_cmd_name must match.
75 typedef enum {
76 C_SCRIPTINFO,
77 C_RESOURCEINFO,
78 C_REGISTER,
79 C_QUERYREMOVE,
80 C_PREREMOVE,
81 C_POSTREMOVE,
82 C_UNDOREMOVE,
83 C_QUERYCAPACITY,
84 C_PRECAPACITY,
85 C_POSTCAPACITY,
86 C_QUERYSUSPEND,
87 C_PRESUSPEND,
88 C_POSTRESUME,
89 C_CANCELSUSPEND
90 } script_cmd_id_t;
92 /* NOTE: command positions in script_cmd_id_t and script_cmd_name must match */
93 static char *script_cmd_name[] = {
94 "scriptinfo",
95 "resourceinfo",
96 "register",
97 "queryremove",
98 "preremove",
99 "postremove",
100 "undoremove",
101 "querycapacity",
102 "precapacity",
103 "postcapacity",
104 "querysuspend",
105 "presuspend",
106 "postresume",
107 "cancelsuspend",
108 NULL
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.
116 typedef enum {
117 D_SCRIPT_VERSION,
118 D_SCRIPT_FUNC_INFO,
119 D_CMD_TIMEOUT,
120 D_RESOURCE_NAME,
121 D_RESOURCE_USAGE_INFO,
122 D_FAILURE_REASON,
123 D_LOG_ERR,
124 D_LOG_WARN,
125 D_LOG_INFO,
126 D_LOG_DEBUG
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",
136 "rcm_cmd_timeout",
137 "rcm_resource_name",
138 "rcm_resource_usage_info",
139 "rcm_failure_reason",
140 "rcm_log_err",
141 "rcm_log_warn",
142 "rcm_log_info",
143 "rcm_log_debug",
144 NULL
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,
192 uint32_t);
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 =
252 RCM_MOD_OPS_VERSION,
253 script_register_interest, /* register */
254 script_register_interest, /* unregister */
255 script_get_info,
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,
263 NULL
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")
277 #define MF_NV_ERR \
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")
297 #define MS_LOG_MSG \
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 */
311 build_env();
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,
320 USYNC_THREAD, NULL);
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,
330 PS_STATE_FILE_VER);
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);
343 return (0);
347 * Do any cleanup.
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);
355 return (0);
359 * Initialize the given rcm script.
360 * module->name contains the name of the rcm script.
362 struct rcm_mod_ops *
363 script_init(module_t *module)
365 script_info_t *rsi;
366 size_t len;
367 char *script_path;
369 rcm_log_message(RSCR_TRACE, "script_init: script name = %s\n",
370 module->name);
372 module->rsi = NULL;
374 if ((script_path = rcm_get_script_dir(module->name)) == NULL)
375 return (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,
389 module->name);
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
398 * default
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);
409 module->rsi = rsi;
410 return (&script_ops);
413 (void) mutex_unlock(&rsi->channel_lock);
415 free(rsi->script_full_name);
416 free(rsi);
417 return (NULL);
421 * Returns a string describing the script's functionality.
422 * module->name contains the name of the rcm script for which information
423 * is requested.
425 char *
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",
431 rsi->script_name);
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.
438 * Do any cleanup.
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",
446 rsi->script_name);
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);
458 free(rsi);
460 module->rsi = NULL;
462 return (RCM_SUCCESS);
465 /* build base environment for scripts */
466 static void
467 build_env(void)
469 const char *env_list[] = { "LANG", "LC_COLLATE", "LC_CTYPE",
470 "LC_MESSAGES", "LC_MONETARY", "LC_NUMERIC", "LC_TIME",
471 "LC_ALL", "TZ", NULL };
472 char *x;
473 int len;
474 int i, j = 0;
475 int d;
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]);
482 if (x) {
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",
487 env_list[i], x);
491 len = strlen("RCM_ENV_DEBUG_LEVEL") + 3;
492 script_env[j] = (char *)rcmscript_malloc(len);
494 if (debug_level < 0)
495 d = 0;
496 else if (debug_level > 9)
497 d = 9;
498 else
499 d = debug_level;
501 (void) snprintf(script_env[j++], len, "RCM_ENV_DEBUG_LEVEL=%d", d);
503 script_env[j] = NULL;
506 static void
507 copy_env(char *src[], char *dst[])
509 int i;
511 for (i = 0; src[i] != NULL; i++)
512 dst[i] = src[i];
514 dst[i] = NULL;
518 * Open (or create if the file does not exist) the given state file
519 * and mmap it.
521 static void
522 open_state_file(const char *filename,
523 state_file_descr_t *statefd,
524 size_t element_size,
525 int chunk_size,
526 uint32_t version)
528 struct stat stats;
529 int error_num;
531 if ((statefd->fd = open(filename, O_CREAT|O_RDWR, 0600)) ==
532 -1) {
533 error_num = errno;
534 rcm_log_message(RCM_ERROR, MF_STATE_FILE_ERR,
535 "open", strerror(error_num));
536 rcmd_exit(error_num);
537 /*NOTREACHED*/
540 if (fstat(statefd->fd, &stats) != 0) {
541 error_num = errno;
542 rcm_log_message(RCM_ERROR, MF_STATE_FILE_ERR,
543 "fstat", strerror(error_num));
544 rcmd_exit(error_num);
545 /*NOTREACHED*/
548 if (stats.st_size != 0) {
549 /* LINTED */
550 statefd->state_file = mmap(NULL,
551 stats.st_size, PROT_READ|PROT_WRITE, MAP_SHARED,
552 statefd->fd, 0);
554 if (statefd->state_file == MAP_FAILED) {
555 error_num = errno;
556 rcm_log_message(RCM_ERROR, MF_STATE_FILE_ERR,
557 "mmap", strerror(error_num));
558 rcmd_exit(error_num);
559 /*NOTREACHED*/
562 if (statefd->state_file->version != version) {
563 (void) munmap((void *)statefd->state_file,
564 stats.st_size);
565 statefd->state_file = NULL;
566 (void) ftruncate(statefd->fd, 0);
568 } else {
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;
576 statefd->index = 0;
579 static void
580 truncate_state_file(state_file_descr_t *statefd)
582 size_t size;
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);
594 static void
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
604 * and mmap it.
606 static void
607 grow_state_file(state_file_descr_t *statefd)
609 size_t size;
610 int max_elements;
611 int error_num;
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) {
621 error_num = errno;
622 rcm_log_message(RCM_ERROR, MF_STATE_FILE_ERR,
623 "ftruncate", strerror(error_num));
624 rcmd_exit(error_num);
625 /*NOTREACHED*/
628 /* LINTED */
629 statefd->state_file = mmap(NULL, size,
630 PROT_READ|PROT_WRITE, MAP_SHARED, statefd->fd, 0);
632 if (statefd->state_file == MAP_FAILED) {
633 error_num = errno;
634 rcm_log_message(RCM_ERROR, MF_STATE_FILE_ERR,
635 "mmap", strerror(error_num));
636 rcmd_exit(error_num);
637 /*NOTREACHED*/
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
647 * state element.
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.
652 static void *
653 get_state_element(state_file_descr_t *statefd, int index, int *flag)
655 char *ptr;
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;
664 if (flag) {
665 *flag = (((state_element_t *)((void *)ptr))->flags &
666 STATE_ELEMENT_IN_USE) ? 1 : 0;
669 ptr += sizeof (state_element_t);
670 } else
671 ptr = NULL;
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.
682 static void *
683 allocate_state_element(state_file_descr_t *statefd, int *index)
685 void *x;
686 int i;
687 int flag;
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,
693 &flag);
694 assert(x != NULL);
696 if (flag == 0)
697 /* entry is free */
698 break;
700 statefd->index++;
701 if (statefd->index >= statefd->state_file->max_elements)
702 statefd->index = 0;
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);
712 assert(flag == 0);
715 if (index != NULL)
716 *index = statefd->index;
718 statefd->index++;
719 if (statefd->index >= statefd->state_file->max_elements)
720 statefd->index = 0;
722 ((state_element_t *)x - 1)->flags |= STATE_ELEMENT_IN_USE;
723 return (x);
726 static void
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.
735 static void
736 script_ps_state_file_kill_pids(void)
738 ps_state_element_t *x;
739 char procfile[80];
740 psinfo_t psi;
741 int fd, i, flag;
743 /* LINTED */
744 for (i = 0; 1; i++) {
745 if ((x = (ps_state_element_t *)get_state_element(
746 &script_ps_statefd, i, &flag)) == NULL)
747 break;
749 if (flag == 1) { /* the entry is in use */
750 (void) snprintf(procfile, 80, "/proc/%ld/psinfo",
751 (long)x->pid);
752 if ((fd = open(procfile, O_RDONLY)) != -1 &&
753 read(fd, &psi, sizeof (psi)) == sizeof (psi) &&
754 strcmp(psi.pr_fname,
755 x->script_name) == 0) {
757 (void) close(fd);
760 * just a safety check to not to blow up
761 * system processes if the file is ever corrupt
763 if (x->pid > 1) {
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);
772 } else {
773 if (fd != -1)
774 (void) close(fd);
776 free_state_element((void *)x);
782 * Add a state element entry to ps state file.
784 static void
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);
794 x->pid = pid;
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
804 * ps state file.
806 static void
807 script_ps_state_file_remove_entry(pid_t pid)
809 ps_state_element_t *x;
810 int flag, i;
812 (void) mutex_lock(&script_lock);
814 /* LINTED */
815 for (i = 0; 1; i++) {
816 if ((x = (ps_state_element_t *)get_state_element(
817 &script_ps_statefd, i, &flag)) == NULL)
818 break;
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);
823 break;
827 (void) mutex_unlock(&script_lock);
831 * Get data item id given data item name
833 static int
834 dname_to_id(char *dname)
836 int i;
838 for (i = 0; script_data_item_name[i] != NULL; i++) {
839 if (strcmp(dname, script_data_item_name[i]) == 0)
840 return (i);
843 return (-1);
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.
851 static void
852 script_process_sema_wait(void)
854 int error_num;
856 /* LINTED */
857 while (1) {
858 if (sema_wait(&script_process_sema) == 0)
859 return;
861 if (errno != EINTR && errno != EAGAIN) {
862 error_num = errno;
863 rcm_log_message(RCM_ERROR, MF_FUNC_CALL_ERR,
864 "sema_wait", strerror(error_num));
865 rcmd_exit(error_num);
866 /*NOTREACHED*/
870 /*NOTREACHED*/
874 * Fork and execute the script.
876 static int
877 run_script(script_info_t *rsi, char *argv[], char *envp[], char **errmsg)
879 int i, p1 = -1, p2 = -1;
880 struct rlimit rlp;
881 struct stat stats;
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",
888 i, argv[i]);
890 *errmsg = NULL;
892 /* check that the script exists */
893 if (stat(rsi->script_full_name, &stats) != 0)
894 goto error;
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
899 * open file count.
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
911 * openlog
912 * Some files which are opened briefly and closed such as
913 * directory files.
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
917 * parallel.
919 if ((p1 = pipe(rsi->pipe1)) == -1 || (p2 = pipe(rsi->pipe2)) == -1) {
920 if ((errno == EMFILE) &&
921 (getrlimit(RLIMIT_NOFILE, &rlp) == 0)) {
923 rlp.rlim_cur += 16;
924 if (rlp.rlim_max < rlp.rlim_cur)
925 rlp.rlim_max = rlp.rlim_cur;
926 (void) setrlimit(RLIMIT_NOFILE, &rlp);
928 if (p1 == -1) {
929 if ((p1 = pipe(rsi->pipe1)) == -1)
930 goto error;
932 if ((p2 = pipe(rsi->pipe2)) == -1)
933 goto error;
934 } else
935 goto error;
938 forkagain:
939 if ((rsi->pid = fork1()) == (pid_t)-1) {
940 if (errno == EINTR || errno == EAGAIN)
941 goto forkagain;
943 goto error;
946 if (rsi->pid == 0) {
947 /* child process */
949 (void) setsid();
951 /* close stdin, stdout and stderr */
952 (void) close(0);
953 (void) close(1);
954 (void) close(2);
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 */
964 closefrom(3);
966 /* restore original file limit */
967 (void) setrlimit(RLIMIT_NOFILE, &file_limit);
969 /* set current working dir */
970 if (stats.st_uid == 0) {
971 /* root */
972 if (chdir("/var/run") == -1)
973 _exit(127);
974 } else {
975 if (chdir("/tmp") == -1)
976 _exit(127);
980 * setuid sets real, effective and saved user ids to the
981 * given id.
982 * setgid sets real, effective and saved group ids to the
983 * given id.
985 (void) setgid(stats.st_gid);
986 (void) setuid(stats.st_uid);
988 (void) execve(rsi->script_full_name, argv, envp);
989 _exit(127);
990 /*NOTREACHED*/
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);
998 return (0);
1000 error:
1001 *errmsg = dup_err(RCM_ERROR, MS_SCRIPT_ERR,
1002 rsi->script_name, strerror(errno));
1004 if (p1 != -1) {
1005 (void) close(rsi->pipe1[PARENT_END_OF_PIPE]);
1006 (void) close(rsi->pipe1[CHILD_END_OF_PIPE]);
1009 if (p2 != -1) {
1010 (void) close(rsi->pipe2[PARENT_END_OF_PIPE]);
1011 (void) close(rsi->pipe2[CHILD_END_OF_PIPE]);
1014 return (-1);
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.
1026 * Return values:
1027 * 0 success
1028 * -1 an error occured
1029 * -2 timeout occurred
1030 * -3 script exited
1032 static int
1033 get_line(int fd,
1034 char *fdname,
1035 char *buf,
1036 int maxbuflen,
1037 size_t *buflen,
1038 time_t timeoutval,
1039 int *error_num)
1041 char c = '\0';
1042 struct pollfd fds[1];
1043 int x;
1044 size_t len = 0;
1045 char *ptr;
1046 int timeit;
1047 time_t deadline;
1048 int rval = 0;
1050 if (timeoutval) {
1051 timeit = TRUE;
1052 deadline = time(NULL) + timeoutval;
1053 fds[0].fd = fd;
1054 fds[0].events = POLLIN;
1055 } else
1056 timeit = FALSE;
1058 ptr = buf;
1060 while (c != '\n' && len < (maxbuflen -1)) {
1061 if (timeit) {
1062 pollagain:
1063 fds[0].revents = 0;
1064 timeoutval = deadline - time(NULL);
1065 if (timeoutval <= 0) {
1066 rval = -2;
1067 break;
1069 x = poll(fds, 1, timeoutval*1000);
1070 if (x <= 0) {
1071 if (x == 0)
1072 /* poll timedout */
1073 rval = -2;
1074 else {
1075 if (errno == EINTR || errno == EAGAIN)
1076 goto pollagain;
1077 *error_num = errno;
1078 rval = -1;
1080 break;
1083 readagain:
1084 if ((x = read(fd, &c, 1)) != 1) {
1085 if (x == 0)
1087 * Script exited. Or more specifically the
1088 * script has closed its end of the pipe.
1090 rval = -3;
1091 else {
1092 if (errno == EINTR || errno == EAGAIN)
1093 goto readagain;
1094 *error_num = errno;
1095 rval = -1;
1097 break;
1100 *ptr++ = c;
1101 len++;
1104 *ptr = '\0';
1105 *buflen = len;
1107 rcm_log_message(RSCR_TRACE,
1108 "get_line(%s): rval = %d buflen = %d line = %s\n",
1109 fdname, rval, *buflen, buf);
1110 return (rval);
1113 static void
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);
1129 rsi->pid = 0;
1130 (void) sema_post(&script_process_sema);
1134 * Kill the specified process group
1136 static int
1137 kill_pid(pid_t pid)
1139 time_t deadline, timeleft;
1140 int child_status;
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;
1147 do {
1148 if (waitpid(pid, &child_status, WNOHANG) == pid)
1149 return (0);
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 */
1158 return (-1);
1162 * Kill the specified script.
1164 static void
1165 kill_script(script_info_t *rsi)
1167 if (rsi->pid > 1) {
1168 (void) kill_pid(rsi->pid);
1169 script_exited(rsi);
1170 remove_drreq_all(rsi);
1175 * Convert rcm flags parameter to a string.
1176 * Used for debug prints.
1178 static char *
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" : "");
1185 return (buf);
1188 static void
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;
1195 argv[3] = NULL;
1196 } else
1197 argv[2] = NULL;
1201 * stderr thread:
1202 * Reads stderr and logs to syslog.
1203 * Runs as a separate thread.
1205 static void *
1206 read_stderr(script_info_t *rsi)
1208 char buf[MAX_LINE_LEN];
1209 size_t buflen;
1210 int error_num;
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);
1217 if (buflen)
1218 log_msg(rsi, RCM_ERROR, buf);
1220 return (NULL);
1223 /* process return data items passed by scripts to the framework */
1224 static int
1225 process_dataitem(script_info_t *rsi, int token, char *value, char **errmsg)
1227 char *ptr;
1228 int status;
1230 *errmsg = NULL;
1232 if (*value == '\0')
1233 goto error;
1235 switch (token) {
1236 case D_SCRIPT_VERSION:
1237 if (rsi->cmd != C_SCRIPTINFO)
1238 goto error;
1240 /* check that value contains only digits */
1241 for (ptr = value; *ptr != '\0'; ptr++)
1242 if (isdigit((int)(*ptr)) == 0)
1243 break;
1245 if (*ptr == '\0')
1246 rsi->ver = atoi(value);
1247 else
1248 goto error;
1250 break;
1252 case D_SCRIPT_FUNC_INFO:
1253 if (rsi->cmd != C_SCRIPTINFO)
1254 goto error;
1256 rcmscript_snprintf(&rsi->func_info_buf,
1257 &rsi->func_info_buf_len,
1258 &rsi->func_info_buf_curptr,
1259 "%s", value);
1260 break;
1262 case D_CMD_TIMEOUT:
1263 if (rsi->cmd != C_SCRIPTINFO)
1264 goto error;
1266 /* check that value contains only digits */
1267 for (ptr = value; *ptr != '\0'; ptr++)
1268 if (isdigit((int)(*ptr)) == 0)
1269 break;
1271 if (*ptr == '\0')
1272 rsi->cmd_timeout = atoi(value);
1273 else
1274 goto error;
1275 break;
1277 case D_RESOURCE_NAME:
1278 if (rsi->cmd != C_REGISTER)
1279 goto error;
1281 if (get_capacity_descr(value) != NULL)
1282 status = rcm_register_capacity(rsi->hdl, value,
1283 0, NULL);
1284 else
1285 status = rcm_register_interest(rsi->hdl, value, 0,
1286 NULL);
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);
1297 break;
1299 case D_RESOURCE_USAGE_INFO:
1300 if (rsi->cmd != C_RESOURCEINFO)
1301 goto error;
1303 rcmscript_snprintf(&rsi->resource_usage_info_buf,
1304 &rsi->resource_usage_info_buf_len,
1305 &rsi->resource_usage_info_buf_curptr,
1306 "%s", value);
1307 break;
1309 case D_FAILURE_REASON:
1310 rcmscript_snprintf(&rsi->failure_reason_buf,
1311 &rsi->failure_reason_buf_len,
1312 &rsi->failure_reason_buf_curptr,
1313 "%s", value);
1314 break;
1316 default:
1317 goto error;
1320 return (0);
1322 error:
1323 *errmsg = dup_err(RCM_ERROR, MS_PROTOCOL_ERR, rsi->script_name);
1324 return (-1);
1327 /* Send the given command to the script and process return data */
1328 static int
1329 do_cmd(script_info_t *rsi, char *argv[], char *envp[], char **errmsg)
1331 char buf[MAX_LINE_LEN];
1332 size_t buflen;
1333 int loglevel = -1, continuelog = 0;
1334 char *ptr, *dname, *value;
1335 time_t maxsecs;
1336 time_t deadline;
1337 int sigaborted = 0;
1338 int rval, child_status, token;
1339 int error_num;
1340 int cmd_timeout = rsi->cmd_timeout;
1342 *errmsg = NULL;
1344 script_process_sema_wait();
1346 if (run_script(rsi, argv, envp, errmsg) == -1) {
1347 (void) sema_post(&script_process_sema);
1348 goto error2;
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));
1358 goto error1;
1360 rsi->flags |= STDERR_THREAD_CREATED;
1362 /* LINTED */
1363 while (1) {
1364 if (cmd_timeout > 0) {
1365 maxsecs = deadline - time(NULL);
1366 if (maxsecs <= 0)
1367 goto timedout;
1368 } else
1369 maxsecs = 0;
1371 rval = get_line(rsi->pipe1[PARENT_END_OF_PIPE],
1372 "stdout", buf, MAX_LINE_LEN, &buflen,
1373 maxsecs, &error_num);
1375 if (buflen) {
1376 if (continuelog)
1377 log_msg(rsi, loglevel, buf);
1378 else {
1379 if ((ptr = strchr(buf, '=')) == NULL)
1380 goto error;
1382 *ptr = '\0';
1383 dname = buf;
1384 value = ptr + 1;
1385 if ((token = dname_to_id(dname)) == -1)
1386 goto error;
1388 switch (token) {
1389 case D_LOG_ERR:
1390 loglevel = RCM_ERROR;
1391 break;
1393 case D_LOG_WARN:
1394 loglevel = RCM_WARNING;
1395 break;
1397 case D_LOG_INFO:
1398 loglevel = RCM_INFO;
1399 break;
1401 case D_LOG_DEBUG:
1402 loglevel = RCM_DEBUG;
1403 break;
1405 default:
1406 loglevel = -1;
1407 break;
1410 if (loglevel != -1) {
1411 log_msg(rsi, loglevel, value);
1412 if (buf[buflen - 1] == '\n')
1413 continuelog = 0;
1414 else
1415 continuelog = 1;
1416 } else {
1417 if (buf[buflen - 1] != '\n')
1418 goto error;
1420 buf[buflen - 1] = '\0';
1421 if (process_dataitem(rsi, token,
1422 value, errmsg) != 0)
1423 goto error1;
1428 if (rval == -3) {
1429 /* script exited */
1430 waitagain:
1431 if (waitpid(rsi->pid, &child_status, 0)
1432 != rsi->pid) {
1433 if (errno == EINTR || errno == EAGAIN)
1434 goto waitagain;
1435 *errmsg = dup_err(RCM_ERROR, MS_SCRIPT_ERR,
1436 rsi->script_name, strerror(errno));
1437 goto error1;
1440 if (WIFEXITED(child_status)) {
1441 script_exited(rsi);
1442 rsi->exit_status = WEXITSTATUS(child_status);
1443 } else {
1444 if (sigaborted)
1445 *errmsg = dup_err(RCM_ERROR,
1446 MS_TIMEOUT_ERR, rsi->script_name);
1447 else
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);
1453 script_exited(rsi);
1454 goto error2;
1457 break;
1460 if (rval == -1) {
1461 *errmsg = dup_err(RCM_ERROR, MS_SCRIPT_ERR,
1462 rsi->script_name, strerror(errno));
1463 goto error1;
1466 if (rval == -2) {
1467 timedout:
1468 /* timeout occurred */
1469 if (sigaborted == 0) {
1470 (void) kill(rsi->pid, SIGABRT);
1471 sigaborted = 1;
1472 /* extend deadline */
1473 deadline += SCRIPT_ABORT_TIMEOUT;
1474 } else {
1475 *errmsg = dup_err(RCM_ERROR,
1476 MS_TIMEOUT_ERR, rsi->script_name);
1477 goto error1;
1482 return (0);
1484 error:
1485 *errmsg = dup_err(RCM_ERROR, MS_PROTOCOL_ERR, rsi->script_name);
1487 error1:
1488 kill_script(rsi);
1490 error2:
1491 return (-1);
1494 static int
1495 do_script_info(script_info_t *rsi)
1497 char *argv[MAX_ARGS];
1498 int status = RCM_FAILURE;
1499 int err = 0;
1500 char *errmsg = NULL;
1502 rcm_log_message(RSCR_TRACE, "do_script_info: script name = %s\n",
1503 rsi->script_name);
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) {
1512 case E_SUCCESS:
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;
1519 else
1520 rcm_log_message(RCM_ERROR,
1521 MS_UNSUPPORTED_VER, rsi->script_name,
1522 rsi->ver);
1523 } else
1524 err = 1;
1525 break;
1527 case E_FAILURE:
1528 if (rsi->failure_reason_buf != NULL) {
1529 rcm_log_message(RCM_ERROR, MS_SCRIPTINFO_ERR,
1530 rsi->script_name,
1531 rsi->failure_reason_buf);
1532 } else
1533 err = 1;
1534 break;
1536 default:
1537 err = 1;
1538 break;
1540 if (err)
1541 rcm_log_message(RCM_ERROR, MS_PROTOCOL_ERR,
1542 rsi->script_name);
1543 } else if (errmsg)
1544 (void) free(errmsg);
1546 if (status != RCM_SUCCESS)
1547 free(rsi->func_info_buf);
1549 free(rsi->failure_reason_buf);
1551 return (status);
1554 static int
1555 do_dr(script_info_t *rsi, char *argv[], char *envp[], char **info)
1557 int status = RCM_FAILURE;
1558 int err = 0;
1560 rsi->failure_reason_buf = NULL;
1562 if (do_cmd(rsi, argv, envp, info) == 0) {
1563 switch (rsi->exit_status) {
1564 case E_SUCCESS:
1565 case E_UNSUPPORTED_CMD:
1566 if (rsi->failure_reason_buf == NULL)
1567 status = RCM_SUCCESS;
1568 else
1569 err = 1;
1570 break;
1572 case E_FAILURE:
1573 case E_REFUSE:
1574 if (rsi->failure_reason_buf != NULL) {
1575 *info = rsi->failure_reason_buf;
1576 rsi->failure_reason_buf = NULL;
1577 } else
1578 err = 1;
1579 break;
1581 default:
1582 err = 1;
1583 break;
1586 if (err)
1587 *info = dup_err(RCM_ERROR, MS_PROTOCOL_ERR,
1588 rsi->script_name);
1591 free(rsi->failure_reason_buf);
1593 return (status);
1597 * get_info entry point
1599 /* ARGSUSED */
1600 static int
1601 script_get_info(rcm_handle_t *hdl,
1602 char *resource_name,
1603 pid_t pid,
1604 uint_t flag,
1605 char **info,
1606 char **error,
1607 nvlist_t *props,
1608 rcm_info_t **dependent_info)
1610 script_info_t *rsi = hdl->module->rsi;
1611 char *argv[MAX_ARGS];
1612 int status = RCM_FAILURE;
1613 int err = 0;
1615 rcm_log_message(RSCR_TRACE, "script_get_info: resource = %s\n",
1616 resource_name);
1618 *info = NULL;
1619 *error = NULL;
1621 (void) mutex_lock(&rsi->channel_lock);
1623 rsi->hdl = hdl;
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) {
1631 case E_SUCCESS:
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;
1638 } else
1639 err = 1;
1640 break;
1642 case E_FAILURE:
1643 if (rsi->failure_reason_buf != NULL) {
1644 *error = rsi->failure_reason_buf;
1645 rsi->failure_reason_buf = NULL;
1646 } else
1647 err = 1;
1648 break;
1650 default:
1651 err = 1;
1652 break;
1654 if (err)
1655 *error = dup_err(RCM_ERROR, MS_PROTOCOL_ERR,
1656 rsi->script_name);
1659 free(rsi->resource_usage_info_buf);
1661 free(rsi->failure_reason_buf);
1663 (void) mutex_unlock(&rsi->channel_lock);
1665 return (status);
1668 static void
1669 add_for_unregister(script_info_t *rsi)
1671 module_t *module = rsi->module;
1672 client_t *client;
1673 rcm_queue_t *head;
1674 rcm_queue_t *q;
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);
1688 static void
1689 remove_from_unregister(script_info_t *rsi, char *resource_name)
1691 module_t *module = rsi->module;
1692 client_t *client;
1693 rcm_queue_t *head;
1694 rcm_queue_t *q;
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;
1704 break;
1708 (void) mutex_unlock(&rcm_req_lock);
1711 static void
1712 complete_unregister(script_info_t *rsi)
1714 module_t *module = rsi->module;
1715 client_t *client;
1716 rcm_queue_t *head;
1717 rcm_queue_t *q;
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
1737 static int
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;
1743 int err = 0;
1744 char *errmsg = NULL;
1746 rcm_log_message(RSCR_TRACE,
1747 "script_register_interest: script name = %s\n",
1748 rsi->script_name);
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);
1758 rsi->hdl = hdl;
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) {
1767 case E_SUCCESS:
1768 status = RCM_SUCCESS;
1769 break;
1771 case E_FAILURE:
1772 if (rsi->failure_reason_buf != NULL) {
1773 rcm_log_message(RCM_ERROR, MS_REGISTER_ERR,
1774 rsi->script_name,
1775 rsi->failure_reason_buf);
1776 } else
1777 err = 1;
1778 break;
1780 default:
1781 err = 1;
1782 break;
1784 if (err)
1785 rcm_log_message(RCM_ERROR, MS_PROTOCOL_ERR,
1786 rsi->script_name);
1787 } else if (errmsg)
1788 (void) free(errmsg);
1790 complete_unregister(rsi);
1792 free(rsi->failure_reason_buf);
1794 (void) mutex_unlock(&rsi->channel_lock);
1796 return (status);
1800 * Add the specified resource name to the drreq_q.
1802 static void
1803 add_drreq(script_info_t *rsi, char *resource_name)
1805 rcm_queue_t *head = &rsi->drreq_q;
1806 rcm_queue_t *q;
1807 drreq_t *drreq;
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 */
1814 return;
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.
1826 static void
1827 remove_drreq(script_info_t *rsi, char *resource_name)
1829 rcm_queue_t *head = &rsi->drreq_q;
1830 rcm_queue_t *q;
1831 drreq_t *drreq;
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)
1837 break;
1840 if (q != head) {
1841 /* found drreq on the queue */
1842 rcm_dequeue(&drreq->queue);
1843 free(drreq->resource_name);
1844 free(drreq);
1849 * Remove all dr req's.
1851 static void
1852 remove_drreq_all(script_info_t *rsi)
1854 drreq_t *drreq;
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
1866 /* ARGSUSED */
1867 static int
1868 script_request_offline(rcm_handle_t *hdl,
1869 char *resource_name,
1870 pid_t pid,
1871 uint_t flag,
1872 char **info,
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];
1879 int status;
1880 int i;
1882 rcm_log_message(RSCR_TRACE,
1883 "script_request_offline: resource = %s flags = %s\n",
1884 resource_name,
1885 flags_to_name(flag, flags_name, MAX_FLAGS_NAME_LEN));
1887 *info = NULL;
1889 (void) mutex_lock(&rsi->channel_lock);
1891 rsi->hdl = hdl;
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;
1902 envp[i] = NULL;
1904 status = do_dr(rsi, argv, envp, info);
1906 (void) mutex_unlock(&rsi->channel_lock);
1907 return (status);
1911 * notify_online entry point
1913 /* ARGSUSED */
1914 static int
1915 script_notify_online(rcm_handle_t *hdl,
1916 char *resource_name,
1917 pid_t pid,
1918 uint_t flag,
1919 char **info,
1920 rcm_info_t **dependent_info)
1922 script_info_t *rsi = hdl->module->rsi;
1923 char *argv[MAX_ARGS];
1924 int status;
1926 rcm_log_message(RSCR_TRACE, "script_notify_online: resource = %s\n",
1927 resource_name);
1929 *info = NULL;
1931 (void) mutex_lock(&rsi->channel_lock);
1933 rsi->hdl = hdl;
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);
1942 return (status);
1946 * notify_remove entry point
1948 /* ARGSUSED */
1949 static int
1950 script_notify_remove(rcm_handle_t *hdl,
1951 char *resource_name,
1952 pid_t pid,
1953 uint_t flag,
1954 char **info,
1955 rcm_info_t **dependent_info)
1957 script_info_t *rsi = hdl->module->rsi;
1958 char *argv[MAX_ARGS];
1959 int status;
1961 rcm_log_message(RSCR_TRACE, "script_notify_remove: resource = %s\n",
1962 resource_name);
1964 *info = NULL;
1966 (void) mutex_lock(&rsi->channel_lock);
1968 rsi->hdl = hdl;
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);
1977 return (status);
1981 * request_suspend entry point
1983 /* ARGSUSED */
1984 static int
1985 script_request_suspend(rcm_handle_t *hdl,
1986 char *resource_name,
1987 pid_t pid,
1988 timespec_t *interval,
1989 uint_t flag,
1990 char **info,
1991 rcm_info_t **dependent_info)
1993 script_info_t *rsi = hdl->module->rsi;
1994 char *buf = NULL;
1995 char *curptr = NULL;
1996 char *argv[MAX_ARGS];
1997 char *envp[MAX_ENV_PARAMS];
1998 char flags_name[MAX_FLAGS_NAME_LEN];
1999 int buflen = 0;
2000 long seconds;
2001 int status;
2002 int i;
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));
2008 *info = NULL;
2010 (void) mutex_lock(&rsi->channel_lock);
2012 rsi->hdl = hdl;
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;
2025 if (interval) {
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,
2034 seconds);
2035 envp[i++] = buf;
2038 envp[i] = NULL;
2040 status = do_dr(rsi, argv, envp, info);
2042 (void) mutex_unlock(&rsi->channel_lock);
2043 free(buf);
2044 return (status);
2048 * notify_resume entry point
2050 /* ARGSUSED */
2051 static int
2052 script_notify_resume(rcm_handle_t *hdl,
2053 char *resource_name,
2054 pid_t pid,
2055 uint_t flag,
2056 char **info,
2057 rcm_info_t **dependent_info)
2059 script_info_t *rsi = hdl->module->rsi;
2060 char *argv[MAX_ARGS];
2061 int status;
2063 rcm_log_message(RSCR_TRACE, "script_notify_resume: resource = %s\n",
2064 resource_name);
2066 *info = NULL;
2068 (void) mutex_lock(&rsi->channel_lock);
2070 rsi->hdl = hdl;
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);
2079 return (status);
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",
2086 "", ""},
2087 { "SUNW_cpu", MATCH_EXACT,
2088 "new_total", "RCM_ENV_CAPACITY",
2089 "new_cpu_list", "RCM_ENV_CPU_IDS",
2090 "", ""},
2091 { "SUNW_cpu/set", MATCH_PREFIX,
2092 "new_total", "RCM_ENV_CAPACITY",
2093 "new_cpu_list", "RCM_ENV_CPU_IDS",
2094 "", ""},
2095 { "", MATCH_INVALID, "", "" }
2098 static capacity_descr_t *
2099 get_capacity_descr(char *resource_name)
2101 int i;
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,
2109 resource_name,
2110 strlen(capacity_type[i].resource_name)) == 0))
2112 return (&capacity_type[i]);
2115 return (NULL);
2118 static int
2119 build_env_for_capacity(script_info_t *rsi,
2120 char *resource_name,
2121 uint_t flag,
2122 nvlist_t *capacity_info,
2123 char *envp[],
2124 int *dynamic_env_index,
2125 char **errmsg)
2127 int p, i;
2128 capacity_descr_t *capa = NULL;
2129 nvpair_t *nvpair;
2130 char *buf;
2131 char *curptr;
2132 int buflen;
2133 int error;
2134 uint_t n;
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 :
2142 script_env_noforce;
2144 envp[p] = NULL;
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);
2150 return (-1);
2153 for (i = 0; *capa->param[i].nvname != '\0'; i++) {
2154 nvpair = NULL;
2155 while ((nvpair = nvlist_next_nvpair(capacity_info, nvpair))
2156 != NULL) {
2157 if (strcmp(nvpair_name(nvpair),
2158 capa->param[i].nvname) == 0)
2159 break;
2162 if (nvpair == NULL) {
2163 *errmsg = dup_err(RCM_ERROR, MF_NV_ERR,
2164 rsi->script_name);
2165 return (-1);
2168 error = 0;
2169 buf = NULL;
2171 rcmscript_snprintf(&buf, &buflen, &curptr, "%s=",
2172 capa->param[i].envname);
2174 switch (nvpair_type(nvpair)) {
2175 case DATA_TYPE_INT16:
2177 int16_t x;
2179 if (nvpair_value_int16(nvpair, &x) == 0) {
2180 rcmscript_snprintf(&buf, &buflen, &curptr,
2181 "%hd", (short)x);
2182 } else
2183 error = 1;
2184 break;
2187 case DATA_TYPE_UINT16:
2189 uint16_t x;
2191 if (nvpair_value_uint16(nvpair, &x) == 0) {
2192 rcmscript_snprintf(&buf, &buflen, &curptr,
2193 "%hu", (unsigned short)x);
2194 } else
2195 error = 1;
2196 break;
2199 case DATA_TYPE_INT32:
2201 int32_t x;
2203 if (nvpair_value_int32(nvpair, &x) == 0) {
2204 rcmscript_snprintf(&buf, &buflen, &curptr,
2205 "%d", (int)x);
2206 } else
2207 error = 1;
2208 break;
2211 case DATA_TYPE_UINT32:
2213 uint32_t x;
2215 if (nvpair_value_uint32(nvpair, &x) == 0) {
2216 rcmscript_snprintf(&buf, &buflen, &curptr,
2217 "%u", (uint_t)x);
2218 } else
2219 error = 1;
2220 break;
2223 case DATA_TYPE_INT64:
2225 int64_t x;
2227 if (nvpair_value_int64(nvpair, &x) == 0) {
2228 rcmscript_snprintf(&buf, &buflen, &curptr,
2229 "%lld", (long long)x);
2230 } else
2231 error = 1;
2232 break;
2235 case DATA_TYPE_UINT64:
2237 uint64_t x;
2239 if (nvpair_value_uint64(nvpair, &x) == 0) {
2240 rcmscript_snprintf(&buf, &buflen, &curptr,
2241 "%llu", (unsigned long long)x);
2242 } else
2243 error = 1;
2244 break;
2247 case DATA_TYPE_INT16_ARRAY:
2249 int16_t *x;
2251 if (nvpair_value_int16_array(nvpair, &x, &n) == 0) {
2252 while (n--) {
2253 rcmscript_snprintf(&buf, &buflen,
2254 &curptr, "%hd%s",
2255 (short)(*x),
2256 (n == 0) ? "" : " ");
2257 x++;
2259 } else
2260 error = 1;
2261 break;
2264 case DATA_TYPE_UINT16_ARRAY:
2266 uint16_t *x;
2268 if (nvpair_value_uint16_array(nvpair, &x, &n) == 0) {
2269 while (n--) {
2270 rcmscript_snprintf(&buf, &buflen,
2271 &curptr, "%hu%s",
2272 (unsigned short)(*x),
2273 (n == 0) ? "" : " ");
2274 x++;
2276 } else
2277 error = 1;
2278 break;
2281 case DATA_TYPE_INT32_ARRAY:
2283 int32_t *x;
2285 if (nvpair_value_int32_array(nvpair, &x, &n) == 0) {
2286 while (n--) {
2287 rcmscript_snprintf(&buf, &buflen,
2288 &curptr, "%d%s",
2289 (int)(*x),
2290 (n == 0) ? "" : " ");
2291 x++;
2293 } else
2294 error = 1;
2295 break;
2298 case DATA_TYPE_UINT32_ARRAY:
2300 uint32_t *x;
2302 if (nvpair_value_uint32_array(nvpair, &x, &n) == 0) {
2303 while (n--) {
2304 rcmscript_snprintf(&buf, &buflen,
2305 &curptr, "%u%s",
2306 (uint_t)(*x),
2307 (n == 0) ? "" : " ");
2308 x++;
2310 } else
2311 error = 1;
2312 break;
2315 case DATA_TYPE_INT64_ARRAY:
2317 int64_t *x;
2319 if (nvpair_value_int64_array(nvpair, &x, &n) == 0) {
2320 while (n--) {
2321 rcmscript_snprintf(&buf, &buflen,
2322 &curptr, "%lld%s",
2323 (long long)(*x),
2324 (n == 0) ? "" : " ");
2325 x++;
2327 } else
2328 error = 1;
2329 break;
2332 case DATA_TYPE_UINT64_ARRAY:
2334 uint64_t *x;
2336 if (nvpair_value_uint64_array(nvpair, &x, &n) == 0) {
2337 while (n--) {
2338 rcmscript_snprintf(&buf, &buflen,
2339 &curptr, "%llu%s",
2340 (unsigned long long)(*x),
2341 (n == 0) ? "" : " ");
2342 x++;
2344 } else
2345 error = 1;
2346 break;
2349 case DATA_TYPE_STRING:
2351 char *x;
2353 if (nvpair_value_string(nvpair, &x) == 0) {
2354 rcmscript_snprintf(&buf, &buflen, &curptr,
2355 "%s", x);
2356 } else
2357 error = 1;
2358 break;
2362 default:
2363 error = 1;
2364 break;
2367 envp[p++] = buf;
2369 if (error) {
2370 envp[p] = NULL;
2371 for (p = *dynamic_env_index; envp[p] != NULL; p++)
2372 free(envp[p]);
2373 *errmsg = dup_err(RCM_ERROR, MF_NV_ERR,
2374 rsi->script_name);
2375 return (-1);
2379 envp[p] = NULL;
2381 return (0);
2385 * request_capacity_change entry point
2387 /* ARGSUSED */
2388 static int
2389 script_request_capacity_change(rcm_handle_t *hdl,
2390 char *resource_name,
2391 pid_t pid,
2392 uint_t flag,
2393 nvlist_t *capacity_info,
2394 char **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];
2401 int status;
2402 int dynamic_env_index;
2404 rcm_log_message(RSCR_TRACE,
2405 "script_request_capacity_change: resource = %s flags = %s\n",
2406 resource_name,
2407 flags_to_name(flag, flags_name, MAX_FLAGS_NAME_LEN));
2409 *info = NULL;
2411 (void) mutex_lock(&rsi->channel_lock);
2413 rsi->hdl = hdl;
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++;
2426 } else
2427 status = RCM_FAILURE;
2429 (void) mutex_unlock(&rsi->channel_lock);
2430 return (status);
2434 * notify_capacity_change entry point
2436 /* ARGSUSED */
2437 static int
2438 script_notify_capacity_change(rcm_handle_t *hdl,
2439 char *resource_name,
2440 pid_t pid,
2441 uint_t flag,
2442 nvlist_t *capacity_info,
2443 char **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];
2449 int status;
2450 int dynamic_env_index;
2452 rcm_log_message(RSCR_TRACE,
2453 "script_notify_capacity_change: resource = %s\n", resource_name);
2455 *info = NULL;
2457 (void) mutex_lock(&rsi->channel_lock);
2459 rsi->hdl = hdl;
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++;
2472 } else
2473 status = RCM_FAILURE;
2475 (void) mutex_unlock(&rsi->channel_lock);
2476 return (status);
2479 /* Log the message to syslog */
2480 static void
2481 log_msg(script_info_t *rsi, int level, char *msg)
2483 rcm_log_msg(level, MS_LOG_MSG, rsi->script_name, msg);
2486 /*PRINTFLIKE2*/
2487 static char *
2488 dup_err(int level, char *format, ...)
2490 va_list ap;
2491 char buf1[1];
2492 char *buf2;
2493 int n;
2495 va_start(ap, format);
2496 n = vsnprintf(buf1, 1, format, ap);
2497 va_end(ap);
2499 if (n > 0) {
2500 n++;
2501 if (buf2 = (char *)malloc(n)) {
2502 va_start(ap, format);
2503 n = vsnprintf(buf2, n, format, ap);
2504 va_end(ap);
2505 if (n > 0) {
2506 if (level != -1)
2507 rcm_log_message(level, buf2);
2508 return (buf2);
2510 free(buf2);
2514 return (NULL);
2517 /*PRINTFLIKE4*/
2518 static void
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
2525 va_list ap;
2526 int offset, bytesneeded, bytesleft, error_num;
2528 if (*buf == NULL) {
2529 *buflen = 0;
2530 *curptr = NULL;
2533 offset = *curptr - *buf;
2534 bytesneeded = SPRINTF_MIN_CHUNK_LEN;
2535 bytesleft = *buflen - offset;
2537 /* LINTED */
2538 while (1) {
2539 if (bytesneeded > bytesleft) {
2540 *buflen += RSCR_ROUNDUP(bytesneeded - bytesleft,
2541 SPRINTF_CHUNK_LEN);
2542 if ((*buf = (char *)realloc(*buf, *buflen)) == NULL) {
2543 error_num = errno;
2544 rcm_log_message(RCM_ERROR,
2545 MF_MEMORY_ALLOCATION_ERR,
2546 strerror(error_num));
2547 rcmd_exit(error_num);
2548 /*NOTREACHED*/
2550 *curptr = *buf + offset;
2551 bytesleft = *buflen - offset;
2554 va_start(ap, format);
2555 bytesneeded = vsnprintf(*curptr, bytesleft, format, ap);
2556 va_end(ap);
2558 if (bytesneeded < 0) {
2559 /* vsnprintf encountered an error */
2560 error_num = errno;
2561 rcm_log_message(RCM_ERROR, MF_FUNC_CALL_ERR,
2562 "vsnprintf", strerror(error_num));
2563 rcmd_exit(error_num);
2564 /*NOTREACHED*/
2566 } else if (bytesneeded < bytesleft) {
2567 /* vsnprintf succeeded */
2568 *curptr += bytesneeded;
2569 return;
2571 } else {
2572 bytesneeded++; /* to account for storage for '\0' */
2577 static char *
2578 rcmscript_strdup(char *str)
2580 char *dupstr;
2582 if ((dupstr = strdup(str)) == NULL) {
2583 rcm_log_message(RCM_ERROR, MF_MEMORY_ALLOCATION_ERR,
2584 strerror(errno));
2585 rcmd_exit(errno);
2586 /*NOTREACHED*/
2589 return (dupstr);
2592 static void *
2593 rcmscript_malloc(size_t len)
2595 void *ptr;
2597 if ((ptr = malloc(len)) == NULL) {
2598 rcm_log_message(RCM_ERROR, MF_MEMORY_ALLOCATION_ERR,
2599 strerror(errno));
2600 rcmd_exit(errno);
2601 /*NOTREACHED*/
2604 return (ptr);
2607 static void *
2608 rcmscript_calloc(size_t nelem, size_t elsize)
2610 void *ptr;
2612 if ((ptr = calloc(nelem, elsize)) == NULL) {
2613 rcm_log_message(RCM_ERROR, MF_MEMORY_ALLOCATION_ERR,
2614 strerror(errno));
2615 rcmd_exit(errno);
2616 /*NOTREACHED*/
2619 return (ptr);