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 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include "rcm_impl.h" 29 #include "rcm_module.h" 30 31 /* 32 * Short-circuits unloading of modules with no registrations, so that 33 * they are present during the next db_sync cycle. 34 */ 35 #define MOD_REFCNT_INIT 2 36 37 int need_cleanup; /* flag indicating if clean up is needed */ 38 39 static mutex_t mod_lock; /* protects module list */ 40 static module_t *module_head; /* linked list of modules */ 41 static rsrc_node_t *rsrc_root; /* root of all resources */ 42 43 /* 44 * Misc help routines 45 */ 46 static void rcmd_db_print(); 47 static void rcm_handle_free(rcm_handle_t *); 48 static rcm_handle_t *rcm_handle_alloc(module_t *); 49 static void rsrc_clients_free(client_t *); 50 static struct rcm_mod_ops *modops_from_v1(void *); 51 static int call_getinfo(struct rcm_mod_ops *, rcm_handle_t *, char *, id_t, 52 uint_t, char **, char **, nvlist_t *, rcm_info_t **); 53 static int node_action(rsrc_node_t *, void *); 54 55 extern void start_polling_thread(); 56 57 /* 58 * translate /dev name to a /devices path 59 * 60 * N.B. This routine can be enhanced to understand network names 61 * and friendly names in the future. 62 */ 63 char * 64 resolve_name(char *alias) 65 { 66 char *tmp; 67 const char *dev = "/dev/"; 68 69 if (strlen(alias) == 0) 70 return (NULL); 71 72 if (strncmp(alias, dev, strlen(dev)) == 0) { 73 /* 74 * Treat /dev/... as a symbolic link 75 */ 76 tmp = s_malloc(PATH_MAX); 77 if (realpath(alias, tmp) != NULL) { 78 return (tmp); 79 } else { 80 free(tmp); 81 } 82 /* Fail to resolve /dev/ name, use the name as is */ 83 } 84 85 return (s_strdup(alias)); 86 } 87 88 /* 89 * Figure out resource type based on "resolved" name 90 * 91 * N.B. This routine does not figure out file system mount points. 92 * This is determined at runtime when filesys module register 93 * with RCM_FILESYS flag. 94 */ 95 int 96 rsrc_get_type(const char *resolved_name) 97 { 98 if (resolved_name[0] != '/') 99 return (RSRC_TYPE_ABSTRACT); 100 101 if (strncmp("/devices/", resolved_name, 9) == 0) 102 return (RSRC_TYPE_DEVICE); 103 104 return (RSRC_TYPE_NORMAL); 105 } 106 107 /* 108 * Module operations: 109 * module_load, module_unload, module_info, module_attach, module_detach, 110 * cli_module_hold, cli_module_rele 111 */ 112 113 #ifdef ENABLE_MODULE_DETACH 114 /* 115 * call unregister() entry point to allow module to unregister for 116 * resources without getting confused. 117 */ 118 static void 119 module_detach(module_t *module) 120 { 121 struct rcm_mod_ops *ops = module->modops; 122 123 rcm_log_message(RCM_TRACE2, "module_detach(name=%s)\n", module->name); 124 125 ops->rcmop_unregister(module->rcmhandle); 126 } 127 #endif /* ENABLE_MODULE_DETACH */ 128 129 /* 130 * call register() entry point to allow module to register for resources 131 */ 132 static void 133 module_attach(module_t *module) 134 { 135 struct rcm_mod_ops *ops = module->modops; 136 137 rcm_log_message(RCM_TRACE2, "module_attach(name=%s)\n", module->name); 138 139 if (ops->rcmop_register(module->rcmhandle) != RCM_SUCCESS) { 140 rcm_log_message(RCM_WARNING, 141 gettext("module %s register() failed\n"), module->name); 142 } 143 } 144 145 struct rcm_mod_ops * 146 module_init(module_t *module) 147 { 148 if (module->dlhandle) 149 /* rcm module */ 150 return (module->init()); 151 else 152 /* rcm script */ 153 return (script_init(module)); 154 } 155 156 /* 157 * call rmc_mod_info() entry of module 158 */ 159 static const char * 160 module_info(module_t *module) 161 { 162 if (module->dlhandle) 163 /* rcm module */ 164 return (module->info()); 165 else 166 /* rcm script */ 167 return (script_info(module)); 168 } 169 170 int 171 module_fini(module_t *module) 172 { 173 if (module->dlhandle) 174 /* rcm module */ 175 return (module->fini()); 176 else 177 /* rcm script */ 178 return (script_fini(module)); 179 } 180 181 /* 182 * call rmc_mod_fini() entry of module, dlclose module, and free memory 183 */ 184 static void 185 module_unload(module_t *module) 186 { 187 int version = module->modops->version; 188 189 rcm_log_message(RCM_DEBUG, "module_unload(name=%s)\n", module->name); 190 191 (void) module_fini(module); 192 193 rcm_handle_free(module->rcmhandle); 194 free(module->name); 195 196 switch (version) { 197 case RCM_MOD_OPS_V1: 198 /* 199 * Free memory associated with converted ops vector 200 */ 201 free(module->modops); 202 break; 203 204 case RCM_MOD_OPS_VERSION: 205 default: 206 break; 207 } 208 209 if (module->dlhandle) 210 rcm_module_close(module->dlhandle); 211 212 free(module); 213 } 214 215 /* 216 * Locate the module, execute rcm_mod_init() and check ops vector version 217 */ 218 static module_t * 219 module_load(char *modname) 220 { 221 module_t *module; 222 223 rcm_log_message(RCM_DEBUG, "module_load(name=%s)\n", modname); 224 225 /* 226 * dlopen the module 227 */ 228 module = s_calloc(1, sizeof (*module)); 229 module->name = s_strdup(modname); 230 module->modops = NULL; 231 rcm_init_queue(&module->client_q); 232 233 if (rcm_is_script(modname) == 0) { 234 /* rcm module */ 235 module->dlhandle = rcm_module_open(modname); 236 237 if (module->dlhandle == NULL) { 238 rcm_log_message(RCM_NOTICE, 239 gettext("cannot open module %s\n"), modname); 240 goto fail; 241 } 242 243 /* 244 * dlsym rcm_mod_init/fini/info() entry points 245 */ 246 module->init = (struct rcm_mod_ops *(*)())dlsym( 247 module->dlhandle, "rcm_mod_init"); 248 module->fini = (int (*)())dlsym( 249 module->dlhandle, "rcm_mod_fini"); 250 module->info = (const char *(*)())dlsym(module->dlhandle, 251 "rcm_mod_info"); 252 if (module->init == NULL || module->fini == NULL || 253 module->info == NULL) { 254 rcm_log_message(RCM_ERROR, 255 gettext("missing entries in module %s\n"), modname); 256 goto fail; 257 } 258 259 } else { 260 /* rcm script */ 261 module->dlhandle = NULL; 262 module->init = (struct rcm_mod_ops *(*)()) NULL; 263 module->fini = (int (*)()) NULL; 264 module->info = (const char *(*)()) NULL; 265 } 266 267 if ((module->modops = module_init(module)) == NULL) { 268 if (module->dlhandle) 269 rcm_log_message(RCM_ERROR, 270 gettext("cannot init module %s\n"), modname); 271 goto fail; 272 } 273 274 /* 275 * Check ops vector version 276 */ 277 switch (module->modops->version) { 278 case RCM_MOD_OPS_V1: 279 module->modops = modops_from_v1((void *)module->modops); 280 break; 281 282 case RCM_MOD_OPS_VERSION: 283 break; 284 285 default: 286 rcm_log_message(RCM_ERROR, 287 gettext("module %s rejected: version %d not supported\n"), 288 modname, module->modops->version); 289 (void) module_fini(module); 290 goto fail; 291 } 292 293 /* 294 * Make sure all fields are set 295 */ 296 if ((module->modops->rcmop_register == NULL) || 297 (module->modops->rcmop_unregister == NULL) || 298 (module->modops->rcmop_get_info == NULL) || 299 (module->modops->rcmop_request_suspend == NULL) || 300 (module->modops->rcmop_notify_resume == NULL) || 301 (module->modops->rcmop_request_offline == NULL) || 302 (module->modops->rcmop_notify_online == NULL) || 303 (module->modops->rcmop_notify_remove == NULL)) { 304 rcm_log_message(RCM_ERROR, 305 gettext("module %s rejected: has NULL ops fields\n"), 306 modname); 307 (void) module_fini(module); 308 goto fail; 309 } 310 311 module->rcmhandle = rcm_handle_alloc(module); 312 return (module); 313 314 fail: 315 if (module->modops && module->modops->version == RCM_MOD_OPS_V1) 316 free(module->modops); 317 318 if (module->dlhandle) 319 rcm_module_close(module->dlhandle); 320 321 free(module->name); 322 free(module); 323 return (NULL); 324 } 325 326 /* 327 * add one to module hold count. load the module if not loaded 328 */ 329 static module_t * 330 cli_module_hold(char *modname) 331 { 332 module_t *module; 333 334 rcm_log_message(RCM_TRACE3, "cli_module_hold(%s)\n", modname); 335 336 (void) mutex_lock(&mod_lock); 337 module = module_head; 338 while (module) { 339 if (strcmp(module->name, modname) == 0) { 340 break; 341 } 342 module = module->next; 343 } 344 345 if (module) { 346 module->ref_count++; 347 (void) mutex_unlock(&mod_lock); 348 return (module); 349 } 350 351 /* 352 * Module not found, attempt to load it 353 */ 354 if ((module = module_load(modname)) == NULL) { 355 (void) mutex_unlock(&mod_lock); 356 return (NULL); 357 } 358 359 /* 360 * Hold module and link module into module list 361 */ 362 module->ref_count = MOD_REFCNT_INIT; 363 module->next = module_head; 364 module_head = module; 365 366 (void) mutex_unlock(&mod_lock); 367 368 return (module); 369 } 370 371 /* 372 * decrement module hold count. Unload it if no reference 373 */ 374 static void 375 cli_module_rele(module_t *module) 376 { 377 module_t *curr = module_head, *prev = NULL; 378 379 rcm_log_message(RCM_TRACE3, "cli_module_rele(name=%s)\n", module->name); 380 381 (void) mutex_lock(&mod_lock); 382 if (--(module->ref_count) != 0) { 383 (void) mutex_unlock(&mod_lock); 384 return; 385 } 386 387 rcm_log_message(RCM_TRACE2, "unloading module %s\n", module->name); 388 389 /* 390 * Unlink the module from list 391 */ 392 while (curr && (curr != module)) { 393 prev = curr; 394 curr = curr->next; 395 } 396 if (curr == NULL) { 397 rcm_log_message(RCM_ERROR, 398 gettext("Unexpected error: module %s not found.\n"), 399 module->name); 400 } else if (prev == NULL) { 401 module_head = curr->next; 402 } else { 403 prev->next = curr->next; 404 } 405 (void) mutex_unlock(&mod_lock); 406 407 module_unload(module); 408 } 409 410 /* 411 * Gather usage info be passed back to requester. Discard info if user does 412 * not care (list == NULL). 413 */ 414 void 415 add_busy_rsrc_to_list(char *alias, pid_t pid, int state, int seq_num, 416 char *modname, const char *infostr, const char *errstr, 417 nvlist_t *client_props, rcm_info_t **list) 418 { 419 rcm_info_t *info; 420 rcm_info_t *tmp; 421 char *buf = NULL; 422 size_t buflen = 0; 423 424 if (list == NULL) { 425 return; 426 } 427 428 info = s_calloc(1, sizeof (*info)); 429 if (errno = nvlist_alloc(&(info->info), NV_UNIQUE_NAME, 0)) { 430 rcm_log_message(RCM_ERROR, "failed (nvlist_alloc=%s).\n", 431 strerror(errno)); 432 rcmd_exit(errno); 433 } 434 435 /*LINTED*/ 436 if ((errno = nvlist_add_string(info->info, RCM_RSRCNAME, alias)) || 437 (errno = nvlist_add_int32(info->info, RCM_SEQ_NUM, seq_num)) || 438 (errno = nvlist_add_int64(info->info, RCM_CLIENT_ID, pid)) || 439 (errno = nvlist_add_int32(info->info, RCM_RSRCSTATE, state))) { 440 rcm_log_message(RCM_ERROR, "failed (nvlist_add=%s).\n", 441 strerror(errno)); 442 rcmd_exit(errno); 443 } 444 445 /* 446 * Daemon calls to add_busy_rsrc_to_list may pass in 447 * error/info. Add these through librcm interfaces. 448 */ 449 if (errstr) { 450 rcm_log_message(RCM_TRACE3, "adding error string: %s\n", 451 errstr); 452 if (errno = nvlist_add_string(info->info, RCM_CLIENT_ERROR, 453 (char *)errstr)) { 454 rcm_log_message(RCM_ERROR, "failed (nvlist_add=%s).\n", 455 strerror(errno)); 456 rcmd_exit(errno); 457 } 458 } 459 460 if (infostr) { 461 if (errno = nvlist_add_string(info->info, RCM_CLIENT_INFO, 462 (char *)infostr)) { 463 rcm_log_message(RCM_ERROR, "failed (nvlist_add=%s).\n", 464 strerror(errno)); 465 rcmd_exit(errno); 466 } 467 } 468 469 if (modname) { 470 if (errno = nvlist_add_string(info->info, RCM_CLIENT_MODNAME, 471 modname)) { 472 rcm_log_message(RCM_ERROR, "failed (nvlist_add=%s).\n", 473 strerror(errno)); 474 rcmd_exit(errno); 475 } 476 } 477 478 if (client_props) { 479 if (errno = nvlist_pack(client_props, &buf, &buflen, 480 NV_ENCODE_NATIVE, 0)) { 481 rcm_log_message(RCM_ERROR, "failed (nvlist_pack=%s).\n", 482 strerror(errno)); 483 rcmd_exit(errno); 484 } 485 if (errno = nvlist_add_byte_array(info->info, 486 RCM_CLIENT_PROPERTIES, (uchar_t *)buf, buflen)) { 487 rcm_log_message(RCM_ERROR, "failed (nvlist_add=%s).\n", 488 strerror(errno)); 489 rcmd_exit(errno); 490 } 491 (void) free(buf); 492 } 493 494 495 /* link info at end of list */ 496 if (*list) { 497 tmp = *list; 498 while (tmp->next) 499 tmp = tmp->next; 500 tmp->next = info; 501 } else { 502 *list = info; 503 } 504 } 505 506 /* 507 * Resource client realted operations: 508 * rsrc_client_alloc, rsrc_client_find, rsrc_client_add, 509 * rsrc_client_remove, rsrc_client_action, rsrc_client_action_list 510 */ 511 512 /* Allocate rsrc_client_t structure. Load module if necessary. */ 513 /*ARGSUSED*/ 514 static client_t * 515 rsrc_client_alloc(char *alias, char *modname, pid_t pid, uint_t flag) 516 { 517 client_t *client; 518 module_t *mod; 519 520 assert((alias != NULL) && (modname != NULL)); 521 522 rcm_log_message(RCM_TRACE4, "rsrc_client_alloc(%s, %s, %ld)\n", 523 alias, modname, pid); 524 525 if ((mod = cli_module_hold(modname)) == NULL) { 526 return (NULL); 527 } 528 529 client = s_calloc(1, sizeof (client_t)); 530 client->module = mod; 531 client->pid = pid; 532 client->alias = s_strdup(alias); 533 client->prv_flags = 0; 534 client->state = RCM_STATE_ONLINE; 535 client->flag = flag; 536 537 /* This queue is protected by rcm_req_lock */ 538 rcm_enqueue_tail(&mod->client_q, &client->queue); 539 540 return (client); 541 } 542 543 /* Find client in list matching modname and pid */ 544 client_t * 545 rsrc_client_find(char *modname, pid_t pid, client_t **list) 546 { 547 client_t *client = *list; 548 549 rcm_log_message(RCM_TRACE4, "rsrc_client_find(%s, %ld, %p)\n", 550 modname, pid, (void *)list); 551 552 while (client) { 553 if ((client->pid == pid) && 554 strcmp(modname, client->module->name) == 0) { 555 break; 556 } 557 client = client->next; 558 } 559 return (client); 560 } 561 562 /* Add a client to client list */ 563 static void 564 rsrc_client_add(client_t *client, client_t **list) 565 { 566 rcm_log_message(RCM_TRACE4, "rsrc_client_add: %s, %s, %ld\n", 567 client->alias, client->module->name, client->pid); 568 569 client->next = *list; 570 *list = client; 571 } 572 573 /* Remove client from list and destroy it */ 574 static void 575 rsrc_client_remove(client_t *client, client_t **list) 576 { 577 client_t *tmp, *prev = NULL; 578 579 rcm_log_message(RCM_TRACE4, "rsrc_client_remove: %s, %s, %ld\n", 580 client->alias, client->module->name, client->pid); 581 582 tmp = *list; 583 while (tmp) { 584 if (client != tmp) { 585 prev = tmp; 586 tmp = tmp->next; 587 continue; 588 } 589 if (prev) { 590 prev->next = tmp->next; 591 } else { 592 *list = tmp->next; 593 } 594 tmp->next = NULL; 595 rsrc_clients_free(tmp); 596 return; 597 } 598 } 599 600 /* Free a list of clients. Called from cleanup thread only */ 601 static void 602 rsrc_clients_free(client_t *list) 603 { 604 client_t *client = list; 605 606 while (client) { 607 608 /* 609 * Note that the rcm daemon is single threaded while 610 * executing this routine. So there is no need to acquire 611 * rcm_req_lock here while dequeuing. 612 */ 613 rcm_dequeue(&client->queue); 614 615 if (client->module) { 616 cli_module_rele(client->module); 617 } 618 list = client->next; 619 if (client->alias) { 620 free(client->alias); 621 } 622 free(client); 623 client = list; 624 } 625 } 626 627 /* 628 * Invoke a callback into a single client 629 * This is the core of rcm_mod_ops interface 630 */ 631 static int 632 rsrc_client_action(client_t *client, int cmd, void *arg) 633 { 634 int rval = RCM_SUCCESS; 635 char *dummy_error = NULL; 636 char *error = NULL; 637 char *info = NULL; 638 rcm_handle_t *hdl; 639 nvlist_t *client_props = NULL; 640 rcm_info_t *depend_info = NULL; 641 struct rcm_mod_ops *ops = client->module->modops; 642 tree_walk_arg_t *targ = (tree_walk_arg_t *)arg; 643 644 rcm_log_message(RCM_TRACE4, 645 "rsrc_client_action: %s, %s, cmd=%d, flag=0x%x\n", client->alias, 646 client->module->name, cmd, targ->flag); 647 648 /* 649 * Create a per-operation handle, increment seq_num by 1 so we will 650 * know if a module uses this handle to callback into rcm_daemon. 651 */ 652 hdl = rcm_handle_alloc(client->module); 653 hdl->seq_num = targ->seq_num + 1; 654 655 /* 656 * Filter out operations for which the client didn't register. 657 */ 658 switch (cmd) { 659 case CMD_SUSPEND: 660 case CMD_RESUME: 661 case CMD_OFFLINE: 662 case CMD_ONLINE: 663 case CMD_REMOVE: 664 if ((client->flag & RCM_REGISTER_DR) == 0) { 665 rcm_handle_free(hdl); 666 return (RCM_SUCCESS); 667 } 668 break; 669 case CMD_REQUEST_CHANGE: 670 case CMD_NOTIFY_CHANGE: 671 if ((client->flag & RCM_REGISTER_CAPACITY) == 0) { 672 rcm_handle_free(hdl); 673 return (RCM_SUCCESS); 674 } 675 break; 676 case CMD_EVENT: 677 if ((client->flag & RCM_REGISTER_EVENT) == 0) { 678 rcm_handle_free(hdl); 679 return (RCM_SUCCESS); 680 } 681 break; 682 } 683 684 /* 685 * Create nvlist_t for any client-specific properties. 686 */ 687 if (errno = nvlist_alloc(&client_props, NV_UNIQUE_NAME, 0)) { 688 rcm_log_message(RCM_ERROR, 689 "client action failed (nvlist_alloc=%s)\n", 690 strerror(errno)); 691 rcmd_exit(errno); 692 } 693 694 /* 695 * Process the operation via a callback to the client module. 696 */ 697 switch (cmd) { 698 case CMD_GETINFO: 699 rval = call_getinfo(ops, hdl, client->alias, client->pid, 700 targ->flag, &info, &error, client_props, &depend_info); 701 break; 702 703 case CMD_SUSPEND: 704 if (((targ->flag & RCM_QUERY_CANCEL) == 0) && 705 (client->state == RCM_STATE_SUSPEND)) { 706 break; 707 } 708 709 if ((targ->flag & RCM_QUERY) == 0) { 710 rcm_log_message(RCM_DEBUG, "suspending %s\n", 711 client->alias); 712 } else if ((targ->flag & RCM_QUERY_CANCEL) == 0) { 713 rcm_log_message(RCM_DEBUG, "suspend query %s\n", 714 client->alias); 715 } else { 716 rcm_log_message(RCM_DEBUG, 717 "suspend query %s cancelled\n", client->alias); 718 } 719 720 /* 721 * Update the client's state before the operation. 722 * If this is a cancelled query, then updating the state is 723 * the only thing that needs to be done, so break afterwards. 724 */ 725 if ((targ->flag & RCM_QUERY) == 0) { 726 client->state = RCM_STATE_SUSPENDING; 727 } else if ((targ->flag & RCM_QUERY_CANCEL) == 0) { 728 client->state = RCM_STATE_SUSPEND_QUERYING; 729 } else { 730 client->state = RCM_STATE_ONLINE; 731 break; 732 } 733 734 rval = ops->rcmop_request_suspend(hdl, client->alias, 735 client->pid, targ->interval, targ->flag, &error, 736 &depend_info); 737 738 /* Update the client's state after the operation. */ 739 if ((targ->flag & RCM_QUERY) == 0) { 740 if (rval == RCM_SUCCESS) { 741 client->state = RCM_STATE_SUSPEND; 742 } else { 743 client->state = RCM_STATE_SUSPEND_FAIL; 744 } 745 } else { 746 if (rval == RCM_SUCCESS) { 747 client->state = RCM_STATE_SUSPEND_QUERY; 748 } else { 749 client->state = RCM_STATE_SUSPEND_QUERY_FAIL; 750 } 751 } 752 break; 753 754 case CMD_RESUME: 755 if (client->state == RCM_STATE_ONLINE) { 756 break; 757 } 758 client->state = RCM_STATE_RESUMING; 759 rval = ops->rcmop_notify_resume(hdl, client->alias, client->pid, 760 targ->flag, &error, &depend_info); 761 762 /* online state is unconditional */ 763 client->state = RCM_STATE_ONLINE; 764 break; 765 766 case CMD_OFFLINE: 767 if (((targ->flag & RCM_QUERY_CANCEL) == 0) && 768 (client->state == RCM_STATE_OFFLINE)) { 769 break; 770 } 771 772 if ((targ->flag & RCM_QUERY) == 0) { 773 rcm_log_message(RCM_DEBUG, "offlining %s\n", 774 client->alias); 775 } else if ((targ->flag & RCM_QUERY_CANCEL) == 0) { 776 rcm_log_message(RCM_DEBUG, "offline query %s\n", 777 client->alias); 778 } else { 779 rcm_log_message(RCM_DEBUG, 780 "offline query %s cancelled\n", client->alias); 781 } 782 783 /* 784 * Update the client's state before the operation. 785 * If this is a cancelled query, then updating the state is 786 * the only thing that needs to be done, so break afterwards. 787 */ 788 if ((targ->flag & RCM_QUERY) == 0) { 789 client->state = RCM_STATE_OFFLINING; 790 } else if ((targ->flag & RCM_QUERY_CANCEL) == 0) { 791 client->state = RCM_STATE_OFFLINE_QUERYING; 792 } else { 793 client->state = RCM_STATE_ONLINE; 794 break; 795 } 796 797 rval = ops->rcmop_request_offline(hdl, client->alias, 798 client->pid, targ->flag, &error, &depend_info); 799 800 /* Update the client's state after the operation. */ 801 if ((targ->flag & RCM_QUERY) == 0) { 802 if (rval == RCM_SUCCESS) { 803 client->state = RCM_STATE_OFFLINE; 804 } else { 805 client->state = RCM_STATE_OFFLINE_FAIL; 806 } 807 } else { 808 if (rval == RCM_SUCCESS) { 809 client->state = RCM_STATE_OFFLINE_QUERY; 810 } else { 811 client->state = RCM_STATE_OFFLINE_QUERY_FAIL; 812 } 813 } 814 break; 815 816 case CMD_ONLINE: 817 if (client->state == RCM_STATE_ONLINE) { 818 break; 819 } 820 821 rcm_log_message(RCM_DEBUG, "onlining %s\n", client->alias); 822 823 client->state = RCM_STATE_ONLINING; 824 rval = ops->rcmop_notify_online(hdl, client->alias, client->pid, 825 targ->flag, &error, &depend_info); 826 client->state = RCM_STATE_ONLINE; 827 break; 828 829 case CMD_REMOVE: 830 rcm_log_message(RCM_DEBUG, "removing %s\n", client->alias); 831 client->state = RCM_STATE_REMOVING; 832 rval = ops->rcmop_notify_remove(hdl, client->alias, client->pid, 833 targ->flag, &error, &depend_info); 834 client->state = RCM_STATE_REMOVE; 835 break; 836 837 case CMD_REQUEST_CHANGE: 838 rcm_log_message(RCM_DEBUG, "requesting state change of %s\n", 839 client->alias); 840 if (ops->rcmop_request_capacity_change) 841 rval = ops->rcmop_request_capacity_change(hdl, 842 client->alias, client->pid, targ->flag, targ->nvl, 843 &error, &depend_info); 844 break; 845 846 case CMD_NOTIFY_CHANGE: 847 rcm_log_message(RCM_DEBUG, "requesting state change of %s\n", 848 client->alias); 849 if (ops->rcmop_notify_capacity_change) 850 rval = ops->rcmop_notify_capacity_change(hdl, 851 client->alias, client->pid, targ->flag, targ->nvl, 852 &error, &depend_info); 853 break; 854 855 case CMD_EVENT: 856 rcm_log_message(RCM_DEBUG, "delivering event to %s\n", 857 client->alias); 858 if (ops->rcmop_notify_event) 859 rval = ops->rcmop_notify_event(hdl, client->alias, 860 client->pid, targ->flag, &error, targ->nvl, 861 &depend_info); 862 break; 863 864 default: 865 rcm_log_message(RCM_ERROR, gettext("unknown command %d\n"), 866 cmd); 867 rval = RCM_FAILURE; 868 break; 869 } 870 871 /* reset error code to the most significant error */ 872 if (rval != RCM_SUCCESS) 873 targ->retcode = rval; 874 875 /* 876 * XXX - The code below may produce duplicate rcm_info_t's on error? 877 */ 878 if ((cmd != CMD_GETINFO) && 879 ((rval != RCM_SUCCESS) || 880 (error != NULL) || 881 (targ->flag & RCM_SCOPE))) { 882 (void) call_getinfo(ops, hdl, client->alias, client->pid, 883 targ->flag & (~(RCM_INCLUDE_DEPENDENT|RCM_INCLUDE_SUBTREE)), 884 &info, &dummy_error, client_props, &depend_info); 885 if (dummy_error) 886 (void) free(dummy_error); 887 } else if (cmd != CMD_GETINFO) { 888 nvlist_free(client_props); 889 client_props = NULL; 890 } 891 892 if (client_props) { 893 add_busy_rsrc_to_list(client->alias, client->pid, client->state, 894 targ->seq_num, client->module->name, info, error, 895 client_props, targ->info); 896 nvlist_free(client_props); 897 } 898 899 if (info) 900 (void) free(info); 901 if (error) 902 (void) free(error); 903 904 if (depend_info) { 905 if (targ->info) { 906 (void) rcm_append_info(targ->info, depend_info); 907 } else { 908 rcm_free_info(depend_info); 909 } 910 } 911 912 rcm_handle_free(hdl); 913 return (rval); 914 } 915 916 /* 917 * invoke a callback into a list of clients, return 0 if all success 918 */ 919 int 920 rsrc_client_action_list(client_t *list, int cmd, void *arg) 921 { 922 int error, rval = RCM_SUCCESS; 923 924 while (list) { 925 client_t *client = list; 926 list = client->next; 927 928 if (client->state == RCM_STATE_REMOVE) 929 continue; 930 931 error = rsrc_client_action(client, cmd, arg); 932 if (error != RCM_SUCCESS) { 933 rval = error; 934 } 935 } 936 937 return (rval); 938 } 939 940 /* 941 * Node realted operations: 942 * 943 * rn_alloc, rn_free, rn_find_child, 944 * rn_get_child, rn_get_sibling, 945 * rsrc_node_find, rsrc_node_add_user, rsrc_node_remove_user, 946 */ 947 948 /* Allocate node based on a logical or physical name */ 949 static rsrc_node_t * 950 rn_alloc(char *name, int type) 951 { 952 rsrc_node_t *node; 953 954 rcm_log_message(RCM_TRACE4, "rn_alloc(%s, %d)\n", name, type); 955 956 node = s_calloc(1, sizeof (*node)); 957 node->name = s_strdup(name); 958 node->type = type; 959 960 return (node); 961 } 962 963 /* 964 * Free node along with its siblings and children 965 */ 966 static void 967 rn_free(rsrc_node_t *node) 968 { 969 if (node == NULL) { 970 return; 971 } 972 973 if (node->child) { 974 rn_free(node->child); 975 } 976 977 if (node->sibling) { 978 rn_free(node->sibling); 979 } 980 981 rsrc_clients_free(node->users); 982 free(node->name); 983 free(node); 984 } 985 986 /* 987 * Find next sibling 988 */ 989 static rsrc_node_t * 990 rn_get_sibling(rsrc_node_t *node) 991 { 992 return (node->sibling); 993 } 994 995 /* 996 * Find first child 997 */ 998 static rsrc_node_t * 999 rn_get_child(rsrc_node_t *node) 1000 { 1001 return (node->child); 1002 } 1003 1004 /* 1005 * Find child named childname. Create it if flag is RSRC_NODE_CRTEATE 1006 */ 1007 static rsrc_node_t * 1008 rn_find_child(rsrc_node_t *parent, char *childname, int flag, int type) 1009 { 1010 rsrc_node_t *child = parent->child; 1011 rsrc_node_t *new, *prev = NULL; 1012 1013 rcm_log_message(RCM_TRACE4, 1014 "rn_find_child(parent=%s, child=%s, 0x%x, %d)\n", 1015 parent->name, childname, flag, type); 1016 1017 /* 1018 * Children are ordered based on strcmp. 1019 */ 1020 while (child && (strcmp(child->name, childname) < 0)) { 1021 prev = child; 1022 child = child->sibling; 1023 } 1024 1025 if (child && (strcmp(child->name, childname) == 0)) { 1026 return (child); 1027 } 1028 1029 if (flag != RSRC_NODE_CREATE) 1030 return (NULL); 1031 1032 new = rn_alloc(childname, type); 1033 new->parent = parent; 1034 new->sibling = child; 1035 1036 /* 1037 * Set this linkage last so we don't break ongoing operations. 1038 * 1039 * N.B. Assume setting a pointer is an atomic operation. 1040 */ 1041 if (prev == NULL) { 1042 parent->child = new; 1043 } else { 1044 prev->sibling = new; 1045 } 1046 1047 return (new); 1048 } 1049 1050 /* 1051 * Pathname related help functions 1052 */ 1053 static void 1054 pn_preprocess(char *pathname, int type) 1055 { 1056 char *tmp; 1057 1058 if (type != RSRC_TYPE_DEVICE) 1059 return; 1060 1061 /* 1062 * For devices, convert ':' to '/' (treat minor nodes and children) 1063 */ 1064 tmp = strchr(pathname, ':'); 1065 if (tmp == NULL) 1066 return; 1067 1068 *tmp = '/'; 1069 } 1070 1071 static char * 1072 pn_getnextcomp(char *pathname, char **lasts) 1073 { 1074 char *slash; 1075 1076 if (pathname == NULL) 1077 return (NULL); 1078 1079 /* skip slashes' */ 1080 while (*pathname == '/') 1081 ++pathname; 1082 1083 if (*pathname == '\0') 1084 return (NULL); 1085 1086 slash = strchr(pathname, '/'); 1087 if (slash != NULL) { 1088 *slash = '\0'; 1089 *lasts = slash + 1; 1090 } else { 1091 *lasts = NULL; 1092 } 1093 1094 return (pathname); 1095 } 1096 1097 /* 1098 * Find a node in tree based on device, which is the physical pathname 1099 * of the form /sbus@.../esp@.../sd@... 1100 */ 1101 int 1102 rsrc_node_find(char *rsrcname, int flag, rsrc_node_t **nodep) 1103 { 1104 char *pathname, *nodename, *lasts; 1105 rsrc_node_t *node; 1106 int type; 1107 1108 rcm_log_message(RCM_TRACE4, "rn_node_find(%s, 0x%x)\n", rsrcname, flag); 1109 1110 /* 1111 * For RSRC_TYPE_ABSTRACT, look under /ABSTRACT. For other types, 1112 * look under /SYSTEM. 1113 */ 1114 pathname = resolve_name(rsrcname); 1115 if (pathname == NULL) 1116 return (EINVAL); 1117 1118 type = rsrc_get_type(pathname); 1119 switch (type) { 1120 case RSRC_TYPE_DEVICE: 1121 case RSRC_TYPE_NORMAL: 1122 node = rn_find_child(rsrc_root, "SYSTEM", RSRC_NODE_CREATE, 1123 RSRC_TYPE_NORMAL); 1124 break; 1125 1126 case RSRC_TYPE_ABSTRACT: 1127 node = rn_find_child(rsrc_root, "ABSTRACT", RSRC_NODE_CREATE, 1128 RSRC_TYPE_NORMAL); 1129 break; 1130 1131 default: 1132 /* just to make sure */ 1133 free(pathname); 1134 return (EINVAL); 1135 } 1136 1137 /* 1138 * Find position of device within tree. Upon exiting the loop, device 1139 * should be placed between prev and curr. 1140 */ 1141 pn_preprocess(pathname, type); 1142 lasts = pathname; 1143 while ((nodename = pn_getnextcomp(lasts, &lasts)) != NULL) { 1144 rsrc_node_t *parent = node; 1145 node = rn_find_child(parent, nodename, flag, type); 1146 if (node == NULL) { 1147 assert((flag & RSRC_NODE_CREATE) == 0); 1148 free(pathname); 1149 *nodep = NULL; 1150 return (RCM_SUCCESS); 1151 } 1152 } 1153 free(pathname); 1154 *nodep = node; 1155 return (RCM_SUCCESS); 1156 } 1157 1158 /* 1159 * add a usage client to a node 1160 */ 1161 /*ARGSUSED*/ 1162 int 1163 rsrc_node_add_user(rsrc_node_t *node, char *alias, char *modname, pid_t pid, 1164 uint_t flag) 1165 { 1166 client_t *user; 1167 1168 rcm_log_message(RCM_TRACE3, 1169 "rsrc_node_add_user(%s, %s, %s, %ld, 0x%x)\n", 1170 node->name, alias, modname, pid, flag); 1171 1172 user = rsrc_client_find(modname, pid, &node->users); 1173 1174 /* 1175 * If a client_t already exists, add the registration and return 1176 * success if it's a valid registration request. 1177 * 1178 * Return EALREADY if the resource is already registered. 1179 * This means either the client_t already has the requested 1180 * registration flagged, or that a DR registration was attempted 1181 * on a resource already in use in the DR operations state model. 1182 */ 1183 if (user != NULL) { 1184 1185 if (user->flag & (flag & RCM_REGISTER_MASK)) { 1186 return (EALREADY); 1187 } 1188 1189 if ((flag & RCM_REGISTER_DR) && 1190 (user->state != RCM_STATE_REMOVE)) { 1191 return (EALREADY); 1192 } 1193 1194 user->flag |= (flag & RCM_REGISTER_MASK); 1195 if ((flag & RCM_REGISTER_DR) || 1196 (user->state == RCM_STATE_REMOVE)) { 1197 user->state = RCM_STATE_ONLINE; 1198 } 1199 1200 return (RCM_SUCCESS); 1201 } 1202 1203 /* 1204 * Otherwise create a new client_t and create a new registration. 1205 */ 1206 if ((user = rsrc_client_alloc(alias, modname, pid, flag)) != NULL) { 1207 rsrc_client_add(user, &node->users); 1208 } 1209 if (flag & RCM_FILESYS) 1210 node->type = RSRC_TYPE_FILESYS; 1211 1212 return (RCM_SUCCESS); 1213 } 1214 1215 /* 1216 * remove a usage client of a node 1217 */ 1218 int 1219 rsrc_node_remove_user(rsrc_node_t *node, char *modname, pid_t pid, uint_t flag) 1220 { 1221 client_t *user; 1222 1223 rcm_log_message(RCM_TRACE3, 1224 "rsrc_node_remove_user(%s, %s, %ld, 0x%x)\n", node->name, modname, 1225 pid, flag); 1226 1227 user = rsrc_client_find(modname, pid, &node->users); 1228 if ((user == NULL) || (user->state == RCM_STATE_REMOVE)) { 1229 rcm_log_message(RCM_NOTICE, gettext( 1230 "client not registered: module=%s, pid=%d, dev=%s\n"), 1231 modname, pid, node->name); 1232 return (ENOENT); 1233 } 1234 1235 /* Strip off the registration being removed (DR, event, capacity) */ 1236 user->flag = user->flag & (~(flag & RCM_REGISTER_MASK)); 1237 1238 /* 1239 * Mark the client as removed if all registrations have been removed 1240 */ 1241 if ((user->flag & RCM_REGISTER_MASK) == 0) 1242 user->state = RCM_STATE_REMOVE; 1243 1244 return (RCM_SUCCESS); 1245 } 1246 1247 /* 1248 * Tree walking function - rsrc_walk 1249 */ 1250 1251 #define MAX_TREE_DEPTH 32 1252 1253 #define RN_WALK_CONTINUE 0 1254 #define RN_WALK_PRUNESIB 1 1255 #define RN_WALK_PRUNECHILD 2 1256 #define RN_WALK_TERMINATE 3 1257 1258 #define EMPTY_STACK(sp) ((sp)->depth == 0) 1259 #define TOP_NODE(sp) ((sp)->node[(sp)->depth - 1]) 1260 #define PRUNE_SIB(sp) ((sp)->prunesib[(sp)->depth - 1]) 1261 #define PRUNE_CHILD(sp) ((sp)->prunechild[(sp)->depth - 1]) 1262 #define POP_STACK(sp) ((sp)->depth)-- 1263 #define PUSH_STACK(sp, rn) \ 1264 (sp)->node[(sp)->depth] = (rn); \ 1265 (sp)->prunesib[(sp)->depth] = 0; \ 1266 (sp)->prunechild[(sp)->depth] = 0; \ 1267 ((sp)->depth)++ 1268 1269 struct rn_stack { 1270 rsrc_node_t *node[MAX_TREE_DEPTH]; 1271 char prunesib[MAX_TREE_DEPTH]; 1272 char prunechild[MAX_TREE_DEPTH]; 1273 int depth; 1274 }; 1275 1276 /* walking one node and update node stack */ 1277 /*ARGSUSED*/ 1278 static void 1279 walk_one_node(struct rn_stack *sp, void *arg, 1280 int (*node_callback)(rsrc_node_t *, void *)) 1281 { 1282 int prunesib; 1283 rsrc_node_t *child, *sibling; 1284 rsrc_node_t *node = TOP_NODE(sp); 1285 1286 rcm_log_message(RCM_TRACE4, "walk_one_node(%s)\n", node->name); 1287 1288 switch (node_callback(node, arg)) { 1289 case RN_WALK_TERMINATE: 1290 POP_STACK(sp); 1291 while (!EMPTY_STACK(sp)) { 1292 node = TOP_NODE(sp); 1293 POP_STACK(sp); 1294 } 1295 return; 1296 1297 case RN_WALK_PRUNESIB: 1298 PRUNE_SIB(sp) = 1; 1299 break; 1300 1301 case RN_WALK_PRUNECHILD: 1302 PRUNE_CHILD(sp) = 1; 1303 break; 1304 1305 case RN_WALK_CONTINUE: 1306 default: 1307 break; 1308 } 1309 1310 /* 1311 * Push child on the stack 1312 */ 1313 if (!PRUNE_CHILD(sp) && (child = rn_get_child(node)) != NULL) { 1314 PUSH_STACK(sp, child); 1315 return; 1316 } 1317 1318 /* 1319 * Pop the stack till a node's sibling can be pushed 1320 */ 1321 prunesib = PRUNE_SIB(sp); 1322 POP_STACK(sp); 1323 while (!EMPTY_STACK(sp) && 1324 (prunesib || (sibling = rn_get_sibling(node)) == NULL)) { 1325 node = TOP_NODE(sp); 1326 prunesib = PRUNE_SIB(sp); 1327 POP_STACK(sp); 1328 } 1329 1330 if (EMPTY_STACK(sp)) { 1331 return; 1332 } 1333 1334 /* 1335 * push sibling onto the stack 1336 */ 1337 PUSH_STACK(sp, sibling); 1338 } 1339 1340 /* 1341 * walk tree rooted at root in child-first order 1342 */ 1343 static void 1344 rsrc_walk(rsrc_node_t *root, void *arg, 1345 int (*node_callback)(rsrc_node_t *, void *)) 1346 { 1347 struct rn_stack stack; 1348 1349 rcm_log_message(RCM_TRACE3, "rsrc_walk(%s)\n", root->name); 1350 1351 /* 1352 * Push root on stack and walk in child-first order 1353 */ 1354 stack.depth = 0; 1355 PUSH_STACK(&stack, root); 1356 PRUNE_SIB(&stack) = 1; 1357 1358 while (!EMPTY_STACK(&stack)) { 1359 walk_one_node(&stack, arg, node_callback); 1360 } 1361 } 1362 1363 /* 1364 * Callback for a command action on a node 1365 */ 1366 static int 1367 node_action(rsrc_node_t *node, void *arg) 1368 { 1369 tree_walk_arg_t *targ = (tree_walk_arg_t *)arg; 1370 uint_t flag = targ->flag; 1371 1372 rcm_log_message(RCM_TRACE4, "node_action(%s)\n", node->name); 1373 1374 /* 1375 * If flag indicates operation on a filesystem, we don't callback on 1376 * the filesystem root to avoid infinite recursion on filesystem module. 1377 * 1378 * N.B. Such request should only come from filesystem RCM module. 1379 */ 1380 if (flag & RCM_FILESYS) { 1381 assert(node->type == RSRC_TYPE_FILESYS); 1382 targ->flag &= ~RCM_FILESYS; 1383 return (RN_WALK_CONTINUE); 1384 } 1385 1386 /* 1387 * Execute state change callback 1388 */ 1389 (void) rsrc_client_action_list(node->users, targ->cmd, arg); 1390 1391 /* 1392 * Upon hitting a filesys root, prune children. 1393 * The filesys module should have taken care of 1394 * children by now. 1395 */ 1396 if (node->type == RSRC_TYPE_FILESYS) 1397 return (RN_WALK_PRUNECHILD); 1398 1399 return (RN_WALK_CONTINUE); 1400 } 1401 1402 /* 1403 * Execute a command on a subtree under root. 1404 */ 1405 int 1406 rsrc_tree_action(rsrc_node_t *root, int cmd, tree_walk_arg_t *arg) 1407 { 1408 rcm_log_message(RCM_TRACE2, "tree_action(%s, %d)\n", root->name, cmd); 1409 1410 arg->cmd = cmd; 1411 arg->retcode = RCM_SUCCESS; 1412 rsrc_walk(root, (void *)arg, node_action); 1413 1414 return (arg->retcode); 1415 } 1416 1417 /* 1418 * Get info on current regsitrations 1419 */ 1420 int 1421 rsrc_usage_info(char **rsrcnames, uint_t flag, int seq_num, rcm_info_t **info) 1422 { 1423 rsrc_node_t *node; 1424 rcm_info_t *result = NULL; 1425 tree_walk_arg_t arg; 1426 int initial_req; 1427 int rv; 1428 int i; 1429 1430 arg.flag = flag; 1431 arg.info = &result; 1432 arg.seq_num = seq_num; 1433 1434 for (i = 0; rsrcnames[i] != NULL; i++) { 1435 1436 rcm_log_message(RCM_TRACE2, "rsrc_usage_info(%s, 0x%x, %d)\n", 1437 rsrcnames[i], flag, seq_num); 1438 1439 if (flag & RCM_INCLUDE_DEPENDENT) { 1440 initial_req = ((seq_num & SEQ_NUM_MASK) == 0); 1441 1442 /* 1443 * if redundant request, skip the operation 1444 */ 1445 if (info_req_add(rsrcnames[i], flag, seq_num) != 0) { 1446 continue; 1447 } 1448 } 1449 1450 rv = rsrc_node_find(rsrcnames[i], 0, &node); 1451 if ((rv != RCM_SUCCESS) || (node == NULL)) { 1452 if ((flag & RCM_INCLUDE_DEPENDENT) && initial_req) 1453 info_req_remove(seq_num); 1454 continue; 1455 } 1456 1457 /* 1458 * Based on RCM_INCLUDE_SUBTREE flag, query either the subtree 1459 * or just the node. 1460 */ 1461 if (flag & RCM_INCLUDE_SUBTREE) { 1462 (void) rsrc_tree_action(node, CMD_GETINFO, &arg); 1463 } else { 1464 arg.cmd = CMD_GETINFO; 1465 (void) node_action(node, (void *)&arg); 1466 } 1467 1468 if ((flag & RCM_INCLUDE_DEPENDENT) && initial_req) 1469 info_req_remove(seq_num); 1470 } 1471 1472 out: 1473 (void) rcm_append_info(info, result); 1474 return (rv); 1475 } 1476 1477 /* 1478 * Get the list of currently loaded module 1479 */ 1480 rcm_info_t * 1481 rsrc_mod_info() 1482 { 1483 module_t *mod; 1484 rcm_info_t *info = NULL; 1485 1486 (void) mutex_lock(&mod_lock); 1487 mod = module_head; 1488 while (mod) { 1489 char *modinfo = s_strdup(module_info(mod)); 1490 add_busy_rsrc_to_list("dummy", 0, 0, 0, mod->name, 1491 modinfo, NULL, NULL, &info); 1492 mod = mod->next; 1493 } 1494 (void) mutex_unlock(&mod_lock); 1495 1496 return (info); 1497 } 1498 1499 /* 1500 * Initialize resource map - load all modules 1501 */ 1502 void 1503 rcmd_db_init() 1504 { 1505 char *tmp; 1506 DIR *mod_dir; 1507 struct dirent *entp; 1508 int i; 1509 char *dir_name; 1510 int rcm_script; 1511 1512 rcm_log_message(RCM_DEBUG, "rcmd_db_init(): initialize database\n"); 1513 1514 if (script_main_init() == -1) 1515 rcmd_exit(errno); 1516 1517 rsrc_root = rn_alloc("/", RSRC_TYPE_NORMAL); 1518 1519 for (i = 0; (dir_name = rcm_dir(i, &rcm_script)) != NULL; i++) { 1520 1521 if ((mod_dir = opendir(dir_name)) == NULL) { 1522 continue; /* try next directory */ 1523 } 1524 1525 rcm_log_message(RCM_TRACE2, "search directory %s\n", dir_name); 1526 1527 while ((entp = readdir(mod_dir)) != NULL) { 1528 module_t *module; 1529 1530 if (strcmp(entp->d_name, ".") == 0 || 1531 strcmp(entp->d_name, "..") == 0) 1532 continue; 1533 1534 if (rcm_script == 0) { 1535 /* rcm module */ 1536 if (((tmp = strstr(entp->d_name, 1537 RCM_MODULE_SUFFIX)) == NULL) || 1538 (tmp[strlen(RCM_MODULE_SUFFIX)] != '\0')) { 1539 continue; 1540 } 1541 } 1542 1543 module = cli_module_hold(entp->d_name); 1544 if (module == NULL) { 1545 if (rcm_script == 0) 1546 rcm_log_message(RCM_ERROR, 1547 gettext("%s: failed to load\n"), 1548 entp->d_name); 1549 continue; 1550 } 1551 1552 if (module->ref_count == MOD_REFCNT_INIT) { 1553 /* 1554 * ask module to register for resource 1st time 1555 */ 1556 module_attach(module); 1557 } 1558 cli_module_rele(module); 1559 } 1560 (void) closedir(mod_dir); 1561 } 1562 1563 rcmd_db_print(); 1564 } 1565 1566 /* 1567 * sync resource map - ask all modules to register again 1568 */ 1569 void 1570 rcmd_db_sync() 1571 { 1572 static time_t sync_time = (time_t)-1; 1573 const time_t interval = 5; /* resync at most every 5 sec */ 1574 1575 module_t *mod; 1576 time_t curr = time(NULL); 1577 1578 if ((sync_time != (time_t)-1) && (curr - sync_time < interval)) 1579 return; 1580 1581 sync_time = curr; 1582 (void) mutex_lock(&mod_lock); 1583 mod = module_head; 1584 while (mod) { 1585 /* 1586 * Hold module by incrementing ref count and release 1587 * mod_lock to avoid deadlock, since rcmop_register() 1588 * may callback into the daemon and request mod_lock. 1589 */ 1590 mod->ref_count++; 1591 (void) mutex_unlock(&mod_lock); 1592 1593 mod->modops->rcmop_register(mod->rcmhandle); 1594 1595 (void) mutex_lock(&mod_lock); 1596 mod->ref_count--; 1597 mod = mod->next; 1598 } 1599 (void) mutex_unlock(&mod_lock); 1600 } 1601 1602 /* 1603 * Determine if a process is alive 1604 */ 1605 int 1606 proc_exist(pid_t pid) 1607 { 1608 char path[64]; 1609 const char *procfs = "/proc"; 1610 struct stat sb; 1611 1612 if (pid == (pid_t)0) { 1613 return (1); 1614 } 1615 1616 (void) snprintf(path, sizeof (path), "%s/%ld", procfs, pid); 1617 return (stat(path, &sb) == 0); 1618 } 1619 1620 /* 1621 * Cleaup client list 1622 * 1623 * N.B. This routine runs in a single-threaded environment only. It is only 1624 * called by the cleanup thread, which never runs in parallel with other 1625 * threads. 1626 */ 1627 static void 1628 clean_client_list(client_t **listp) 1629 { 1630 client_t *client = *listp; 1631 1632 /* 1633 * Cleanup notification clients for which pid no longer exists 1634 */ 1635 while (client) { 1636 if ((client->state != RCM_STATE_REMOVE) && 1637 proc_exist(client->pid)) { 1638 listp = &client->next; 1639 client = *listp; 1640 continue; 1641 } 1642 1643 /* 1644 * Destroy this client_t. rsrc_client_remove updates 1645 * listp to point to the next client. 1646 */ 1647 rsrc_client_remove(client, listp); 1648 client = *listp; 1649 } 1650 } 1651 1652 /*ARGSUSED*/ 1653 static int 1654 clean_node(rsrc_node_t *node, void *arg) 1655 { 1656 rcm_log_message(RCM_TRACE4, "clean_node(%s)\n", node->name); 1657 1658 clean_client_list(&node->users); 1659 1660 return (RN_WALK_CONTINUE); 1661 } 1662 1663 static void 1664 clean_rsrc_tree() 1665 { 1666 rcm_log_message(RCM_TRACE4, 1667 "clean_rsrc_tree(): delete stale dr clients\n"); 1668 1669 rsrc_walk(rsrc_root, NULL, clean_node); 1670 } 1671 1672 static void 1673 db_clean() 1674 { 1675 extern barrier_t barrier; 1676 extern void clean_dr_list(); 1677 1678 for (;;) { 1679 (void) mutex_lock(&rcm_req_lock); 1680 start_polling_thread(); 1681 (void) mutex_unlock(&rcm_req_lock); 1682 1683 (void) mutex_lock(&barrier.lock); 1684 while (need_cleanup == 0) 1685 (void) cond_wait(&barrier.cv, &barrier.lock); 1686 (void) mutex_unlock(&barrier.lock); 1687 1688 /* 1689 * Make sure all other threads are either blocked or exited. 1690 */ 1691 rcmd_set_state(RCMD_CLEANUP); 1692 1693 need_cleanup = 0; 1694 1695 /* 1696 * clean dr_req_list 1697 */ 1698 clean_dr_list(); 1699 1700 /* 1701 * clean resource tree 1702 */ 1703 clean_rsrc_tree(); 1704 1705 rcmd_set_state(RCMD_NORMAL); 1706 } 1707 } 1708 1709 void 1710 rcmd_db_clean() 1711 { 1712 rcm_log_message(RCM_DEBUG, 1713 "rcm_db_clean(): launch thread to clean database\n"); 1714 1715 if (thr_create(NULL, NULL, (void *(*)(void *))db_clean, 1716 NULL, THR_DETACHED, NULL) != 0) { 1717 rcm_log_message(RCM_WARNING, 1718 gettext("failed to create cleanup thread %s\n"), 1719 strerror(errno)); 1720 } 1721 } 1722 1723 /*ARGSUSED*/ 1724 static int 1725 print_node(rsrc_node_t *node, void *arg) 1726 { 1727 client_t *user; 1728 1729 rcm_log_message(RCM_DEBUG, "rscname: %s, state = 0x%x\n", node->name); 1730 rcm_log_message(RCM_DEBUG, " users:\n"); 1731 1732 if ((user = node->users) == NULL) { 1733 rcm_log_message(RCM_DEBUG, " none\n"); 1734 return (RN_WALK_CONTINUE); 1735 } 1736 1737 while (user) { 1738 rcm_log_message(RCM_DEBUG, " %s, %d, %s\n", 1739 user->module->name, user->pid, user->alias); 1740 user = user->next; 1741 } 1742 return (RN_WALK_CONTINUE); 1743 } 1744 1745 static void 1746 rcmd_db_print() 1747 { 1748 module_t *mod; 1749 1750 rcm_log_message(RCM_DEBUG, "modules:\n"); 1751 (void) mutex_lock(&mod_lock); 1752 mod = module_head; 1753 while (mod) { 1754 rcm_log_message(RCM_DEBUG, " %s\n", mod->name); 1755 mod = mod->next; 1756 } 1757 (void) mutex_unlock(&mod_lock); 1758 1759 rcm_log_message(RCM_DEBUG, "\nresource tree:\n"); 1760 1761 rsrc_walk(rsrc_root, NULL, print_node); 1762 1763 rcm_log_message(RCM_DEBUG, "\n"); 1764 } 1765 1766 /* 1767 * Allocate handle from calling into each RCM module 1768 */ 1769 static rcm_handle_t * 1770 rcm_handle_alloc(module_t *module) 1771 { 1772 rcm_handle_t *hdl; 1773 1774 hdl = s_malloc(sizeof (rcm_handle_t)); 1775 1776 hdl->modname = module->name; 1777 hdl->pid = 0; 1778 hdl->lrcm_ops = &rcm_ops; /* for callback into daemon directly */ 1779 hdl->module = module; 1780 1781 return (hdl); 1782 } 1783 1784 /* 1785 * Free rcm_handle_t 1786 */ 1787 static void 1788 rcm_handle_free(rcm_handle_t *handle) 1789 { 1790 free(handle); 1791 } 1792 1793 /* 1794 * help function that exit on memory outage 1795 */ 1796 void * 1797 s_malloc(size_t size) 1798 { 1799 void *buf = malloc(size); 1800 1801 if (buf == NULL) { 1802 rcmd_exit(ENOMEM); 1803 } 1804 return (buf); 1805 } 1806 1807 void * 1808 s_calloc(int n, size_t size) 1809 { 1810 void *buf = calloc(n, size); 1811 1812 if (buf == NULL) { 1813 rcmd_exit(ENOMEM); 1814 } 1815 return (buf); 1816 } 1817 1818 void * 1819 s_realloc(void *ptr, size_t size) 1820 { 1821 void *new = realloc(ptr, size); 1822 1823 if (new == NULL) { 1824 rcmd_exit(ENOMEM); 1825 } 1826 return (new); 1827 } 1828 1829 char * 1830 s_strdup(const char *str) 1831 { 1832 char *buf = strdup(str); 1833 1834 if (buf == NULL) { 1835 rcmd_exit(ENOMEM); 1836 } 1837 return (buf); 1838 } 1839 1840 /* 1841 * Convert a version 1 ops vector to current ops vector 1842 * Fields missing in version 1 are set to NULL. 1843 */ 1844 static struct rcm_mod_ops * 1845 modops_from_v1(void *ops_v1) 1846 { 1847 struct rcm_mod_ops *ops; 1848 1849 ops = s_calloc(1, sizeof (struct rcm_mod_ops)); 1850 bcopy(ops_v1, ops, sizeof (struct rcm_mod_ops_v1)); 1851 return (ops); 1852 } 1853 1854 /* call a module's getinfo routine; detects v1 ops and adjusts the call */ 1855 static int 1856 call_getinfo(struct rcm_mod_ops *ops, rcm_handle_t *hdl, char *alias, id_t pid, 1857 uint_t flag, char **info, char **error, nvlist_t *client_props, 1858 rcm_info_t **infop) 1859 { 1860 int rval; 1861 struct rcm_mod_ops_v1 *v1_ops; 1862 1863 if (ops->version == RCM_MOD_OPS_V1) { 1864 v1_ops = (struct rcm_mod_ops_v1 *)ops; 1865 rval = v1_ops->rcmop_get_info(hdl, alias, pid, flag, info, 1866 infop); 1867 if (rval != RCM_SUCCESS && *info != NULL) 1868 *error = strdup(*info); 1869 return (rval); 1870 } else { 1871 return (ops->rcmop_get_info(hdl, alias, pid, flag, info, error, 1872 client_props, infop)); 1873 } 1874 } 1875 1876 void 1877 rcm_init_queue(rcm_queue_t *head) 1878 { 1879 head->next = head->prev = head; 1880 } 1881 1882 void 1883 rcm_enqueue_head(rcm_queue_t *head, rcm_queue_t *element) 1884 { 1885 rcm_enqueue(head, element); 1886 } 1887 1888 void 1889 rcm_enqueue_tail(rcm_queue_t *head, rcm_queue_t *element) 1890 { 1891 rcm_enqueue(head->prev, element); 1892 } 1893 1894 void 1895 rcm_enqueue(rcm_queue_t *list_element, rcm_queue_t *element) 1896 { 1897 element->next = list_element->next; 1898 element->prev = list_element; 1899 element->next->prev = element; 1900 list_element->next = element; 1901 } 1902 1903 rcm_queue_t * 1904 rcm_dequeue_head(rcm_queue_t *head) 1905 { 1906 rcm_queue_t *element = head->next; 1907 rcm_dequeue(element); 1908 return (element); 1909 } 1910 1911 rcm_queue_t * 1912 rcm_dequeue_tail(rcm_queue_t *head) 1913 { 1914 rcm_queue_t *element = head->prev; 1915 rcm_dequeue(element); 1916 return (element); 1917 } 1918 1919 void 1920 rcm_dequeue(rcm_queue_t *element) 1921 { 1922 element->prev->next = element->next; 1923 element->next->prev = element->prev; 1924 element->next = element->prev = NULL; 1925 } 1926