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