8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / libhotplug / common / libhotplug.c
blob91609f457a022d395f918139ecc7eab20395067b
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
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>
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 **);
51 * Global flag to enable debug features.
53 int libhotplug_debug = 0;
56 * hp_init()
58 * Initialize a hotplug information snapshot.
60 hp_node_t
61 hp_init(const char *path, const char *connection, uint_t flags)
63 nvlist_t *args;
64 nvlist_t *results;
65 hp_node_t root = NULL;
66 int rv;
68 i_hp_dprintf("hp_init: path=%p, connection=%p, flags=0x%x\n",
69 (void *)path, (void *)connection, flags);
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);
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);
86 /* Make the door call to hotplugd */
87 rv = i_hp_call_hotplugd(args, &results);
89 /* Arguments no longer needed */
90 nvlist_free(args);
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);
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);
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);
114 /* Success */
115 return (root);
119 * hp_fini()
121 * Terminate and clean-up a hotplug information snapshot.
123 void
124 hp_fini(hp_node_t root)
126 hp_node_t node;
127 hp_node_t sibling;
128 char *basepath;
130 i_hp_dprintf("hp_fini: root=%p\n", (void *)root);
132 if (root == NULL) {
133 i_hp_dprintf("hp_fini: invalid arguments.\n");
134 return;
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);
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;
163 * hp_traverse()
165 * Walk a graph of hotplug nodes, executing a callback on each node.
168 hp_traverse(hp_node_t root, void *arg, int (*hp_callback)(hp_node_t, void *arg))
170 int rv;
171 hp_node_t node;
173 i_hp_dprintf("hp_traverse: root=%p, arg=%p, hp_callback=%p\n",
174 (void *)root, arg, (void *)hp_callback);
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);
183 for (node = root; node; node = node->hp_sibling) {
184 rv = hp_callback(node, arg);
186 if (rv == HP_WALK_TERMINATE) {
187 i_hp_dprintf("hp_traverse: walk terminated.\n");
188 return (HP_WALK_TERMINATE);
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);
198 if (rv == HP_WALK_PRUNESIBLING)
199 break;
202 return (0);
206 * hp_type()
208 * Return a node's type.
211 hp_type(hp_node_t node)
213 i_hp_dprintf("hp_type: node=%p\n", (void *)node);
215 if (node == NULL) {
216 i_hp_dprintf("hp_type: invalid arguments.\n");
217 errno = EINVAL;
218 return (-1);
221 return (node->hp_type);
225 * hp_name()
227 * Return a node's name.
229 char *
230 hp_name(hp_node_t node)
232 i_hp_dprintf("hp_name: node=%p\n", (void *)node);
234 if (node == NULL) {
235 i_hp_dprintf("hp_name: invalid arguments.\n");
236 errno = EINVAL;
237 return (NULL);
240 if (node->hp_name == NULL) {
241 i_hp_dprintf("hp_name: missing name value.\n");
242 errno = EFAULT;
245 return (node->hp_name);
249 * hp_state()
251 * Return a node's current state.
254 hp_state(hp_node_t node)
256 i_hp_dprintf("hp_state: node=%p\n", (void *)node);
258 if (node == NULL) {
259 i_hp_dprintf("hp_state: invalid arguments.\n");
260 errno = EINVAL;
261 return (-1);
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);
271 return (node->hp_state);
275 * hp_usage()
277 * Return a usage description for usage nodes.
279 char *
280 hp_usage(hp_node_t node)
282 i_hp_dprintf("hp_usage: node=%p\n", (void *)node);
284 if (node == NULL) {
285 i_hp_dprintf("hp_usage: invalid arguments.\n");
286 errno = EINVAL;
287 return (NULL);
290 if (node->hp_type != HP_NODE_USAGE) {
291 i_hp_dprintf("hp_usage: operation not supported.\n");
292 errno = ENOTSUP;
293 return (NULL);
296 if (node->hp_usage == NULL) {
297 i_hp_dprintf("hp_usage: missing usage value.\n");
298 errno = EFAULT;
301 return (node->hp_usage);
305 * hp_description()
307 * Return a type description (e.g. "PCI slot") for connection nodes.
309 char *
310 hp_description(hp_node_t node)
312 i_hp_dprintf("hp_description: node=%p\n", (void *)node);
314 if (node == NULL) {
315 i_hp_dprintf("hp_description: invalid arguments.\n");
316 errno = EINVAL;
317 return (NULL);
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);
327 if (node->hp_description == NULL) {
328 i_hp_dprintf("hp_description: missing description value.\n");
329 errno = EFAULT;
332 return (node->hp_description);
336 * hp_last_change()
338 * Return when the state of a connection was last changed.
340 time_t
341 hp_last_change(hp_node_t node)
343 i_hp_dprintf("hp_last_change: node=%p\n", (void *)node);
345 if (node == NULL) {
346 i_hp_dprintf("hp_last_change: invalid arguments.\n");
347 errno = EINVAL;
348 return (NULL);
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);
358 return (node->hp_last_change);
362 * hp_parent()
364 * Return a node's parent node.
366 hp_node_t
367 hp_parent(hp_node_t node)
369 i_hp_dprintf("hp_parent: node=%p\n", (void *)node);
371 if (node == NULL) {
372 i_hp_dprintf("hp_parent: invalid arguments.\n");
373 errno = EINVAL;
374 return (NULL);
377 if (node->hp_parent == NULL) {
378 i_hp_dprintf("hp_parent: node has no parent.\n");
379 errno = ENXIO;
382 return (node->hp_parent);
386 * hp_child()
388 * Return a node's first child node.
390 hp_node_t
391 hp_child(hp_node_t node)
393 i_hp_dprintf("hp_child: node=%p\n", (void *)node);
395 if (node == NULL) {
396 i_hp_dprintf("hp_child: invalid arguments.\n");
397 errno = EINVAL;
398 return (NULL);
401 if (node->hp_child == NULL) {
402 i_hp_dprintf("hp_child: node has no child.\n");
403 errno = ENXIO;
406 return (node->hp_child);
410 * hp_sibling()
412 * Return a node's next sibling node.
414 hp_node_t
415 hp_sibling(hp_node_t node)
417 i_hp_dprintf("hp_sibling: node=%p\n", (void *)node);
419 if (node == NULL) {
420 i_hp_dprintf("hp_sibling: invalid arguments.\n");
421 errno = EINVAL;
422 return (NULL);
425 if (node->hp_sibling == NULL) {
426 i_hp_dprintf("hp_sibling: node has no sibling.\n");
427 errno = ENXIO;
430 return (node->hp_sibling);
434 * hp_path()
436 * Return the path (and maybe connection name) of a node.
437 * The caller must supply two buffers, each MAXPATHLEN size.
440 hp_path(hp_node_t node, char *path, char *connection)
442 hp_node_t root = NULL;
443 hp_node_t parent;
444 int i;
445 char *s;
446 char components[MAXPATHLEN];
448 i_hp_dprintf("hp_path: node=%p, path=%p, connection=%p\n", (void *)node,
449 (void *)path, (void *)connection);
451 if ((node == NULL) || (path == NULL) || (connection == NULL)) {
452 i_hp_dprintf("hp_path: invalid arguments.\n");
453 return (EINVAL);
456 (void) memset(path, 0, MAXPATHLEN);
457 (void) memset(connection, 0, MAXPATHLEN);
458 (void) memset(components, 0, MAXPATHLEN);
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);
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);
471 if (parent->hp_parent == NULL)
472 root = parent;
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);
482 * Construct the path. Start with the base path from the root
483 * node, then append the accumulated components in reverse order.
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';
491 for (i = strlen(components) - 1; i >= 0; i--) {
492 if (components[i] == '/') {
493 (void) strlcat(path, &components[i], MAXPATHLEN);
494 components[i] = '\0';
498 return (0);
502 * hp_set_state()
504 * Initiate a state change operation on a node.
507 hp_set_state(hp_node_t node, uint_t flags, int state, hp_node_t *resultsp)
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];
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);
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);
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);
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);
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);
550 break;
551 default:
552 i_hp_dprintf("hp_set_state: invalid target state.\n");
553 return (EINVAL);
556 /* Get path and connection of specified node */
557 if ((rv = hp_path(node, path, connection)) != 0)
558 return (rv);
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);
565 /* Make the door call to hotplugd */
566 rv = i_hp_call_hotplugd(args, &results);
568 /* Arguments no longer needed */
569 nvlist_free(args);
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;
578 /* Done */
579 return (rv);
583 * hp_set_private()
585 * Set bus private options on the hotplug connection
586 * indicated by the given hotplug information node.
589 hp_set_private(hp_node_t node, const char *options, char **resultsp)
591 int rv;
592 nvlist_t *args;
593 nvlist_t *results;
594 char *values = NULL;
595 char path[MAXPATHLEN];
596 char connection[MAXPATHLEN];
598 i_hp_dprintf("hp_set_private: node=%p, options=%p, resultsp=%p\n",
599 (void *)node, (void *)options, (void *)resultsp);
601 /* Check arguments */
602 if ((node == NULL) || (options == NULL) || (resultsp == NULL)) {
603 i_hp_dprintf("hp_set_private: invalid arguments.\n");
604 return (EINVAL);
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);
613 /* Initialize results */
614 *resultsp = NULL;
616 /* Get path and connection of specified node */
617 if ((rv = hp_path(node, path, connection)) != 0)
618 return (rv);
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);
625 /* Make the door call to hotplugd */
626 rv = i_hp_call_hotplugd(args, &results);
628 /* Arguments no longer needed */
629 nvlist_free(args);
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;
638 /* Done */
639 return (rv);
643 * hp_get_private()
645 * Get bus private options on the hotplug connection
646 * indicated by the given hotplug information node.
649 hp_get_private(hp_node_t node, const char *options, char **resultsp)
651 int rv;
652 nvlist_t *args;
653 nvlist_t *results;
654 char *values = NULL;
655 char path[MAXPATHLEN];
656 char connection[MAXPATHLEN];
658 i_hp_dprintf("hp_get_private: node=%p, options=%p, resultsp=%p\n",
659 (void *)node, (void *)options, (void *)resultsp);
661 /* Check arguments */
662 if ((node == NULL) || (options == NULL) || (resultsp == NULL)) {
663 i_hp_dprintf("hp_get_private: invalid arguments.\n");
664 return (EINVAL);
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);
673 /* Initialize results */
674 *resultsp = NULL;
676 /* Get path and connection of specified node */
677 if ((rv = hp_path(node, path, connection)) != 0)
678 return (rv);
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);
685 /* Make the door call to hotplugd */
686 rv = i_hp_call_hotplugd(args, &results);
688 /* Arguments no longer needed */
689 nvlist_free(args);
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;
698 /* Done */
699 return (rv);
703 * hp_pack()
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.
710 hp_pack(hp_node_t root, char **bufp, size_t *lenp)
712 hp_node_t node;
713 nvlist_t *nvl;
714 char *buf;
715 size_t len;
716 int rv;
718 i_hp_dprintf("hp_pack: root=%p, bufp=%p, lenp=%p\n", (void *)root,
719 (void *)bufp, (void *)lenp);
721 if ((root == NULL) || (bufp == NULL) || (lenp == NULL)) {
722 i_hp_dprintf("hp_pack: invalid arguments.\n");
723 return (EINVAL);
726 *lenp = 0;
727 *bufp = NULL;
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);
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);
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);
749 if (rv != 0) {
750 nvlist_free(nvl);
751 return (rv);
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;
762 nvlist_free(nvl);
764 return (rv);
768 * hp_unpack()
770 * Unpack a hotplug information snapshot for normal usage.
773 hp_unpack(char *packed_buf, size_t packed_len, hp_node_t *retp)
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;
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);
786 if ((packed_buf == NULL) || (packed_len == 0) || (retp == NULL)) {
787 i_hp_dprintf("hp_unpack: invalid arguments.\n");
788 return (EINVAL);
791 if ((rv = nvlist_unpack(packed_buf, packed_len, &nvl, 0)) != 0)
792 return (rv);
794 if (nvlist_next_nvpair(nvl, NULL) == NULL) {
795 nvlist_free(nvl);
796 errno = EINVAL;
797 return (NULL);
800 for (nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) {
802 rv = EINVAL;
804 if (strcmp(nvpair_name(nvp), HP_INFO_BASE) == 0) {
805 char *val_string;
807 if ((rv = nvpair_value_string(nvp, &val_string)) == 0) {
808 if ((basepath = strdup(val_string)) == NULL)
809 rv = ENOMEM;
812 } else if (strcmp(nvpair_name(nvp), HP_INFO_BRANCH) == 0) {
813 size_t len = 0;
814 char *buf = NULL;
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);
821 if (rv == 0) {
822 if (prev_root) {
823 prev_root->hp_sibling = root;
824 } else {
825 root_list = root;
827 prev_root = root;
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);
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;
847 nvlist_free(nvl);
848 *retp = root_list;
849 return (0);
853 * i_hp_dprintf()
855 * Print debug messages to stderr, but only when the debug flag
856 * (libhotplug_debug) is set.
858 /*PRINTFLIKE1*/
859 static void
860 i_hp_dprintf(const char *fmt, ...)
862 va_list ap;
864 if (libhotplug_debug) {
865 va_start(ap, fmt);
866 (void) vfprintf(stderr, fmt, ap);
867 va_end(ap);
872 * i_hp_pack_branch()
874 * Pack an individual branch of a hotplug information snapshot.
876 static int
877 i_hp_pack_branch(hp_node_t root, char **bufp, size_t *lenp)
879 hp_node_t child;
880 nvlist_t *nvl;
881 char *buf;
882 size_t len;
883 int rv;
885 *lenp = 0;
886 *bufp = NULL;
888 /* Allocate an nvlist for this branch */
889 if (nvlist_alloc(&nvl, 0, 0) != 0)
890 return (ENOMEM);
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);
898 if (rv != 0) {
899 nvlist_free(nvl);
900 return (rv);
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);
910 if (rv != 0) {
911 nvlist_free(nvl);
912 return (rv);
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;
924 /* Free the nvlist */
925 nvlist_free(nvl);
927 return (rv);
931 * i_hp_pack_node()
933 * Pack an individual node of a hotplug information snapshot.
935 static int
936 i_hp_pack_node(hp_node_t node, char **bufp, size_t *lenp)
938 nvlist_t *nvl;
939 char *buf = NULL;
940 size_t len = 0;
941 int rv;
943 if (nvlist_alloc(&nvl, 0, 0) != 0)
944 return (ENOMEM);
946 if ((rv = nvlist_add_uint32(nvl, HP_INFO_TYPE,
947 (uint32_t)node->hp_type)) != 0)
948 goto fail;
950 if ((node->hp_name) &&
951 ((rv = nvlist_add_string(nvl, HP_INFO_NAME, node->hp_name)) != 0))
952 goto fail;
954 if ((node->hp_usage) &&
955 ((rv = nvlist_add_string(nvl, HP_INFO_USAGE, node->hp_usage)) != 0))
956 goto fail;
958 if ((node->hp_description) &&
959 ((rv = nvlist_add_string(nvl, HP_INFO_DESC,
960 node->hp_description)) != 0))
961 goto fail;
963 if ((rv = nvlist_add_uint32(nvl, HP_INFO_STATE, node->hp_state)) != 0)
964 goto fail;
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;
971 if ((rv = nvlist_pack(nvl, &buf, &len, NV_ENCODE_NATIVE, 0)) != 0)
972 goto fail;
974 *bufp = buf;
975 *lenp = len;
976 nvlist_free(nvl);
977 return (0);
979 fail:
980 *bufp = NULL;
981 *lenp = 0;
982 nvlist_free(nvl);
983 return (rv);
987 * i_hp_unpack_branch()
989 * Unpack a branch of hotplug information nodes.
991 static int
992 i_hp_unpack_branch(char *packed_buf, size_t packed_len, hp_node_t parent,
993 hp_node_t *retp)
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;
1004 /* Initialize results */
1005 *retp = NULL;
1007 /* Unpack the nvlist for this branch */
1008 if ((rv = nvlist_unpack(packed_buf, packed_len, &nvl, 0)) != 0)
1009 return (rv);
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.
1016 for (nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) {
1018 len = 0;
1019 buf = NULL;
1021 if (strcmp(nvpair_name(nvp), HP_INFO_NODE) == 0) {
1023 /* Check that there is only one root node */
1024 if (node != NULL) {
1025 hp_fini(node);
1026 nvlist_free(nvl);
1027 return (EFAULT);
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);
1034 if (rv != 0) {
1035 nvlist_free(nvl);
1036 return (rv);
1039 } else if (strcmp(nvpair_name(nvp), HP_INFO_BRANCH) == 0) {
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);
1045 if (rv != 0) {
1046 hp_fini(node);
1047 nvlist_free(nvl);
1048 return (rv);
1051 if (prev_child) {
1052 prev_child->hp_sibling = child;
1053 } else {
1054 node->hp_child = child;
1056 prev_child = child;
1060 nvlist_free(nvl);
1061 *retp = node;
1062 return (0);
1066 * i_hp_unpack_node()
1068 * Unpack an individual hotplug information node.
1070 static int
1071 i_hp_unpack_node(char *buf, size_t len, hp_node_t parent, hp_node_t *retp)
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;
1080 /* Initialize results */
1081 *retp = NULL;
1083 /* Unpack node into an nvlist */
1084 if ((nvlist_unpack(buf, len, &nvl, 0) != 0))
1085 return (EINVAL);
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);
1093 /* Iterate through nvlist, unpacking each field */
1094 for (nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) {
1096 if ((strcmp(nvpair_name(nvp), HP_INFO_TYPE) == 0) &&
1097 (nvpair_type(nvp) == DATA_TYPE_UINT32)) {
1099 (void) nvpair_value_uint32(nvp, &val_uint32);
1100 node->hp_type = val_uint32;
1102 } else if ((strcmp(nvpair_name(nvp), HP_INFO_NAME) == 0) &&
1103 (nvpair_type(nvp) == DATA_TYPE_STRING)) {
1105 (void) nvpair_value_string(nvp, &val_string);
1106 if ((node->hp_name = strdup(val_string)) == NULL) {
1107 rv = ENOMEM;
1108 break;
1111 } else if ((strcmp(nvpair_name(nvp), HP_INFO_STATE) == 0) &&
1112 (nvpair_type(nvp) == DATA_TYPE_UINT32)) {
1114 (void) nvpair_value_uint32(nvp, &val_uint32);
1115 node->hp_state = val_uint32;
1117 } else if ((strcmp(nvpair_name(nvp), HP_INFO_USAGE) == 0) &&
1118 (nvpair_type(nvp) == DATA_TYPE_STRING)) {
1120 (void) nvpair_value_string(nvp, &val_string);
1121 if ((node->hp_usage = strdup(val_string)) == NULL) {
1122 rv = ENOMEM;
1123 break;
1126 } else if ((strcmp(nvpair_name(nvp), HP_INFO_DESC) == 0) &&
1127 (nvpair_type(nvp) == DATA_TYPE_STRING)) {
1129 (void) nvpair_value_string(nvp, &val_string);
1130 if ((node->hp_description = strdup(val_string))
1131 == NULL) {
1132 rv = ENOMEM;
1133 break;
1136 } else if ((strcmp(nvpair_name(nvp), HP_INFO_TIME) == 0) &&
1137 (nvpair_type(nvp) == DATA_TYPE_UINT32)) {
1139 (void) nvpair_value_uint32(nvp, &val_uint32);
1140 node->hp_last_change = (time_t)val_uint32;
1142 } else {
1143 i_hp_dprintf("i_hp_unpack_node: unrecognized: '%s'\n",
1144 nvpair_name(nvp));
1148 /* Unpacked nvlist no longer needed */
1149 nvlist_free(nvl);
1151 /* Check for errors */
1152 if (rv != 0) {
1153 hp_fini(node);
1154 return (rv);
1157 /* Success */
1158 node->hp_parent = parent;
1159 *retp = node;
1160 return (0);
1164 * i_hp_call_hotplugd()
1166 * Perform a door call to the hotplug daemon.
1168 static int
1169 i_hp_call_hotplugd(nvlist_t *args, nvlist_t **resultsp)
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;
1179 /* Initialize results */
1180 *resultsp = NULL;
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);
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);
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);
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);
1214 /* The arguments are no longer needed */
1215 free(buf);
1218 * If results are not in the original buffer provided,
1219 * then check and process the new results buffer.
1221 if (door_arg.rbuf != (char *)(uintptr_t)&rv) {
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.
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;
1234 } else if (door_arg.data_size == sizeof (rv)) {
1235 rv = *(int *)(uintptr_t)door_arg.rbuf;
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;
1245 /* Unmap the results buffer */
1246 if (door_arg.rbuf != NULL)
1247 (void) munmap(door_arg.rbuf, door_arg.rsize);
1250 * In the case of a packed nvlist, notify the daemon
1251 * that it can free the result buffer from its heap.
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);
1266 *resultsp = results;
1269 (void) close(door_fd);
1270 return (rv);
1274 * i_hp_set_args()
1276 * Construct an nvlist of arguments for a hotplugd door call.
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)
1282 nvlist_t *args;
1284 /* Allocate a new nvlist */
1285 if (nvlist_alloc(&args, NV_UNIQUE_NAME_TYPE, 0) != 0)
1286 return (NULL);
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);
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);
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);
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);
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);
1322 return (args);
1326 * i_hp_parse_results()
1328 * Parse out individual fields of an nvlist of results from
1329 * a hotplugd door call.
1331 static int
1332 i_hp_parse_results(nvlist_t *results, hp_node_t *rootp, char **optionsp)
1334 int rv;
1336 /* Parse an information snapshot */
1337 if (rootp) {
1338 char *buf = NULL;
1339 size_t len = 0;
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);
1349 /* Parse a bus private option string */
1350 if (optionsp) {
1351 char *str;
1353 *optionsp = NULL;
1354 if ((nvlist_lookup_string(results, HPD_OPTIONS, &str) == 0) &&
1355 ((*optionsp = strdup(str)) == NULL)) {
1356 return (ENOMEM);
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);
1366 return (rv);