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 (0); 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 (0); 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 = NULL; 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; parent != NULL; parent = parent->hp_parent) { 467 if (parent->hp_type == HP_NODE_DEVICE) { 468 (void) strlcat(components, "/", MAXPATHLEN); 469 (void) strlcat(components, parent->hp_name, MAXPATHLEN); 470 } 471 if (parent->hp_parent == NULL) 472 root = parent; 473 } 474 475 /* Ensure the snapshot actually contains a base path */ 476 if (root->hp_basepath == NULL) { 477 i_hp_dprintf("hp_path: missing base pathname.\n"); 478 return (EFAULT); 479 } 480 481 /* 482 * Construct the path. Start with the base path from the root 483 * node, then append the accumulated components in reverse order. 484 */ 485 if (strcmp(root->hp_basepath, "/") != 0) { 486 (void) strlcat(path, root->hp_basepath, MAXPATHLEN); 487 if ((root->hp_type == HP_NODE_DEVICE) && 488 ((s = strrchr(path, '/')) != NULL)) 489 *s = '\0'; 490 } 491 for (i = strlen(components) - 1; i >= 0; i--) { 492 if (components[i] == '/') { 493 (void) strlcat(path, &components[i], MAXPATHLEN); 494 components[i] = '\0'; 495 } 496 } 497 498 return (0); 499 } 500 501 /* 502 * hp_set_state() 503 * 504 * Initiate a state change operation on a node. 505 */ 506 int 507 hp_set_state(hp_node_t node, uint_t flags, int state, hp_node_t *resultsp) 508 { 509 hp_node_t root = NULL; 510 nvlist_t *args; 511 nvlist_t *results; 512 int rv; 513 char path[MAXPATHLEN]; 514 char connection[MAXPATHLEN]; 515 516 i_hp_dprintf("hp_set_state: node=%p, flags=0x%x, state=0x%x, " 517 "resultsp=%p\n", (void *)node, flags, state, (void *)resultsp); 518 519 /* Check arguments */ 520 if ((node == NULL) || (resultsp == NULL) || 521 !HP_SET_STATE_FLAGS_VALID(flags)) { 522 i_hp_dprintf("hp_set_state: invalid arguments.\n"); 523 return (EINVAL); 524 } 525 526 /* Check node type */ 527 if ((node->hp_type != HP_NODE_CONNECTOR) && 528 (node->hp_type != HP_NODE_PORT)) { 529 i_hp_dprintf("hp_set_state: operation not supported.\n"); 530 return (ENOTSUP); 531 } 532 533 /* Check that target state is valid */ 534 switch (state) { 535 case DDI_HP_CN_STATE_PRESENT: 536 case DDI_HP_CN_STATE_POWERED: 537 case DDI_HP_CN_STATE_ENABLED: 538 if (node->hp_type != HP_NODE_CONNECTOR) { 539 i_hp_dprintf("hp_set_state: mismatched target.\n"); 540 return (ENOTSUP); 541 } 542 break; 543 case DDI_HP_CN_STATE_PORT_PRESENT: 544 case DDI_HP_CN_STATE_OFFLINE: 545 case DDI_HP_CN_STATE_ONLINE: 546 if (node->hp_type != HP_NODE_PORT) { 547 i_hp_dprintf("hp_set_state: mismatched target.\n"); 548 return (ENOTSUP); 549 } 550 break; 551 default: 552 i_hp_dprintf("hp_set_state: invalid target state.\n"); 553 return (EINVAL); 554 } 555 556 /* Get path and connection of specified node */ 557 if ((rv = hp_path(node, path, connection)) != 0) 558 return (rv); 559 560 /* Build arguments for door call */ 561 if ((args = i_hp_set_args(HP_CMD_CHANGESTATE, path, connection, flags, 562 NULL, state)) == NULL) 563 return (ENOMEM); 564 565 /* Make the door call to hotplugd */ 566 rv = i_hp_call_hotplugd(args, &results); 567 568 /* Arguments no longer needed */ 569 nvlist_free(args); 570 571 /* Parse additional results, if any */ 572 if ((rv == 0) && (results != NULL)) { 573 rv = i_hp_parse_results(results, &root, NULL); 574 nvlist_free(results); 575 *resultsp = root; 576 } 577 578 /* Done */ 579 return (rv); 580 } 581 582 /* 583 * hp_set_private() 584 * 585 * Set bus private options on the hotplug connection 586 * indicated by the given hotplug information node. 587 */ 588 int 589 hp_set_private(hp_node_t node, const char *options, char **resultsp) 590 { 591 int rv; 592 nvlist_t *args; 593 nvlist_t *results; 594 char *values = NULL; 595 char path[MAXPATHLEN]; 596 char connection[MAXPATHLEN]; 597 598 i_hp_dprintf("hp_set_private: node=%p, options=%p, resultsp=%p\n", 599 (void *)node, (void *)options, (void *)resultsp); 600 601 /* Check arguments */ 602 if ((node == NULL) || (options == NULL) || (resultsp == NULL)) { 603 i_hp_dprintf("hp_set_private: invalid arguments.\n"); 604 return (EINVAL); 605 } 606 607 /* Check node type */ 608 if (node->hp_type != HP_NODE_CONNECTOR) { 609 i_hp_dprintf("hp_set_private: operation not supported.\n"); 610 return (ENOTSUP); 611 } 612 613 /* Initialize results */ 614 *resultsp = NULL; 615 616 /* Get path and connection of specified node */ 617 if ((rv = hp_path(node, path, connection)) != 0) 618 return (rv); 619 620 /* Build arguments for door call */ 621 if ((args = i_hp_set_args(HP_CMD_SETPRIVATE, path, connection, 0, 622 options, 0)) == NULL) 623 return (ENOMEM); 624 625 /* Make the door call to hotplugd */ 626 rv = i_hp_call_hotplugd(args, &results); 627 628 /* Arguments no longer needed */ 629 nvlist_free(args); 630 631 /* Parse additional results, if any */ 632 if ((rv == 0) && (results != NULL)) { 633 rv = i_hp_parse_results(results, NULL, &values); 634 nvlist_free(results); 635 *resultsp = values; 636 } 637 638 /* Done */ 639 return (rv); 640 } 641 642 /* 643 * hp_get_private() 644 * 645 * Get bus private options on the hotplug connection 646 * indicated by the given hotplug information node. 647 */ 648 int 649 hp_get_private(hp_node_t node, const char *options, char **resultsp) 650 { 651 int rv; 652 nvlist_t *args; 653 nvlist_t *results; 654 char *values = NULL; 655 char path[MAXPATHLEN]; 656 char connection[MAXPATHLEN]; 657 658 i_hp_dprintf("hp_get_private: node=%p, options=%p, resultsp=%p\n", 659 (void *)node, (void *)options, (void *)resultsp); 660 661 /* Check arguments */ 662 if ((node == NULL) || (options == NULL) || (resultsp == NULL)) { 663 i_hp_dprintf("hp_get_private: invalid arguments.\n"); 664 return (EINVAL); 665 } 666 667 /* Check node type */ 668 if (node->hp_type != HP_NODE_CONNECTOR) { 669 i_hp_dprintf("hp_get_private: operation not supported.\n"); 670 return (ENOTSUP); 671 } 672 673 /* Initialize results */ 674 *resultsp = NULL; 675 676 /* Get path and connection of specified node */ 677 if ((rv = hp_path(node, path, connection)) != 0) 678 return (rv); 679 680 /* Build arguments for door call */ 681 if ((args = i_hp_set_args(HP_CMD_GETPRIVATE, path, connection, 0, 682 options, 0)) == NULL) 683 return (ENOMEM); 684 685 /* Make the door call to hotplugd */ 686 rv = i_hp_call_hotplugd(args, &results); 687 688 /* Arguments no longer needed */ 689 nvlist_free(args); 690 691 /* Parse additional results, if any */ 692 if ((rv == 0) && (results != NULL)) { 693 rv = i_hp_parse_results(results, NULL, &values); 694 nvlist_free(results); 695 *resultsp = values; 696 } 697 698 /* Done */ 699 return (rv); 700 } 701 702 /* 703 * hp_pack() 704 * 705 * Given the root of a hotplug information snapshot, pack 706 * it into a contiguous byte array so that it is suitable 707 * for network transport. 708 */ 709 int 710 hp_pack(hp_node_t root, char **bufp, size_t *lenp) 711 { 712 hp_node_t node; 713 nvlist_t *nvl; 714 char *buf; 715 size_t len; 716 int rv; 717 718 i_hp_dprintf("hp_pack: root=%p, bufp=%p, lenp=%p\n", (void *)root, 719 (void *)bufp, (void *)lenp); 720 721 if ((root == NULL) || (bufp == NULL) || (lenp == NULL)) { 722 i_hp_dprintf("hp_pack: invalid arguments.\n"); 723 return (EINVAL); 724 } 725 726 *lenp = 0; 727 *bufp = NULL; 728 729 if (nvlist_alloc(&nvl, 0, 0) != 0) { 730 i_hp_dprintf("hp_pack: nvlist_alloc() failed (%s).\n", 731 strerror(errno)); 732 return (ENOMEM); 733 } 734 735 if (root->hp_basepath != NULL) { 736 rv = nvlist_add_string(nvl, HP_INFO_BASE, root->hp_basepath); 737 if (rv != 0) { 738 nvlist_free(nvl); 739 return (rv); 740 } 741 } 742 743 for (node = root; node != NULL; node = node->hp_sibling) { 744 if ((rv = i_hp_pack_branch(node, &buf, &len)) == 0) { 745 rv = nvlist_add_byte_array(nvl, HP_INFO_BRANCH, 746 (uchar_t *)buf, len); 747 free(buf); 748 } 749 if (rv != 0) { 750 nvlist_free(nvl); 751 return (rv); 752 } 753 } 754 755 len = 0; 756 buf = NULL; 757 if ((rv = nvlist_pack(nvl, &buf, &len, NV_ENCODE_NATIVE, 0)) == 0) { 758 *lenp = len; 759 *bufp = buf; 760 } 761 762 nvlist_free(nvl); 763 764 return (rv); 765 } 766 767 /* 768 * hp_unpack() 769 * 770 * Unpack a hotplug information snapshot for normal usage. 771 */ 772 int 773 hp_unpack(char *packed_buf, size_t packed_len, hp_node_t *retp) 774 { 775 hp_node_t root; 776 hp_node_t root_list = NULL; 777 hp_node_t prev_root = NULL; 778 nvlist_t *nvl = NULL; 779 nvpair_t *nvp; 780 char *basepath = NULL; 781 int rv; 782 783 i_hp_dprintf("hp_unpack: packed_buf=%p, packed_len=%u, retp=%p\n", 784 (void *)packed_buf, (uint32_t)packed_len, (void *)retp); 785 786 if ((packed_buf == NULL) || (packed_len == 0) || (retp == NULL)) { 787 i_hp_dprintf("hp_unpack: invalid arguments.\n"); 788 return (EINVAL); 789 } 790 791 if ((rv = nvlist_unpack(packed_buf, packed_len, &nvl, 0)) != 0) 792 return (rv); 793 794 if (nvlist_next_nvpair(nvl, NULL) == NULL) { 795 nvlist_free(nvl); 796 errno = EINVAL; 797 return (0); 798 } 799 800 for (nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) { 801 802 rv = EINVAL; 803 804 if (strcmp(nvpair_name(nvp), HP_INFO_BASE) == 0) { 805 char *val_string; 806 807 if ((rv = nvpair_value_string(nvp, &val_string)) == 0) { 808 if ((basepath = strdup(val_string)) == NULL) 809 rv = ENOMEM; 810 } 811 812 } else if (strcmp(nvpair_name(nvp), HP_INFO_BRANCH) == 0) { 813 size_t len = 0; 814 char *buf = NULL; 815 816 if ((rv = nvpair_value_byte_array(nvp, 817 (uchar_t **)&buf, (uint_t *)&len)) == 0) { 818 rv = i_hp_unpack_branch(buf, len, NULL, &root); 819 } 820 821 if (rv == 0) { 822 if (prev_root) { 823 prev_root->hp_sibling = root; 824 } else { 825 root_list = root; 826 } 827 prev_root = root; 828 } 829 } 830 831 if (rv != 0) { 832 if (basepath) 833 free(basepath); 834 nvlist_free(nvl); 835 hp_fini(root_list); 836 *retp = NULL; 837 return (rv); 838 } 839 } 840 841 /* Store the base path in each root node */ 842 if (basepath) { 843 for (root = root_list; root; root = root->hp_sibling) 844 root->hp_basepath = basepath; 845 } 846 847 nvlist_free(nvl); 848 *retp = root_list; 849 return (0); 850 } 851 852 /* 853 * i_hp_dprintf() 854 * 855 * Print debug messages to stderr, but only when the debug flag 856 * (libhotplug_debug) is set. 857 */ 858 /*PRINTFLIKE1*/ 859 static void 860 i_hp_dprintf(const char *fmt, ...) 861 { 862 va_list ap; 863 864 if (libhotplug_debug) { 865 va_start(ap, fmt); 866 (void) vfprintf(stderr, fmt, ap); 867 va_end(ap); 868 } 869 } 870 871 /* 872 * i_hp_pack_branch() 873 * 874 * Pack an individual branch of a hotplug information snapshot. 875 */ 876 static int 877 i_hp_pack_branch(hp_node_t root, char **bufp, size_t *lenp) 878 { 879 hp_node_t child; 880 nvlist_t *nvl; 881 char *buf; 882 size_t len; 883 int rv; 884 885 *lenp = 0; 886 *bufp = NULL; 887 888 /* Allocate an nvlist for this branch */ 889 if (nvlist_alloc(&nvl, 0, 0) != 0) 890 return (ENOMEM); 891 892 /* Pack the root of the branch and add it to the nvlist */ 893 if ((rv = i_hp_pack_node(root, &buf, &len)) == 0) { 894 rv = nvlist_add_byte_array(nvl, HP_INFO_NODE, 895 (uchar_t *)buf, len); 896 free(buf); 897 } 898 if (rv != 0) { 899 nvlist_free(nvl); 900 return (rv); 901 } 902 903 /* Pack each subordinate branch, and add it to the nvlist */ 904 for (child = root->hp_child; child != NULL; child = child->hp_sibling) { 905 if ((rv = i_hp_pack_branch(child, &buf, &len)) == 0) { 906 rv = nvlist_add_byte_array(nvl, HP_INFO_BRANCH, 907 (uchar_t *)buf, len); 908 free(buf); 909 } 910 if (rv != 0) { 911 nvlist_free(nvl); 912 return (rv); 913 } 914 } 915 916 /* Pack the resulting nvlist into a single buffer */ 917 len = 0; 918 buf = NULL; 919 if ((rv = nvlist_pack(nvl, &buf, &len, NV_ENCODE_NATIVE, 0)) == 0) { 920 *lenp = len; 921 *bufp = buf; 922 } 923 924 /* Free the nvlist */ 925 nvlist_free(nvl); 926 927 return (rv); 928 } 929 930 /* 931 * i_hp_pack_node() 932 * 933 * Pack an individual node of a hotplug information snapshot. 934 */ 935 static int 936 i_hp_pack_node(hp_node_t node, char **bufp, size_t *lenp) 937 { 938 nvlist_t *nvl; 939 char *buf = NULL; 940 size_t len = 0; 941 int rv; 942 943 if (nvlist_alloc(&nvl, 0, 0) != 0) 944 return (ENOMEM); 945 946 if ((rv = nvlist_add_uint32(nvl, HP_INFO_TYPE, 947 (uint32_t)node->hp_type)) != 0) 948 goto fail; 949 950 if ((node->hp_name) && 951 ((rv = nvlist_add_string(nvl, HP_INFO_NAME, node->hp_name)) != 0)) 952 goto fail; 953 954 if ((node->hp_usage) && 955 ((rv = nvlist_add_string(nvl, HP_INFO_USAGE, node->hp_usage)) != 0)) 956 goto fail; 957 958 if ((node->hp_description) && 959 ((rv = nvlist_add_string(nvl, HP_INFO_DESC, 960 node->hp_description)) != 0)) 961 goto fail; 962 963 if ((rv = nvlist_add_uint32(nvl, HP_INFO_STATE, node->hp_state)) != 0) 964 goto fail; 965 966 if ((node->hp_last_change != 0) && 967 ((rv = nvlist_add_uint32(nvl, HP_INFO_TIME, 968 node->hp_last_change)) != 0)) 969 goto fail; 970 971 if ((rv = nvlist_pack(nvl, &buf, &len, NV_ENCODE_NATIVE, 0)) != 0) 972 goto fail; 973 974 *bufp = buf; 975 *lenp = len; 976 nvlist_free(nvl); 977 return (0); 978 979 fail: 980 *bufp = NULL; 981 *lenp = 0; 982 nvlist_free(nvl); 983 return (rv); 984 } 985 986 /* 987 * i_hp_unpack_branch() 988 * 989 * Unpack a branch of hotplug information nodes. 990 */ 991 static int 992 i_hp_unpack_branch(char *packed_buf, size_t packed_len, hp_node_t parent, 993 hp_node_t *retp) 994 { 995 hp_node_t node = NULL; 996 hp_node_t child; 997 hp_node_t prev_child = NULL; 998 nvlist_t *nvl = NULL; 999 nvpair_t *nvp; 1000 char *buf; 1001 size_t len; 1002 int rv; 1003 1004 /* Initialize results */ 1005 *retp = NULL; 1006 1007 /* Unpack the nvlist for this branch */ 1008 if ((rv = nvlist_unpack(packed_buf, packed_len, &nvl, 0)) != 0) 1009 return (rv); 1010 1011 /* 1012 * Unpack the branch. The first item in the nvlist is 1013 * always the root node. And zero or more subordinate 1014 * branches may be packed afterward. 1015 */ 1016 for (nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) { 1017 1018 len = 0; 1019 buf = NULL; 1020 1021 if (strcmp(nvpair_name(nvp), HP_INFO_NODE) == 0) { 1022 1023 /* Check that there is only one root node */ 1024 if (node != NULL) { 1025 hp_fini(node); 1026 nvlist_free(nvl); 1027 return (EFAULT); 1028 } 1029 1030 if ((rv = nvpair_value_byte_array(nvp, (uchar_t **)&buf, 1031 (uint_t *)&len)) == 0) 1032 rv = i_hp_unpack_node(buf, len, parent, &node); 1033 1034 if (rv != 0) { 1035 nvlist_free(nvl); 1036 return (rv); 1037 } 1038 1039 } else if (strcmp(nvpair_name(nvp), HP_INFO_BRANCH) == 0) { 1040 1041 if ((rv = nvpair_value_byte_array(nvp, (uchar_t **)&buf, 1042 (uint_t *)&len)) == 0) 1043 rv = i_hp_unpack_branch(buf, len, node, &child); 1044 1045 if (rv != 0) { 1046 hp_fini(node); 1047 nvlist_free(nvl); 1048 return (rv); 1049 } 1050 1051 if (prev_child) { 1052 prev_child->hp_sibling = child; 1053 } else { 1054 node->hp_child = child; 1055 } 1056 prev_child = child; 1057 } 1058 } 1059 1060 nvlist_free(nvl); 1061 *retp = node; 1062 return (0); 1063 } 1064 1065 /* 1066 * i_hp_unpack_node() 1067 * 1068 * Unpack an individual hotplug information node. 1069 */ 1070 static int 1071 i_hp_unpack_node(char *buf, size_t len, hp_node_t parent, hp_node_t *retp) 1072 { 1073 hp_node_t node; 1074 nvlist_t *nvl; 1075 nvpair_t *nvp; 1076 uint32_t val_uint32; 1077 char *val_string; 1078 int rv = 0; 1079 1080 /* Initialize results */ 1081 *retp = NULL; 1082 1083 /* Unpack node into an nvlist */ 1084 if ((nvlist_unpack(buf, len, &nvl, 0) != 0)) 1085 return (EINVAL); 1086 1087 /* Allocate the new node */ 1088 if ((node = (hp_node_t)calloc(1, sizeof (struct hp_node))) == NULL) { 1089 nvlist_free(nvl); 1090 return (ENOMEM); 1091 } 1092 1093 /* Iterate through nvlist, unpacking each field */ 1094 for (nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) { 1095 1096 if ((strcmp(nvpair_name(nvp), HP_INFO_TYPE) == 0) && 1097 (nvpair_type(nvp) == DATA_TYPE_UINT32)) { 1098 1099 (void) nvpair_value_uint32(nvp, &val_uint32); 1100 node->hp_type = val_uint32; 1101 1102 } else if ((strcmp(nvpair_name(nvp), HP_INFO_NAME) == 0) && 1103 (nvpair_type(nvp) == DATA_TYPE_STRING)) { 1104 1105 (void) nvpair_value_string(nvp, &val_string); 1106 if ((node->hp_name = strdup(val_string)) == NULL) { 1107 rv = ENOMEM; 1108 break; 1109 } 1110 1111 } else if ((strcmp(nvpair_name(nvp), HP_INFO_STATE) == 0) && 1112 (nvpair_type(nvp) == DATA_TYPE_UINT32)) { 1113 1114 (void) nvpair_value_uint32(nvp, &val_uint32); 1115 node->hp_state = val_uint32; 1116 1117 } else if ((strcmp(nvpair_name(nvp), HP_INFO_USAGE) == 0) && 1118 (nvpair_type(nvp) == DATA_TYPE_STRING)) { 1119 1120 (void) nvpair_value_string(nvp, &val_string); 1121 if ((node->hp_usage = strdup(val_string)) == NULL) { 1122 rv = ENOMEM; 1123 break; 1124 } 1125 1126 } else if ((strcmp(nvpair_name(nvp), HP_INFO_DESC) == 0) && 1127 (nvpair_type(nvp) == DATA_TYPE_STRING)) { 1128 1129 (void) nvpair_value_string(nvp, &val_string); 1130 if ((node->hp_description = strdup(val_string)) 1131 == NULL) { 1132 rv = ENOMEM; 1133 break; 1134 } 1135 1136 } else if ((strcmp(nvpair_name(nvp), HP_INFO_TIME) == 0) && 1137 (nvpair_type(nvp) == DATA_TYPE_UINT32)) { 1138 1139 (void) nvpair_value_uint32(nvp, &val_uint32); 1140 node->hp_last_change = (time_t)val_uint32; 1141 1142 } else { 1143 i_hp_dprintf("i_hp_unpack_node: unrecognized: '%s'\n", 1144 nvpair_name(nvp)); 1145 } 1146 } 1147 1148 /* Unpacked nvlist no longer needed */ 1149 nvlist_free(nvl); 1150 1151 /* Check for errors */ 1152 if (rv != 0) { 1153 hp_fini(node); 1154 return (rv); 1155 } 1156 1157 /* Success */ 1158 node->hp_parent = parent; 1159 *retp = node; 1160 return (0); 1161 } 1162 1163 /* 1164 * i_hp_call_hotplugd() 1165 * 1166 * Perform a door call to the hotplug daemon. 1167 */ 1168 static int 1169 i_hp_call_hotplugd(nvlist_t *args, nvlist_t **resultsp) 1170 { 1171 door_arg_t door_arg; 1172 nvlist_t *results = NULL; 1173 char *buf = NULL; 1174 size_t len = 0; 1175 uint64_t seqnum; 1176 int door_fd; 1177 int rv; 1178 1179 /* Initialize results */ 1180 *resultsp = NULL; 1181 1182 /* Open door */ 1183 if ((door_fd = open(HOTPLUGD_DOOR, O_RDONLY)) < 0) { 1184 i_hp_dprintf("i_hp_call_hotplugd: cannot open door (%s)\n", 1185 strerror(errno)); 1186 return (EBADF); 1187 } 1188 1189 /* Pack the nvlist of arguments */ 1190 if ((rv = nvlist_pack(args, &buf, &len, NV_ENCODE_NATIVE, 0)) != 0) { 1191 i_hp_dprintf("i_hp_call_hotplugd: cannot pack arguments (%s)\n", 1192 strerror(rv)); 1193 return (rv); 1194 } 1195 1196 /* Set the door argument using the packed arguments */ 1197 door_arg.data_ptr = buf; 1198 door_arg.data_size = len; 1199 door_arg.desc_ptr = NULL; 1200 door_arg.desc_num = 0; 1201 door_arg.rbuf = (char *)(uintptr_t)&rv; 1202 door_arg.rsize = sizeof (rv); 1203 1204 /* Attempt the door call */ 1205 if (door_call(door_fd, &door_arg) != 0) { 1206 rv = errno; 1207 i_hp_dprintf("i_hp_call_hotplugd: door call failed (%s)\n", 1208 strerror(rv)); 1209 (void) close(door_fd); 1210 free(buf); 1211 return (rv); 1212 } 1213 1214 /* The arguments are no longer needed */ 1215 free(buf); 1216 1217 /* 1218 * If results are not in the original buffer provided, 1219 * then check and process the new results buffer. 1220 */ 1221 if (door_arg.rbuf != (char *)(uintptr_t)&rv) { 1222 1223 /* 1224 * First check that the buffer is valid. Then check for 1225 * the simple case where a short result code was sent. 1226 * The last case is a packed nvlist was returned, which 1227 * needs to be unpacked. 1228 */ 1229 if ((door_arg.rbuf == NULL) || 1230 (door_arg.data_size < sizeof (rv))) { 1231 i_hp_dprintf("i_hp_call_hotplugd: invalid results.\n"); 1232 rv = EFAULT; 1233 1234 } else if (door_arg.data_size == sizeof (rv)) { 1235 rv = *(int *)(uintptr_t)door_arg.rbuf; 1236 1237 } else if ((rv = nvlist_unpack(door_arg.rbuf, 1238 door_arg.data_size, &results, 0)) != 0) { 1239 i_hp_dprintf("i_hp_call_hotplugd: " 1240 "cannot unpack results (%s).\n", strerror(rv)); 1241 results = NULL; 1242 rv = EFAULT; 1243 } 1244 1245 /* Unmap the results buffer */ 1246 if (door_arg.rbuf != NULL) 1247 (void) munmap(door_arg.rbuf, door_arg.rsize); 1248 1249 /* 1250 * In the case of a packed nvlist, notify the daemon 1251 * that it can free the result buffer from its heap. 1252 */ 1253 if ((results != NULL) && 1254 (nvlist_lookup_uint64(results, HPD_SEQNUM, &seqnum) == 0)) { 1255 door_arg.data_ptr = (char *)(uintptr_t)&seqnum; 1256 door_arg.data_size = sizeof (seqnum); 1257 door_arg.desc_ptr = NULL; 1258 door_arg.desc_num = 0; 1259 door_arg.rbuf = NULL; 1260 door_arg.rsize = 0; 1261 (void) door_call(door_fd, &door_arg); 1262 if (door_arg.rbuf != NULL) 1263 (void) munmap(door_arg.rbuf, door_arg.rsize); 1264 } 1265 1266 *resultsp = results; 1267 } 1268 1269 (void) close(door_fd); 1270 return (rv); 1271 } 1272 1273 /* 1274 * i_hp_set_args() 1275 * 1276 * Construct an nvlist of arguments for a hotplugd door call. 1277 */ 1278 static nvlist_t * 1279 i_hp_set_args(hp_cmd_t cmd, const char *path, const char *connection, 1280 uint_t flags, const char *options, int state) 1281 { 1282 nvlist_t *args; 1283 1284 /* Allocate a new nvlist */ 1285 if (nvlist_alloc(&args, NV_UNIQUE_NAME_TYPE, 0) != 0) 1286 return (NULL); 1287 1288 /* Add common arguments */ 1289 if ((nvlist_add_int32(args, HPD_CMD, cmd) != 0) || 1290 (nvlist_add_string(args, HPD_PATH, path) != 0)) { 1291 nvlist_free(args); 1292 return (NULL); 1293 } 1294 1295 /* Add connection, but only if defined */ 1296 if ((connection != NULL) && (connection[0] != '\0') && 1297 (nvlist_add_string(args, HPD_CONNECTION, connection) != 0)) { 1298 nvlist_free(args); 1299 return (NULL); 1300 } 1301 1302 /* Add flags, but only if defined */ 1303 if ((flags != 0) && (nvlist_add_uint32(args, HPD_FLAGS, flags) != 0)) { 1304 nvlist_free(args); 1305 return (NULL); 1306 } 1307 1308 /* Add options, but only if defined */ 1309 if ((options != NULL) && 1310 (nvlist_add_string(args, HPD_OPTIONS, options) != 0)) { 1311 nvlist_free(args); 1312 return (NULL); 1313 } 1314 1315 /* Add state, but only for CHANGESTATE command */ 1316 if ((cmd == HP_CMD_CHANGESTATE) && 1317 (nvlist_add_int32(args, HPD_STATE, state) != 0)) { 1318 nvlist_free(args); 1319 return (NULL); 1320 } 1321 1322 return (args); 1323 } 1324 1325 /* 1326 * i_hp_parse_results() 1327 * 1328 * Parse out individual fields of an nvlist of results from 1329 * a hotplugd door call. 1330 */ 1331 static int 1332 i_hp_parse_results(nvlist_t *results, hp_node_t *rootp, char **optionsp) 1333 { 1334 int rv; 1335 1336 /* Parse an information snapshot */ 1337 if (rootp) { 1338 char *buf = NULL; 1339 size_t len = 0; 1340 1341 *rootp = NULL; 1342 if (nvlist_lookup_byte_array(results, HPD_INFO, 1343 (uchar_t **)&buf, (uint_t *)&len) == 0) { 1344 if ((rv = hp_unpack(buf, len, rootp)) != 0) 1345 return (rv); 1346 } 1347 } 1348 1349 /* Parse a bus private option string */ 1350 if (optionsp) { 1351 char *str; 1352 1353 *optionsp = NULL; 1354 if ((nvlist_lookup_string(results, HPD_OPTIONS, &str) == 0) && 1355 ((*optionsp = strdup(str)) == NULL)) { 1356 return (ENOMEM); 1357 } 1358 } 1359 1360 /* Parse result code of the operation */ 1361 if (nvlist_lookup_int32(results, HPD_STATUS, &rv) != 0) { 1362 i_hp_dprintf("i_hp_call_hotplugd: missing status.\n"); 1363 return (EFAULT); 1364 } 1365 1366 return (rv); 1367 } 1368