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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <stdarg.h> 29 #include <unistd.h> 30 #include <fcntl.h> 31 #include <errno.h> 32 #include <string.h> 33 #include <door.h> 34 #include <libnvpair.h> 35 #include <libhotplug.h> 36 #include <libhotplug_impl.h> 37 #include <sys/sunddi.h> 38 #include <sys/ddi_hp.h> 39 40 static void i_hp_dprintf(const char *fmt, ...); 41 static int i_hp_pack_branch(hp_node_t, char **, size_t *); 42 static int i_hp_pack_node(hp_node_t, char **, size_t *); 43 static int i_hp_unpack_node(char *, size_t, hp_node_t, hp_node_t *); 44 static int i_hp_unpack_branch(char *, size_t, hp_node_t, hp_node_t *); 45 static int i_hp_call_hotplugd(nvlist_t *, nvlist_t **); 46 static nvlist_t *i_hp_set_args(hp_cmd_t, const char *, const char *, uint_t, 47 const char *, int); 48 static int i_hp_parse_results(nvlist_t *, hp_node_t *, char **); 49 50 /* 51 * Global flag to enable debug features. 52 */ 53 int libhotplug_debug = 0; 54 55 /* 56 * hp_init() 57 * 58 * Initialize a hotplug information snapshot. 59 */ 60 hp_node_t 61 hp_init(const char *path, const char *connection, uint_t flags) 62 { 63 nvlist_t *args; 64 nvlist_t *results; 65 hp_node_t root = NULL; 66 int rv; 67 68 i_hp_dprintf("hp_init: path=%p, connection=%p, flags=0x%x\n", 69 (void *)path, (void *)connection, flags); 70 71 /* Check arguments */ 72 if ((path == NULL) || !HP_INIT_FLAGS_VALID(flags)) { 73 i_hp_dprintf("hp_init: invalid arguments.\n"); 74 errno = EINVAL; 75 return (NULL); 76 } 77 78 /* Build arguments for door call */ 79 if ((args = i_hp_set_args(HP_CMD_GETINFO, path, connection, flags, 80 NULL, 0)) == NULL) { 81 i_hp_dprintf("hp_init: cannot build arguments nvlist.\n"); 82 errno = ENOMEM; 83 return (NULL); 84 } 85 86 /* Make the door call to hotplugd */ 87 rv = i_hp_call_hotplugd(args, &results); 88 89 /* Arguments no longer needed */ 90 nvlist_free(args); 91 92 /* Parse additional results, if any */ 93 if ((rv == 0) && (results != NULL)) { 94 rv = i_hp_parse_results(results, &root, NULL); 95 nvlist_free(results); 96 } 97 98 /* Check for errors */ 99 if (rv != 0) { 100 i_hp_dprintf("hp_init: failure (%s).\n", strerror(rv)); 101 if (root) 102 hp_fini(root); 103 errno = rv; 104 return (NULL); 105 } 106 107 /* Success requires an info snapshot */ 108 if (root == NULL) { 109 i_hp_dprintf("hp_init: missing info snapshot.\n"); 110 errno = EFAULT; 111 return (NULL); 112 } 113 114 /* Success */ 115 return (root); 116 } 117 118 /* 119 * hp_fini() 120 * 121 * Terminate and clean-up a hotplug information snapshot. 122 */ 123 void 124 hp_fini(hp_node_t root) 125 { 126 hp_node_t node; 127 hp_node_t sibling; 128 char *basepath; 129 130 i_hp_dprintf("hp_fini: root=%p\n", (void *)root); 131 132 if (root == NULL) { 133 i_hp_dprintf("hp_fini: invalid arguments.\n"); 134 return; 135 } 136 137 /* Extract and free base path */ 138 if (root->hp_basepath) { 139 basepath = root->hp_basepath; 140 for (node = root; node != NULL; node = node->hp_sibling) 141 node->hp_basepath = NULL; 142 free(basepath); 143 } 144 145 /* Destroy the nodes */ 146 node = root; 147 while (node) { 148 sibling = node->hp_sibling; 149 if (node->hp_child) 150 hp_fini(node->hp_child); 151 if (node->hp_name) 152 free(node->hp_name); 153 if (node->hp_usage) 154 free(node->hp_usage); 155 if (node->hp_description) 156 free(node->hp_description); 157 free(node); 158 node = sibling; 159 } 160 } 161 162 /* 163 * hp_traverse() 164 * 165 * Walk a graph of hotplug nodes, executing a callback on each node. 166 */ 167 int 168 hp_traverse(hp_node_t root, void *arg, int (*hp_callback)(hp_node_t, void *arg)) 169 { 170 int rv; 171 hp_node_t node; 172 173 i_hp_dprintf("hp_traverse: root=%p, arg=%p, hp_callback=%p\n", 174 (void *)root, arg, (void *)hp_callback); 175 176 /* Check arguments */ 177 if ((root == NULL) || (hp_callback == NULL)) { 178 i_hp_dprintf("hp_traverse: invalid arguments.\n"); 179 errno = EINVAL; 180 return (-1); 181 } 182 183 for (node = root; node; node = node->hp_sibling) { 184 rv = hp_callback(node, arg); 185 186 if (rv == HP_WALK_TERMINATE) { 187 i_hp_dprintf("hp_traverse: walk terminated.\n"); 188 return (HP_WALK_TERMINATE); 189 } 190 191 if (node->hp_child && (rv != HP_WALK_PRUNECHILD)) 192 if (hp_traverse(node->hp_child, arg, hp_callback) == 193 HP_WALK_TERMINATE) { 194 i_hp_dprintf("hp_traverse: walk terminated.\n"); 195 return (HP_WALK_TERMINATE); 196 } 197 198 if (rv == HP_WALK_PRUNESIBLING) 199 break; 200 } 201 202 return (0); 203 } 204 205 /* 206 * hp_type() 207 * 208 * Return a node's type. 209 */ 210 int 211 hp_type(hp_node_t node) 212 { 213 i_hp_dprintf("hp_type: node=%p\n", (void *)node); 214 215 if (node == NULL) { 216 i_hp_dprintf("hp_type: invalid arguments.\n"); 217 errno = EINVAL; 218 return (-1); 219 } 220 221 return (node->hp_type); 222 } 223 224 /* 225 * hp_name() 226 * 227 * Return a node's name. 228 */ 229 char * 230 hp_name(hp_node_t node) 231 { 232 i_hp_dprintf("hp_name: node=%p\n", (void *)node); 233 234 if (node == NULL) { 235 i_hp_dprintf("hp_name: invalid arguments.\n"); 236 errno = EINVAL; 237 return (NULL); 238 } 239 240 if (node->hp_name == NULL) { 241 i_hp_dprintf("hp_name: missing name value.\n"); 242 errno = EFAULT; 243 } 244 245 return (node->hp_name); 246 } 247 248 /* 249 * hp_state() 250 * 251 * Return a node's current state. 252 */ 253 int 254 hp_state(hp_node_t node) 255 { 256 i_hp_dprintf("hp_state: node=%p\n", (void *)node); 257 258 if (node == NULL) { 259 i_hp_dprintf("hp_state: invalid arguments.\n"); 260 errno = EINVAL; 261 return (-1); 262 } 263 264 if ((node->hp_type != HP_NODE_CONNECTOR) && 265 (node->hp_type != HP_NODE_PORT)) { 266 i_hp_dprintf("hp_state: operation not supported.\n"); 267 errno = ENOTSUP; 268 return (-1); 269 } 270 271 return (node->hp_state); 272 } 273 274 /* 275 * hp_usage() 276 * 277 * Return a usage description for usage nodes. 278 */ 279 char * 280 hp_usage(hp_node_t node) 281 { 282 i_hp_dprintf("hp_usage: node=%p\n", (void *)node); 283 284 if (node == NULL) { 285 i_hp_dprintf("hp_usage: invalid arguments.\n"); 286 errno = EINVAL; 287 return (NULL); 288 } 289 290 if (node->hp_type != HP_NODE_USAGE) { 291 i_hp_dprintf("hp_usage: operation not supported.\n"); 292 errno = ENOTSUP; 293 return (NULL); 294 } 295 296 if (node->hp_usage == NULL) { 297 i_hp_dprintf("hp_usage: missing usage value.\n"); 298 errno = EFAULT; 299 } 300 301 return (node->hp_usage); 302 } 303 304 /* 305 * hp_description() 306 * 307 * Return a type description (e.g. "PCI slot") for connection nodes. 308 */ 309 char * 310 hp_description(hp_node_t node) 311 { 312 i_hp_dprintf("hp_description: node=%p\n", (void *)node); 313 314 if (node == NULL) { 315 i_hp_dprintf("hp_description: invalid arguments.\n"); 316 errno = EINVAL; 317 return (NULL); 318 } 319 320 if ((node->hp_type != HP_NODE_CONNECTOR) && 321 (node->hp_type != HP_NODE_PORT)) { 322 i_hp_dprintf("hp_description: operation not supported.\n"); 323 errno = ENOTSUP; 324 return (NULL); 325 } 326 327 if (node->hp_description == NULL) { 328 i_hp_dprintf("hp_description: missing description value.\n"); 329 errno = EFAULT; 330 } 331 332 return (node->hp_description); 333 } 334 335 /* 336 * hp_last_change() 337 * 338 * Return when the state of a connection was last changed. 339 */ 340 time_t 341 hp_last_change(hp_node_t node) 342 { 343 i_hp_dprintf("hp_last_change: node=%p\n", (void *)node); 344 345 if (node == NULL) { 346 i_hp_dprintf("hp_last_change: invalid arguments.\n"); 347 errno = EINVAL; 348 return (NULL); 349 } 350 351 if ((node->hp_type != HP_NODE_CONNECTOR) && 352 (node->hp_type != HP_NODE_PORT)) { 353 i_hp_dprintf("hp_last_change: operation not supported.\n"); 354 errno = ENOTSUP; 355 return (NULL); 356 } 357 358 return (node->hp_last_change); 359 } 360 361 /* 362 * hp_parent() 363 * 364 * Return a node's parent node. 365 */ 366 hp_node_t 367 hp_parent(hp_node_t node) 368 { 369 i_hp_dprintf("hp_parent: node=%p\n", (void *)node); 370 371 if (node == NULL) { 372 i_hp_dprintf("hp_parent: invalid arguments.\n"); 373 errno = EINVAL; 374 return (NULL); 375 } 376 377 if (node->hp_parent == NULL) { 378 i_hp_dprintf("hp_parent: node has no parent.\n"); 379 errno = ENXIO; 380 } 381 382 return (node->hp_parent); 383 } 384 385 /* 386 * hp_child() 387 * 388 * Return a node's first child node. 389 */ 390 hp_node_t 391 hp_child(hp_node_t node) 392 { 393 i_hp_dprintf("hp_child: node=%p\n", (void *)node); 394 395 if (node == NULL) { 396 i_hp_dprintf("hp_child: invalid arguments.\n"); 397 errno = EINVAL; 398 return (NULL); 399 } 400 401 if (node->hp_child == NULL) { 402 i_hp_dprintf("hp_child: node has no child.\n"); 403 errno = ENXIO; 404 } 405 406 return (node->hp_child); 407 } 408 409 /* 410 * hp_sibling() 411 * 412 * Return a node's next sibling node. 413 */ 414 hp_node_t 415 hp_sibling(hp_node_t node) 416 { 417 i_hp_dprintf("hp_sibling: node=%p\n", (void *)node); 418 419 if (node == NULL) { 420 i_hp_dprintf("hp_sibling: invalid arguments.\n"); 421 errno = EINVAL; 422 return (NULL); 423 } 424 425 if (node->hp_sibling == NULL) { 426 i_hp_dprintf("hp_sibling: node has no sibling.\n"); 427 errno = ENXIO; 428 } 429 430 return (node->hp_sibling); 431 } 432 433 /* 434 * hp_path() 435 * 436 * Return the path (and maybe connection name) of a node. 437 * The caller must supply two buffers, each MAXPATHLEN size. 438 */ 439 int 440 hp_path(hp_node_t node, char *path, char *connection) 441 { 442 hp_node_t root; 443 hp_node_t parent; 444 int i; 445 char *s; 446 char components[MAXPATHLEN]; 447 448 i_hp_dprintf("hp_path: node=%p, path=%p, connection=%p\n", (void *)node, 449 (void *)path, (void *)connection); 450 451 if ((node == NULL) || (path == NULL) || (connection == NULL)) { 452 i_hp_dprintf("hp_path: invalid arguments.\n"); 453 return (EINVAL); 454 } 455 456 (void) memset(path, 0, MAXPATHLEN); 457 (void) memset(connection, 0, MAXPATHLEN); 458 (void) memset(components, 0, MAXPATHLEN); 459 460 /* Set 'connection' only for connectors and ports */ 461 if ((node->hp_type == HP_NODE_CONNECTOR) || 462 (node->hp_type == HP_NODE_PORT)) 463 (void) strlcpy(connection, node->hp_name, MAXPATHLEN); 464 465 /* Trace back to the root node, accumulating components */ 466 for (parent = node, root = parent; parent != NULL; 467 root = parent, parent = parent->hp_parent) { 468 if (parent->hp_type == HP_NODE_DEVICE) { 469 (void) strlcat(components, "/", MAXPATHLEN); 470 (void) strlcat(components, parent->hp_name, MAXPATHLEN); 471 } 472 } 473 474 /* Ensure the snapshot actually contains a base path */ 475 if (root->hp_basepath == NULL) { 476 i_hp_dprintf("hp_path: missing base pathname.\n"); 477 return (EFAULT); 478 } 479 480 /* 481 * Construct the path. Start with the base path from the root 482 * node, then append the accumulated components in reverse order. 483 */ 484 if (strcmp(root->hp_basepath, "/") != 0) { 485 (void) strlcat(path, root->hp_basepath, MAXPATHLEN); 486 if ((root->hp_type == HP_NODE_DEVICE) && 487 ((s = strrchr(path, '/')) != NULL)) 488 *s = '\0'; 489 } 490 for (i = strlen(components) - 1; i >= 0; i--) { 491 if (components[i] == '/') { 492 (void) strlcat(path, &components[i], MAXPATHLEN); 493 components[i] = '\0'; 494 } 495 } 496 497 return (0); 498 } 499 500 /* 501 * hp_set_state() 502 * 503 * Initiate a state change operation on a node. 504 */ 505 int 506 hp_set_state(hp_node_t node, uint_t flags, int state, hp_node_t *resultsp) 507 { 508 hp_node_t root = NULL; 509 nvlist_t *args; 510 nvlist_t *results; 511 int rv; 512 char path[MAXPATHLEN]; 513 char connection[MAXPATHLEN]; 514 515 i_hp_dprintf("hp_set_state: node=%p, flags=0x%x, state=0x%x, " 516 "resultsp=%p\n", (void *)node, flags, state, (void *)resultsp); 517 518 /* Check arguments */ 519 if ((node == NULL) || (resultsp == NULL) || 520 !HP_SET_STATE_FLAGS_VALID(flags)) { 521 i_hp_dprintf("hp_set_state: invalid arguments.\n"); 522 return (EINVAL); 523 } 524 525 /* Check node type */ 526 if ((node->hp_type != HP_NODE_CONNECTOR) && 527 (node->hp_type != HP_NODE_PORT)) { 528 i_hp_dprintf("hp_set_state: operation not supported.\n"); 529 return (ENOTSUP); 530 } 531 532 /* Check that target state is valid */ 533 switch (state) { 534 case DDI_HP_CN_STATE_PRESENT: 535 case DDI_HP_CN_STATE_POWERED: 536 case DDI_HP_CN_STATE_ENABLED: 537 if (node->hp_type != HP_NODE_CONNECTOR) { 538 i_hp_dprintf("hp_set_state: mismatched target.\n"); 539 return (ENOTSUP); 540 } 541 break; 542 case DDI_HP_CN_STATE_PORT_PRESENT: 543 case DDI_HP_CN_STATE_OFFLINE: 544 case DDI_HP_CN_STATE_ONLINE: 545 if (node->hp_type != HP_NODE_PORT) { 546 i_hp_dprintf("hp_set_state: mismatched target.\n"); 547 return (ENOTSUP); 548 } 549 break; 550 default: 551 i_hp_dprintf("hp_set_state: invalid target state.\n"); 552 return (EINVAL); 553 } 554 555 /* Get path and connection of specified node */ 556 if ((rv = hp_path(node, path, connection)) != 0) 557 return (rv); 558 559 /* Build arguments for door call */ 560 if ((args = i_hp_set_args(HP_CMD_CHANGESTATE, path, connection, flags, 561 NULL, state)) == NULL) 562 return (ENOMEM); 563 564 /* Make the door call to hotplugd */ 565 rv = i_hp_call_hotplugd(args, &results); 566 567 /* Arguments no longer needed */ 568 nvlist_free(args); 569 570 /* Parse additional results, if any */ 571 if ((rv == 0) && (results != NULL)) { 572 rv = i_hp_parse_results(results, &root, NULL); 573 nvlist_free(results); 574 *resultsp = root; 575 } 576 577 /* Done */ 578 return (rv); 579 } 580 581 /* 582 * hp_set_private() 583 * 584 * Set bus private options on the hotplug connection 585 * indicated by the given hotplug information node. 586 */ 587 int 588 hp_set_private(hp_node_t node, const char *options, char **resultsp) 589 { 590 int rv; 591 nvlist_t *args; 592 nvlist_t *results; 593 char *values = NULL; 594 char path[MAXPATHLEN]; 595 char connection[MAXPATHLEN]; 596 597 i_hp_dprintf("hp_set_private: node=%p, options=%p, resultsp=%p\n", 598 (void *)node, (void *)options, (void *)resultsp); 599 600 /* Check arguments */ 601 if ((node == NULL) || (options == NULL) || (resultsp == NULL)) { 602 i_hp_dprintf("hp_set_private: invalid arguments.\n"); 603 return (EINVAL); 604 } 605 606 /* Check node type */ 607 if (node->hp_type != HP_NODE_CONNECTOR) { 608 i_hp_dprintf("hp_set_private: operation not supported.\n"); 609 return (ENOTSUP); 610 } 611 612 /* Initialize results */ 613 *resultsp = NULL; 614 615 /* Get path and connection of specified node */ 616 if ((rv = hp_path(node, path, connection)) != 0) 617 return (rv); 618 619 /* Build arguments for door call */ 620 if ((args = i_hp_set_args(HP_CMD_SETPRIVATE, path, connection, 0, 621 options, 0)) == NULL) 622 return (ENOMEM); 623 624 /* Make the door call to hotplugd */ 625 rv = i_hp_call_hotplugd(args, &results); 626 627 /* Arguments no longer needed */ 628 nvlist_free(args); 629 630 /* Parse additional results, if any */ 631 if ((rv == 0) && (results != NULL)) { 632 rv = i_hp_parse_results(results, NULL, &values); 633 nvlist_free(results); 634 *resultsp = values; 635 } 636 637 /* Done */ 638 return (rv); 639 } 640 641 /* 642 * hp_get_private() 643 * 644 * Get bus private options on the hotplug connection 645 * indicated by the given hotplug information node. 646 */ 647 int 648 hp_get_private(hp_node_t node, const char *options, char **resultsp) 649 { 650 int rv; 651 nvlist_t *args; 652 nvlist_t *results; 653 char *values = NULL; 654 char path[MAXPATHLEN]; 655 char connection[MAXPATHLEN]; 656 657 i_hp_dprintf("hp_get_private: node=%p, options=%p, resultsp=%p\n", 658 (void *)node, (void *)options, (void *)resultsp); 659 660 /* Check arguments */ 661 if ((node == NULL) || (options == NULL) || (resultsp == NULL)) { 662 i_hp_dprintf("hp_get_private: invalid arguments.\n"); 663 return (EINVAL); 664 } 665 666 /* Check node type */ 667 if (node->hp_type != HP_NODE_CONNECTOR) { 668 i_hp_dprintf("hp_get_private: operation not supported.\n"); 669 return (ENOTSUP); 670 } 671 672 /* Initialize results */ 673 *resultsp = NULL; 674 675 /* Get path and connection of specified node */ 676 if ((rv = hp_path(node, path, connection)) != 0) 677 return (rv); 678 679 /* Build arguments for door call */ 680 if ((args = i_hp_set_args(HP_CMD_GETPRIVATE, path, connection, 0, 681 options, 0)) == NULL) 682 return (ENOMEM); 683 684 /* Make the door call to hotplugd */ 685 rv = i_hp_call_hotplugd(args, &results); 686 687 /* Arguments no longer needed */ 688 nvlist_free(args); 689 690 /* Parse additional results, if any */ 691 if ((rv == 0) && (results != NULL)) { 692 rv = i_hp_parse_results(results, NULL, &values); 693 nvlist_free(results); 694 *resultsp = values; 695 } 696 697 /* Done */ 698 return (rv); 699 } 700 701 /* 702 * hp_pack() 703 * 704 * Given the root of a hotplug information snapshot, pack 705 * it into a contiguous byte array so that it is suitable 706 * for network transport. 707 */ 708 int 709 hp_pack(hp_node_t root, char **bufp, size_t *lenp) 710 { 711 hp_node_t node; 712 nvlist_t *nvl; 713 char *buf; 714 size_t len; 715 int rv; 716 717 i_hp_dprintf("hp_pack: root=%p, bufp=%p, lenp=%p\n", (void *)root, 718 (void *)bufp, (void *)lenp); 719 720 if ((root == NULL) || (bufp == NULL) || (lenp == NULL)) { 721 i_hp_dprintf("hp_pack: invalid arguments.\n"); 722 return (EINVAL); 723 } 724 725 *lenp = 0; 726 *bufp = NULL; 727 728 if (nvlist_alloc(&nvl, 0, 0) != 0) { 729 i_hp_dprintf("hp_pack: nvlist_alloc() failed (%s).\n", 730 strerror(errno)); 731 return (ENOMEM); 732 } 733 734 if (root->hp_basepath != NULL) { 735 rv = nvlist_add_string(nvl, HP_INFO_BASE, root->hp_basepath); 736 if (rv != 0) { 737 nvlist_free(nvl); 738 return (rv); 739 } 740 } 741 742 for (node = root; node != NULL; node = node->hp_sibling) { 743 if ((rv = i_hp_pack_branch(node, &buf, &len)) == 0) { 744 rv = nvlist_add_byte_array(nvl, HP_INFO_BRANCH, 745 (uchar_t *)buf, len); 746 free(buf); 747 } 748 if (rv != 0) { 749 nvlist_free(nvl); 750 return (rv); 751 } 752 } 753 754 len = 0; 755 buf = NULL; 756 if ((rv = nvlist_pack(nvl, &buf, &len, NV_ENCODE_NATIVE, 0)) == 0) { 757 *lenp = len; 758 *bufp = buf; 759 } 760 761 nvlist_free(nvl); 762 763 return (rv); 764 } 765 766 /* 767 * hp_unpack() 768 * 769 * Unpack a hotplug information snapshot for normal usage. 770 */ 771 int 772 hp_unpack(char *packed_buf, size_t packed_len, hp_node_t *retp) 773 { 774 hp_node_t root; 775 hp_node_t root_list = NULL; 776 hp_node_t prev_root = NULL; 777 nvlist_t *nvl = NULL; 778 nvpair_t *nvp; 779 char *basepath = NULL; 780 int rv; 781 782 i_hp_dprintf("hp_unpack: packed_buf=%p, packed_len=%u, retp=%p\n", 783 (void *)packed_buf, (uint32_t)packed_len, (void *)retp); 784 785 if ((packed_buf == NULL) || (packed_len == 0) || (retp == NULL)) { 786 i_hp_dprintf("hp_unpack: invalid arguments.\n"); 787 return (EINVAL); 788 } 789 790 if ((rv = nvlist_unpack(packed_buf, packed_len, &nvl, 0)) != 0) 791 return (rv); 792 793 if (nvlist_next_nvpair(nvl, NULL) == NULL) { 794 nvlist_free(nvl); 795 errno = EINVAL; 796 return (NULL); 797 } 798 799 for (nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) { 800 801 rv = EINVAL; 802 803 if (strcmp(nvpair_name(nvp), HP_INFO_BASE) == 0) { 804 char *val_string; 805 806 if ((rv = nvpair_value_string(nvp, &val_string)) == 0) { 807 if ((basepath = strdup(val_string)) == NULL) 808 rv = ENOMEM; 809 } 810 811 } else if (strcmp(nvpair_name(nvp), HP_INFO_BRANCH) == 0) { 812 size_t len = 0; 813 char *buf = NULL; 814 815 if ((rv = nvpair_value_byte_array(nvp, 816 (uchar_t **)&buf, (uint_t *)&len)) == 0) { 817 rv = i_hp_unpack_branch(buf, len, NULL, &root); 818 } 819 820 if (rv == 0) { 821 if (prev_root) { 822 prev_root->hp_sibling = root; 823 } else { 824 root_list = root; 825 } 826 prev_root = root; 827 } 828 } 829 830 if (rv != 0) { 831 if (basepath) 832 free(basepath); 833 nvlist_free(nvl); 834 hp_fini(root_list); 835 *retp = NULL; 836 return (rv); 837 } 838 } 839 840 /* Store the base path in each root node */ 841 if (basepath) { 842 for (root = root_list; root; root = root->hp_sibling) 843 root->hp_basepath = basepath; 844 } 845 846 nvlist_free(nvl); 847 *retp = root_list; 848 return (0); 849 } 850 851 /* 852 * i_hp_dprintf() 853 * 854 * Print debug messages to stderr, but only when the debug flag 855 * (libhotplug_debug) is set. 856 */ 857 /*PRINTFLIKE1*/ 858 static void 859 i_hp_dprintf(const char *fmt, ...) 860 { 861 va_list ap; 862 863 if (libhotplug_debug) { 864 va_start(ap, fmt); 865 (void) vfprintf(stderr, fmt, ap); 866 va_end(ap); 867 } 868 } 869 870 /* 871 * i_hp_pack_branch() 872 * 873 * Pack an individual branch of a hotplug information snapshot. 874 */ 875 static int 876 i_hp_pack_branch(hp_node_t root, char **bufp, size_t *lenp) 877 { 878 hp_node_t child; 879 nvlist_t *nvl; 880 char *buf; 881 size_t len; 882 int rv; 883 884 *lenp = 0; 885 *bufp = NULL; 886 887 /* Allocate an nvlist for this branch */ 888 if (nvlist_alloc(&nvl, 0, 0) != 0) 889 return (ENOMEM); 890 891 /* Pack the root of the branch and add it to the nvlist */ 892 if ((rv = i_hp_pack_node(root, &buf, &len)) == 0) { 893 rv = nvlist_add_byte_array(nvl, HP_INFO_NODE, 894 (uchar_t *)buf, len); 895 free(buf); 896 } 897 if (rv != 0) { 898 nvlist_free(nvl); 899 return (rv); 900 } 901 902 /* Pack each subordinate branch, and add it to the nvlist */ 903 for (child = root->hp_child; child != NULL; child = child->hp_sibling) { 904 if ((rv = i_hp_pack_branch(child, &buf, &len)) == 0) { 905 rv = nvlist_add_byte_array(nvl, HP_INFO_BRANCH, 906 (uchar_t *)buf, len); 907 free(buf); 908 } 909 if (rv != 0) { 910 nvlist_free(nvl); 911 return (rv); 912 } 913 } 914 915 /* Pack the resulting nvlist into a single buffer */ 916 len = 0; 917 buf = NULL; 918 if ((rv = nvlist_pack(nvl, &buf, &len, NV_ENCODE_NATIVE, 0)) == 0) { 919 *lenp = len; 920 *bufp = buf; 921 } 922 923 /* Free the nvlist */ 924 nvlist_free(nvl); 925 926 return (rv); 927 } 928 929 /* 930 * i_hp_pack_node() 931 * 932 * Pack an individual node of a hotplug information snapshot. 933 */ 934 static int 935 i_hp_pack_node(hp_node_t node, char **bufp, size_t *lenp) 936 { 937 nvlist_t *nvl; 938 char *buf = NULL; 939 size_t len = 0; 940 int rv; 941 942 if (nvlist_alloc(&nvl, 0, 0) != 0) 943 return (ENOMEM); 944 945 if ((rv = nvlist_add_uint32(nvl, HP_INFO_TYPE, 946 (uint32_t)node->hp_type)) != 0) 947 goto fail; 948 949 if ((node->hp_name) && 950 ((rv = nvlist_add_string(nvl, HP_INFO_NAME, node->hp_name)) != 0)) 951 goto fail; 952 953 if ((node->hp_usage) && 954 ((rv = nvlist_add_string(nvl, HP_INFO_USAGE, node->hp_usage)) != 0)) 955 goto fail; 956 957 if ((node->hp_description) && 958 ((rv = nvlist_add_string(nvl, HP_INFO_DESC, 959 node->hp_description)) != 0)) 960 goto fail; 961 962 if ((rv = nvlist_add_uint32(nvl, HP_INFO_STATE, node->hp_state)) != 0) 963 goto fail; 964 965 if ((node->hp_last_change != 0) && 966 ((rv = nvlist_add_uint32(nvl, HP_INFO_TIME, 967 node->hp_last_change)) != 0)) 968 goto fail; 969 970 if ((rv = nvlist_pack(nvl, &buf, &len, NV_ENCODE_NATIVE, 0)) != 0) 971 goto fail; 972 973 *bufp = buf; 974 *lenp = len; 975 nvlist_free(nvl); 976 return (0); 977 978 fail: 979 *bufp = NULL; 980 *lenp = 0; 981 nvlist_free(nvl); 982 return (rv); 983 } 984 985 /* 986 * i_hp_unpack_branch() 987 * 988 * Unpack a branch of hotplug information nodes. 989 */ 990 static int 991 i_hp_unpack_branch(char *packed_buf, size_t packed_len, hp_node_t parent, 992 hp_node_t *retp) 993 { 994 hp_node_t node = NULL; 995 hp_node_t child; 996 hp_node_t prev_child = NULL; 997 nvlist_t *nvl = NULL; 998 nvpair_t *nvp; 999 char *buf; 1000 size_t len; 1001 int rv; 1002 1003 /* Initialize results */ 1004 *retp = NULL; 1005 1006 /* Unpack the nvlist for this branch */ 1007 if ((rv = nvlist_unpack(packed_buf, packed_len, &nvl, 0)) != 0) 1008 return (rv); 1009 1010 /* 1011 * Unpack the branch. The first item in the nvlist is 1012 * always the root node. And zero or more subordinate 1013 * branches may be packed afterward. 1014 */ 1015 for (nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) { 1016 1017 len = 0; 1018 buf = NULL; 1019 1020 if (strcmp(nvpair_name(nvp), HP_INFO_NODE) == 0) { 1021 1022 /* Check that there is only one root node */ 1023 if (node != NULL) { 1024 hp_fini(node); 1025 nvlist_free(nvl); 1026 return (EFAULT); 1027 } 1028 1029 if ((rv = nvpair_value_byte_array(nvp, (uchar_t **)&buf, 1030 (uint_t *)&len)) == 0) 1031 rv = i_hp_unpack_node(buf, len, parent, &node); 1032 1033 if (rv != 0) { 1034 nvlist_free(nvl); 1035 return (rv); 1036 } 1037 1038 } else if (strcmp(nvpair_name(nvp), HP_INFO_BRANCH) == 0) { 1039 1040 if ((rv = nvpair_value_byte_array(nvp, (uchar_t **)&buf, 1041 (uint_t *)&len)) == 0) 1042 rv = i_hp_unpack_branch(buf, len, node, &child); 1043 1044 if (rv != 0) { 1045 hp_fini(node); 1046 nvlist_free(nvl); 1047 return (rv); 1048 } 1049 1050 if (prev_child) { 1051 prev_child->hp_sibling = child; 1052 } else { 1053 node->hp_child = child; 1054 } 1055 prev_child = child; 1056 } 1057 } 1058 1059 nvlist_free(nvl); 1060 *retp = node; 1061 return (0); 1062 } 1063 1064 /* 1065 * i_hp_unpack_node() 1066 * 1067 * Unpack an individual hotplug information node. 1068 */ 1069 static int 1070 i_hp_unpack_node(char *buf, size_t len, hp_node_t parent, hp_node_t *retp) 1071 { 1072 hp_node_t node; 1073 nvlist_t *nvl; 1074 nvpair_t *nvp; 1075 uint32_t val_uint32; 1076 char *val_string; 1077 int rv = 0; 1078 1079 /* Initialize results */ 1080 *retp = NULL; 1081 1082 /* Unpack node into an nvlist */ 1083 if ((nvlist_unpack(buf, len, &nvl, 0) != 0)) 1084 return (EINVAL); 1085 1086 /* Allocate the new node */ 1087 if ((node = (hp_node_t)calloc(1, sizeof (struct hp_node))) == NULL) { 1088 nvlist_free(nvl); 1089 return (ENOMEM); 1090 } 1091 1092 /* Iterate through nvlist, unpacking each field */ 1093 for (nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) { 1094 1095 if ((strcmp(nvpair_name(nvp), HP_INFO_TYPE) == 0) && 1096 (nvpair_type(nvp) == DATA_TYPE_UINT32)) { 1097 1098 (void) nvpair_value_uint32(nvp, &val_uint32); 1099 node->hp_type = val_uint32; 1100 1101 } else if ((strcmp(nvpair_name(nvp), HP_INFO_NAME) == 0) && 1102 (nvpair_type(nvp) == DATA_TYPE_STRING)) { 1103 1104 (void) nvpair_value_string(nvp, &val_string); 1105 if ((node->hp_name = strdup(val_string)) == NULL) { 1106 rv = ENOMEM; 1107 break; 1108 } 1109 1110 } else if ((strcmp(nvpair_name(nvp), HP_INFO_STATE) == 0) && 1111 (nvpair_type(nvp) == DATA_TYPE_UINT32)) { 1112 1113 (void) nvpair_value_uint32(nvp, &val_uint32); 1114 node->hp_state = val_uint32; 1115 1116 } else if ((strcmp(nvpair_name(nvp), HP_INFO_USAGE) == 0) && 1117 (nvpair_type(nvp) == DATA_TYPE_STRING)) { 1118 1119 (void) nvpair_value_string(nvp, &val_string); 1120 if ((node->hp_usage = strdup(val_string)) == NULL) { 1121 rv = ENOMEM; 1122 break; 1123 } 1124 1125 } else if ((strcmp(nvpair_name(nvp), HP_INFO_DESC) == 0) && 1126 (nvpair_type(nvp) == DATA_TYPE_STRING)) { 1127 1128 (void) nvpair_value_string(nvp, &val_string); 1129 if ((node->hp_description = strdup(val_string)) 1130 == NULL) { 1131 rv = ENOMEM; 1132 break; 1133 } 1134 1135 } else if ((strcmp(nvpair_name(nvp), HP_INFO_TIME) == 0) && 1136 (nvpair_type(nvp) == DATA_TYPE_UINT32)) { 1137 1138 (void) nvpair_value_uint32(nvp, &val_uint32); 1139 node->hp_last_change = (time_t)val_uint32; 1140 1141 } else { 1142 i_hp_dprintf("i_hp_unpack_node: unrecognized: '%s'\n", 1143 nvpair_name(nvp)); 1144 } 1145 } 1146 1147 /* Unpacked nvlist no longer needed */ 1148 nvlist_free(nvl); 1149 1150 /* Check for errors */ 1151 if (rv != 0) { 1152 hp_fini(node); 1153 return (rv); 1154 } 1155 1156 /* Success */ 1157 node->hp_parent = parent; 1158 *retp = node; 1159 return (0); 1160 } 1161 1162 /* 1163 * i_hp_call_hotplugd() 1164 * 1165 * Perform a door call to the hotplug daemon. 1166 */ 1167 static int 1168 i_hp_call_hotplugd(nvlist_t *args, nvlist_t **resultsp) 1169 { 1170 door_arg_t door_arg; 1171 nvlist_t *results = NULL; 1172 char *buf = NULL; 1173 size_t len = 0; 1174 uint64_t seqnum; 1175 int door_fd; 1176 int rv; 1177 1178 /* Initialize results */ 1179 *resultsp = NULL; 1180 1181 /* Open door */ 1182 if ((door_fd = open(HOTPLUGD_DOOR, O_RDONLY)) < 0) { 1183 i_hp_dprintf("i_hp_call_hotplugd: cannot open door (%s)\n", 1184 strerror(errno)); 1185 return (EBADF); 1186 } 1187 1188 /* Pack the nvlist of arguments */ 1189 if ((rv = nvlist_pack(args, &buf, &len, NV_ENCODE_NATIVE, 0)) != 0) { 1190 i_hp_dprintf("i_hp_call_hotplugd: cannot pack arguments (%s)\n", 1191 strerror(rv)); 1192 return (rv); 1193 } 1194 1195 /* Set the door argument using the packed arguments */ 1196 door_arg.data_ptr = buf; 1197 door_arg.data_size = len; 1198 door_arg.desc_ptr = NULL; 1199 door_arg.desc_num = 0; 1200 door_arg.rbuf = (char *)(uintptr_t)&rv; 1201 door_arg.rsize = sizeof (rv); 1202 1203 /* Attempt the door call */ 1204 if (door_call(door_fd, &door_arg) != 0) { 1205 rv = errno; 1206 i_hp_dprintf("i_hp_call_hotplugd: door call failed (%s)\n", 1207 strerror(rv)); 1208 (void) close(door_fd); 1209 free(buf); 1210 return (rv); 1211 } 1212 1213 /* The arguments are no longer needed */ 1214 free(buf); 1215 1216 /* 1217 * If results are not in the original buffer provided, 1218 * then check and process the new results buffer. 1219 */ 1220 if (door_arg.rbuf != (char *)(uintptr_t)&rv) { 1221 1222 /* 1223 * First check that the buffer is valid. Then check for 1224 * the simple case where a short result code was sent. 1225 * The last case is a packed nvlist was returned, which 1226 * needs to be unpacked. 1227 */ 1228 if ((door_arg.rbuf == NULL) || 1229 (door_arg.data_size < sizeof (rv))) { 1230 i_hp_dprintf("i_hp_call_hotplugd: invalid results.\n"); 1231 rv = EFAULT; 1232 1233 } else if (door_arg.data_size == sizeof (rv)) { 1234 rv = *(int *)(uintptr_t)door_arg.rbuf; 1235 1236 } else if ((rv = nvlist_unpack(door_arg.rbuf, 1237 door_arg.data_size, &results, 0)) != 0) { 1238 i_hp_dprintf("i_hp_call_hotplugd: " 1239 "cannot unpack results (%s).\n", strerror(rv)); 1240 results = NULL; 1241 rv = EFAULT; 1242 } 1243 1244 /* Unmap the results buffer */ 1245 if (door_arg.rbuf != NULL) 1246 (void) munmap(door_arg.rbuf, door_arg.rsize); 1247 1248 /* 1249 * In the case of a packed nvlist, notify the daemon 1250 * that it can free the result buffer from its heap. 1251 */ 1252 if ((results != NULL) && 1253 (nvlist_lookup_uint64(results, HPD_SEQNUM, &seqnum) == 0)) { 1254 door_arg.data_ptr = (char *)(uintptr_t)&seqnum; 1255 door_arg.data_size = sizeof (seqnum); 1256 door_arg.desc_ptr = NULL; 1257 door_arg.desc_num = 0; 1258 door_arg.rbuf = NULL; 1259 door_arg.rsize = 0; 1260 (void) door_call(door_fd, &door_arg); 1261 if (door_arg.rbuf != NULL) 1262 (void) munmap(door_arg.rbuf, door_arg.rsize); 1263 } 1264 1265 *resultsp = results; 1266 } 1267 1268 (void) close(door_fd); 1269 return (rv); 1270 } 1271 1272 /* 1273 * i_hp_set_args() 1274 * 1275 * Construct an nvlist of arguments for a hotplugd door call. 1276 */ 1277 static nvlist_t * 1278 i_hp_set_args(hp_cmd_t cmd, const char *path, const char *connection, 1279 uint_t flags, const char *options, int state) 1280 { 1281 nvlist_t *args; 1282 1283 /* Allocate a new nvlist */ 1284 if (nvlist_alloc(&args, NV_UNIQUE_NAME_TYPE, 0) != 0) 1285 return (NULL); 1286 1287 /* Add common arguments */ 1288 if ((nvlist_add_int32(args, HPD_CMD, cmd) != 0) || 1289 (nvlist_add_string(args, HPD_PATH, path) != 0)) { 1290 nvlist_free(args); 1291 return (NULL); 1292 } 1293 1294 /* Add connection, but only if defined */ 1295 if ((connection != NULL) && (connection[0] != '\0') && 1296 (nvlist_add_string(args, HPD_CONNECTION, connection) != 0)) { 1297 nvlist_free(args); 1298 return (NULL); 1299 } 1300 1301 /* Add flags, but only if defined */ 1302 if ((flags != 0) && (nvlist_add_uint32(args, HPD_FLAGS, flags) != 0)) { 1303 nvlist_free(args); 1304 return (NULL); 1305 } 1306 1307 /* Add options, but only if defined */ 1308 if ((options != NULL) && 1309 (nvlist_add_string(args, HPD_OPTIONS, options) != 0)) { 1310 nvlist_free(args); 1311 return (NULL); 1312 } 1313 1314 /* Add state, but only for CHANGESTATE command */ 1315 if ((cmd == HP_CMD_CHANGESTATE) && 1316 (nvlist_add_int32(args, HPD_STATE, state) != 0)) { 1317 nvlist_free(args); 1318 return (NULL); 1319 } 1320 1321 return (args); 1322 } 1323 1324 /* 1325 * i_hp_parse_results() 1326 * 1327 * Parse out individual fields of an nvlist of results from 1328 * a hotplugd door call. 1329 */ 1330 static int 1331 i_hp_parse_results(nvlist_t *results, hp_node_t *rootp, char **optionsp) 1332 { 1333 int rv; 1334 1335 /* Parse an information snapshot */ 1336 if (rootp) { 1337 char *buf = NULL; 1338 size_t len = 0; 1339 1340 *rootp = NULL; 1341 if (nvlist_lookup_byte_array(results, HPD_INFO, 1342 (uchar_t **)&buf, (uint_t *)&len) == 0) { 1343 if ((rv = hp_unpack(buf, len, rootp)) != 0) 1344 return (rv); 1345 } 1346 } 1347 1348 /* Parse a bus private option string */ 1349 if (optionsp) { 1350 char *str; 1351 1352 *optionsp = NULL; 1353 if ((nvlist_lookup_string(results, HPD_OPTIONS, &str) == 0) && 1354 ((*optionsp = strdup(str)) == NULL)) { 1355 return (ENOMEM); 1356 } 1357 } 1358 1359 /* Parse result code of the operation */ 1360 if (nvlist_lookup_int32(results, HPD_STATUS, &rv) != 0) { 1361 i_hp_dprintf("i_hp_call_hotplugd: missing status.\n"); 1362 return (EFAULT); 1363 } 1364 1365 return (rv); 1366 }