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