xref: /illumos-gate/usr/src/cmd/rcm_daemon/common/rcm_script.c (revision 2a8bcb4efb45d99ac41c94a75c396b362c414f7f)
1 /*
2  * CDDL HEADER START
3  *
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.
8  *
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.
13  *
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]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * rcm scripting module:
29  *
30  * This module implements rcm scripting interfaces.
31  * It translates rcm module based interfaces to rcm script based
32  * interfaces.
33  *
34  * Entry points:
35  *
36  *   int script_main_init()
37  *	Initialize the rcm scripting framework.
38  *	Called during the rcm daemon initialization
39  *
40  *   int script_main_fini()
41  *	Called at the time of the rcm daemon exit.
42  *
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.
48  *
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.
53  *
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.
58  *
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.
62  */
63 
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>
70 
71 /*
72  * All rcm scripting commands are enumerated here.
73  * NOTE: command positions in script_cmd_id_t and script_cmd_name must match.
74  */
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;
91 
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
109 };
110 
111 /*
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.
115  */
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;
128 
129 /*
130  * NOTE: data item positions in script_data_item_id_t and
131  * script_data_item_name must match.
132  */
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
145 };
146 
147 /*
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"
155  */
156 static const int script_max_parallelism = 64;
157 
158 /*
159  * semaphore to limit the number of rcm script processes running in
160  * parallel to script_max_parallelism.
161  */
162 static sema_t script_process_sema;
163 
164 /* mutex to protect the any global data */
165 static mutex_t script_lock;
166 
167 /* contains head to a queue of script_info structures */
168 static rcm_queue_t script_info_q;
169 
170 /*
171  * This mmapped state file is used to store the process id and
172  * rcm script name of all currently running rcm scripts.
173  */
174 static const char *script_ps_state_file = "/var/run/rcm_script_state";
175 static state_file_descr_t script_ps_statefd;
176 
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";
180 
181 #define	RSCR_TRACE		RCM_TRACE1
182 
183 /* rcm script base environment */
184 static char *script_env[MAX_ENV_PARAMS];
185 
186 struct rlimit file_limit;
187 
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);
248 
249 
250 static struct rcm_mod_ops script_ops =
251 {
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
264 };
265 
266 /*
267  * Messages fall into two categories:
268  *   framework messages (MF_..)
269  *   errors directly attributable to scripts (MS_..)
270  */
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")
299 
300 
301 /*
302  * Initialize rcm scripting framework.
303  * Called during initialization of rcm daemon.
304  */
305 int
script_main_init(void)306 script_main_init(void)
307 {
308 #define	PS_STATE_FILE_CHUNK_SIZE	32
309 
310 	/* set base script environment */
311 	build_env();
312 
313 	rcm_init_queue(&script_info_q);
314 
315 	/*
316 	 * Initialize the semaphore to limit the number of rcm script
317 	 * process running in parallel to script_max_parallelism.
318 	 */
319 	(void) sema_init(&script_process_sema, script_max_parallelism,
320 			USYNC_THREAD, NULL);
321 
322 	(void) mutex_init(&script_lock, USYNC_THREAD, NULL);
323 
324 	/* save original file limit */
325 	(void) getrlimit(RLIMIT_NOFILE, &file_limit);
326 
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);
331 
332 	/*
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.
337 	 */
338 	if (script_ps_statefd.state_file) {
339 		script_ps_state_file_kill_pids();
340 		truncate_state_file(&script_ps_statefd);
341 	}
342 
343 	return (0);
344 }
345 
346 /*
347  * Do any cleanup.
348  * Called at the time of normal rcm daemon exit.
349  */
350 int
script_main_fini(void)351 script_main_fini(void)
352 {
353 	script_ps_state_file_kill_pids();
354 	close_state_file(script_ps_state_file, &script_ps_statefd);
355 	return (0);
356 }
357 
358 /*
359  * Initialize the given rcm script.
360  * module->name contains the name of the rcm script.
361  */
362 struct rcm_mod_ops *
script_init(module_t * module)363 script_init(module_t *module)
364 {
365 	script_info_t *rsi;
366 	size_t len;
367 	char *script_path;
368 
369 	rcm_log_message(RSCR_TRACE, "script_init: script name = %s\n",
370 						module->name);
371 
372 	module->rsi = NULL;
373 
374 	if ((script_path = rcm_get_script_dir(module->name)) == NULL)
375 		return (NULL);
376 
377 	len = strlen(script_path) + strlen(module->name) + 2;
378 
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);
382 
383 	rsi->module = module;
384 	rcm_init_queue(&rsi->drreq_q);
385 
386 	(void) mutex_init(&rsi->channel_lock, USYNC_THREAD, NULL);
387 
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;
391 
392 	(void) mutex_lock(&rsi->channel_lock);
393 
394 	rsi->cmd_timeout = -1; /* don't time scriptinfo command */
395 	if (do_script_info(rsi) == RCM_SUCCESS) {
396 		/*
397 		 * if the script hasn't specified a timeout value set it to
398 		 * default
399 		 */
400 		if (rsi->cmd_timeout == -1)
401 			rsi->cmd_timeout = SCRIPT_CMD_TIMEOUT;
402 		(void) mutex_unlock(&rsi->channel_lock);
403 
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);
408 
409 		module->rsi = rsi;
410 		return (&script_ops);
411 	}
412 
413 	(void) mutex_unlock(&rsi->channel_lock);
414 
415 	free(rsi->script_full_name);
416 	free(rsi);
417 	return (NULL);
418 }
419 
420 /*
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.
424  */
425 char *
script_info(module_t * module)426 script_info(module_t *module)
427 {
428 	script_info_t *rsi = module->rsi;
429 
430 	rcm_log_message(RSCR_TRACE, "script_info: script name = %s\n",
431 						rsi->script_name);
432 	return (rsi->func_info_buf);
433 }
434 
435 /*
436  * Called before unloading the script.
437  * module->name contains the name of the rcm script which is being unloaded.
438  * Do any cleanup.
439  */
440 int
script_fini(module_t * module)441 script_fini(module_t *module)
442 {
443 	script_info_t *rsi = module->rsi;
444 
445 	rcm_log_message(RSCR_TRACE, "script_fini: script name = %s\n",
446 						rsi->script_name);
447 
448 	/* remove rsi from script_info_q */
449 	(void) mutex_lock(&script_lock);
450 	rcm_dequeue(&rsi->queue);
451 	(void) mutex_unlock(&script_lock);
452 
453 	remove_drreq_all(rsi);
454 
455 	if (rsi->func_info_buf)
456 		free(rsi->func_info_buf);
457 
458 	free(rsi->script_full_name);
459 	free(rsi);
460 
461 	module->rsi = NULL;
462 
463 	return (RCM_SUCCESS);
464 }
465 
466 /* build base environment for scripts */
467 static void
build_env(void)468 build_env(void)
469 {
470 	const char *env_list[] = { "LANG", "LC_COLLATE", "LC_CTYPE",
471 		"LC_MESSAGES", "LC_MONETARY", "LC_NUMERIC", "LC_TIME",
472 		"LC_ALL", "TZ", NULL };
473 	char *x;
474 	int len;
475 	int i, j = 0;
476 	int d;
477 	extern int debug_level;
478 
479 	script_env[j++] = rcmscript_strdup("PATH=/usr/sbin:/usr/bin");
480 
481 	for (i = 0; env_list[i] != NULL; i++) {
482 		x = getenv(env_list[i]);
483 		if (x) {
484 			len = strlen(env_list[i]) + strlen(x) + 2;
485 			script_env[j] = (char *)rcmscript_malloc(len);
486 
487 			(void) snprintf(script_env[j++], len, "%s=%s",
488 				env_list[i], x);
489 		}
490 	}
491 
492 	len = strlen("RCM_ENV_DEBUG_LEVEL") + 3;
493 	script_env[j] = (char *)rcmscript_malloc(len);
494 
495 	if (debug_level < 0)
496 		d = 0;
497 	else if (debug_level > 9)
498 		d = 9;
499 	else
500 		d = debug_level;
501 
502 	(void) snprintf(script_env[j++], len, "RCM_ENV_DEBUG_LEVEL=%d", d);
503 
504 	script_env[j] = NULL;
505 }
506 
507 static void
copy_env(char * src[],char * dst[])508 copy_env(char *src[], char *dst[])
509 {
510 	int i;
511 
512 	for (i = 0; src[i] != NULL; i++)
513 		dst[i] = src[i];
514 
515 	dst[i] = NULL;
516 }
517 
518 /*
519  * Open (or create if the file does not exist) the given state file
520  * and mmap it.
521  */
522 static void
open_state_file(const char * filename,state_file_descr_t * statefd,size_t element_size,int chunk_size,uint32_t version)523 open_state_file(const char *filename,
524 	state_file_descr_t *statefd,
525 	size_t element_size,
526 	int chunk_size,
527 	uint32_t version)
528 {
529 	struct stat stats;
530 	int error_num;
531 
532 	if ((statefd->fd = open(filename, O_CREAT|O_RDWR, 0600)) ==
533 			-1) {
534 		error_num = errno;
535 		rcm_log_message(RCM_ERROR, MF_STATE_FILE_ERR,
536 			"open", strerror(error_num));
537 		rcmd_exit(error_num);
538 		/*NOTREACHED*/
539 	}
540 
541 	if (fstat(statefd->fd, &stats) != 0) {
542 		error_num = errno;
543 		rcm_log_message(RCM_ERROR, MF_STATE_FILE_ERR,
544 			"fstat", strerror(error_num));
545 		rcmd_exit(error_num);
546 		/*NOTREACHED*/
547 	}
548 
549 	if (stats.st_size != 0) {
550 		/* LINTED */
551 		statefd->state_file = (state_file_t *)mmap(NULL,
552 			stats.st_size, PROT_READ|PROT_WRITE, MAP_SHARED,
553 			statefd->fd, 0);
554 
555 		if (statefd->state_file == MAP_FAILED) {
556 			error_num = errno;
557 			rcm_log_message(RCM_ERROR, MF_STATE_FILE_ERR,
558 				"mmap", strerror(error_num));
559 			rcmd_exit(error_num);
560 			/*NOTREACHED*/
561 		}
562 
563 		if (statefd->state_file->version != version) {
564 			(void) munmap((void *)statefd->state_file,
565 					stats.st_size);
566 			statefd->state_file = NULL;
567 			(void) ftruncate(statefd->fd, 0);
568 		}
569 	} else {
570 		statefd->state_file = NULL;
571 	}
572 
573 	statefd->version = version;
574 	statefd->element_size = sizeof (state_element_t) +
575 				RSCR_ROUNDUP(element_size, 8);
576 	statefd->chunk_size = chunk_size;
577 	statefd->index = 0;
578 }
579 
580 static void
truncate_state_file(state_file_descr_t * statefd)581 truncate_state_file(state_file_descr_t *statefd)
582 {
583 	size_t size;
584 
585 	if (statefd->state_file) {
586 		size = sizeof (state_file_t) + statefd->element_size *
587 			statefd->state_file->max_elements;
588 
589 		(void) munmap((void *)statefd->state_file, size);
590 		statefd->state_file = NULL;
591 	}
592 	(void) ftruncate(statefd->fd, 0);
593 }
594 
595 static void
close_state_file(const char * filename,state_file_descr_t * statefd)596 close_state_file(const char *filename, state_file_descr_t *statefd)
597 {
598 	truncate_state_file(statefd);
599 	(void) close(statefd->fd);
600 	(void) unlink(filename);
601 }
602 
603 /*
604  * Grow the state file by the chunk size specified in statefd
605  * and mmap it.
606  */
607 static void
grow_state_file(state_file_descr_t * statefd)608 grow_state_file(state_file_descr_t *statefd)
609 {
610 	size_t size;
611 	int max_elements;
612 	int error_num;
613 
614 	max_elements = statefd->chunk_size;
615 	if (statefd->state_file)
616 		max_elements += statefd->state_file->max_elements;
617 
618 	size = sizeof (state_file_t) +
619 		statefd->element_size * max_elements;
620 
621 	if (ftruncate(statefd->fd, size) != 0) {
622 		error_num = errno;
623 		rcm_log_message(RCM_ERROR, MF_STATE_FILE_ERR,
624 			"ftruncate", strerror(error_num));
625 		rcmd_exit(error_num);
626 		/*NOTREACHED*/
627 	}
628 
629 	/* LINTED */
630 	statefd->state_file = (state_file_t *)mmap(NULL, size,
631 		PROT_READ|PROT_WRITE, MAP_SHARED, statefd->fd, 0);
632 
633 	if (statefd->state_file == MAP_FAILED) {
634 		error_num = errno;
635 		rcm_log_message(RCM_ERROR, MF_STATE_FILE_ERR,
636 			"mmap", strerror(error_num));
637 		rcmd_exit(error_num);
638 		/*NOTREACHED*/
639 	}
640 
641 	statefd->index = statefd->state_file->max_elements;
642 	statefd->state_file->max_elements = max_elements;
643 	statefd->state_file->version = statefd->version;
644 }
645 
646 /*
647  * Given index into state element array, get the pointer to the actual
648  * state element.
649  * If flag is non-null set *flag to
650  *	TRUE if the state element is currently is use.
651  *	FALSE if the state element is free.
652  */
653 static void *
get_state_element(state_file_descr_t * statefd,int index,int * flag)654 get_state_element(state_file_descr_t *statefd, int index, int *flag)
655 {
656 	char *ptr;
657 
658 	if (statefd->state_file &&
659 	    (index < statefd->state_file->max_elements)) {
660 
661 		ptr = (char *)(statefd->state_file);
662 		ptr += sizeof (state_file_t) +
663 			index * statefd->element_size;
664 
665 		if (flag) {
666 			*flag = (((state_element_t *)((void *)ptr))->flags &
667 				STATE_ELEMENT_IN_USE) ? 1 : 0;
668 		}
669 
670 		ptr += sizeof (state_element_t);
671 	} else
672 		ptr = NULL;
673 
674 	return ((void *)ptr);
675 }
676 
677 /*
678  * Allocate a state element entry in the state file and return a pointer
679  * to the allocated entry.
680  * If index is non-null set *index to index into the state element array
681  * of the allocated entry.
682  */
683 static void *
allocate_state_element(state_file_descr_t * statefd,int * index)684 allocate_state_element(state_file_descr_t *statefd, int *index)
685 {
686 	void *x;
687 	int i;
688 	int flag;
689 
690 	if (statefd->state_file) {
691 		/* find an empty slot */
692 		for (i = 0; i < statefd->state_file->max_elements; i++) {
693 			x = get_state_element(statefd, statefd->index,
694 						&flag);
695 			assert(x != NULL);
696 
697 			if (flag == 0)
698 				/* entry is free */
699 				break;
700 
701 			statefd->index++;
702 			if (statefd->index >= statefd->state_file->max_elements)
703 				statefd->index = 0;
704 		}
705 	}
706 
707 	if (statefd->state_file == NULL ||
708 		i == statefd->state_file->max_elements) {
709 
710 		/* All entries are in use. Grow the list */
711 		grow_state_file(statefd);
712 		x = get_state_element(statefd, statefd->index, &flag);
713 		assert(flag == 0);
714 	}
715 
716 	if (index != NULL)
717 		*index = statefd->index;
718 
719 	statefd->index++;
720 	if (statefd->index >= statefd->state_file->max_elements)
721 		statefd->index = 0;
722 
723 	((state_element_t *)x - 1)->flags |= STATE_ELEMENT_IN_USE;
724 	return (x);
725 }
726 
727 static void
free_state_element(void * x)728 free_state_element(void *x)
729 {
730 	((state_element_t *)x - 1)->flags &= ~STATE_ELEMENT_IN_USE;
731 }
732 
733 /*
734  * Kill the pids contained in ps state file.
735  */
736 static void
script_ps_state_file_kill_pids(void)737 script_ps_state_file_kill_pids(void)
738 {
739 	ps_state_element_t *x;
740 	char procfile[80];
741 	psinfo_t psi;
742 	int fd, i, flag;
743 
744 	/* LINTED */
745 	for (i = 0; 1; i++) {
746 		if ((x = (ps_state_element_t *)get_state_element(
747 					&script_ps_statefd, i, &flag)) == NULL)
748 			break;
749 
750 		if (flag == 1) { /* the entry is in use */
751 			(void) snprintf(procfile, 80, "/proc/%ld/psinfo",
752 					(long)x->pid);
753 			if ((fd = open(procfile, O_RDONLY)) != -1 &&
754 				read(fd, &psi, sizeof (psi)) == sizeof (psi) &&
755 				strcmp(psi.pr_fname,
756 				x->script_name) == 0) {
757 
758 				(void) close(fd);
759 
760 				/*
761 				 * just a safety check to not to blow up
762 				 * system processes if the file is ever corrupt
763 				 */
764 				if (x->pid > 1) {
765 					rcm_log_message(RCM_DEBUG,
766 					"script_ps_state_file_kill_pids: "
767 					"killing script_name = %s pid = %ld\n",
768 					x->script_name, x->pid);
769 
770 					/* kill the process group */
771 					(void) kill(-(x->pid), SIGKILL);
772 				}
773 			} else {
774 				if (fd != -1)
775 					(void) close(fd);
776 			}
777 			free_state_element((void *)x);
778 		}
779 	}
780 }
781 
782 /*
783  * Add a state element entry to ps state file.
784  */
785 static void
script_ps_state_file_add_entry(pid_t pid,char * script_name)786 script_ps_state_file_add_entry(pid_t pid, char *script_name)
787 {
788 	ps_state_element_t *x;
789 
790 	(void) mutex_lock(&script_lock);
791 
792 	x = (ps_state_element_t *)allocate_state_element(
793 		&script_ps_statefd, NULL);
794 
795 	x->pid = pid;
796 	(void) strlcpy(x->script_name, script_name, MAXNAMELEN);
797 
798 	(void) fsync(script_ps_statefd.fd);
799 
800 	(void) mutex_unlock(&script_lock);
801 }
802 
803 /*
804  * Remove the state element entry corresponding to pid from the
805  * ps state file.
806  */
807 static void
script_ps_state_file_remove_entry(pid_t pid)808 script_ps_state_file_remove_entry(pid_t pid)
809 {
810 	ps_state_element_t *x;
811 	int flag, i;
812 
813 	(void) mutex_lock(&script_lock);
814 
815 	/* LINTED */
816 	for (i = 0; 1; i++) {
817 		if ((x = (ps_state_element_t *)get_state_element(
818 					&script_ps_statefd, i, &flag)) == NULL)
819 			break;
820 
821 		/* if the state element entry is in use and pid matches */
822 		if (flag == 1 && x->pid == pid) {
823 			free_state_element((void *)x);
824 			break;
825 		}
826 	}
827 
828 	(void) mutex_unlock(&script_lock);
829 }
830 
831 /*
832  * Get data item id given data item name
833  */
834 static int
dname_to_id(char * dname)835 dname_to_id(char *dname)
836 {
837 	int i;
838 
839 	for (i = 0; script_data_item_name[i] != NULL; i++) {
840 		if (strcmp(dname, script_data_item_name[i]) == 0)
841 			return (i);
842 	}
843 
844 	return (-1);
845 }
846 
847 /*
848  * Called before running any script.
849  * This routine waits until the number of script processes running in
850  * parallel drops down below to script_max_parallelism.
851  */
852 static void
script_process_sema_wait(void)853 script_process_sema_wait(void)
854 {
855 	int error_num;
856 
857 	/* LINTED */
858 	while (1) {
859 		if (sema_wait(&script_process_sema) == 0)
860 			return;
861 
862 		if (errno != EINTR && errno != EAGAIN) {
863 			error_num = errno;
864 			rcm_log_message(RCM_ERROR, MF_FUNC_CALL_ERR,
865 				"sema_wait", strerror(error_num));
866 			rcmd_exit(error_num);
867 			/*NOTREACHED*/
868 		}
869 	}
870 
871 	/*NOTREACHED*/
872 }
873 
874 /*
875  * Fork and execute the script.
876  */
877 static int
run_script(script_info_t * rsi,char * argv[],char * envp[],char ** errmsg)878 run_script(script_info_t *rsi, char *argv[], char *envp[], char **errmsg)
879 {
880 	int i, p1 = -1, p2 = -1;
881 	struct rlimit rlp;
882 	struct stat stats;
883 
884 	rcm_log_message(RSCR_TRACE, "run_script: script name = %s\n",
885 					rsi->script_full_name);
886 
887 	for (i = 0; argv[i] != NULL; i++)
888 		rcm_log_message(RSCR_TRACE, "run_script: argv[%d] = %s\n",
889 					i, argv[i]);
890 
891 	*errmsg = NULL;
892 
893 	/* check that the script exists */
894 	if (stat(rsi->script_full_name, &stats) != 0)
895 		goto error;
896 
897 	/*
898 	 * If the syscall pipe fails because of reaching the max open file
899 	 * count per process then dynamically increase the limit on the max
900 	 * open file count.
901 	 *
902 	 * At present the rcm_daemon consumes file descriptor
903 	 * entries for the following files.
904 	 *   RCM_STATE_FILE   - /var/run/rcm_daemon_state
905 	 *   DAEMON_LOCK_FILE - /var/run/rcm_daemon_lock
906 	 *   RCM_SERVICE_DOOR - /var/run/rcm_daemon_door
907 	 *   proc files in the format "/proc/pid/as" for each pid
908 	 *	communicating with the rcm_daemon via doors
909 	 *   dlopen for each rcm module
910 	 *   When in daemon mode stdin, stdout and stderr are closed;
911 	 *	/dev/null opened and duped to stdout, and stderr
912 	 *   openlog
913 	 *   Some files which are opened briefly and closed such as
914 	 *	directory files.
915 	 *   Two file descriptors for each script in running state.
916 	 *	Note that the constant script_max_parallelism sets an
917 	 *	upper cap on how many rcm scripts can run in
918 	 *	parallel.
919 	 */
920 	if ((p1 = pipe(rsi->pipe1)) == -1 || (p2 = pipe(rsi->pipe2)) == -1) {
921 		if ((errno == EMFILE) &&
922 			(getrlimit(RLIMIT_NOFILE, &rlp) == 0)) {
923 
924 			rlp.rlim_cur += 16;
925 			if (rlp.rlim_max < rlp.rlim_cur)
926 				rlp.rlim_max = rlp.rlim_cur;
927 			(void) setrlimit(RLIMIT_NOFILE, &rlp);
928 
929 			if (p1 == -1) {
930 				if ((p1 = pipe(rsi->pipe1)) == -1)
931 					goto error;
932 			}
933 			if ((p2 = pipe(rsi->pipe2)) == -1)
934 				goto error;
935 		} else
936 			goto error;
937 	}
938 
939 forkagain:
940 	if ((rsi->pid = fork1()) == (pid_t)-1) {
941 		if (errno == EINTR || errno == EAGAIN)
942 			goto forkagain;
943 
944 		goto error;
945 	}
946 
947 	if (rsi->pid == 0) {
948 		/* child process */
949 
950 		(void) setsid();
951 
952 		/* close stdin, stdout and stderr */
953 		(void) close(0);
954 		(void) close(1);
955 		(void) close(2);
956 
957 		/* set stdin to /dev/null */
958 		(void) open("/dev/null", O_RDWR, 0);
959 
960 		/* redirect stdout and stderr to pipe */
961 		(void) dup2(rsi->pipe1[CHILD_END_OF_PIPE], 1);
962 		(void) dup2(rsi->pipe2[CHILD_END_OF_PIPE], 2);
963 
964 		/* close all other file descriptors */
965 		closefrom(3);
966 
967 		/* restore original file limit */
968 		(void) setrlimit(RLIMIT_NOFILE, &file_limit);
969 
970 		/* set current working dir */
971 		if (stats.st_uid == 0) {
972 			/* root */
973 			if (chdir("/var/run") == -1)
974 				_exit(127);
975 		} else {
976 			if (chdir("/tmp") == -1)
977 				_exit(127);
978 		}
979 
980 		/*
981 		 * setuid sets real, effective and saved user ids to the
982 		 * given id.
983 		 * setgid sets real, effective and saved group ids to the
984 		 * given id.
985 		 */
986 		(void) setgid(stats.st_gid);
987 		(void) setuid(stats.st_uid);
988 
989 		(void) execve(rsi->script_full_name, argv, envp);
990 		_exit(127);
991 		/*NOTREACHED*/
992 	}
993 
994 	(void) close(rsi->pipe1[CHILD_END_OF_PIPE]);
995 	(void) close(rsi->pipe2[CHILD_END_OF_PIPE]);
996 
997 	script_ps_state_file_add_entry(rsi->pid, rsi->script_name);
998 
999 	return (0);
1000 
1001 error:
1002 	*errmsg = dup_err(RCM_ERROR, MS_SCRIPT_ERR,
1003 			rsi->script_name, strerror(errno));
1004 
1005 	if (p1 != -1) {
1006 		(void) close(rsi->pipe1[PARENT_END_OF_PIPE]);
1007 		(void) close(rsi->pipe1[CHILD_END_OF_PIPE]);
1008 	}
1009 
1010 	if (p2 != -1) {
1011 		(void) close(rsi->pipe2[PARENT_END_OF_PIPE]);
1012 		(void) close(rsi->pipe2[CHILD_END_OF_PIPE]);
1013 	}
1014 
1015 	return (-1);
1016 }
1017 
1018 /*
1019  * Reads one line of input (including the newline character) from the
1020  * given file descriptor "fd" to buf.
1021  * maxbuflen specifies the size of memory allocated for buf.
1022  * Timeoutval is the max timeout value in seconds for the script to supply
1023  * input. A timeoutval of 0 implies no timeout.
1024  *
1025  * Upon return *buflen contains the number of bytes read.
1026  *
1027  * Return values:
1028  *   0  success
1029  *   -1 an error occured
1030  *   -2 timeout occurred
1031  *   -3 script exited
1032  */
1033 static int
get_line(int fd,char * fdname,char * buf,int maxbuflen,size_t * buflen,time_t timeoutval,int * error_num)1034 get_line(int fd,
1035 	char *fdname,
1036 	char *buf,
1037 	int maxbuflen,
1038 	size_t *buflen,
1039 	time_t timeoutval,
1040 	int *error_num)
1041 {
1042 	char c = '\0';
1043 	struct pollfd fds[1];
1044 	int x;
1045 	size_t len = 0;
1046 	char *ptr;
1047 	int timeit;
1048 	time_t deadline;
1049 	int rval = 0;
1050 
1051 	if (timeoutval) {
1052 		timeit = TRUE;
1053 		deadline = time(NULL) + timeoutval;
1054 		fds[0].fd = fd;
1055 		fds[0].events = POLLIN;
1056 	} else
1057 		timeit = FALSE;
1058 
1059 	ptr = buf;
1060 
1061 	while (c != '\n' && len < (maxbuflen -1)) {
1062 		if (timeit) {
1063 pollagain:
1064 			fds[0].revents = 0;
1065 			timeoutval = deadline - time(NULL);
1066 			if (timeoutval <= 0) {
1067 				rval = -2;
1068 				break;
1069 			}
1070 			x = poll(fds, 1, timeoutval*1000);
1071 			if (x <= 0) {
1072 				if (x == 0)
1073 					/* poll timedout */
1074 					rval = -2;
1075 				else {
1076 					if (errno == EINTR || errno == EAGAIN)
1077 						goto pollagain;
1078 					*error_num = errno;
1079 					rval = -1;
1080 				}
1081 				break;
1082 			}
1083 		}
1084 readagain:
1085 		if ((x = read(fd, &c, 1)) != 1) {
1086 			if (x == 0)
1087 				/*
1088 				 * Script exited. Or more specifically the
1089 				 * script has closed its end of the pipe.
1090 				 */
1091 				rval = -3;
1092 			else {
1093 				if (errno == EINTR || errno == EAGAIN)
1094 					goto readagain;
1095 				*error_num = errno;
1096 				rval = -1;
1097 			}
1098 			break;
1099 		}
1100 
1101 		*ptr++ = c;
1102 		len++;
1103 	}
1104 
1105 	*ptr = '\0';
1106 	*buflen = len;
1107 
1108 	rcm_log_message(RSCR_TRACE,
1109 		"get_line(%s): rval = %d buflen = %d line = %s\n",
1110 		fdname, rval, *buflen, buf);
1111 	return (rval);
1112 }
1113 
1114 static void
script_exited(script_info_t * rsi)1115 script_exited(script_info_t *rsi)
1116 {
1117 	if (rsi->flags & STDERR_THREAD_CREATED) {
1118 		rcm_log_message(RSCR_TRACE,
1119 		    "script_exited: doing thr_join (%s)\n", rsi->script_name);
1120 		(void) thr_join(rsi->tid, NULL, NULL);
1121 		rsi->flags &= ~STDERR_THREAD_CREATED;
1122 	}
1123 
1124 	(void) close(rsi->pipe1[PARENT_END_OF_PIPE]);
1125 	(void) close(rsi->pipe2[PARENT_END_OF_PIPE]);
1126 	rsi->pipe1[PARENT_END_OF_PIPE] = -1;
1127 	rsi->pipe2[PARENT_END_OF_PIPE] = -1;
1128 
1129 	script_ps_state_file_remove_entry(rsi->pid);
1130 	rsi->pid = 0;
1131 	(void) sema_post(&script_process_sema);
1132 }
1133 
1134 /*
1135  * Kill the specified process group
1136  */
1137 static int
kill_pid(pid_t pid)1138 kill_pid(pid_t pid)
1139 {
1140 	time_t deadline, timeleft;
1141 	int child_status;
1142 
1143 	/* kill the entire process group */
1144 	(void) kill(-(pid), SIGKILL);
1145 
1146 	/* give some time for the script to be killed */
1147 	deadline = time(NULL) + SCRIPT_KILL_TIMEOUT;
1148 	do {
1149 		if (waitpid(pid, &child_status, WNOHANG) == pid)
1150 			return (0);
1151 
1152 		/* wait for 100 ms */
1153 		(void) poll(NULL, 0, 100);
1154 
1155 		timeleft = deadline - time(NULL);
1156 	} while (timeleft > 0);
1157 
1158 	/* script process was not killed successfully */
1159 	return (-1);
1160 }
1161 
1162 /*
1163  * Kill the specified script.
1164  */
1165 static void
kill_script(script_info_t * rsi)1166 kill_script(script_info_t *rsi)
1167 {
1168 	if (rsi->pid > 1) {
1169 		(void) kill_pid(rsi->pid);
1170 		script_exited(rsi);
1171 		remove_drreq_all(rsi);
1172 	}
1173 }
1174 
1175 /*
1176  * Convert rcm flags parameter to a string.
1177  * Used for debug prints.
1178  */
1179 static char *
flags_to_name(int flags,char * buf,int maxbuflen)1180 flags_to_name(int flags, char *buf, int maxbuflen)
1181 {
1182 	(void) snprintf(buf, maxbuflen, "%s%s",
1183 		(flags & RCM_QUERY) ? "RCM_QUERY " : "",
1184 		(flags & RCM_FORCE) ? "RCM_FORCE" : "");
1185 
1186 	return (buf);
1187 }
1188 
1189 static void
fill_argv(script_info_t * rsi,char * argv[],char * resource_name)1190 fill_argv(script_info_t *rsi, char *argv[], char *resource_name)
1191 {
1192 	argv[0] = rsi->script_full_name;
1193 	argv[1] = script_cmd_name[rsi->cmd];
1194 	if (resource_name) {
1195 		argv[2] = resource_name;
1196 		argv[3] = NULL;
1197 	} else
1198 		argv[2] = NULL;
1199 }
1200 
1201 /*
1202  * stderr thread:
1203  * Reads stderr and logs to syslog.
1204  * Runs as a separate thread.
1205  */
1206 static void *
read_stderr(script_info_t * rsi)1207 read_stderr(script_info_t *rsi)
1208 {
1209 	char buf[MAX_LINE_LEN];
1210 	size_t buflen;
1211 	int error_num;
1212 
1213 	while ((get_line(rsi->pipe2[PARENT_END_OF_PIPE], "stderr",
1214 		buf, MAX_LINE_LEN, &buflen, 0, &error_num)) == 0) {
1215 		log_msg(rsi, RCM_ERROR, buf);
1216 	}
1217 
1218 	if (buflen)
1219 		log_msg(rsi, RCM_ERROR, buf);
1220 
1221 	return (NULL);
1222 }
1223 
1224 /* process return data items passed by scripts to the framework */
1225 static int
process_dataitem(script_info_t * rsi,int token,char * value,char ** errmsg)1226 process_dataitem(script_info_t *rsi, int token, char *value, char **errmsg)
1227 {
1228 	char *ptr;
1229 	int status;
1230 
1231 	*errmsg = NULL;
1232 
1233 	if (*value == '\0')
1234 		goto error;
1235 
1236 	switch (token) {
1237 	case D_SCRIPT_VERSION:
1238 		if (rsi->cmd != C_SCRIPTINFO)
1239 			goto error;
1240 
1241 		/* check that value contains only digits */
1242 		for (ptr = value; *ptr != '\0'; ptr++)
1243 			if (isdigit((int)(*ptr)) == 0)
1244 				break;
1245 
1246 		if (*ptr == '\0')
1247 			rsi->ver = atoi(value);
1248 		else
1249 			goto error;
1250 
1251 		break;
1252 
1253 	case D_SCRIPT_FUNC_INFO:
1254 		if (rsi->cmd != C_SCRIPTINFO)
1255 			goto error;
1256 
1257 		rcmscript_snprintf(&rsi->func_info_buf,
1258 			&rsi->func_info_buf_len,
1259 			&rsi->func_info_buf_curptr,
1260 			"%s", value);
1261 		break;
1262 
1263 	case D_CMD_TIMEOUT:
1264 		if (rsi->cmd != C_SCRIPTINFO)
1265 			goto error;
1266 
1267 		/* check that value contains only digits */
1268 		for (ptr = value; *ptr != '\0'; ptr++)
1269 			if (isdigit((int)(*ptr)) == 0)
1270 				break;
1271 
1272 		if (*ptr == '\0')
1273 			rsi->cmd_timeout = atoi(value);
1274 		else
1275 			goto error;
1276 		break;
1277 
1278 	case D_RESOURCE_NAME:
1279 		if (rsi->cmd != C_REGISTER)
1280 			goto error;
1281 
1282 		if (get_capacity_descr(value) != NULL)
1283 			status = rcm_register_capacity(rsi->hdl, value,
1284 					0, NULL);
1285 		else
1286 			status = rcm_register_interest(rsi->hdl, value, 0,
1287 					NULL);
1288 
1289 		if (status == RCM_FAILURE && errno == EALREADY)
1290 			status = RCM_SUCCESS;
1291 
1292 		if (status != RCM_SUCCESS) {
1293 			rcm_log_message(RCM_ERROR, MS_REGISTER_RSRC_ERR,
1294 				rsi->script_name, value);
1295 		}
1296 
1297 		remove_from_unregister(rsi, value);
1298 		break;
1299 
1300 	case D_RESOURCE_USAGE_INFO:
1301 		if (rsi->cmd != C_RESOURCEINFO)
1302 			goto error;
1303 
1304 		rcmscript_snprintf(&rsi->resource_usage_info_buf,
1305 			&rsi->resource_usage_info_buf_len,
1306 			&rsi->resource_usage_info_buf_curptr,
1307 			"%s", value);
1308 		break;
1309 
1310 	case D_FAILURE_REASON:
1311 		rcmscript_snprintf(&rsi->failure_reason_buf,
1312 			&rsi->failure_reason_buf_len,
1313 			&rsi->failure_reason_buf_curptr,
1314 			"%s", value);
1315 		break;
1316 
1317 	default:
1318 		goto error;
1319 	}
1320 
1321 	return (0);
1322 
1323 error:
1324 	*errmsg = dup_err(RCM_ERROR, MS_PROTOCOL_ERR, rsi->script_name);
1325 	return (-1);
1326 }
1327 
1328 /* Send the given command to the script and process return data */
1329 static int
do_cmd(script_info_t * rsi,char * argv[],char * envp[],char ** errmsg)1330 do_cmd(script_info_t *rsi, char *argv[], char *envp[], char **errmsg)
1331 {
1332 	char buf[MAX_LINE_LEN];
1333 	size_t buflen;
1334 	int loglevel = -1, continuelog = 0;
1335 	char *ptr, *dname, *value;
1336 	time_t maxsecs;
1337 	time_t deadline;
1338 	int sigaborted = 0;
1339 	int rval, child_status, token;
1340 	int error_num;
1341 	int cmd_timeout = rsi->cmd_timeout;
1342 
1343 	*errmsg = NULL;
1344 
1345 	script_process_sema_wait();
1346 
1347 	if (run_script(rsi, argv, envp, errmsg) == -1) {
1348 		(void) sema_post(&script_process_sema);
1349 		goto error2;
1350 	}
1351 
1352 	(void) time(&rsi->lastrun);
1353 	deadline = rsi->lastrun + cmd_timeout;
1354 
1355 	if (thr_create(NULL, 0, (void *(*)(void *))read_stderr, rsi,
1356 	    0, &rsi->tid) != 0) {
1357 		*errmsg = dup_err(RCM_ERROR, MF_FUNC_CALL_ERR,
1358 				"thr_create", strerror(errno));
1359 		goto error1;
1360 	}
1361 	rsi->flags |= STDERR_THREAD_CREATED;
1362 
1363 	/* LINTED */
1364 	while (1) {
1365 		if (cmd_timeout > 0) {
1366 			maxsecs = deadline - time(NULL);
1367 			if (maxsecs <= 0)
1368 				goto timedout;
1369 		} else
1370 			maxsecs = 0;
1371 
1372 		rval = get_line(rsi->pipe1[PARENT_END_OF_PIPE],
1373 				"stdout", buf, MAX_LINE_LEN, &buflen,
1374 				maxsecs, &error_num);
1375 
1376 		if (buflen) {
1377 			if (continuelog)
1378 				log_msg(rsi, loglevel, buf);
1379 			else {
1380 				if ((ptr = strchr(buf, '=')) == NULL)
1381 					goto error;
1382 
1383 				*ptr = '\0';
1384 				dname = buf;
1385 				value = ptr + 1;
1386 				if ((token = dname_to_id(dname)) == -1)
1387 					goto error;
1388 
1389 				switch (token) {
1390 				case D_LOG_ERR:
1391 					loglevel = RCM_ERROR;
1392 					break;
1393 
1394 				case D_LOG_WARN:
1395 					loglevel = RCM_WARNING;
1396 					break;
1397 
1398 				case D_LOG_INFO:
1399 					loglevel = RCM_INFO;
1400 					break;
1401 
1402 				case D_LOG_DEBUG:
1403 					loglevel = RCM_DEBUG;
1404 					break;
1405 
1406 				default:
1407 					loglevel = -1;
1408 					break;
1409 				}
1410 
1411 				if (loglevel != -1) {
1412 					log_msg(rsi, loglevel, value);
1413 					if (buf[buflen - 1] == '\n')
1414 						continuelog = 0;
1415 					else
1416 						continuelog = 1;
1417 				} else {
1418 					if (buf[buflen - 1] != '\n')
1419 						goto error;
1420 
1421 					buf[buflen - 1] = '\0';
1422 					if (process_dataitem(rsi, token,
1423 						value, errmsg) != 0)
1424 						goto error1;
1425 				}
1426 			}
1427 		}
1428 
1429 		if (rval == -3) {
1430 			/* script exited */
1431 waitagain:
1432 			if (waitpid(rsi->pid, &child_status, 0)
1433 					!= rsi->pid) {
1434 				if (errno == EINTR || errno == EAGAIN)
1435 					goto waitagain;
1436 				*errmsg = dup_err(RCM_ERROR, MS_SCRIPT_ERR,
1437 					rsi->script_name, strerror(errno));
1438 				goto error1;
1439 			}
1440 
1441 			if (WIFEXITED(child_status)) {
1442 				script_exited(rsi);
1443 				rsi->exit_status = WEXITSTATUS(child_status);
1444 			} else {
1445 				if (sigaborted)
1446 					*errmsg = dup_err(RCM_ERROR,
1447 					MS_TIMEOUT_ERR, rsi->script_name);
1448 				else
1449 					*errmsg = dup_err(RCM_ERROR,
1450 					MS_UNKNOWN_ERR, rsi->script_name);
1451 
1452 				/* kill any remaining processes in the pgrp */
1453 				(void) kill(-(rsi->pid), SIGKILL);
1454 				script_exited(rsi);
1455 				goto error2;
1456 			}
1457 
1458 			break;
1459 		}
1460 
1461 		if (rval == -1) {
1462 			*errmsg = dup_err(RCM_ERROR, MS_SCRIPT_ERR,
1463 				rsi->script_name, strerror(errno));
1464 			goto error1;
1465 		}
1466 
1467 		if (rval == -2) {
1468 timedout:
1469 			/* timeout occurred */
1470 			if (sigaborted == 0) {
1471 				(void) kill(rsi->pid, SIGABRT);
1472 				sigaborted = 1;
1473 				/* extend deadline */
1474 				deadline += SCRIPT_ABORT_TIMEOUT;
1475 			} else {
1476 				*errmsg = dup_err(RCM_ERROR,
1477 					MS_TIMEOUT_ERR, rsi->script_name);
1478 				goto error1;
1479 			}
1480 		}
1481 	}
1482 
1483 	return (0);
1484 
1485 error:
1486 	*errmsg = dup_err(RCM_ERROR, MS_PROTOCOL_ERR, rsi->script_name);
1487 
1488 error1:
1489 	kill_script(rsi);
1490 
1491 error2:
1492 	return (-1);
1493 }
1494 
1495 static int
do_script_info(script_info_t * rsi)1496 do_script_info(script_info_t *rsi)
1497 {
1498 	char *argv[MAX_ARGS];
1499 	int status = RCM_FAILURE;
1500 	int err = 0;
1501 	char *errmsg = NULL;
1502 
1503 	rcm_log_message(RSCR_TRACE, "do_script_info: script name = %s\n",
1504 						rsi->script_name);
1505 
1506 	rsi->cmd = C_SCRIPTINFO;
1507 	rsi->func_info_buf = NULL;
1508 	rsi->failure_reason_buf = NULL;
1509 	fill_argv(rsi, argv, NULL);
1510 
1511 	if (do_cmd(rsi, argv, script_env, &errmsg) == 0) {
1512 		switch (rsi->exit_status) {
1513 		case E_SUCCESS:
1514 			if (rsi->func_info_buf != NULL &&
1515 				rsi->failure_reason_buf == NULL) {
1516 
1517 				if (rsi->ver >= SCRIPT_API_MIN_VER &&
1518 					rsi->ver <= SCRIPT_API_MAX_VER)
1519 					status = RCM_SUCCESS;
1520 				else
1521 					rcm_log_message(RCM_ERROR,
1522 					MS_UNSUPPORTED_VER, rsi->script_name,
1523 					rsi->ver);
1524 			} else
1525 				err = 1;
1526 			break;
1527 
1528 		case E_FAILURE:
1529 			if (rsi->failure_reason_buf != NULL) {
1530 				rcm_log_message(RCM_ERROR, MS_SCRIPTINFO_ERR,
1531 					rsi->script_name,
1532 					rsi->failure_reason_buf);
1533 			} else
1534 				err = 1;
1535 			break;
1536 
1537 		default:
1538 			err = 1;
1539 			break;
1540 		}
1541 		if (err)
1542 			rcm_log_message(RCM_ERROR, MS_PROTOCOL_ERR,
1543 				rsi->script_name);
1544 	} else if (errmsg)
1545 		(void) free(errmsg);
1546 
1547 	if (status != RCM_SUCCESS && rsi->func_info_buf != NULL)
1548 		free(rsi->func_info_buf);
1549 
1550 	if (rsi->failure_reason_buf)
1551 		free(rsi->failure_reason_buf);
1552 
1553 	return (status);
1554 }
1555 
1556 static int
do_dr(script_info_t * rsi,char * argv[],char * envp[],char ** info)1557 do_dr(script_info_t *rsi, char *argv[], char *envp[], char **info)
1558 {
1559 	int status = RCM_FAILURE;
1560 	int err = 0;
1561 
1562 	rsi->failure_reason_buf = NULL;
1563 
1564 	if (do_cmd(rsi, argv, envp, info) == 0) {
1565 		switch (rsi->exit_status) {
1566 		case E_SUCCESS:
1567 		case E_UNSUPPORTED_CMD:
1568 			if (rsi->failure_reason_buf == NULL)
1569 				status = RCM_SUCCESS;
1570 			else
1571 				err = 1;
1572 			break;
1573 
1574 		case E_FAILURE:
1575 		case E_REFUSE:
1576 			if (rsi->failure_reason_buf != NULL) {
1577 				*info = rsi->failure_reason_buf;
1578 				rsi->failure_reason_buf = NULL;
1579 			} else
1580 				err = 1;
1581 			break;
1582 
1583 		default:
1584 			err = 1;
1585 			break;
1586 		}
1587 
1588 		if (err)
1589 			*info = dup_err(RCM_ERROR, MS_PROTOCOL_ERR,
1590 				rsi->script_name);
1591 	}
1592 
1593 	if (rsi->failure_reason_buf)
1594 		free(rsi->failure_reason_buf);
1595 
1596 	return (status);
1597 }
1598 
1599 /*
1600  * get_info entry point
1601  */
1602 /* ARGSUSED */
1603 static int
script_get_info(rcm_handle_t * hdl,char * resource_name,pid_t pid,uint_t flag,char ** info,char ** error,nvlist_t * props,rcm_info_t ** dependent_info)1604 script_get_info(rcm_handle_t *hdl,
1605 	char *resource_name,
1606 	pid_t pid,
1607 	uint_t flag,
1608 	char **info,
1609 	char **error,
1610 	nvlist_t *props,
1611 	rcm_info_t **dependent_info)
1612 {
1613 	script_info_t *rsi = hdl->module->rsi;
1614 	char *argv[MAX_ARGS];
1615 	int status = RCM_FAILURE;
1616 	int err = 0;
1617 
1618 	rcm_log_message(RSCR_TRACE, "script_get_info: resource = %s\n",
1619 				resource_name);
1620 
1621 	*info = NULL;
1622 	*error = NULL;
1623 
1624 	(void) mutex_lock(&rsi->channel_lock);
1625 
1626 	rsi->hdl = hdl;
1627 	rsi->cmd = C_RESOURCEINFO;
1628 	rsi->resource_usage_info_buf = NULL;
1629 	rsi->failure_reason_buf = NULL;
1630 	fill_argv(rsi, argv, resource_name);
1631 
1632 	if (do_cmd(rsi, argv, script_env, error) == 0) {
1633 		switch (rsi->exit_status) {
1634 		case E_SUCCESS:
1635 			if (rsi->resource_usage_info_buf != NULL &&
1636 				rsi->failure_reason_buf == NULL) {
1637 
1638 				*info = rsi->resource_usage_info_buf;
1639 				rsi->resource_usage_info_buf = NULL;
1640 				status = RCM_SUCCESS;
1641 			} else
1642 				err = 1;
1643 			break;
1644 
1645 		case E_FAILURE:
1646 			if (rsi->failure_reason_buf != NULL) {
1647 				*error = rsi->failure_reason_buf;
1648 				rsi->failure_reason_buf = NULL;
1649 			} else
1650 				err = 1;
1651 			break;
1652 
1653 		default:
1654 			err = 1;
1655 			break;
1656 		}
1657 		if (err)
1658 			*error = dup_err(RCM_ERROR, MS_PROTOCOL_ERR,
1659 				rsi->script_name);
1660 	}
1661 
1662 	if (rsi->resource_usage_info_buf)
1663 		free(rsi->resource_usage_info_buf);
1664 
1665 	if (rsi->failure_reason_buf)
1666 		free(rsi->failure_reason_buf);
1667 
1668 	(void) mutex_unlock(&rsi->channel_lock);
1669 
1670 	return (status);
1671 }
1672 
1673 static void
add_for_unregister(script_info_t * rsi)1674 add_for_unregister(script_info_t *rsi)
1675 {
1676 	module_t *module = rsi->module;
1677 	client_t *client;
1678 	rcm_queue_t *head;
1679 	rcm_queue_t *q;
1680 
1681 	(void) mutex_lock(&rcm_req_lock);
1682 
1683 	head = &module->client_q;
1684 
1685 	for (q = head->next; q != head; q = q->next) {
1686 		client = RCM_STRUCT_BASE_ADDR(client_t, q, queue);
1687 		client->prv_flags |= RCM_NEED_TO_UNREGISTER;
1688 	}
1689 
1690 	(void) mutex_unlock(&rcm_req_lock);
1691 }
1692 
1693 static void
remove_from_unregister(script_info_t * rsi,char * resource_name)1694 remove_from_unregister(script_info_t *rsi, char *resource_name)
1695 {
1696 	module_t *module = rsi->module;
1697 	client_t *client;
1698 	rcm_queue_t *head;
1699 	rcm_queue_t *q;
1700 
1701 	(void) mutex_lock(&rcm_req_lock);
1702 
1703 	head = &module->client_q;
1704 
1705 	for (q = head->next; q != head; q = q->next) {
1706 		client = RCM_STRUCT_BASE_ADDR(client_t, q, queue);
1707 		if (strcmp(client->alias, resource_name) == 0) {
1708 			client->prv_flags &= ~RCM_NEED_TO_UNREGISTER;
1709 			break;
1710 		}
1711 	}
1712 
1713 	(void) mutex_unlock(&rcm_req_lock);
1714 }
1715 
1716 static void
complete_unregister(script_info_t * rsi)1717 complete_unregister(script_info_t *rsi)
1718 {
1719 	module_t *module = rsi->module;
1720 	client_t *client;
1721 	rcm_queue_t *head;
1722 	rcm_queue_t *q;
1723 
1724 	(void) mutex_lock(&rcm_req_lock);
1725 
1726 	head = &module->client_q;
1727 
1728 	for (q = head->next; q != head; q = q->next) {
1729 		client = RCM_STRUCT_BASE_ADDR(client_t, q, queue);
1730 		if (client->prv_flags & RCM_NEED_TO_UNREGISTER) {
1731 			client->prv_flags &= ~RCM_NEED_TO_UNREGISTER;
1732 			client->state = RCM_STATE_REMOVE;
1733 		}
1734 	}
1735 
1736 	(void) mutex_unlock(&rcm_req_lock);
1737 }
1738 
1739 /*
1740  * register_interest entry point
1741  */
1742 static int
script_register_interest(rcm_handle_t * hdl)1743 script_register_interest(rcm_handle_t *hdl)
1744 {
1745 	script_info_t *rsi = hdl->module->rsi;
1746 	char *argv[MAX_ARGS];
1747 	int status = RCM_FAILURE;
1748 	int err = 0;
1749 	char *errmsg = NULL;
1750 
1751 	rcm_log_message(RSCR_TRACE,
1752 		"script_register_interest: script name = %s\n",
1753 		rsi->script_name);
1754 
1755 	(void) mutex_lock(&rsi->channel_lock);
1756 
1757 	if (rsi->drreq_q.next != &rsi->drreq_q) {
1758 		/* if DR is already in progress no need to register again */
1759 		(void) mutex_unlock(&rsi->channel_lock);
1760 		return (RCM_SUCCESS);
1761 	}
1762 
1763 	rsi->hdl = hdl;
1764 	rsi->cmd = C_REGISTER;
1765 	rsi->failure_reason_buf = NULL;
1766 	fill_argv(rsi, argv, NULL);
1767 
1768 	add_for_unregister(rsi);
1769 
1770 	if (do_cmd(rsi, argv, script_env, &errmsg) == 0) {
1771 		switch (rsi->exit_status) {
1772 		case E_SUCCESS:
1773 			status = RCM_SUCCESS;
1774 			break;
1775 
1776 		case E_FAILURE:
1777 			if (rsi->failure_reason_buf != NULL) {
1778 				rcm_log_message(RCM_ERROR, MS_REGISTER_ERR,
1779 					rsi->script_name,
1780 					rsi->failure_reason_buf);
1781 			} else
1782 				err = 1;
1783 			break;
1784 
1785 		default:
1786 			err = 1;
1787 			break;
1788 		}
1789 		if (err)
1790 			rcm_log_message(RCM_ERROR, MS_PROTOCOL_ERR,
1791 				rsi->script_name);
1792 	} else if (errmsg)
1793 		(void) free(errmsg);
1794 
1795 	complete_unregister(rsi);
1796 
1797 	if (rsi->failure_reason_buf)
1798 		free(rsi->failure_reason_buf);
1799 
1800 	(void) mutex_unlock(&rsi->channel_lock);
1801 
1802 	return (status);
1803 }
1804 
1805 /*
1806  * Add the specified resource name to the drreq_q.
1807  */
1808 static void
add_drreq(script_info_t * rsi,char * resource_name)1809 add_drreq(script_info_t *rsi, char *resource_name)
1810 {
1811 	rcm_queue_t *head = &rsi->drreq_q;
1812 	rcm_queue_t *q;
1813 	drreq_t *drreq;
1814 
1815 	/* check if the dr req is already in the list */
1816 	for (q = head->next; q != head; q = q->next) {
1817 		drreq = RCM_STRUCT_BASE_ADDR(drreq_t, q, queue);
1818 		if (strcmp(drreq->resource_name, resource_name) == 0)
1819 			/* dr req is already present in the queue */
1820 			return;
1821 	}
1822 
1823 	drreq = (drreq_t *)rcmscript_calloc(1, sizeof (drreq_t));
1824 	drreq->resource_name = rcmscript_strdup(resource_name);
1825 
1826 	rcm_enqueue_tail(&rsi->drreq_q, &drreq->queue);
1827 }
1828 
1829 /*
1830  * Remove the dr req for the specified resource name from the drreq_q.
1831  */
1832 static void
remove_drreq(script_info_t * rsi,char * resource_name)1833 remove_drreq(script_info_t *rsi, char *resource_name)
1834 {
1835 	rcm_queue_t *head = &rsi->drreq_q;
1836 	rcm_queue_t *q;
1837 	drreq_t *drreq;
1838 
1839 	/* search for dr req and remove from the list */
1840 	for (q = head->next; q != head; q = q->next) {
1841 		drreq = RCM_STRUCT_BASE_ADDR(drreq_t, q, queue);
1842 		if (strcmp(drreq->resource_name, resource_name) == 0)
1843 			break;
1844 	}
1845 
1846 	if (q != head) {
1847 		/* found drreq on the queue */
1848 		rcm_dequeue(&drreq->queue);
1849 		free(drreq->resource_name);
1850 		free(drreq);
1851 	}
1852 }
1853 
1854 /*
1855  * Remove all dr req's.
1856  */
1857 static void
remove_drreq_all(script_info_t * rsi)1858 remove_drreq_all(script_info_t *rsi)
1859 {
1860 	drreq_t *drreq;
1861 
1862 	while (rsi->drreq_q.next != &rsi->drreq_q) {
1863 		drreq = RCM_STRUCT_BASE_ADDR(drreq_t,
1864 					rsi->drreq_q.next, queue);
1865 		remove_drreq(rsi, drreq->resource_name);
1866 	}
1867 }
1868 
1869 /*
1870  * request_offline entry point
1871  */
1872 /* ARGSUSED */
1873 static int
script_request_offline(rcm_handle_t * hdl,char * resource_name,pid_t pid,uint_t flag,char ** info,rcm_info_t ** dependent_info)1874 script_request_offline(rcm_handle_t *hdl,
1875 	char *resource_name,
1876 	pid_t pid,
1877 	uint_t flag,
1878 	char **info,
1879 	rcm_info_t **dependent_info)
1880 {
1881 	script_info_t *rsi = hdl->module->rsi;
1882 	char *argv[MAX_ARGS];
1883 	char *envp[MAX_ENV_PARAMS];
1884 	char flags_name[MAX_FLAGS_NAME_LEN];
1885 	int status;
1886 	int i;
1887 
1888 	rcm_log_message(RSCR_TRACE,
1889 		"script_request_offline: resource = %s flags = %s\n",
1890 			resource_name,
1891 			flags_to_name(flag, flags_name, MAX_FLAGS_NAME_LEN));
1892 
1893 	*info = NULL;
1894 
1895 	(void) mutex_lock(&rsi->channel_lock);
1896 
1897 	rsi->hdl = hdl;
1898 	rsi->cmd = (flag & RCM_QUERY) ? C_QUERYREMOVE : C_PREREMOVE;
1899 
1900 	if (rsi->cmd == C_PREREMOVE)
1901 		add_drreq(rsi, resource_name);
1902 
1903 	fill_argv(rsi, argv, resource_name);
1904 	copy_env(script_env, envp);
1905 	for (i = 0; envp[i] != NULL; i++)
1906 		;
1907 	envp[i++] = (flag & RCM_FORCE) ? script_env_force : script_env_noforce;
1908 	envp[i] = NULL;
1909 
1910 	status = do_dr(rsi, argv, envp, info);
1911 
1912 	(void) mutex_unlock(&rsi->channel_lock);
1913 	return (status);
1914 }
1915 
1916 /*
1917  * notify_online entry point
1918  */
1919 /* ARGSUSED */
1920 static int
script_notify_online(rcm_handle_t * hdl,char * resource_name,pid_t pid,uint_t flag,char ** info,rcm_info_t ** dependent_info)1921 script_notify_online(rcm_handle_t *hdl,
1922 	char *resource_name,
1923 	pid_t pid,
1924 	uint_t flag,
1925 	char **info,
1926 	rcm_info_t **dependent_info)
1927 {
1928 	script_info_t *rsi = hdl->module->rsi;
1929 	char *argv[MAX_ARGS];
1930 	int status;
1931 
1932 	rcm_log_message(RSCR_TRACE, "script_notify_online: resource = %s\n",
1933 				resource_name);
1934 
1935 	*info = NULL;
1936 
1937 	(void) mutex_lock(&rsi->channel_lock);
1938 
1939 	rsi->hdl = hdl;
1940 	rsi->cmd = C_UNDOREMOVE;
1941 	fill_argv(rsi, argv, resource_name);
1942 
1943 	status = do_dr(rsi, argv, script_env, info);
1944 
1945 	remove_drreq(rsi, resource_name);
1946 
1947 	(void) mutex_unlock(&rsi->channel_lock);
1948 	return (status);
1949 }
1950 
1951 /*
1952  * notify_remove entry point
1953  */
1954 /* ARGSUSED */
1955 static int
script_notify_remove(rcm_handle_t * hdl,char * resource_name,pid_t pid,uint_t flag,char ** info,rcm_info_t ** dependent_info)1956 script_notify_remove(rcm_handle_t *hdl,
1957 	char *resource_name,
1958 	pid_t pid,
1959 	uint_t flag,
1960 	char **info,
1961 	rcm_info_t **dependent_info)
1962 {
1963 	script_info_t *rsi = hdl->module->rsi;
1964 	char *argv[MAX_ARGS];
1965 	int status;
1966 
1967 	rcm_log_message(RSCR_TRACE, "script_notify_remove: resource = %s\n",
1968 				resource_name);
1969 
1970 	*info = NULL;
1971 
1972 	(void) mutex_lock(&rsi->channel_lock);
1973 
1974 	rsi->hdl = hdl;
1975 	rsi->cmd = C_POSTREMOVE;
1976 	fill_argv(rsi, argv, resource_name);
1977 
1978 	status = do_dr(rsi, argv, script_env, info);
1979 
1980 	remove_drreq(rsi, resource_name);
1981 
1982 	(void) mutex_unlock(&rsi->channel_lock);
1983 	return (status);
1984 }
1985 
1986 /*
1987  * request_suspend entry point
1988  */
1989 /* ARGSUSED */
1990 static int
script_request_suspend(rcm_handle_t * hdl,char * resource_name,pid_t pid,timespec_t * interval,uint_t flag,char ** info,rcm_info_t ** dependent_info)1991 script_request_suspend(rcm_handle_t *hdl,
1992 	char *resource_name,
1993 	pid_t pid,
1994 	timespec_t *interval,
1995 	uint_t flag,
1996 	char **info,
1997 	rcm_info_t **dependent_info)
1998 {
1999 	script_info_t *rsi = hdl->module->rsi;
2000 	char *buf = NULL;
2001 	char *curptr = NULL;
2002 	char *argv[MAX_ARGS];
2003 	char *envp[MAX_ENV_PARAMS];
2004 	char flags_name[MAX_FLAGS_NAME_LEN];
2005 	int buflen = 0;
2006 	long seconds;
2007 	int status;
2008 	int i;
2009 
2010 	rcm_log_message(RSCR_TRACE,
2011 	    "script_request_suspend: resource = %s flags = %s\n", resource_name,
2012 	    flags_to_name(flag, flags_name, MAX_FLAGS_NAME_LEN));
2013 
2014 	*info = NULL;
2015 
2016 	(void) mutex_lock(&rsi->channel_lock);
2017 
2018 	rsi->hdl = hdl;
2019 	rsi->cmd = (flag & RCM_QUERY) ? C_QUERYSUSPEND : C_PRESUSPEND;
2020 
2021 	if (rsi->cmd == C_PRESUSPEND)
2022 		add_drreq(rsi, resource_name);
2023 
2024 	fill_argv(rsi, argv, resource_name);
2025 
2026 	copy_env(script_env, envp);
2027 	for (i = 0; envp[i] != NULL; i++);
2028 
2029 	envp[i++] = (flag & RCM_FORCE) ? script_env_force : script_env_noforce;
2030 
2031 	if (interval) {
2032 		/*
2033 		 * Merge the seconds and nanoseconds, rounding up if there
2034 		 * are any remainder nanoseconds.
2035 		 */
2036 		seconds = interval->tv_sec + (interval->tv_nsec / 1000000000L);
2037 		if (interval->tv_nsec % 1000000000L)
2038 			seconds += (interval->tv_sec > 0) ? 1L : -1L;
2039 		rcmscript_snprintf(&buf, &buflen, &curptr, script_env_interval,
2040 		    seconds);
2041 		envp[i++] = buf;
2042 	}
2043 
2044 	envp[i] = NULL;
2045 
2046 	status = do_dr(rsi, argv, envp, info);
2047 
2048 	(void) mutex_unlock(&rsi->channel_lock);
2049 	if (buf)
2050 		free(buf);
2051 	return (status);
2052 }
2053 
2054 /*
2055  * notify_resume entry point
2056  */
2057 /* ARGSUSED */
2058 static int
script_notify_resume(rcm_handle_t * hdl,char * resource_name,pid_t pid,uint_t flag,char ** info,rcm_info_t ** dependent_info)2059 script_notify_resume(rcm_handle_t *hdl,
2060 	char *resource_name,
2061 	pid_t pid,
2062 	uint_t flag,
2063 	char **info,
2064 	rcm_info_t **dependent_info)
2065 {
2066 	script_info_t *rsi = hdl->module->rsi;
2067 	char *argv[MAX_ARGS];
2068 	int status;
2069 
2070 	rcm_log_message(RSCR_TRACE, "script_notify_resume: resource = %s\n",
2071 	    resource_name);
2072 
2073 	*info = NULL;
2074 
2075 	(void) mutex_lock(&rsi->channel_lock);
2076 
2077 	rsi->hdl = hdl;
2078 	rsi->cmd = (flag & RCM_SUSPENDED) ? C_POSTRESUME : C_CANCELSUSPEND;
2079 	fill_argv(rsi, argv, resource_name);
2080 
2081 	status = do_dr(rsi, argv, script_env, info);
2082 
2083 	remove_drreq(rsi, resource_name);
2084 
2085 	(void) mutex_unlock(&rsi->channel_lock);
2086 	return (status);
2087 }
2088 
2089 static capacity_descr_t capacity_type[] = {
2090 	{ "SUNW_memory", MATCH_EXACT,
2091 		"new_pages", "RCM_ENV_CAPACITY",
2092 		"page_size", "RCM_ENV_UNIT_SIZE",
2093 		"", ""},
2094 	{ "SUNW_cpu", MATCH_EXACT,
2095 		"new_total", "RCM_ENV_CAPACITY",
2096 		"new_cpu_list", "RCM_ENV_CPU_IDS",
2097 		"", ""},
2098 	{ "SUNW_cpu/set", MATCH_PREFIX,
2099 		"new_total", "RCM_ENV_CAPACITY",
2100 		"new_cpu_list", "RCM_ENV_CPU_IDS",
2101 		"", ""},
2102 	{ "", MATCH_INVALID, "", "" }
2103 };
2104 
2105 static capacity_descr_t *
get_capacity_descr(char * resource_name)2106 get_capacity_descr(char *resource_name)
2107 {
2108 	int i;
2109 
2110 	for (i = 0; *capacity_type[i].resource_name != '\0'; i++) {
2111 		if ((capacity_type[i].match_type == MATCH_EXACT &&
2112 			strcmp(capacity_type[i].resource_name,
2113 			resource_name) == 0) ||
2114 			(capacity_type[i].match_type == MATCH_PREFIX &&
2115 			strncmp(capacity_type[i].resource_name,
2116 			resource_name,
2117 			strlen(capacity_type[i].resource_name)) == 0))
2118 
2119 			return (&capacity_type[i]);
2120 	}
2121 
2122 	return (NULL);
2123 }
2124 
2125 static int
build_env_for_capacity(script_info_t * rsi,char * resource_name,uint_t flag,nvlist_t * capacity_info,char * envp[],int * dynamic_env_index,char ** errmsg)2126 build_env_for_capacity(script_info_t *rsi,
2127 	char *resource_name,
2128 	uint_t flag,
2129 	nvlist_t *capacity_info,
2130 	char *envp[],
2131 	int *dynamic_env_index,
2132 	char **errmsg)
2133 {
2134 	int p, i;
2135 	capacity_descr_t *capa = NULL;
2136 	nvpair_t *nvpair;
2137 	char *buf;
2138 	char *curptr;
2139 	int buflen;
2140 	int error;
2141 	uint_t n;
2142 
2143 	copy_env(script_env, envp);
2144 	for (p = 0; envp[p] != NULL; p++)
2145 		;
2146 
2147 	if (rsi->cmd == C_QUERYCAPACITY || rsi->cmd == C_PRECAPACITY)
2148 		envp[p++] = (flag & RCM_FORCE) ? script_env_force :
2149 						script_env_noforce;
2150 
2151 	envp[p] = NULL;
2152 	*dynamic_env_index = p;
2153 
2154 	if ((capa = get_capacity_descr(resource_name)) == NULL) {
2155 		*errmsg = dup_err(RCM_ERROR, MF_UNKNOWN_RSRC_ERR,
2156 			resource_name, rsi->script_name);
2157 		return (-1);
2158 	}
2159 
2160 	for (i = 0; *capa->param[i].nvname != '\0'; i++) {
2161 		nvpair = NULL;
2162 		while ((nvpair = nvlist_next_nvpair(capacity_info, nvpair))
2163 				!= NULL) {
2164 			if (strcmp(nvpair_name(nvpair),
2165 					capa->param[i].nvname) == 0)
2166 				break;
2167 		}
2168 
2169 		if (nvpair == NULL) {
2170 			*errmsg = dup_err(RCM_ERROR, MF_NV_ERR,
2171 				rsi->script_name);
2172 			return (-1);
2173 		}
2174 
2175 		error = 0;
2176 		buf = NULL;
2177 
2178 		rcmscript_snprintf(&buf, &buflen, &curptr, "%s=",
2179 				capa->param[i].envname);
2180 
2181 		switch (nvpair_type(nvpair)) {
2182 		case DATA_TYPE_INT16:
2183 		{
2184 			int16_t x;
2185 
2186 			if (nvpair_value_int16(nvpair, &x) == 0) {
2187 				rcmscript_snprintf(&buf, &buflen, &curptr,
2188 						"%hd", (short)x);
2189 			} else
2190 				error = 1;
2191 			break;
2192 		}
2193 
2194 		case DATA_TYPE_UINT16:
2195 		{
2196 			uint16_t x;
2197 
2198 			if (nvpair_value_uint16(nvpair, &x) == 0) {
2199 				rcmscript_snprintf(&buf, &buflen, &curptr,
2200 						"%hu", (unsigned short)x);
2201 			} else
2202 				error = 1;
2203 			break;
2204 		}
2205 
2206 		case DATA_TYPE_INT32:
2207 		{
2208 			int32_t x;
2209 
2210 			if (nvpair_value_int32(nvpair, &x) == 0) {
2211 				rcmscript_snprintf(&buf, &buflen, &curptr,
2212 						"%d", (int)x);
2213 			} else
2214 				error = 1;
2215 			break;
2216 		}
2217 
2218 		case DATA_TYPE_UINT32:
2219 		{
2220 			uint32_t x;
2221 
2222 			if (nvpair_value_uint32(nvpair, &x) == 0) {
2223 				rcmscript_snprintf(&buf, &buflen, &curptr,
2224 						"%u", (uint_t)x);
2225 			} else
2226 				error = 1;
2227 			break;
2228 		}
2229 
2230 		case DATA_TYPE_INT64:
2231 		{
2232 			int64_t x;
2233 
2234 			if (nvpair_value_int64(nvpair, &x) == 0) {
2235 				rcmscript_snprintf(&buf, &buflen, &curptr,
2236 						"%lld", (long long)x);
2237 			} else
2238 				error = 1;
2239 			break;
2240 		}
2241 
2242 		case DATA_TYPE_UINT64:
2243 		{
2244 			uint64_t x;
2245 
2246 			if (nvpair_value_uint64(nvpair, &x) == 0) {
2247 				rcmscript_snprintf(&buf, &buflen, &curptr,
2248 					"%llu", (unsigned long long)x);
2249 			} else
2250 				error = 1;
2251 			break;
2252 		}
2253 
2254 		case DATA_TYPE_INT16_ARRAY:
2255 		{
2256 			int16_t *x;
2257 
2258 			if (nvpair_value_int16_array(nvpair, &x, &n) == 0) {
2259 				while (n--) {
2260 					rcmscript_snprintf(&buf, &buflen,
2261 						&curptr, "%hd%s",
2262 						(short)(*x),
2263 						(n == 0) ? "" : " ");
2264 					x++;
2265 				}
2266 			} else
2267 				error = 1;
2268 			break;
2269 		}
2270 
2271 		case DATA_TYPE_UINT16_ARRAY:
2272 		{
2273 			uint16_t *x;
2274 
2275 			if (nvpair_value_uint16_array(nvpair, &x, &n) == 0) {
2276 				while (n--) {
2277 					rcmscript_snprintf(&buf, &buflen,
2278 						&curptr, "%hu%s",
2279 						(unsigned short)(*x),
2280 						(n == 0) ? "" : " ");
2281 					x++;
2282 				}
2283 			} else
2284 				error = 1;
2285 			break;
2286 		}
2287 
2288 		case DATA_TYPE_INT32_ARRAY:
2289 		{
2290 			int32_t *x;
2291 
2292 			if (nvpair_value_int32_array(nvpair, &x, &n) == 0) {
2293 				while (n--) {
2294 					rcmscript_snprintf(&buf, &buflen,
2295 						&curptr, "%d%s",
2296 						(int)(*x),
2297 						(n == 0) ? "" : " ");
2298 					x++;
2299 				}
2300 			} else
2301 				error = 1;
2302 			break;
2303 		}
2304 
2305 		case DATA_TYPE_UINT32_ARRAY:
2306 		{
2307 			uint32_t *x;
2308 
2309 			if (nvpair_value_uint32_array(nvpair, &x, &n) == 0) {
2310 				while (n--) {
2311 					rcmscript_snprintf(&buf, &buflen,
2312 						&curptr, "%u%s",
2313 						(uint_t)(*x),
2314 						(n == 0) ? "" : " ");
2315 					x++;
2316 				}
2317 			} else
2318 				error = 1;
2319 			break;
2320 		}
2321 
2322 		case DATA_TYPE_INT64_ARRAY:
2323 		{
2324 			int64_t *x;
2325 
2326 			if (nvpair_value_int64_array(nvpair, &x, &n) == 0) {
2327 				while (n--) {
2328 					rcmscript_snprintf(&buf, &buflen,
2329 						&curptr, "%lld%s",
2330 						(long long)(*x),
2331 						(n == 0) ? "" : " ");
2332 					x++;
2333 				}
2334 			} else
2335 				error = 1;
2336 			break;
2337 		}
2338 
2339 		case DATA_TYPE_UINT64_ARRAY:
2340 		{
2341 			uint64_t *x;
2342 
2343 			if (nvpair_value_uint64_array(nvpair, &x, &n) == 0) {
2344 				while (n--) {
2345 					rcmscript_snprintf(&buf, &buflen,
2346 						&curptr, "%llu%s",
2347 						(unsigned long long)(*x),
2348 						(n == 0) ? "" : " ");
2349 					x++;
2350 				}
2351 			} else
2352 				error = 1;
2353 			break;
2354 		}
2355 
2356 		case DATA_TYPE_STRING:
2357 		{
2358 			char *x;
2359 
2360 			if (nvpair_value_string(nvpair, &x) == 0) {
2361 				rcmscript_snprintf(&buf, &buflen, &curptr,
2362 						"%s", x);
2363 			} else
2364 				error = 1;
2365 			break;
2366 		}
2367 
2368 
2369 		default:
2370 			error = 1;
2371 			break;
2372 		}
2373 
2374 		envp[p++] = buf;
2375 
2376 		if (error) {
2377 			envp[p] = NULL;
2378 			for (p = *dynamic_env_index; envp[p] != NULL; p++)
2379 				free(envp[p]);
2380 			*errmsg = dup_err(RCM_ERROR, MF_NV_ERR,
2381 				rsi->script_name);
2382 			return (-1);
2383 		}
2384 	}
2385 
2386 	envp[p] = NULL;
2387 
2388 	return (0);
2389 }
2390 
2391 /*
2392  * request_capacity_change entry point
2393  */
2394 /* ARGSUSED */
2395 static int
script_request_capacity_change(rcm_handle_t * hdl,char * resource_name,pid_t pid,uint_t flag,nvlist_t * capacity_info,char ** info,rcm_info_t ** dependent_info)2396 script_request_capacity_change(rcm_handle_t *hdl,
2397 	char *resource_name,
2398 	pid_t pid,
2399 	uint_t flag,
2400 	nvlist_t *capacity_info,
2401 	char **info,
2402 	rcm_info_t **dependent_info)
2403 {
2404 	script_info_t *rsi = hdl->module->rsi;
2405 	char *argv[MAX_ARGS];
2406 	char *envp[MAX_ENV_PARAMS];
2407 	char flags_name[MAX_FLAGS_NAME_LEN];
2408 	int status;
2409 	int dynamic_env_index;
2410 
2411 	rcm_log_message(RSCR_TRACE,
2412 		"script_request_capacity_change: resource = %s flags = %s\n",
2413 			resource_name,
2414 			flags_to_name(flag, flags_name, MAX_FLAGS_NAME_LEN));
2415 
2416 	*info = NULL;
2417 
2418 	(void) mutex_lock(&rsi->channel_lock);
2419 
2420 	rsi->hdl = hdl;
2421 	rsi->cmd = (flag & RCM_QUERY) ? C_QUERYCAPACITY : C_PRECAPACITY;
2422 	fill_argv(rsi, argv, resource_name);
2423 
2424 	if (build_env_for_capacity(rsi, resource_name, flag,
2425 			capacity_info, envp, &dynamic_env_index, info) == 0) {
2426 
2427 		status = do_dr(rsi, argv, envp, info);
2428 
2429 		while (envp[dynamic_env_index] != NULL) {
2430 			free(envp[dynamic_env_index]);
2431 			dynamic_env_index++;
2432 		}
2433 	} else
2434 		status = RCM_FAILURE;
2435 
2436 	(void) mutex_unlock(&rsi->channel_lock);
2437 	return (status);
2438 }
2439 
2440 /*
2441  * notify_capacity_change entry point
2442  */
2443 /* ARGSUSED */
2444 static int
script_notify_capacity_change(rcm_handle_t * hdl,char * resource_name,pid_t pid,uint_t flag,nvlist_t * capacity_info,char ** info,rcm_info_t ** dependent_info)2445 script_notify_capacity_change(rcm_handle_t *hdl,
2446 	char *resource_name,
2447 	pid_t pid,
2448 	uint_t flag,
2449 	nvlist_t *capacity_info,
2450 	char **info,
2451 	rcm_info_t **dependent_info)
2452 {
2453 	script_info_t *rsi = hdl->module->rsi;
2454 	char *argv[MAX_ARGS];
2455 	char *envp[MAX_ENV_PARAMS];
2456 	int status;
2457 	int dynamic_env_index;
2458 
2459 	rcm_log_message(RSCR_TRACE,
2460 	"script_notify_capacity_change: resource = %s\n", resource_name);
2461 
2462 	*info = NULL;
2463 
2464 	(void) mutex_lock(&rsi->channel_lock);
2465 
2466 	rsi->hdl = hdl;
2467 	rsi->cmd = C_POSTCAPACITY;
2468 	fill_argv(rsi, argv, resource_name);
2469 
2470 	if (build_env_for_capacity(rsi, resource_name, flag,
2471 			capacity_info, envp, &dynamic_env_index, info) == 0) {
2472 
2473 		status = do_dr(rsi, argv, envp, info);
2474 
2475 		while (envp[dynamic_env_index] != NULL) {
2476 			free(envp[dynamic_env_index]);
2477 			dynamic_env_index++;
2478 		}
2479 	} else
2480 		status = RCM_FAILURE;
2481 
2482 	(void) mutex_unlock(&rsi->channel_lock);
2483 	return (status);
2484 }
2485 
2486 /* Log the message to syslog */
2487 static void
log_msg(script_info_t * rsi,int level,char * msg)2488 log_msg(script_info_t *rsi, int level, char *msg)
2489 {
2490 	rcm_log_msg(level, MS_LOG_MSG, rsi->script_name, msg);
2491 }
2492 
2493 /*PRINTFLIKE2*/
2494 static char *
dup_err(int level,char * format,...)2495 dup_err(int level, char *format, ...)
2496 {
2497 	va_list ap;
2498 	char buf1[1];
2499 	char *buf2;
2500 	int n;
2501 
2502 	va_start(ap, format);
2503 	n = vsnprintf(buf1, 1, format, ap);
2504 	va_end(ap);
2505 
2506 	if (n > 0) {
2507 		n++;
2508 		if (buf2 = (char *)malloc(n)) {
2509 			va_start(ap, format);
2510 			n = vsnprintf(buf2, n, format, ap);
2511 			va_end(ap);
2512 			if (n > 0) {
2513 				if (level != -1)
2514 					rcm_log_message(level, buf2);
2515 				return (buf2);
2516 			}
2517 			free(buf2);
2518 		}
2519 	}
2520 
2521 	return (NULL);
2522 }
2523 
2524 /*PRINTFLIKE4*/
2525 static void
rcmscript_snprintf(char ** buf,int * buflen,char ** curptr,char * format,...)2526 rcmscript_snprintf(char **buf, int *buflen, char **curptr, char *format, ...)
2527 {
2528 /* must be power of 2 otherwise RSCR_ROUNDUP would break */
2529 #define	SPRINTF_CHUNK_LEN	512
2530 #define	SPRINTF_MIN_CHUNK_LEN	64
2531 
2532 	va_list ap;
2533 	int offset, bytesneeded, bytesleft, error_num;
2534 
2535 	if (*buf == NULL) {
2536 		*buflen = 0;
2537 		*curptr = NULL;
2538 	}
2539 
2540 	offset = *curptr - *buf;
2541 	bytesneeded = SPRINTF_MIN_CHUNK_LEN;
2542 	bytesleft = *buflen - offset;
2543 
2544 	/* LINTED */
2545 	while (1) {
2546 		if (bytesneeded > bytesleft) {
2547 			*buflen += RSCR_ROUNDUP(bytesneeded - bytesleft,
2548 					SPRINTF_CHUNK_LEN);
2549 			if ((*buf = (char *)realloc(*buf, *buflen)) == NULL) {
2550 				error_num = errno;
2551 				rcm_log_message(RCM_ERROR,
2552 					MF_MEMORY_ALLOCATION_ERR,
2553 					strerror(error_num));
2554 				rcmd_exit(error_num);
2555 				/*NOTREACHED*/
2556 			}
2557 			*curptr = *buf + offset;
2558 			bytesleft = *buflen - offset;
2559 		}
2560 
2561 		va_start(ap, format);
2562 		bytesneeded = vsnprintf(*curptr, bytesleft, format, ap);
2563 		va_end(ap);
2564 
2565 		if (bytesneeded < 0)  {
2566 			/* vsnprintf encountered an error */
2567 			error_num = errno;
2568 			rcm_log_message(RCM_ERROR, MF_FUNC_CALL_ERR,
2569 				"vsnprintf", strerror(error_num));
2570 			rcmd_exit(error_num);
2571 			/*NOTREACHED*/
2572 
2573 		} else if (bytesneeded < bytesleft) {
2574 			/* vsnprintf succeeded */
2575 			*curptr += bytesneeded;
2576 			return;
2577 
2578 		} else {
2579 			bytesneeded++; /* to account for storage for '\0' */
2580 		}
2581 	}
2582 }
2583 
2584 static char *
rcmscript_strdup(char * str)2585 rcmscript_strdup(char *str)
2586 {
2587 	char *dupstr;
2588 
2589 	if ((dupstr = strdup(str)) == NULL) {
2590 		rcm_log_message(RCM_ERROR, MF_MEMORY_ALLOCATION_ERR,
2591 			strerror(errno));
2592 		rcmd_exit(errno);
2593 		/*NOTREACHED*/
2594 	}
2595 
2596 	return (dupstr);
2597 }
2598 
2599 static void *
rcmscript_malloc(size_t len)2600 rcmscript_malloc(size_t len)
2601 {
2602 	void *ptr;
2603 
2604 	if ((ptr = malloc(len)) == NULL) {
2605 		rcm_log_message(RCM_ERROR, MF_MEMORY_ALLOCATION_ERR,
2606 			strerror(errno));
2607 		rcmd_exit(errno);
2608 		/*NOTREACHED*/
2609 	}
2610 
2611 	return (ptr);
2612 }
2613 
2614 static void *
rcmscript_calloc(size_t nelem,size_t elsize)2615 rcmscript_calloc(size_t nelem, size_t elsize)
2616 {
2617 	void *ptr;
2618 
2619 	if ((ptr = calloc(nelem, elsize)) == NULL) {
2620 		rcm_log_message(RCM_ERROR, MF_MEMORY_ALLOCATION_ERR,
2621 			strerror(errno));
2622 		rcmd_exit(errno);
2623 		/*NOTREACHED*/
2624 	}
2625 
2626 	return (ptr);
2627 }
2628