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