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 }