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; 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 (NULL);
 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 }