dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libhotplug / common / libhotplug.c
blobbde9b1389fa94567c22dbfcd6a1f4c71c9b6905e
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 free(node->hp_name);
152 free(node->hp_usage);
153 free(node->hp_description);
154 free(node);
155 node = sibling;
160 * hp_traverse()
162 * Walk a graph of hotplug nodes, executing a callback on each node.
165 hp_traverse(hp_node_t root, void *arg, int (*hp_callback)(hp_node_t, void *arg))
167 int rv;
168 hp_node_t node;
170 i_hp_dprintf("hp_traverse: root=%p, arg=%p, hp_callback=%p\n",
171 (void *)root, arg, (void *)hp_callback);
173 /* Check arguments */
174 if ((root == NULL) || (hp_callback == NULL)) {
175 i_hp_dprintf("hp_traverse: invalid arguments.\n");
176 errno = EINVAL;
177 return (-1);
180 for (node = root; node; node = node->hp_sibling) {
181 rv = hp_callback(node, arg);
183 if (rv == HP_WALK_TERMINATE) {
184 i_hp_dprintf("hp_traverse: walk terminated.\n");
185 return (HP_WALK_TERMINATE);
188 if (node->hp_child && (rv != HP_WALK_PRUNECHILD))
189 if (hp_traverse(node->hp_child, arg, hp_callback) ==
190 HP_WALK_TERMINATE) {
191 i_hp_dprintf("hp_traverse: walk terminated.\n");
192 return (HP_WALK_TERMINATE);
195 if (rv == HP_WALK_PRUNESIBLING)
196 break;
199 return (0);
203 * hp_type()
205 * Return a node's type.
208 hp_type(hp_node_t node)
210 i_hp_dprintf("hp_type: node=%p\n", (void *)node);
212 if (node == NULL) {
213 i_hp_dprintf("hp_type: invalid arguments.\n");
214 errno = EINVAL;
215 return (-1);
218 return (node->hp_type);
222 * hp_name()
224 * Return a node's name.
226 char *
227 hp_name(hp_node_t node)
229 i_hp_dprintf("hp_name: node=%p\n", (void *)node);
231 if (node == NULL) {
232 i_hp_dprintf("hp_name: invalid arguments.\n");
233 errno = EINVAL;
234 return (NULL);
237 if (node->hp_name == NULL) {
238 i_hp_dprintf("hp_name: missing name value.\n");
239 errno = EFAULT;
242 return (node->hp_name);
246 * hp_state()
248 * Return a node's current state.
251 hp_state(hp_node_t node)
253 i_hp_dprintf("hp_state: node=%p\n", (void *)node);
255 if (node == NULL) {
256 i_hp_dprintf("hp_state: invalid arguments.\n");
257 errno = EINVAL;
258 return (-1);
261 if ((node->hp_type != HP_NODE_CONNECTOR) &&
262 (node->hp_type != HP_NODE_PORT)) {
263 i_hp_dprintf("hp_state: operation not supported.\n");
264 errno = ENOTSUP;
265 return (-1);
268 return (node->hp_state);
272 * hp_usage()
274 * Return a usage description for usage nodes.
276 char *
277 hp_usage(hp_node_t node)
279 i_hp_dprintf("hp_usage: node=%p\n", (void *)node);
281 if (node == NULL) {
282 i_hp_dprintf("hp_usage: invalid arguments.\n");
283 errno = EINVAL;
284 return (NULL);
287 if (node->hp_type != HP_NODE_USAGE) {
288 i_hp_dprintf("hp_usage: operation not supported.\n");
289 errno = ENOTSUP;
290 return (NULL);
293 if (node->hp_usage == NULL) {
294 i_hp_dprintf("hp_usage: missing usage value.\n");
295 errno = EFAULT;
298 return (node->hp_usage);
302 * hp_description()
304 * Return a type description (e.g. "PCI slot") for connection nodes.
306 char *
307 hp_description(hp_node_t node)
309 i_hp_dprintf("hp_description: node=%p\n", (void *)node);
311 if (node == NULL) {
312 i_hp_dprintf("hp_description: invalid arguments.\n");
313 errno = EINVAL;
314 return (NULL);
317 if ((node->hp_type != HP_NODE_CONNECTOR) &&
318 (node->hp_type != HP_NODE_PORT)) {
319 i_hp_dprintf("hp_description: operation not supported.\n");
320 errno = ENOTSUP;
321 return (NULL);
324 if (node->hp_description == NULL) {
325 i_hp_dprintf("hp_description: missing description value.\n");
326 errno = EFAULT;
329 return (node->hp_description);
333 * hp_last_change()
335 * Return when the state of a connection was last changed.
337 time_t
338 hp_last_change(hp_node_t node)
340 i_hp_dprintf("hp_last_change: node=%p\n", (void *)node);
342 if (node == NULL) {
343 i_hp_dprintf("hp_last_change: invalid arguments.\n");
344 errno = EINVAL;
345 return (0);
348 if ((node->hp_type != HP_NODE_CONNECTOR) &&
349 (node->hp_type != HP_NODE_PORT)) {
350 i_hp_dprintf("hp_last_change: operation not supported.\n");
351 errno = ENOTSUP;
352 return (0);
355 return (node->hp_last_change);
359 * hp_parent()
361 * Return a node's parent node.
363 hp_node_t
364 hp_parent(hp_node_t node)
366 i_hp_dprintf("hp_parent: node=%p\n", (void *)node);
368 if (node == NULL) {
369 i_hp_dprintf("hp_parent: invalid arguments.\n");
370 errno = EINVAL;
371 return (NULL);
374 if (node->hp_parent == NULL) {
375 i_hp_dprintf("hp_parent: node has no parent.\n");
376 errno = ENXIO;
379 return (node->hp_parent);
383 * hp_child()
385 * Return a node's first child node.
387 hp_node_t
388 hp_child(hp_node_t node)
390 i_hp_dprintf("hp_child: node=%p\n", (void *)node);
392 if (node == NULL) {
393 i_hp_dprintf("hp_child: invalid arguments.\n");
394 errno = EINVAL;
395 return (NULL);
398 if (node->hp_child == NULL) {
399 i_hp_dprintf("hp_child: node has no child.\n");
400 errno = ENXIO;
403 return (node->hp_child);
407 * hp_sibling()
409 * Return a node's next sibling node.
411 hp_node_t
412 hp_sibling(hp_node_t node)
414 i_hp_dprintf("hp_sibling: node=%p\n", (void *)node);
416 if (node == NULL) {
417 i_hp_dprintf("hp_sibling: invalid arguments.\n");
418 errno = EINVAL;
419 return (NULL);
422 if (node->hp_sibling == NULL) {
423 i_hp_dprintf("hp_sibling: node has no sibling.\n");
424 errno = ENXIO;
427 return (node->hp_sibling);
431 * hp_path()
433 * Return the path (and maybe connection name) of a node.
434 * The caller must supply two buffers, each MAXPATHLEN size.
437 hp_path(hp_node_t node, char *path, char *connection)
439 hp_node_t root = NULL;
440 hp_node_t parent;
441 int i;
442 char *s;
443 char components[MAXPATHLEN];
445 i_hp_dprintf("hp_path: node=%p, path=%p, connection=%p\n", (void *)node,
446 (void *)path, (void *)connection);
448 if ((node == NULL) || (path == NULL) || (connection == NULL)) {
449 i_hp_dprintf("hp_path: invalid arguments.\n");
450 return (EINVAL);
453 (void) memset(path, 0, MAXPATHLEN);
454 (void) memset(connection, 0, MAXPATHLEN);
455 (void) memset(components, 0, MAXPATHLEN);
457 /* Set 'connection' only for connectors and ports */
458 if ((node->hp_type == HP_NODE_CONNECTOR) ||
459 (node->hp_type == HP_NODE_PORT))
460 (void) strlcpy(connection, node->hp_name, MAXPATHLEN);
462 /* Trace back to the root node, accumulating components */
463 for (parent = node, root = parent; parent != NULL;
464 root = parent, parent = parent->hp_parent) {
465 if (parent->hp_type == HP_NODE_DEVICE) {
466 (void) strlcat(components, "/", MAXPATHLEN);
467 (void) strlcat(components, parent->hp_name, MAXPATHLEN);
471 /* Ensure the snapshot actually contains a base path */
472 if (root->hp_basepath == NULL) {
473 i_hp_dprintf("hp_path: missing base pathname.\n");
474 return (EFAULT);
478 * Construct the path. Start with the base path from the root
479 * node, then append the accumulated components in reverse order.
481 if (strcmp(root->hp_basepath, "/") != 0) {
482 (void) strlcat(path, root->hp_basepath, MAXPATHLEN);
483 if ((root->hp_type == HP_NODE_DEVICE) &&
484 ((s = strrchr(path, '/')) != NULL))
485 *s = '\0';
487 for (i = strlen(components) - 1; i >= 0; i--) {
488 if (components[i] == '/') {
489 (void) strlcat(path, &components[i], MAXPATHLEN);
490 components[i] = '\0';
494 return (0);
498 * hp_set_state()
500 * Initiate a state change operation on a node.
503 hp_set_state(hp_node_t node, uint_t flags, int state, hp_node_t *resultsp)
505 hp_node_t root = NULL;
506 nvlist_t *args;
507 nvlist_t *results;
508 int rv;
509 char path[MAXPATHLEN];
510 char connection[MAXPATHLEN];
512 i_hp_dprintf("hp_set_state: node=%p, flags=0x%x, state=0x%x, "
513 "resultsp=%p\n", (void *)node, flags, state, (void *)resultsp);
515 /* Check arguments */
516 if ((node == NULL) || (resultsp == NULL) ||
517 !HP_SET_STATE_FLAGS_VALID(flags)) {
518 i_hp_dprintf("hp_set_state: invalid arguments.\n");
519 return (EINVAL);
522 /* Check node type */
523 if ((node->hp_type != HP_NODE_CONNECTOR) &&
524 (node->hp_type != HP_NODE_PORT)) {
525 i_hp_dprintf("hp_set_state: operation not supported.\n");
526 return (ENOTSUP);
529 /* Check that target state is valid */
530 switch (state) {
531 case DDI_HP_CN_STATE_PRESENT:
532 case DDI_HP_CN_STATE_POWERED:
533 case DDI_HP_CN_STATE_ENABLED:
534 if (node->hp_type != HP_NODE_CONNECTOR) {
535 i_hp_dprintf("hp_set_state: mismatched target.\n");
536 return (ENOTSUP);
538 break;
539 case DDI_HP_CN_STATE_PORT_PRESENT:
540 case DDI_HP_CN_STATE_OFFLINE:
541 case DDI_HP_CN_STATE_ONLINE:
542 if (node->hp_type != HP_NODE_PORT) {
543 i_hp_dprintf("hp_set_state: mismatched target.\n");
544 return (ENOTSUP);
546 break;
547 default:
548 i_hp_dprintf("hp_set_state: invalid target state.\n");
549 return (EINVAL);
552 /* Get path and connection of specified node */
553 if ((rv = hp_path(node, path, connection)) != 0)
554 return (rv);
556 /* Build arguments for door call */
557 if ((args = i_hp_set_args(HP_CMD_CHANGESTATE, path, connection, flags,
558 NULL, state)) == NULL)
559 return (ENOMEM);
561 /* Make the door call to hotplugd */
562 rv = i_hp_call_hotplugd(args, &results);
564 /* Arguments no longer needed */
565 nvlist_free(args);
567 /* Parse additional results, if any */
568 if ((rv == 0) && (results != NULL)) {
569 rv = i_hp_parse_results(results, &root, NULL);
570 nvlist_free(results);
571 *resultsp = root;
574 /* Done */
575 return (rv);
579 * hp_set_private()
581 * Set bus private options on the hotplug connection
582 * indicated by the given hotplug information node.
585 hp_set_private(hp_node_t node, const char *options, char **resultsp)
587 int rv;
588 nvlist_t *args;
589 nvlist_t *results;
590 char *values = NULL;
591 char path[MAXPATHLEN];
592 char connection[MAXPATHLEN];
594 i_hp_dprintf("hp_set_private: node=%p, options=%p, resultsp=%p\n",
595 (void *)node, (void *)options, (void *)resultsp);
597 /* Check arguments */
598 if ((node == NULL) || (options == NULL) || (resultsp == NULL)) {
599 i_hp_dprintf("hp_set_private: invalid arguments.\n");
600 return (EINVAL);
603 /* Check node type */
604 if (node->hp_type != HP_NODE_CONNECTOR) {
605 i_hp_dprintf("hp_set_private: operation not supported.\n");
606 return (ENOTSUP);
609 /* Initialize results */
610 *resultsp = NULL;
612 /* Get path and connection of specified node */
613 if ((rv = hp_path(node, path, connection)) != 0)
614 return (rv);
616 /* Build arguments for door call */
617 if ((args = i_hp_set_args(HP_CMD_SETPRIVATE, path, connection, 0,
618 options, 0)) == NULL)
619 return (ENOMEM);
621 /* Make the door call to hotplugd */
622 rv = i_hp_call_hotplugd(args, &results);
624 /* Arguments no longer needed */
625 nvlist_free(args);
627 /* Parse additional results, if any */
628 if ((rv == 0) && (results != NULL)) {
629 rv = i_hp_parse_results(results, NULL, &values);
630 nvlist_free(results);
631 *resultsp = values;
634 /* Done */
635 return (rv);
639 * hp_get_private()
641 * Get bus private options on the hotplug connection
642 * indicated by the given hotplug information node.
645 hp_get_private(hp_node_t node, const char *options, char **resultsp)
647 int rv;
648 nvlist_t *args;
649 nvlist_t *results;
650 char *values = NULL;
651 char path[MAXPATHLEN];
652 char connection[MAXPATHLEN];
654 i_hp_dprintf("hp_get_private: node=%p, options=%p, resultsp=%p\n",
655 (void *)node, (void *)options, (void *)resultsp);
657 /* Check arguments */
658 if ((node == NULL) || (options == NULL) || (resultsp == NULL)) {
659 i_hp_dprintf("hp_get_private: invalid arguments.\n");
660 return (EINVAL);
663 /* Check node type */
664 if (node->hp_type != HP_NODE_CONNECTOR) {
665 i_hp_dprintf("hp_get_private: operation not supported.\n");
666 return (ENOTSUP);
669 /* Initialize results */
670 *resultsp = NULL;
672 /* Get path and connection of specified node */
673 if ((rv = hp_path(node, path, connection)) != 0)
674 return (rv);
676 /* Build arguments for door call */
677 if ((args = i_hp_set_args(HP_CMD_GETPRIVATE, path, connection, 0,
678 options, 0)) == NULL)
679 return (ENOMEM);
681 /* Make the door call to hotplugd */
682 rv = i_hp_call_hotplugd(args, &results);
684 /* Arguments no longer needed */
685 nvlist_free(args);
687 /* Parse additional results, if any */
688 if ((rv == 0) && (results != NULL)) {
689 rv = i_hp_parse_results(results, NULL, &values);
690 nvlist_free(results);
691 *resultsp = values;
694 /* Done */
695 return (rv);
699 * hp_pack()
701 * Given the root of a hotplug information snapshot, pack
702 * it into a contiguous byte array so that it is suitable
703 * for network transport.
706 hp_pack(hp_node_t root, char **bufp, size_t *lenp)
708 hp_node_t node;
709 nvlist_t *nvl;
710 char *buf;
711 size_t len;
712 int rv;
714 i_hp_dprintf("hp_pack: root=%p, bufp=%p, lenp=%p\n", (void *)root,
715 (void *)bufp, (void *)lenp);
717 if ((root == NULL) || (bufp == NULL) || (lenp == NULL)) {
718 i_hp_dprintf("hp_pack: invalid arguments.\n");
719 return (EINVAL);
722 *lenp = 0;
723 *bufp = NULL;
725 if (nvlist_alloc(&nvl, 0, 0) != 0) {
726 i_hp_dprintf("hp_pack: nvlist_alloc() failed (%s).\n",
727 strerror(errno));
728 return (ENOMEM);
731 if (root->hp_basepath != NULL) {
732 rv = nvlist_add_string(nvl, HP_INFO_BASE, root->hp_basepath);
733 if (rv != 0) {
734 nvlist_free(nvl);
735 return (rv);
739 for (node = root; node != NULL; node = node->hp_sibling) {
740 if ((rv = i_hp_pack_branch(node, &buf, &len)) == 0) {
741 rv = nvlist_add_byte_array(nvl, HP_INFO_BRANCH,
742 (uchar_t *)buf, len);
743 free(buf);
745 if (rv != 0) {
746 nvlist_free(nvl);
747 return (rv);
751 len = 0;
752 buf = NULL;
753 if ((rv = nvlist_pack(nvl, &buf, &len, NV_ENCODE_NATIVE, 0)) == 0) {
754 *lenp = len;
755 *bufp = buf;
758 nvlist_free(nvl);
760 return (rv);
764 * hp_unpack()
766 * Unpack a hotplug information snapshot for normal usage.
769 hp_unpack(char *packed_buf, size_t packed_len, hp_node_t *retp)
771 hp_node_t root;
772 hp_node_t root_list = NULL;
773 hp_node_t prev_root = NULL;
774 nvlist_t *nvl = NULL;
775 nvpair_t *nvp;
776 char *basepath = NULL;
777 int rv;
779 i_hp_dprintf("hp_unpack: packed_buf=%p, packed_len=%u, retp=%p\n",
780 (void *)packed_buf, (uint32_t)packed_len, (void *)retp);
782 if ((packed_buf == NULL) || (packed_len == 0) || (retp == NULL)) {
783 i_hp_dprintf("hp_unpack: invalid arguments.\n");
784 return (EINVAL);
787 if ((rv = nvlist_unpack(packed_buf, packed_len, &nvl, 0)) != 0)
788 return (rv);
790 if (nvlist_next_nvpair(nvl, NULL) == NULL) {
791 nvlist_free(nvl);
792 errno = EINVAL;
793 return (0);
796 for (nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) {
798 rv = EINVAL;
800 if (strcmp(nvpair_name(nvp), HP_INFO_BASE) == 0) {
801 char *val_string;
803 if ((rv = nvpair_value_string(nvp, &val_string)) == 0) {
804 if ((basepath = strdup(val_string)) == NULL)
805 rv = ENOMEM;
808 } else if (strcmp(nvpair_name(nvp), HP_INFO_BRANCH) == 0) {
809 size_t len = 0;
810 char *buf = NULL;
812 if ((rv = nvpair_value_byte_array(nvp,
813 (uchar_t **)&buf, (uint_t *)&len)) == 0) {
814 rv = i_hp_unpack_branch(buf, len, NULL, &root);
817 if (rv == 0) {
818 if (prev_root) {
819 prev_root->hp_sibling = root;
820 } else {
821 root_list = root;
823 prev_root = root;
827 if (rv != 0) {
828 free(basepath);
829 nvlist_free(nvl);
830 hp_fini(root_list);
831 *retp = NULL;
832 return (rv);
836 /* Store the base path in each root node */
837 if (basepath) {
838 for (root = root_list; root; root = root->hp_sibling)
839 root->hp_basepath = basepath;
842 nvlist_free(nvl);
843 *retp = root_list;
844 return (0);
848 * i_hp_dprintf()
850 * Print debug messages to stderr, but only when the debug flag
851 * (libhotplug_debug) is set.
853 /*PRINTFLIKE1*/
854 static void
855 i_hp_dprintf(const char *fmt, ...)
857 va_list ap;
859 if (libhotplug_debug) {
860 va_start(ap, fmt);
861 (void) vfprintf(stderr, fmt, ap);
862 va_end(ap);
867 * i_hp_pack_branch()
869 * Pack an individual branch of a hotplug information snapshot.
871 static int
872 i_hp_pack_branch(hp_node_t root, char **bufp, size_t *lenp)
874 hp_node_t child;
875 nvlist_t *nvl;
876 char *buf;
877 size_t len;
878 int rv;
880 *lenp = 0;
881 *bufp = NULL;
883 /* Allocate an nvlist for this branch */
884 if (nvlist_alloc(&nvl, 0, 0) != 0)
885 return (ENOMEM);
887 /* Pack the root of the branch and add it to the nvlist */
888 if ((rv = i_hp_pack_node(root, &buf, &len)) == 0) {
889 rv = nvlist_add_byte_array(nvl, HP_INFO_NODE,
890 (uchar_t *)buf, len);
891 free(buf);
893 if (rv != 0) {
894 nvlist_free(nvl);
895 return (rv);
898 /* Pack each subordinate branch, and add it to the nvlist */
899 for (child = root->hp_child; child != NULL; child = child->hp_sibling) {
900 if ((rv = i_hp_pack_branch(child, &buf, &len)) == 0) {
901 rv = nvlist_add_byte_array(nvl, HP_INFO_BRANCH,
902 (uchar_t *)buf, len);
903 free(buf);
905 if (rv != 0) {
906 nvlist_free(nvl);
907 return (rv);
911 /* Pack the resulting nvlist into a single buffer */
912 len = 0;
913 buf = NULL;
914 if ((rv = nvlist_pack(nvl, &buf, &len, NV_ENCODE_NATIVE, 0)) == 0) {
915 *lenp = len;
916 *bufp = buf;
919 /* Free the nvlist */
920 nvlist_free(nvl);
922 return (rv);
926 * i_hp_pack_node()
928 * Pack an individual node of a hotplug information snapshot.
930 static int
931 i_hp_pack_node(hp_node_t node, char **bufp, size_t *lenp)
933 nvlist_t *nvl;
934 char *buf = NULL;
935 size_t len = 0;
936 int rv;
938 if (nvlist_alloc(&nvl, 0, 0) != 0)
939 return (ENOMEM);
941 if ((rv = nvlist_add_uint32(nvl, HP_INFO_TYPE,
942 (uint32_t)node->hp_type)) != 0)
943 goto fail;
945 if ((node->hp_name) &&
946 ((rv = nvlist_add_string(nvl, HP_INFO_NAME, node->hp_name)) != 0))
947 goto fail;
949 if ((node->hp_usage) &&
950 ((rv = nvlist_add_string(nvl, HP_INFO_USAGE, node->hp_usage)) != 0))
951 goto fail;
953 if ((node->hp_description) &&
954 ((rv = nvlist_add_string(nvl, HP_INFO_DESC,
955 node->hp_description)) != 0))
956 goto fail;
958 if ((rv = nvlist_add_uint32(nvl, HP_INFO_STATE, node->hp_state)) != 0)
959 goto fail;
961 if ((node->hp_last_change != 0) &&
962 ((rv = nvlist_add_uint32(nvl, HP_INFO_TIME,
963 node->hp_last_change)) != 0))
964 goto fail;
966 if ((rv = nvlist_pack(nvl, &buf, &len, NV_ENCODE_NATIVE, 0)) != 0)
967 goto fail;
969 *bufp = buf;
970 *lenp = len;
971 nvlist_free(nvl);
972 return (0);
974 fail:
975 *bufp = NULL;
976 *lenp = 0;
977 nvlist_free(nvl);
978 return (rv);
982 * i_hp_unpack_branch()
984 * Unpack a branch of hotplug information nodes.
986 static int
987 i_hp_unpack_branch(char *packed_buf, size_t packed_len, hp_node_t parent,
988 hp_node_t *retp)
990 hp_node_t node = NULL;
991 hp_node_t child;
992 hp_node_t prev_child = NULL;
993 nvlist_t *nvl = NULL;
994 nvpair_t *nvp;
995 char *buf;
996 size_t len;
997 int rv;
999 /* Initialize results */
1000 *retp = NULL;
1002 /* Unpack the nvlist for this branch */
1003 if ((rv = nvlist_unpack(packed_buf, packed_len, &nvl, 0)) != 0)
1004 return (rv);
1007 * Unpack the branch. The first item in the nvlist is
1008 * always the root node. And zero or more subordinate
1009 * branches may be packed afterward.
1011 for (nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) {
1013 len = 0;
1014 buf = NULL;
1016 if (strcmp(nvpair_name(nvp), HP_INFO_NODE) == 0) {
1018 /* Check that there is only one root node */
1019 if (node != NULL) {
1020 hp_fini(node);
1021 nvlist_free(nvl);
1022 return (EFAULT);
1025 if ((rv = nvpair_value_byte_array(nvp, (uchar_t **)&buf,
1026 (uint_t *)&len)) == 0)
1027 rv = i_hp_unpack_node(buf, len, parent, &node);
1029 if (rv != 0) {
1030 nvlist_free(nvl);
1031 return (rv);
1034 } else if (strcmp(nvpair_name(nvp), HP_INFO_BRANCH) == 0) {
1036 if ((rv = nvpair_value_byte_array(nvp, (uchar_t **)&buf,
1037 (uint_t *)&len)) == 0)
1038 rv = i_hp_unpack_branch(buf, len, node, &child);
1040 if (rv != 0) {
1041 hp_fini(node);
1042 nvlist_free(nvl);
1043 return (rv);
1046 if (prev_child) {
1047 prev_child->hp_sibling = child;
1048 } else {
1049 node->hp_child = child;
1051 prev_child = child;
1055 nvlist_free(nvl);
1056 *retp = node;
1057 return (0);
1061 * i_hp_unpack_node()
1063 * Unpack an individual hotplug information node.
1065 static int
1066 i_hp_unpack_node(char *buf, size_t len, hp_node_t parent, hp_node_t *retp)
1068 hp_node_t node;
1069 nvlist_t *nvl;
1070 nvpair_t *nvp;
1071 uint32_t val_uint32;
1072 char *val_string;
1073 int rv = 0;
1075 /* Initialize results */
1076 *retp = NULL;
1078 /* Unpack node into an nvlist */
1079 if ((nvlist_unpack(buf, len, &nvl, 0) != 0))
1080 return (EINVAL);
1082 /* Allocate the new node */
1083 if ((node = (hp_node_t)calloc(1, sizeof (struct hp_node))) == NULL) {
1084 nvlist_free(nvl);
1085 return (ENOMEM);
1088 /* Iterate through nvlist, unpacking each field */
1089 for (nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) {
1091 if ((strcmp(nvpair_name(nvp), HP_INFO_TYPE) == 0) &&
1092 (nvpair_type(nvp) == DATA_TYPE_UINT32)) {
1094 (void) nvpair_value_uint32(nvp, &val_uint32);
1095 node->hp_type = val_uint32;
1097 } else if ((strcmp(nvpair_name(nvp), HP_INFO_NAME) == 0) &&
1098 (nvpair_type(nvp) == DATA_TYPE_STRING)) {
1100 (void) nvpair_value_string(nvp, &val_string);
1101 if ((node->hp_name = strdup(val_string)) == NULL) {
1102 rv = ENOMEM;
1103 break;
1106 } else if ((strcmp(nvpair_name(nvp), HP_INFO_STATE) == 0) &&
1107 (nvpair_type(nvp) == DATA_TYPE_UINT32)) {
1109 (void) nvpair_value_uint32(nvp, &val_uint32);
1110 node->hp_state = val_uint32;
1112 } else if ((strcmp(nvpair_name(nvp), HP_INFO_USAGE) == 0) &&
1113 (nvpair_type(nvp) == DATA_TYPE_STRING)) {
1115 (void) nvpair_value_string(nvp, &val_string);
1116 if ((node->hp_usage = strdup(val_string)) == NULL) {
1117 rv = ENOMEM;
1118 break;
1121 } else if ((strcmp(nvpair_name(nvp), HP_INFO_DESC) == 0) &&
1122 (nvpair_type(nvp) == DATA_TYPE_STRING)) {
1124 (void) nvpair_value_string(nvp, &val_string);
1125 if ((node->hp_description = strdup(val_string))
1126 == NULL) {
1127 rv = ENOMEM;
1128 break;
1131 } else if ((strcmp(nvpair_name(nvp), HP_INFO_TIME) == 0) &&
1132 (nvpair_type(nvp) == DATA_TYPE_UINT32)) {
1134 (void) nvpair_value_uint32(nvp, &val_uint32);
1135 node->hp_last_change = (time_t)val_uint32;
1137 } else {
1138 i_hp_dprintf("i_hp_unpack_node: unrecognized: '%s'\n",
1139 nvpair_name(nvp));
1143 /* Unpacked nvlist no longer needed */
1144 nvlist_free(nvl);
1146 /* Check for errors */
1147 if (rv != 0) {
1148 hp_fini(node);
1149 return (rv);
1152 /* Success */
1153 node->hp_parent = parent;
1154 *retp = node;
1155 return (0);
1159 * i_hp_call_hotplugd()
1161 * Perform a door call to the hotplug daemon.
1163 static int
1164 i_hp_call_hotplugd(nvlist_t *args, nvlist_t **resultsp)
1166 door_arg_t door_arg;
1167 nvlist_t *results = NULL;
1168 char *buf = NULL;
1169 size_t len = 0;
1170 uint64_t seqnum;
1171 int door_fd;
1172 int rv;
1174 /* Initialize results */
1175 *resultsp = NULL;
1177 /* Open door */
1178 if ((door_fd = open(HOTPLUGD_DOOR, O_RDONLY)) < 0) {
1179 i_hp_dprintf("i_hp_call_hotplugd: cannot open door (%s)\n",
1180 strerror(errno));
1181 return (EBADF);
1184 /* Pack the nvlist of arguments */
1185 if ((rv = nvlist_pack(args, &buf, &len, NV_ENCODE_NATIVE, 0)) != 0) {
1186 i_hp_dprintf("i_hp_call_hotplugd: cannot pack arguments (%s)\n",
1187 strerror(rv));
1188 return (rv);
1191 /* Set the door argument using the packed arguments */
1192 door_arg.data_ptr = buf;
1193 door_arg.data_size = len;
1194 door_arg.desc_ptr = NULL;
1195 door_arg.desc_num = 0;
1196 door_arg.rbuf = (char *)(uintptr_t)&rv;
1197 door_arg.rsize = sizeof (rv);
1199 /* Attempt the door call */
1200 if (door_call(door_fd, &door_arg) != 0) {
1201 rv = errno;
1202 i_hp_dprintf("i_hp_call_hotplugd: door call failed (%s)\n",
1203 strerror(rv));
1204 (void) close(door_fd);
1205 free(buf);
1206 return (rv);
1209 /* The arguments are no longer needed */
1210 free(buf);
1213 * If results are not in the original buffer provided,
1214 * then check and process the new results buffer.
1216 if (door_arg.rbuf != (char *)(uintptr_t)&rv) {
1219 * First check that the buffer is valid. Then check for
1220 * the simple case where a short result code was sent.
1221 * The last case is a packed nvlist was returned, which
1222 * needs to be unpacked.
1224 if ((door_arg.rbuf == NULL) ||
1225 (door_arg.data_size < sizeof (rv))) {
1226 i_hp_dprintf("i_hp_call_hotplugd: invalid results.\n");
1227 rv = EFAULT;
1229 } else if (door_arg.data_size == sizeof (rv)) {
1230 rv = *(int *)(uintptr_t)door_arg.rbuf;
1232 } else if ((rv = nvlist_unpack(door_arg.rbuf,
1233 door_arg.data_size, &results, 0)) != 0) {
1234 i_hp_dprintf("i_hp_call_hotplugd: "
1235 "cannot unpack results (%s).\n", strerror(rv));
1236 results = NULL;
1237 rv = EFAULT;
1240 /* Unmap the results buffer */
1241 if (door_arg.rbuf != NULL)
1242 (void) munmap(door_arg.rbuf, door_arg.rsize);
1245 * In the case of a packed nvlist, notify the daemon
1246 * that it can free the result buffer from its heap.
1248 if ((results != NULL) &&
1249 (nvlist_lookup_uint64(results, HPD_SEQNUM, &seqnum) == 0)) {
1250 door_arg.data_ptr = (char *)(uintptr_t)&seqnum;
1251 door_arg.data_size = sizeof (seqnum);
1252 door_arg.desc_ptr = NULL;
1253 door_arg.desc_num = 0;
1254 door_arg.rbuf = NULL;
1255 door_arg.rsize = 0;
1256 (void) door_call(door_fd, &door_arg);
1257 if (door_arg.rbuf != NULL)
1258 (void) munmap(door_arg.rbuf, door_arg.rsize);
1261 *resultsp = results;
1264 (void) close(door_fd);
1265 return (rv);
1269 * i_hp_set_args()
1271 * Construct an nvlist of arguments for a hotplugd door call.
1273 static nvlist_t *
1274 i_hp_set_args(hp_cmd_t cmd, const char *path, const char *connection,
1275 uint_t flags, const char *options, int state)
1277 nvlist_t *args;
1279 /* Allocate a new nvlist */
1280 if (nvlist_alloc(&args, NV_UNIQUE_NAME_TYPE, 0) != 0)
1281 return (NULL);
1283 /* Add common arguments */
1284 if ((nvlist_add_int32(args, HPD_CMD, cmd) != 0) ||
1285 (nvlist_add_string(args, HPD_PATH, path) != 0)) {
1286 nvlist_free(args);
1287 return (NULL);
1290 /* Add connection, but only if defined */
1291 if ((connection != NULL) && (connection[0] != '\0') &&
1292 (nvlist_add_string(args, HPD_CONNECTION, connection) != 0)) {
1293 nvlist_free(args);
1294 return (NULL);
1297 /* Add flags, but only if defined */
1298 if ((flags != 0) && (nvlist_add_uint32(args, HPD_FLAGS, flags) != 0)) {
1299 nvlist_free(args);
1300 return (NULL);
1303 /* Add options, but only if defined */
1304 if ((options != NULL) &&
1305 (nvlist_add_string(args, HPD_OPTIONS, options) != 0)) {
1306 nvlist_free(args);
1307 return (NULL);
1310 /* Add state, but only for CHANGESTATE command */
1311 if ((cmd == HP_CMD_CHANGESTATE) &&
1312 (nvlist_add_int32(args, HPD_STATE, state) != 0)) {
1313 nvlist_free(args);
1314 return (NULL);
1317 return (args);
1321 * i_hp_parse_results()
1323 * Parse out individual fields of an nvlist of results from
1324 * a hotplugd door call.
1326 static int
1327 i_hp_parse_results(nvlist_t *results, hp_node_t *rootp, char **optionsp)
1329 int rv;
1331 /* Parse an information snapshot */
1332 if (rootp) {
1333 char *buf = NULL;
1334 size_t len = 0;
1336 *rootp = NULL;
1337 if (nvlist_lookup_byte_array(results, HPD_INFO,
1338 (uchar_t **)&buf, (uint_t *)&len) == 0) {
1339 if ((rv = hp_unpack(buf, len, rootp)) != 0)
1340 return (rv);
1344 /* Parse a bus private option string */
1345 if (optionsp) {
1346 char *str;
1348 *optionsp = NULL;
1349 if ((nvlist_lookup_string(results, HPD_OPTIONS, &str) == 0) &&
1350 ((*optionsp = strdup(str)) == NULL)) {
1351 return (ENOMEM);
1355 /* Parse result code of the operation */
1356 if (nvlist_lookup_int32(results, HPD_STATUS, &rv) != 0) {
1357 i_hp_dprintf("i_hp_call_hotplugd: missing status.\n");
1358 return (EFAULT);
1361 return (rv);