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 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 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 * 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 * 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 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 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 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 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 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 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 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 * 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 * 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 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 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 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 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 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 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 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 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 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 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 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 * 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 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 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 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 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 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 * 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 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 * 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 * 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 * 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