1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <errno.h>
32 #include <string.h>
33 #include <door.h>
34 #include <libnvpair.h>
35 #include <libhotplug.h>
36 #include <libhotplug_impl.h>
37 #include <sys/sunddi.h>
38 #include <sys/ddi_hp.h>
39
40 static void i_hp_dprintf(const char *fmt, ...);
41 static int i_hp_pack_branch(hp_node_t, char **, size_t *);
42 static int i_hp_pack_node(hp_node_t, char **, size_t *);
43 static int i_hp_unpack_node(char *, size_t, hp_node_t, hp_node_t *);
44 static int i_hp_unpack_branch(char *, size_t, hp_node_t, hp_node_t *);
45 static int i_hp_call_hotplugd(nvlist_t *, nvlist_t **);
46 static nvlist_t *i_hp_set_args(hp_cmd_t, const char *, const char *, uint_t,
47 const char *, int);
48 static int i_hp_parse_results(nvlist_t *, hp_node_t *, char **);
49
50 /*
51 * Global flag to enable debug features.
52 */
53 int libhotplug_debug = 0;
54
55 /*
56 * hp_init()
57 *
58 * Initialize a hotplug information snapshot.
59 */
60 hp_node_t
61 hp_init(const char *path, const char *connection, uint_t flags)
62 {
63 nvlist_t *args;
64 nvlist_t *results;
65 hp_node_t root = NULL;
66 int rv;
67
68 i_hp_dprintf("hp_init: path=%p, connection=%p, flags=0x%x\n",
69 (void *)path, (void *)connection, flags);
70
71 /* Check arguments */
72 if ((path == NULL) || !HP_INIT_FLAGS_VALID(flags)) {
73 i_hp_dprintf("hp_init: invalid arguments.\n");
74 errno = EINVAL;
75 return (NULL);
76 }
77
78 /* Build arguments for door call */
79 if ((args = i_hp_set_args(HP_CMD_GETINFO, path, connection, flags,
80 NULL, 0)) == NULL) {
81 i_hp_dprintf("hp_init: cannot build arguments nvlist.\n");
82 errno = ENOMEM;
83 return (NULL);
84 }
85
86 /* Make the door call to hotplugd */
87 rv = i_hp_call_hotplugd(args, &results);
88
89 /* Arguments no longer needed */
90 nvlist_free(args);
91
92 /* Parse additional results, if any */
93 if ((rv == 0) && (results != NULL)) {
94 rv = i_hp_parse_results(results, &root, NULL);
95 nvlist_free(results);
96 }
97
98 /* Check for errors */
99 if (rv != 0) {
100 i_hp_dprintf("hp_init: failure (%s).\n", strerror(rv));
101 if (root)
102 hp_fini(root);
103 errno = rv;
104 return (NULL);
105 }
106
107 /* Success requires an info snapshot */
108 if (root == NULL) {
109 i_hp_dprintf("hp_init: missing info snapshot.\n");
110 errno = EFAULT;
111 return (NULL);
112 }
113
114 /* Success */
115 return (root);
116 }
117
118 /*
119 * hp_fini()
120 *
121 * Terminate and clean-up a hotplug information snapshot.
122 */
123 void
124 hp_fini(hp_node_t root)
125 {
126 hp_node_t node;
127 hp_node_t sibling;
128 char *basepath;
129
130 i_hp_dprintf("hp_fini: root=%p\n", (void *)root);
131
132 if (root == NULL) {
133 i_hp_dprintf("hp_fini: invalid arguments.\n");
134 return;
135 }
136
137 /* Extract and free base path */
138 if (root->hp_basepath) {
139 basepath = root->hp_basepath;
140 for (node = root; node != NULL; node = node->hp_sibling)
141 node->hp_basepath = NULL;
142 free(basepath);
143 }
144
145 /* Destroy the nodes */
146 node = root;
147 while (node) {
148 sibling = node->hp_sibling;
149 if (node->hp_child)
150 hp_fini(node->hp_child);
151 if (node->hp_name)
152 free(node->hp_name);
153 if (node->hp_usage)
154 free(node->hp_usage);
155 if (node->hp_description)
156 free(node->hp_description);
157 free(node);
158 node = sibling;
159 }
160 }
161
162 /*
163 * hp_traverse()
164 *
165 * Walk a graph of hotplug nodes, executing a callback on each node.
166 */
167 int
168 hp_traverse(hp_node_t root, void *arg, int (*hp_callback)(hp_node_t, void *arg))
169 {
170 int rv;
171 hp_node_t node;
172
173 i_hp_dprintf("hp_traverse: root=%p, arg=%p, hp_callback=%p\n",
174 (void *)root, arg, (void *)hp_callback);
175
176 /* Check arguments */
177 if ((root == NULL) || (hp_callback == NULL)) {
178 i_hp_dprintf("hp_traverse: invalid arguments.\n");
179 errno = EINVAL;
180 return (-1);
181 }
182
183 for (node = root; node; node = node->hp_sibling) {
184 rv = hp_callback(node, arg);
185
186 if (rv == HP_WALK_TERMINATE) {
187 i_hp_dprintf("hp_traverse: walk terminated.\n");
188 return (HP_WALK_TERMINATE);
189 }
190
191 if (node->hp_child && (rv != HP_WALK_PRUNECHILD))
192 if (hp_traverse(node->hp_child, arg, hp_callback) ==
193 HP_WALK_TERMINATE) {
194 i_hp_dprintf("hp_traverse: walk terminated.\n");
195 return (HP_WALK_TERMINATE);
196 }
197
198 if (rv == HP_WALK_PRUNESIBLING)
199 break;
200 }
201
202 return (0);
203 }
204
205 /*
206 * hp_type()
207 *
208 * Return a node's type.
209 */
210 int
211 hp_type(hp_node_t node)
212 {
213 i_hp_dprintf("hp_type: node=%p\n", (void *)node);
214
215 if (node == NULL) {
216 i_hp_dprintf("hp_type: invalid arguments.\n");
217 errno = EINVAL;
218 return (-1);
219 }
220
221 return (node->hp_type);
222 }
223
224 /*
225 * hp_name()
226 *
227 * Return a node's name.
228 */
229 char *
230 hp_name(hp_node_t node)
231 {
232 i_hp_dprintf("hp_name: node=%p\n", (void *)node);
233
234 if (node == NULL) {
235 i_hp_dprintf("hp_name: invalid arguments.\n");
236 errno = EINVAL;
237 return (NULL);
238 }
239
240 if (node->hp_name == NULL) {
241 i_hp_dprintf("hp_name: missing name value.\n");
242 errno = EFAULT;
243 }
244
245 return (node->hp_name);
246 }
247
248 /*
249 * hp_state()
250 *
251 * Return a node's current state.
252 */
253 int
254 hp_state(hp_node_t node)
255 {
256 i_hp_dprintf("hp_state: node=%p\n", (void *)node);
257
258 if (node == NULL) {
259 i_hp_dprintf("hp_state: invalid arguments.\n");
260 errno = EINVAL;
261 return (-1);
262 }
263
264 if ((node->hp_type != HP_NODE_CONNECTOR) &&
265 (node->hp_type != HP_NODE_PORT)) {
266 i_hp_dprintf("hp_state: operation not supported.\n");
267 errno = ENOTSUP;
268 return (-1);
269 }
270
271 return (node->hp_state);
272 }
273
274 /*
275 * hp_usage()
276 *
277 * Return a usage description for usage nodes.
278 */
279 char *
280 hp_usage(hp_node_t node)
281 {
282 i_hp_dprintf("hp_usage: node=%p\n", (void *)node);
283
284 if (node == NULL) {
285 i_hp_dprintf("hp_usage: invalid arguments.\n");
286 errno = EINVAL;
287 return (NULL);
288 }
289
290 if (node->hp_type != HP_NODE_USAGE) {
291 i_hp_dprintf("hp_usage: operation not supported.\n");
292 errno = ENOTSUP;
293 return (NULL);
294 }
295
296 if (node->hp_usage == NULL) {
297 i_hp_dprintf("hp_usage: missing usage value.\n");
298 errno = EFAULT;
299 }
300
301 return (node->hp_usage);
302 }
303
304 /*
305 * hp_description()
306 *
307 * Return a type description (e.g. "PCI slot") for connection nodes.
308 */
309 char *
310 hp_description(hp_node_t node)
311 {
312 i_hp_dprintf("hp_description: node=%p\n", (void *)node);
313
314 if (node == NULL) {
315 i_hp_dprintf("hp_description: invalid arguments.\n");
316 errno = EINVAL;
317 return (NULL);
318 }
319
320 if ((node->hp_type != HP_NODE_CONNECTOR) &&
321 (node->hp_type != HP_NODE_PORT)) {
322 i_hp_dprintf("hp_description: operation not supported.\n");
323 errno = ENOTSUP;
324 return (NULL);
325 }
326
327 if (node->hp_description == NULL) {
328 i_hp_dprintf("hp_description: missing description value.\n");
329 errno = EFAULT;
330 }
331
332 return (node->hp_description);
333 }
334
335 /*
336 * hp_last_change()
337 *
338 * Return when the state of a connection was last changed.
339 */
340 time_t
341 hp_last_change(hp_node_t node)
342 {
343 i_hp_dprintf("hp_last_change: node=%p\n", (void *)node);
344
345 if (node == NULL) {
346 i_hp_dprintf("hp_last_change: invalid arguments.\n");
347 errno = EINVAL;
348 return (NULL);
349 }
350
351 if ((node->hp_type != HP_NODE_CONNECTOR) &&
352 (node->hp_type != HP_NODE_PORT)) {
353 i_hp_dprintf("hp_last_change: operation not supported.\n");
354 errno = ENOTSUP;
355 return (NULL);
356 }
357
358 return (node->hp_last_change);
359 }
360
361 /*
362 * hp_parent()
363 *
364 * Return a node's parent node.
365 */
366 hp_node_t
367 hp_parent(hp_node_t node)
368 {
369 i_hp_dprintf("hp_parent: node=%p\n", (void *)node);
370
371 if (node == NULL) {
372 i_hp_dprintf("hp_parent: invalid arguments.\n");
373 errno = EINVAL;
374 return (NULL);
375 }
376
377 if (node->hp_parent == NULL) {
378 i_hp_dprintf("hp_parent: node has no parent.\n");
379 errno = ENXIO;
380 }
381
382 return (node->hp_parent);
383 }
384
385 /*
386 * hp_child()
387 *
388 * Return a node's first child node.
389 */
390 hp_node_t
391 hp_child(hp_node_t node)
392 {
393 i_hp_dprintf("hp_child: node=%p\n", (void *)node);
394
395 if (node == NULL) {
396 i_hp_dprintf("hp_child: invalid arguments.\n");
397 errno = EINVAL;
398 return (NULL);
399 }
400
401 if (node->hp_child == NULL) {
402 i_hp_dprintf("hp_child: node has no child.\n");
403 errno = ENXIO;
404 }
405
406 return (node->hp_child);
407 }
408
409 /*
410 * hp_sibling()
411 *
412 * Return a node's next sibling node.
413 */
414 hp_node_t
415 hp_sibling(hp_node_t node)
416 {
417 i_hp_dprintf("hp_sibling: node=%p\n", (void *)node);
418
419 if (node == NULL) {
420 i_hp_dprintf("hp_sibling: invalid arguments.\n");
421 errno = EINVAL;
422 return (NULL);
423 }
424
425 if (node->hp_sibling == NULL) {
426 i_hp_dprintf("hp_sibling: node has no sibling.\n");
427 errno = ENXIO;
428 }
429
430 return (node->hp_sibling);
431 }
432
433 /*
434 * hp_path()
435 *
436 * Return the path (and maybe connection name) of a node.
437 * The caller must supply two buffers, each MAXPATHLEN size.
438 */
439 int
440 hp_path(hp_node_t node, char *path, char *connection)
441 {
442 hp_node_t root;
443 hp_node_t parent;
444 int i;
445 char *s;
446 char components[MAXPATHLEN];
447
448 i_hp_dprintf("hp_path: node=%p, path=%p, connection=%p\n", (void *)node,
449 (void *)path, (void *)connection);
450
451 if ((node == NULL) || (path == NULL) || (connection == NULL)) {
452 i_hp_dprintf("hp_path: invalid arguments.\n");
453 return (EINVAL);
454 }
455
456 (void) memset(path, 0, MAXPATHLEN);
457 (void) memset(connection, 0, MAXPATHLEN);
458 (void) memset(components, 0, MAXPATHLEN);
459
460 /* Set 'connection' only for connectors and ports */
461 if ((node->hp_type == HP_NODE_CONNECTOR) ||
462 (node->hp_type == HP_NODE_PORT))
463 (void) strlcpy(connection, node->hp_name, MAXPATHLEN);
464
465 /* Trace back to the root node, accumulating components */
466 for (parent = node, root = parent; parent != NULL;
467 root = parent, parent = parent->hp_parent) {
468 if (parent->hp_type == HP_NODE_DEVICE) {
469 (void) strlcat(components, "/", MAXPATHLEN);
470 (void) strlcat(components, parent->hp_name, MAXPATHLEN);
471 }
472 }
473
474 /* Ensure the snapshot actually contains a base path */
475 if (root->hp_basepath == NULL) {
476 i_hp_dprintf("hp_path: missing base pathname.\n");
477 return (EFAULT);
478 }
479
480 /*
481 * Construct the path. Start with the base path from the root
482 * node, then append the accumulated components in reverse order.
483 */
484 if (strcmp(root->hp_basepath, "/") != 0) {
485 (void) strlcat(path, root->hp_basepath, MAXPATHLEN);
486 if ((root->hp_type == HP_NODE_DEVICE) &&
487 ((s = strrchr(path, '/')) != NULL))
488 *s = '\0';
489 }
490 for (i = strlen(components) - 1; i >= 0; i--) {
491 if (components[i] == '/') {
492 (void) strlcat(path, &components[i], MAXPATHLEN);
493 components[i] = '\0';
494 }
495 }
496
497 return (0);
498 }
499
500 /*
501 * hp_set_state()
502 *
503 * Initiate a state change operation on a node.
504 */
505 int
506 hp_set_state(hp_node_t node, uint_t flags, int state, hp_node_t *resultsp)
507 {
508 hp_node_t root = NULL;
509 nvlist_t *args;
510 nvlist_t *results;
511 int rv;
512 char path[MAXPATHLEN];
513 char connection[MAXPATHLEN];
514
515 i_hp_dprintf("hp_set_state: node=%p, flags=0x%x, state=0x%x, "
516 "resultsp=%p\n", (void *)node, flags, state, (void *)resultsp);
517
518 /* Check arguments */
519 if ((node == NULL) || (resultsp == NULL) ||
520 !HP_SET_STATE_FLAGS_VALID(flags)) {
521 i_hp_dprintf("hp_set_state: invalid arguments.\n");
522 return (EINVAL);
523 }
524
525 /* Check node type */
526 if ((node->hp_type != HP_NODE_CONNECTOR) &&
527 (node->hp_type != HP_NODE_PORT)) {
528 i_hp_dprintf("hp_set_state: operation not supported.\n");
529 return (ENOTSUP);
530 }
531
532 /* Check that target state is valid */
533 switch (state) {
534 case DDI_HP_CN_STATE_PRESENT:
535 case DDI_HP_CN_STATE_POWERED:
536 case DDI_HP_CN_STATE_ENABLED:
537 if (node->hp_type != HP_NODE_CONNECTOR) {
538 i_hp_dprintf("hp_set_state: mismatched target.\n");
539 return (ENOTSUP);
540 }
541 break;
542 case DDI_HP_CN_STATE_PORT_PRESENT:
543 case DDI_HP_CN_STATE_OFFLINE:
544 case DDI_HP_CN_STATE_ONLINE:
545 if (node->hp_type != HP_NODE_PORT) {
546 i_hp_dprintf("hp_set_state: mismatched target.\n");
547 return (ENOTSUP);
548 }
549 break;
550 default:
551 i_hp_dprintf("hp_set_state: invalid target state.\n");
552 return (EINVAL);
553 }
554
555 /* Get path and connection of specified node */
556 if ((rv = hp_path(node, path, connection)) != 0)
557 return (rv);
558
559 /* Build arguments for door call */
560 if ((args = i_hp_set_args(HP_CMD_CHANGESTATE, path, connection, flags,
561 NULL, state)) == NULL)
562 return (ENOMEM);
563
564 /* Make the door call to hotplugd */
565 rv = i_hp_call_hotplugd(args, &results);
566
567 /* Arguments no longer needed */
568 nvlist_free(args);
569
570 /* Parse additional results, if any */
571 if ((rv == 0) && (results != NULL)) {
572 rv = i_hp_parse_results(results, &root, NULL);
573 nvlist_free(results);
574 *resultsp = root;
575 }
576
577 /* Done */
578 return (rv);
579 }
580
581 /*
582 * hp_set_private()
583 *
584 * Set bus private options on the hotplug connection
585 * indicated by the given hotplug information node.
586 */
587 int
588 hp_set_private(hp_node_t node, const char *options, char **resultsp)
589 {
590 int rv;
591 nvlist_t *args;
592 nvlist_t *results;
593 char *values = NULL;
594 char path[MAXPATHLEN];
595 char connection[MAXPATHLEN];
596
597 i_hp_dprintf("hp_set_private: node=%p, options=%p, resultsp=%p\n",
598 (void *)node, (void *)options, (void *)resultsp);
599
600 /* Check arguments */
601 if ((node == NULL) || (options == NULL) || (resultsp == NULL)) {
602 i_hp_dprintf("hp_set_private: invalid arguments.\n");
603 return (EINVAL);
604 }
605
606 /* Check node type */
607 if (node->hp_type != HP_NODE_CONNECTOR) {
608 i_hp_dprintf("hp_set_private: operation not supported.\n");
609 return (ENOTSUP);
610 }
611
612 /* Initialize results */
613 *resultsp = NULL;
614
615 /* Get path and connection of specified node */
616 if ((rv = hp_path(node, path, connection)) != 0)
617 return (rv);
618
619 /* Build arguments for door call */
620 if ((args = i_hp_set_args(HP_CMD_SETPRIVATE, path, connection, 0,
621 options, 0)) == NULL)
622 return (ENOMEM);
623
624 /* Make the door call to hotplugd */
625 rv = i_hp_call_hotplugd(args, &results);
626
627 /* Arguments no longer needed */
628 nvlist_free(args);
629
630 /* Parse additional results, if any */
631 if ((rv == 0) && (results != NULL)) {
632 rv = i_hp_parse_results(results, NULL, &values);
633 nvlist_free(results);
634 *resultsp = values;
635 }
636
637 /* Done */
638 return (rv);
639 }
640
641 /*
642 * hp_get_private()
643 *
644 * Get bus private options on the hotplug connection
645 * indicated by the given hotplug information node.
646 */
647 int
648 hp_get_private(hp_node_t node, const char *options, char **resultsp)
649 {
650 int rv;
651 nvlist_t *args;
652 nvlist_t *results;
653 char *values = NULL;
654 char path[MAXPATHLEN];
655 char connection[MAXPATHLEN];
656
657 i_hp_dprintf("hp_get_private: node=%p, options=%p, resultsp=%p\n",
658 (void *)node, (void *)options, (void *)resultsp);
659
660 /* Check arguments */
661 if ((node == NULL) || (options == NULL) || (resultsp == NULL)) {
662 i_hp_dprintf("hp_get_private: invalid arguments.\n");
663 return (EINVAL);
664 }
665
666 /* Check node type */
667 if (node->hp_type != HP_NODE_CONNECTOR) {
668 i_hp_dprintf("hp_get_private: operation not supported.\n");
669 return (ENOTSUP);
670 }
671
672 /* Initialize results */
673 *resultsp = NULL;
674
675 /* Get path and connection of specified node */
676 if ((rv = hp_path(node, path, connection)) != 0)
677 return (rv);
678
679 /* Build arguments for door call */
680 if ((args = i_hp_set_args(HP_CMD_GETPRIVATE, path, connection, 0,
681 options, 0)) == NULL)
682 return (ENOMEM);
683
684 /* Make the door call to hotplugd */
685 rv = i_hp_call_hotplugd(args, &results);
686
687 /* Arguments no longer needed */
688 nvlist_free(args);
689
690 /* Parse additional results, if any */
691 if ((rv == 0) && (results != NULL)) {
692 rv = i_hp_parse_results(results, NULL, &values);
693 nvlist_free(results);
694 *resultsp = values;
695 }
696
697 /* Done */
698 return (rv);
699 }
700
701 /*
702 * hp_pack()
703 *
704 * Given the root of a hotplug information snapshot, pack
705 * it into a contiguous byte array so that it is suitable
706 * for network transport.
707 */
708 int
709 hp_pack(hp_node_t root, char **bufp, size_t *lenp)
710 {
711 hp_node_t node;
712 nvlist_t *nvl;
713 char *buf;
714 size_t len;
715 int rv;
716
717 i_hp_dprintf("hp_pack: root=%p, bufp=%p, lenp=%p\n", (void *)root,
718 (void *)bufp, (void *)lenp);
719
720 if ((root == NULL) || (bufp == NULL) || (lenp == NULL)) {
721 i_hp_dprintf("hp_pack: invalid arguments.\n");
722 return (EINVAL);
723 }
724
725 *lenp = 0;
726 *bufp = NULL;
727
728 if (nvlist_alloc(&nvl, 0, 0) != 0) {
729 i_hp_dprintf("hp_pack: nvlist_alloc() failed (%s).\n",
730 strerror(errno));
731 return (ENOMEM);
732 }
733
734 if (root->hp_basepath != NULL) {
735 rv = nvlist_add_string(nvl, HP_INFO_BASE, root->hp_basepath);
736 if (rv != 0) {
737 nvlist_free(nvl);
738 return (rv);
739 }
740 }
741
742 for (node = root; node != NULL; node = node->hp_sibling) {
743 if ((rv = i_hp_pack_branch(node, &buf, &len)) == 0) {
744 rv = nvlist_add_byte_array(nvl, HP_INFO_BRANCH,
745 (uchar_t *)buf, len);
746 free(buf);
747 }
748 if (rv != 0) {
749 nvlist_free(nvl);
750 return (rv);
751 }
752 }
753
754 len = 0;
755 buf = NULL;
756 if ((rv = nvlist_pack(nvl, &buf, &len, NV_ENCODE_NATIVE, 0)) == 0) {
757 *lenp = len;
758 *bufp = buf;
759 }
760
761 nvlist_free(nvl);
762
763 return (rv);
764 }
765
766 /*
767 * hp_unpack()
768 *
769 * Unpack a hotplug information snapshot for normal usage.
770 */
771 int
772 hp_unpack(char *packed_buf, size_t packed_len, hp_node_t *retp)
773 {
774 hp_node_t root;
775 hp_node_t root_list = NULL;
776 hp_node_t prev_root = NULL;
777 nvlist_t *nvl = NULL;
778 nvpair_t *nvp;
779 char *basepath = NULL;
780 int rv;
781
782 i_hp_dprintf("hp_unpack: packed_buf=%p, packed_len=%u, retp=%p\n",
783 (void *)packed_buf, (uint32_t)packed_len, (void *)retp);
784
785 if ((packed_buf == NULL) || (packed_len == 0) || (retp == NULL)) {
786 i_hp_dprintf("hp_unpack: invalid arguments.\n");
787 return (EINVAL);
788 }
789
790 if ((rv = nvlist_unpack(packed_buf, packed_len, &nvl, 0)) != 0)
791 return (rv);
792
793 if (nvlist_next_nvpair(nvl, NULL) == NULL) {
794 nvlist_free(nvl);
795 errno = EINVAL;
796 return (NULL);
797 }
798
799 for (nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) {
800
801 rv = EINVAL;
802
803 if (strcmp(nvpair_name(nvp), HP_INFO_BASE) == 0) {
804 char *val_string;
805
806 if ((rv = nvpair_value_string(nvp, &val_string)) == 0) {
807 if ((basepath = strdup(val_string)) == NULL)
808 rv = ENOMEM;
809 }
810
811 } else if (strcmp(nvpair_name(nvp), HP_INFO_BRANCH) == 0) {
812 size_t len = 0;
813 char *buf = NULL;
814
815 if ((rv = nvpair_value_byte_array(nvp,
816 (uchar_t **)&buf, (uint_t *)&len)) == 0) {
817 rv = i_hp_unpack_branch(buf, len, NULL, &root);
818 }
819
820 if (rv == 0) {
821 if (prev_root) {
822 prev_root->hp_sibling = root;
823 } else {
824 root_list = root;
825 }
826 prev_root = root;
827 }
828 }
829
830 if (rv != 0) {
831 if (basepath)
832 free(basepath);
833 nvlist_free(nvl);
834 hp_fini(root_list);
835 *retp = NULL;
836 return (rv);
837 }
838 }
839
840 /* Store the base path in each root node */
841 if (basepath) {
842 for (root = root_list; root; root = root->hp_sibling)
843 root->hp_basepath = basepath;
844 }
845
846 nvlist_free(nvl);
847 *retp = root_list;
848 return (0);
849 }
850
851 /*
852 * i_hp_dprintf()
853 *
854 * Print debug messages to stderr, but only when the debug flag
855 * (libhotplug_debug) is set.
856 */
857 /*PRINTFLIKE1*/
858 static void
859 i_hp_dprintf(const char *fmt, ...)
860 {
861 va_list ap;
862
863 if (libhotplug_debug) {
864 va_start(ap, fmt);
865 (void) vfprintf(stderr, fmt, ap);
866 va_end(ap);
867 }
868 }
869
870 /*
871 * i_hp_pack_branch()
872 *
873 * Pack an individual branch of a hotplug information snapshot.
874 */
875 static int
876 i_hp_pack_branch(hp_node_t root, char **bufp, size_t *lenp)
877 {
878 hp_node_t child;
879 nvlist_t *nvl;
880 char *buf;
881 size_t len;
882 int rv;
883
884 *lenp = 0;
885 *bufp = NULL;
886
887 /* Allocate an nvlist for this branch */
888 if (nvlist_alloc(&nvl, 0, 0) != 0)
889 return (ENOMEM);
890
891 /* Pack the root of the branch and add it to the nvlist */
892 if ((rv = i_hp_pack_node(root, &buf, &len)) == 0) {
893 rv = nvlist_add_byte_array(nvl, HP_INFO_NODE,
894 (uchar_t *)buf, len);
895 free(buf);
896 }
897 if (rv != 0) {
898 nvlist_free(nvl);
899 return (rv);
900 }
901
902 /* Pack each subordinate branch, and add it to the nvlist */
903 for (child = root->hp_child; child != NULL; child = child->hp_sibling) {
904 if ((rv = i_hp_pack_branch(child, &buf, &len)) == 0) {
905 rv = nvlist_add_byte_array(nvl, HP_INFO_BRANCH,
906 (uchar_t *)buf, len);
907 free(buf);
908 }
909 if (rv != 0) {
910 nvlist_free(nvl);
911 return (rv);
912 }
913 }
914
915 /* Pack the resulting nvlist into a single buffer */
916 len = 0;
917 buf = NULL;
918 if ((rv = nvlist_pack(nvl, &buf, &len, NV_ENCODE_NATIVE, 0)) == 0) {
919 *lenp = len;
920 *bufp = buf;
921 }
922
923 /* Free the nvlist */
924 nvlist_free(nvl);
925
926 return (rv);
927 }
928
929 /*
930 * i_hp_pack_node()
931 *
932 * Pack an individual node of a hotplug information snapshot.
933 */
934 static int
935 i_hp_pack_node(hp_node_t node, char **bufp, size_t *lenp)
936 {
937 nvlist_t *nvl;
938 char *buf = NULL;
939 size_t len = 0;
940 int rv;
941
942 if (nvlist_alloc(&nvl, 0, 0) != 0)
943 return (ENOMEM);
944
945 if ((rv = nvlist_add_uint32(nvl, HP_INFO_TYPE,
946 (uint32_t)node->hp_type)) != 0)
947 goto fail;
948
949 if ((node->hp_name) &&
950 ((rv = nvlist_add_string(nvl, HP_INFO_NAME, node->hp_name)) != 0))
951 goto fail;
952
953 if ((node->hp_usage) &&
954 ((rv = nvlist_add_string(nvl, HP_INFO_USAGE, node->hp_usage)) != 0))
955 goto fail;
956
957 if ((node->hp_description) &&
958 ((rv = nvlist_add_string(nvl, HP_INFO_DESC,
959 node->hp_description)) != 0))
960 goto fail;
961
962 if ((rv = nvlist_add_uint32(nvl, HP_INFO_STATE, node->hp_state)) != 0)
963 goto fail;
964
965 if ((node->hp_last_change != 0) &&
966 ((rv = nvlist_add_uint32(nvl, HP_INFO_TIME,
967 node->hp_last_change)) != 0))
968 goto fail;
969
970 if ((rv = nvlist_pack(nvl, &buf, &len, NV_ENCODE_NATIVE, 0)) != 0)
971 goto fail;
972
973 *bufp = buf;
974 *lenp = len;
975 nvlist_free(nvl);
976 return (0);
977
978 fail:
979 *bufp = NULL;
980 *lenp = 0;
981 nvlist_free(nvl);
982 return (rv);
983 }
984
985 /*
986 * i_hp_unpack_branch()
987 *
988 * Unpack a branch of hotplug information nodes.
989 */
990 static int
991 i_hp_unpack_branch(char *packed_buf, size_t packed_len, hp_node_t parent,
992 hp_node_t *retp)
993 {
994 hp_node_t node = NULL;
995 hp_node_t child;
996 hp_node_t prev_child = NULL;
997 nvlist_t *nvl = NULL;
998 nvpair_t *nvp;
999 char *buf;
1000 size_t len;
1001 int rv;
1002
1003 /* Initialize results */
1004 *retp = NULL;
1005
1006 /* Unpack the nvlist for this branch */
1007 if ((rv = nvlist_unpack(packed_buf, packed_len, &nvl, 0)) != 0)
1008 return (rv);
1009
1010 /*
1011 * Unpack the branch. The first item in the nvlist is
1012 * always the root node. And zero or more subordinate
1013 * branches may be packed afterward.
1014 */
1015 for (nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) {
1016
1017 len = 0;
1018 buf = NULL;
1019
1020 if (strcmp(nvpair_name(nvp), HP_INFO_NODE) == 0) {
1021
1022 /* Check that there is only one root node */
1023 if (node != NULL) {
1024 hp_fini(node);
1025 nvlist_free(nvl);
1026 return (EFAULT);
1027 }
1028
1029 if ((rv = nvpair_value_byte_array(nvp, (uchar_t **)&buf,
1030 (uint_t *)&len)) == 0)
1031 rv = i_hp_unpack_node(buf, len, parent, &node);
1032
1033 if (rv != 0) {
1034 nvlist_free(nvl);
1035 return (rv);
1036 }
1037
1038 } else if (strcmp(nvpair_name(nvp), HP_INFO_BRANCH) == 0) {
1039
1040 if ((rv = nvpair_value_byte_array(nvp, (uchar_t **)&buf,
1041 (uint_t *)&len)) == 0)
1042 rv = i_hp_unpack_branch(buf, len, node, &child);
1043
1044 if (rv != 0) {
1045 hp_fini(node);
1046 nvlist_free(nvl);
1047 return (rv);
1048 }
1049
1050 if (prev_child) {
1051 prev_child->hp_sibling = child;
1052 } else {
1053 node->hp_child = child;
1054 }
1055 prev_child = child;
1056 }
1057 }
1058
1059 nvlist_free(nvl);
1060 *retp = node;
1061 return (0);
1062 }
1063
1064 /*
1065 * i_hp_unpack_node()
1066 *
1067 * Unpack an individual hotplug information node.
1068 */
1069 static int
1070 i_hp_unpack_node(char *buf, size_t len, hp_node_t parent, hp_node_t *retp)
1071 {
1072 hp_node_t node;
1073 nvlist_t *nvl;
1074 nvpair_t *nvp;
1075 uint32_t val_uint32;
1076 char *val_string;
1077 int rv = 0;
1078
1079 /* Initialize results */
1080 *retp = NULL;
1081
1082 /* Unpack node into an nvlist */
1083 if ((nvlist_unpack(buf, len, &nvl, 0) != 0))
1084 return (EINVAL);
1085
1086 /* Allocate the new node */
1087 if ((node = (hp_node_t)calloc(1, sizeof (struct hp_node))) == NULL) {
1088 nvlist_free(nvl);
1089 return (ENOMEM);
1090 }
1091
1092 /* Iterate through nvlist, unpacking each field */
1093 for (nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) {
1094
1095 if ((strcmp(nvpair_name(nvp), HP_INFO_TYPE) == 0) &&
1096 (nvpair_type(nvp) == DATA_TYPE_UINT32)) {
1097
1098 (void) nvpair_value_uint32(nvp, &val_uint32);
1099 node->hp_type = val_uint32;
1100
1101 } else if ((strcmp(nvpair_name(nvp), HP_INFO_NAME) == 0) &&
1102 (nvpair_type(nvp) == DATA_TYPE_STRING)) {
1103
1104 (void) nvpair_value_string(nvp, &val_string);
1105 if ((node->hp_name = strdup(val_string)) == NULL) {
1106 rv = ENOMEM;
1107 break;
1108 }
1109
1110 } else if ((strcmp(nvpair_name(nvp), HP_INFO_STATE) == 0) &&
1111 (nvpair_type(nvp) == DATA_TYPE_UINT32)) {
1112
1113 (void) nvpair_value_uint32(nvp, &val_uint32);
1114 node->hp_state = val_uint32;
1115
1116 } else if ((strcmp(nvpair_name(nvp), HP_INFO_USAGE) == 0) &&
1117 (nvpair_type(nvp) == DATA_TYPE_STRING)) {
1118
1119 (void) nvpair_value_string(nvp, &val_string);
1120 if ((node->hp_usage = strdup(val_string)) == NULL) {
1121 rv = ENOMEM;
1122 break;
1123 }
1124
1125 } else if ((strcmp(nvpair_name(nvp), HP_INFO_DESC) == 0) &&
1126 (nvpair_type(nvp) == DATA_TYPE_STRING)) {
1127
1128 (void) nvpair_value_string(nvp, &val_string);
1129 if ((node->hp_description = strdup(val_string))
1130 == NULL) {
1131 rv = ENOMEM;
1132 break;
1133 }
1134
1135 } else if ((strcmp(nvpair_name(nvp), HP_INFO_TIME) == 0) &&
1136 (nvpair_type(nvp) == DATA_TYPE_UINT32)) {
1137
1138 (void) nvpair_value_uint32(nvp, &val_uint32);
1139 node->hp_last_change = (time_t)val_uint32;
1140
1141 } else {
1142 i_hp_dprintf("i_hp_unpack_node: unrecognized: '%s'\n",
1143 nvpair_name(nvp));
1144 }
1145 }
1146
1147 /* Unpacked nvlist no longer needed */
1148 nvlist_free(nvl);
1149
1150 /* Check for errors */
1151 if (rv != 0) {
1152 hp_fini(node);
1153 return (rv);
1154 }
1155
1156 /* Success */
1157 node->hp_parent = parent;
1158 *retp = node;
1159 return (0);
1160 }
1161
1162 /*
1163 * i_hp_call_hotplugd()
1164 *
1165 * Perform a door call to the hotplug daemon.
1166 */
1167 static int
1168 i_hp_call_hotplugd(nvlist_t *args, nvlist_t **resultsp)
1169 {
1170 door_arg_t door_arg;
1171 nvlist_t *results = NULL;
1172 char *buf = NULL;
1173 size_t len = 0;
1174 uint64_t seqnum;
1175 int door_fd;
1176 int rv;
1177
1178 /* Initialize results */
1179 *resultsp = NULL;
1180
1181 /* Open door */
1182 if ((door_fd = open(HOTPLUGD_DOOR, O_RDONLY)) < 0) {
1183 i_hp_dprintf("i_hp_call_hotplugd: cannot open door (%s)\n",
1184 strerror(errno));
1185 return (EBADF);
1186 }
1187
1188 /* Pack the nvlist of arguments */
1189 if ((rv = nvlist_pack(args, &buf, &len, NV_ENCODE_NATIVE, 0)) != 0) {
1190 i_hp_dprintf("i_hp_call_hotplugd: cannot pack arguments (%s)\n",
1191 strerror(rv));
1192 return (rv);
1193 }
1194
1195 /* Set the door argument using the packed arguments */
1196 door_arg.data_ptr = buf;
1197 door_arg.data_size = len;
1198 door_arg.desc_ptr = NULL;
1199 door_arg.desc_num = 0;
1200 door_arg.rbuf = (char *)(uintptr_t)&rv;
1201 door_arg.rsize = sizeof (rv);
1202
1203 /* Attempt the door call */
1204 if (door_call(door_fd, &door_arg) != 0) {
1205 rv = errno;
1206 i_hp_dprintf("i_hp_call_hotplugd: door call failed (%s)\n",
1207 strerror(rv));
1208 (void) close(door_fd);
1209 free(buf);
1210 return (rv);
1211 }
1212
1213 /* The arguments are no longer needed */
1214 free(buf);
1215
1216 /*
1217 * If results are not in the original buffer provided,
1218 * then check and process the new results buffer.
1219 */
1220 if (door_arg.rbuf != (char *)(uintptr_t)&rv) {
1221
1222 /*
1223 * First check that the buffer is valid. Then check for
1224 * the simple case where a short result code was sent.
1225 * The last case is a packed nvlist was returned, which
1226 * needs to be unpacked.
1227 */
1228 if ((door_arg.rbuf == NULL) ||
1229 (door_arg.data_size < sizeof (rv))) {
1230 i_hp_dprintf("i_hp_call_hotplugd: invalid results.\n");
1231 rv = EFAULT;
1232
1233 } else if (door_arg.data_size == sizeof (rv)) {
1234 rv = *(int *)(uintptr_t)door_arg.rbuf;
1235
1236 } else if ((rv = nvlist_unpack(door_arg.rbuf,
1237 door_arg.data_size, &results, 0)) != 0) {
1238 i_hp_dprintf("i_hp_call_hotplugd: "
1239 "cannot unpack results (%s).\n", strerror(rv));
1240 results = NULL;
1241 rv = EFAULT;
1242 }
1243
1244 /* Unmap the results buffer */
1245 if (door_arg.rbuf != NULL)
1246 (void) munmap(door_arg.rbuf, door_arg.rsize);
1247
1248 /*
1249 * In the case of a packed nvlist, notify the daemon
1250 * that it can free the result buffer from its heap.
1251 */
1252 if ((results != NULL) &&
1253 (nvlist_lookup_uint64(results, HPD_SEQNUM, &seqnum) == 0)) {
1254 door_arg.data_ptr = (char *)(uintptr_t)&seqnum;
1255 door_arg.data_size = sizeof (seqnum);
1256 door_arg.desc_ptr = NULL;
1257 door_arg.desc_num = 0;
1258 door_arg.rbuf = NULL;
1259 door_arg.rsize = 0;
1260 (void) door_call(door_fd, &door_arg);
1261 if (door_arg.rbuf != NULL)
1262 (void) munmap(door_arg.rbuf, door_arg.rsize);
1263 }
1264
1265 *resultsp = results;
1266 }
1267
1268 (void) close(door_fd);
1269 return (rv);
1270 }
1271
1272 /*
1273 * i_hp_set_args()
1274 *
1275 * Construct an nvlist of arguments for a hotplugd door call.
1276 */
1277 static nvlist_t *
1278 i_hp_set_args(hp_cmd_t cmd, const char *path, const char *connection,
1279 uint_t flags, const char *options, int state)
1280 {
1281 nvlist_t *args;
1282
1283 /* Allocate a new nvlist */
1284 if (nvlist_alloc(&args, NV_UNIQUE_NAME_TYPE, 0) != 0)
1285 return (NULL);
1286
1287 /* Add common arguments */
1288 if ((nvlist_add_int32(args, HPD_CMD, cmd) != 0) ||
1289 (nvlist_add_string(args, HPD_PATH, path) != 0)) {
1290 nvlist_free(args);
1291 return (NULL);
1292 }
1293
1294 /* Add connection, but only if defined */
1295 if ((connection != NULL) && (connection[0] != '\0') &&
1296 (nvlist_add_string(args, HPD_CONNECTION, connection) != 0)) {
1297 nvlist_free(args);
1298 return (NULL);
1299 }
1300
1301 /* Add flags, but only if defined */
1302 if ((flags != 0) && (nvlist_add_uint32(args, HPD_FLAGS, flags) != 0)) {
1303 nvlist_free(args);
1304 return (NULL);
1305 }
1306
1307 /* Add options, but only if defined */
1308 if ((options != NULL) &&
1309 (nvlist_add_string(args, HPD_OPTIONS, options) != 0)) {
1310 nvlist_free(args);
1311 return (NULL);
1312 }
1313
1314 /* Add state, but only for CHANGESTATE command */
1315 if ((cmd == HP_CMD_CHANGESTATE) &&
1316 (nvlist_add_int32(args, HPD_STATE, state) != 0)) {
1317 nvlist_free(args);
1318 return (NULL);
1319 }
1320
1321 return (args);
1322 }
1323
1324 /*
1325 * i_hp_parse_results()
1326 *
1327 * Parse out individual fields of an nvlist of results from
1328 * a hotplugd door call.
1329 */
1330 static int
1331 i_hp_parse_results(nvlist_t *results, hp_node_t *rootp, char **optionsp)
1332 {
1333 int rv;
1334
1335 /* Parse an information snapshot */
1336 if (rootp) {
1337 char *buf = NULL;
1338 size_t len = 0;
1339
1340 *rootp = NULL;
1341 if (nvlist_lookup_byte_array(results, HPD_INFO,
1342 (uchar_t **)&buf, (uint_t *)&len) == 0) {
1343 if ((rv = hp_unpack(buf, len, rootp)) != 0)
1344 return (rv);
1345 }
1346 }
1347
1348 /* Parse a bus private option string */
1349 if (optionsp) {
1350 char *str;
1351
1352 *optionsp = NULL;
1353 if ((nvlist_lookup_string(results, HPD_OPTIONS, &str) == 0) &&
1354 ((*optionsp = strdup(str)) == NULL)) {
1355 return (ENOMEM);
1356 }
1357 }
1358
1359 /* Parse result code of the operation */
1360 if (nvlist_lookup_int32(results, HPD_STATUS, &rv) != 0) {
1361 i_hp_dprintf("i_hp_call_hotplugd: missing status.\n");
1362 return (EFAULT);
1363 }
1364
1365 return (rv);
1366 }