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
hp_init(const char * path,const char * connection,uint_t flags)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
hp_fini(hp_node_t root)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
hp_traverse(hp_node_t root,void * arg,int (* hp_callback)(hp_node_t,void * arg))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
hp_type(hp_node_t node)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 *
hp_name(hp_node_t node)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
hp_state(hp_node_t node)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 *
hp_usage(hp_node_t node)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 *
hp_description(hp_node_t node)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
hp_last_change(hp_node_t node)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 (0);
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 (0);
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
hp_parent(hp_node_t node)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
hp_child(hp_node_t node)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
hp_sibling(hp_node_t node)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
hp_path(hp_node_t node,char * path,char * connection)440 hp_path(hp_node_t node, char *path, char *connection)
441 {
442 hp_node_t root = NULL;
443 hp_node_t parent;
444 int i;
445 char *s;
446 char components[MAXPATHLEN];
447
448 i_hp_dprintf("hp_path: node=%p, path=%p, connection=%p\n", (void *)node,
449 (void *)path, (void *)connection);
450
451 if ((node == NULL) || (path == NULL) || (connection == NULL)) {
452 i_hp_dprintf("hp_path: invalid arguments.\n");
453 return (EINVAL);
454 }
455
456 (void) memset(path, 0, MAXPATHLEN);
457 (void) memset(connection, 0, MAXPATHLEN);
458 (void) memset(components, 0, MAXPATHLEN);
459
460 /* Set 'connection' only for connectors and ports */
461 if ((node->hp_type == HP_NODE_CONNECTOR) ||
462 (node->hp_type == HP_NODE_PORT))
463 (void) strlcpy(connection, node->hp_name, MAXPATHLEN);
464
465 /* Trace back to the root node, accumulating components */
466 for (parent = node; parent != NULL; parent = parent->hp_parent) {
467 if (parent->hp_type == HP_NODE_DEVICE) {
468 (void) strlcat(components, "/", MAXPATHLEN);
469 (void) strlcat(components, parent->hp_name, MAXPATHLEN);
470 }
471 if (parent->hp_parent == NULL)
472 root = parent;
473 }
474
475 /* Ensure the snapshot actually contains a base path */
476 if (root->hp_basepath == NULL) {
477 i_hp_dprintf("hp_path: missing base pathname.\n");
478 return (EFAULT);
479 }
480
481 /*
482 * Construct the path. Start with the base path from the root
483 * node, then append the accumulated components in reverse order.
484 */
485 if (strcmp(root->hp_basepath, "/") != 0) {
486 (void) strlcat(path, root->hp_basepath, MAXPATHLEN);
487 if ((root->hp_type == HP_NODE_DEVICE) &&
488 ((s = strrchr(path, '/')) != NULL))
489 *s = '\0';
490 }
491 for (i = strlen(components) - 1; i >= 0; i--) {
492 if (components[i] == '/') {
493 (void) strlcat(path, &components[i], MAXPATHLEN);
494 components[i] = '\0';
495 }
496 }
497
498 return (0);
499 }
500
501 /*
502 * hp_set_state()
503 *
504 * Initiate a state change operation on a node.
505 */
506 int
hp_set_state(hp_node_t node,uint_t flags,int state,hp_node_t * resultsp)507 hp_set_state(hp_node_t node, uint_t flags, int state, hp_node_t *resultsp)
508 {
509 hp_node_t root = NULL;
510 nvlist_t *args;
511 nvlist_t *results;
512 int rv;
513 char path[MAXPATHLEN];
514 char connection[MAXPATHLEN];
515
516 i_hp_dprintf("hp_set_state: node=%p, flags=0x%x, state=0x%x, "
517 "resultsp=%p\n", (void *)node, flags, state, (void *)resultsp);
518
519 /* Check arguments */
520 if ((node == NULL) || (resultsp == NULL) ||
521 !HP_SET_STATE_FLAGS_VALID(flags)) {
522 i_hp_dprintf("hp_set_state: invalid arguments.\n");
523 return (EINVAL);
524 }
525
526 /* Check node type */
527 if ((node->hp_type != HP_NODE_CONNECTOR) &&
528 (node->hp_type != HP_NODE_PORT)) {
529 i_hp_dprintf("hp_set_state: operation not supported.\n");
530 return (ENOTSUP);
531 }
532
533 /* Check that target state is valid */
534 switch (state) {
535 case DDI_HP_CN_STATE_PRESENT:
536 case DDI_HP_CN_STATE_POWERED:
537 case DDI_HP_CN_STATE_ENABLED:
538 if (node->hp_type != HP_NODE_CONNECTOR) {
539 i_hp_dprintf("hp_set_state: mismatched target.\n");
540 return (ENOTSUP);
541 }
542 break;
543 case DDI_HP_CN_STATE_PORT_PRESENT:
544 case DDI_HP_CN_STATE_OFFLINE:
545 case DDI_HP_CN_STATE_ONLINE:
546 if (node->hp_type != HP_NODE_PORT) {
547 i_hp_dprintf("hp_set_state: mismatched target.\n");
548 return (ENOTSUP);
549 }
550 break;
551 default:
552 i_hp_dprintf("hp_set_state: invalid target state.\n");
553 return (EINVAL);
554 }
555
556 /* Get path and connection of specified node */
557 if ((rv = hp_path(node, path, connection)) != 0)
558 return (rv);
559
560 /* Build arguments for door call */
561 if ((args = i_hp_set_args(HP_CMD_CHANGESTATE, path, connection, flags,
562 NULL, state)) == NULL)
563 return (ENOMEM);
564
565 /* Make the door call to hotplugd */
566 rv = i_hp_call_hotplugd(args, &results);
567
568 /* Arguments no longer needed */
569 nvlist_free(args);
570
571 /* Parse additional results, if any */
572 if ((rv == 0) && (results != NULL)) {
573 rv = i_hp_parse_results(results, &root, NULL);
574 nvlist_free(results);
575 *resultsp = root;
576 }
577
578 /* Done */
579 return (rv);
580 }
581
582 /*
583 * hp_set_private()
584 *
585 * Set bus private options on the hotplug connection
586 * indicated by the given hotplug information node.
587 */
588 int
hp_set_private(hp_node_t node,const char * options,char ** resultsp)589 hp_set_private(hp_node_t node, const char *options, char **resultsp)
590 {
591 int rv;
592 nvlist_t *args;
593 nvlist_t *results;
594 char *values = NULL;
595 char path[MAXPATHLEN];
596 char connection[MAXPATHLEN];
597
598 i_hp_dprintf("hp_set_private: node=%p, options=%p, resultsp=%p\n",
599 (void *)node, (void *)options, (void *)resultsp);
600
601 /* Check arguments */
602 if ((node == NULL) || (options == NULL) || (resultsp == NULL)) {
603 i_hp_dprintf("hp_set_private: invalid arguments.\n");
604 return (EINVAL);
605 }
606
607 /* Check node type */
608 if (node->hp_type != HP_NODE_CONNECTOR) {
609 i_hp_dprintf("hp_set_private: operation not supported.\n");
610 return (ENOTSUP);
611 }
612
613 /* Initialize results */
614 *resultsp = NULL;
615
616 /* Get path and connection of specified node */
617 if ((rv = hp_path(node, path, connection)) != 0)
618 return (rv);
619
620 /* Build arguments for door call */
621 if ((args = i_hp_set_args(HP_CMD_SETPRIVATE, path, connection, 0,
622 options, 0)) == NULL)
623 return (ENOMEM);
624
625 /* Make the door call to hotplugd */
626 rv = i_hp_call_hotplugd(args, &results);
627
628 /* Arguments no longer needed */
629 nvlist_free(args);
630
631 /* Parse additional results, if any */
632 if ((rv == 0) && (results != NULL)) {
633 rv = i_hp_parse_results(results, NULL, &values);
634 nvlist_free(results);
635 *resultsp = values;
636 }
637
638 /* Done */
639 return (rv);
640 }
641
642 /*
643 * hp_get_private()
644 *
645 * Get bus private options on the hotplug connection
646 * indicated by the given hotplug information node.
647 */
648 int
hp_get_private(hp_node_t node,const char * options,char ** resultsp)649 hp_get_private(hp_node_t node, const char *options, char **resultsp)
650 {
651 int rv;
652 nvlist_t *args;
653 nvlist_t *results;
654 char *values = NULL;
655 char path[MAXPATHLEN];
656 char connection[MAXPATHLEN];
657
658 i_hp_dprintf("hp_get_private: node=%p, options=%p, resultsp=%p\n",
659 (void *)node, (void *)options, (void *)resultsp);
660
661 /* Check arguments */
662 if ((node == NULL) || (options == NULL) || (resultsp == NULL)) {
663 i_hp_dprintf("hp_get_private: invalid arguments.\n");
664 return (EINVAL);
665 }
666
667 /* Check node type */
668 if (node->hp_type != HP_NODE_CONNECTOR) {
669 i_hp_dprintf("hp_get_private: operation not supported.\n");
670 return (ENOTSUP);
671 }
672
673 /* Initialize results */
674 *resultsp = NULL;
675
676 /* Get path and connection of specified node */
677 if ((rv = hp_path(node, path, connection)) != 0)
678 return (rv);
679
680 /* Build arguments for door call */
681 if ((args = i_hp_set_args(HP_CMD_GETPRIVATE, path, connection, 0,
682 options, 0)) == NULL)
683 return (ENOMEM);
684
685 /* Make the door call to hotplugd */
686 rv = i_hp_call_hotplugd(args, &results);
687
688 /* Arguments no longer needed */
689 nvlist_free(args);
690
691 /* Parse additional results, if any */
692 if ((rv == 0) && (results != NULL)) {
693 rv = i_hp_parse_results(results, NULL, &values);
694 nvlist_free(results);
695 *resultsp = values;
696 }
697
698 /* Done */
699 return (rv);
700 }
701
702 /*
703 * hp_pack()
704 *
705 * Given the root of a hotplug information snapshot, pack
706 * it into a contiguous byte array so that it is suitable
707 * for network transport.
708 */
709 int
hp_pack(hp_node_t root,char ** bufp,size_t * lenp)710 hp_pack(hp_node_t root, char **bufp, size_t *lenp)
711 {
712 hp_node_t node;
713 nvlist_t *nvl;
714 char *buf;
715 size_t len;
716 int rv;
717
718 i_hp_dprintf("hp_pack: root=%p, bufp=%p, lenp=%p\n", (void *)root,
719 (void *)bufp, (void *)lenp);
720
721 if ((root == NULL) || (bufp == NULL) || (lenp == NULL)) {
722 i_hp_dprintf("hp_pack: invalid arguments.\n");
723 return (EINVAL);
724 }
725
726 *lenp = 0;
727 *bufp = NULL;
728
729 if (nvlist_alloc(&nvl, 0, 0) != 0) {
730 i_hp_dprintf("hp_pack: nvlist_alloc() failed (%s).\n",
731 strerror(errno));
732 return (ENOMEM);
733 }
734
735 if (root->hp_basepath != NULL) {
736 rv = nvlist_add_string(nvl, HP_INFO_BASE, root->hp_basepath);
737 if (rv != 0) {
738 nvlist_free(nvl);
739 return (rv);
740 }
741 }
742
743 for (node = root; node != NULL; node = node->hp_sibling) {
744 if ((rv = i_hp_pack_branch(node, &buf, &len)) == 0) {
745 rv = nvlist_add_byte_array(nvl, HP_INFO_BRANCH,
746 (uchar_t *)buf, len);
747 free(buf);
748 }
749 if (rv != 0) {
750 nvlist_free(nvl);
751 return (rv);
752 }
753 }
754
755 len = 0;
756 buf = NULL;
757 if ((rv = nvlist_pack(nvl, &buf, &len, NV_ENCODE_NATIVE, 0)) == 0) {
758 *lenp = len;
759 *bufp = buf;
760 }
761
762 nvlist_free(nvl);
763
764 return (rv);
765 }
766
767 /*
768 * hp_unpack()
769 *
770 * Unpack a hotplug information snapshot for normal usage.
771 */
772 int
hp_unpack(char * packed_buf,size_t packed_len,hp_node_t * retp)773 hp_unpack(char *packed_buf, size_t packed_len, hp_node_t *retp)
774 {
775 hp_node_t root;
776 hp_node_t root_list = NULL;
777 hp_node_t prev_root = NULL;
778 nvlist_t *nvl = NULL;
779 nvpair_t *nvp;
780 char *basepath = NULL;
781 int rv;
782
783 i_hp_dprintf("hp_unpack: packed_buf=%p, packed_len=%u, retp=%p\n",
784 (void *)packed_buf, (uint32_t)packed_len, (void *)retp);
785
786 if ((packed_buf == NULL) || (packed_len == 0) || (retp == NULL)) {
787 i_hp_dprintf("hp_unpack: invalid arguments.\n");
788 return (EINVAL);
789 }
790
791 if ((rv = nvlist_unpack(packed_buf, packed_len, &nvl, 0)) != 0)
792 return (rv);
793
794 if (nvlist_next_nvpair(nvl, NULL) == NULL) {
795 nvlist_free(nvl);
796 errno = EINVAL;
797 return (0);
798 }
799
800 for (nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) {
801
802 rv = EINVAL;
803
804 if (strcmp(nvpair_name(nvp), HP_INFO_BASE) == 0) {
805 char *val_string;
806
807 if ((rv = nvpair_value_string(nvp, &val_string)) == 0) {
808 if ((basepath = strdup(val_string)) == NULL)
809 rv = ENOMEM;
810 }
811
812 } else if (strcmp(nvpair_name(nvp), HP_INFO_BRANCH) == 0) {
813 size_t len = 0;
814 char *buf = NULL;
815
816 if ((rv = nvpair_value_byte_array(nvp,
817 (uchar_t **)&buf, (uint_t *)&len)) == 0) {
818 rv = i_hp_unpack_branch(buf, len, NULL, &root);
819 }
820
821 if (rv == 0) {
822 if (prev_root) {
823 prev_root->hp_sibling = root;
824 } else {
825 root_list = root;
826 }
827 prev_root = root;
828 }
829 }
830
831 if (rv != 0) {
832 if (basepath)
833 free(basepath);
834 nvlist_free(nvl);
835 hp_fini(root_list);
836 *retp = NULL;
837 return (rv);
838 }
839 }
840
841 /* Store the base path in each root node */
842 if (basepath) {
843 for (root = root_list; root; root = root->hp_sibling)
844 root->hp_basepath = basepath;
845 }
846
847 nvlist_free(nvl);
848 *retp = root_list;
849 return (0);
850 }
851
852 /*
853 * i_hp_dprintf()
854 *
855 * Print debug messages to stderr, but only when the debug flag
856 * (libhotplug_debug) is set.
857 */
858 /*PRINTFLIKE1*/
859 static void
i_hp_dprintf(const char * fmt,...)860 i_hp_dprintf(const char *fmt, ...)
861 {
862 va_list ap;
863
864 if (libhotplug_debug) {
865 va_start(ap, fmt);
866 (void) vfprintf(stderr, fmt, ap);
867 va_end(ap);
868 }
869 }
870
871 /*
872 * i_hp_pack_branch()
873 *
874 * Pack an individual branch of a hotplug information snapshot.
875 */
876 static int
i_hp_pack_branch(hp_node_t root,char ** bufp,size_t * lenp)877 i_hp_pack_branch(hp_node_t root, char **bufp, size_t *lenp)
878 {
879 hp_node_t child;
880 nvlist_t *nvl;
881 char *buf;
882 size_t len;
883 int rv;
884
885 *lenp = 0;
886 *bufp = NULL;
887
888 /* Allocate an nvlist for this branch */
889 if (nvlist_alloc(&nvl, 0, 0) != 0)
890 return (ENOMEM);
891
892 /* Pack the root of the branch and add it to the nvlist */
893 if ((rv = i_hp_pack_node(root, &buf, &len)) == 0) {
894 rv = nvlist_add_byte_array(nvl, HP_INFO_NODE,
895 (uchar_t *)buf, len);
896 free(buf);
897 }
898 if (rv != 0) {
899 nvlist_free(nvl);
900 return (rv);
901 }
902
903 /* Pack each subordinate branch, and add it to the nvlist */
904 for (child = root->hp_child; child != NULL; child = child->hp_sibling) {
905 if ((rv = i_hp_pack_branch(child, &buf, &len)) == 0) {
906 rv = nvlist_add_byte_array(nvl, HP_INFO_BRANCH,
907 (uchar_t *)buf, len);
908 free(buf);
909 }
910 if (rv != 0) {
911 nvlist_free(nvl);
912 return (rv);
913 }
914 }
915
916 /* Pack the resulting nvlist into a single buffer */
917 len = 0;
918 buf = NULL;
919 if ((rv = nvlist_pack(nvl, &buf, &len, NV_ENCODE_NATIVE, 0)) == 0) {
920 *lenp = len;
921 *bufp = buf;
922 }
923
924 /* Free the nvlist */
925 nvlist_free(nvl);
926
927 return (rv);
928 }
929
930 /*
931 * i_hp_pack_node()
932 *
933 * Pack an individual node of a hotplug information snapshot.
934 */
935 static int
i_hp_pack_node(hp_node_t node,char ** bufp,size_t * lenp)936 i_hp_pack_node(hp_node_t node, char **bufp, size_t *lenp)
937 {
938 nvlist_t *nvl;
939 char *buf = NULL;
940 size_t len = 0;
941 int rv;
942
943 if (nvlist_alloc(&nvl, 0, 0) != 0)
944 return (ENOMEM);
945
946 if ((rv = nvlist_add_uint32(nvl, HP_INFO_TYPE,
947 (uint32_t)node->hp_type)) != 0)
948 goto fail;
949
950 if ((node->hp_name) &&
951 ((rv = nvlist_add_string(nvl, HP_INFO_NAME, node->hp_name)) != 0))
952 goto fail;
953
954 if ((node->hp_usage) &&
955 ((rv = nvlist_add_string(nvl, HP_INFO_USAGE, node->hp_usage)) != 0))
956 goto fail;
957
958 if ((node->hp_description) &&
959 ((rv = nvlist_add_string(nvl, HP_INFO_DESC,
960 node->hp_description)) != 0))
961 goto fail;
962
963 if ((rv = nvlist_add_uint32(nvl, HP_INFO_STATE, node->hp_state)) != 0)
964 goto fail;
965
966 if ((node->hp_last_change != 0) &&
967 ((rv = nvlist_add_uint32(nvl, HP_INFO_TIME,
968 node->hp_last_change)) != 0))
969 goto fail;
970
971 if ((rv = nvlist_pack(nvl, &buf, &len, NV_ENCODE_NATIVE, 0)) != 0)
972 goto fail;
973
974 *bufp = buf;
975 *lenp = len;
976 nvlist_free(nvl);
977 return (0);
978
979 fail:
980 *bufp = NULL;
981 *lenp = 0;
982 nvlist_free(nvl);
983 return (rv);
984 }
985
986 /*
987 * i_hp_unpack_branch()
988 *
989 * Unpack a branch of hotplug information nodes.
990 */
991 static int
i_hp_unpack_branch(char * packed_buf,size_t packed_len,hp_node_t parent,hp_node_t * retp)992 i_hp_unpack_branch(char *packed_buf, size_t packed_len, hp_node_t parent,
993 hp_node_t *retp)
994 {
995 hp_node_t node = NULL;
996 hp_node_t child;
997 hp_node_t prev_child = NULL;
998 nvlist_t *nvl = NULL;
999 nvpair_t *nvp;
1000 char *buf;
1001 size_t len;
1002 int rv;
1003
1004 /* Initialize results */
1005 *retp = NULL;
1006
1007 /* Unpack the nvlist for this branch */
1008 if ((rv = nvlist_unpack(packed_buf, packed_len, &nvl, 0)) != 0)
1009 return (rv);
1010
1011 /*
1012 * Unpack the branch. The first item in the nvlist is
1013 * always the root node. And zero or more subordinate
1014 * branches may be packed afterward.
1015 */
1016 for (nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) {
1017
1018 len = 0;
1019 buf = NULL;
1020
1021 if (strcmp(nvpair_name(nvp), HP_INFO_NODE) == 0) {
1022
1023 /* Check that there is only one root node */
1024 if (node != NULL) {
1025 hp_fini(node);
1026 nvlist_free(nvl);
1027 return (EFAULT);
1028 }
1029
1030 if ((rv = nvpair_value_byte_array(nvp, (uchar_t **)&buf,
1031 (uint_t *)&len)) == 0)
1032 rv = i_hp_unpack_node(buf, len, parent, &node);
1033
1034 if (rv != 0) {
1035 nvlist_free(nvl);
1036 return (rv);
1037 }
1038
1039 } else if (strcmp(nvpair_name(nvp), HP_INFO_BRANCH) == 0) {
1040
1041 if ((rv = nvpair_value_byte_array(nvp, (uchar_t **)&buf,
1042 (uint_t *)&len)) == 0)
1043 rv = i_hp_unpack_branch(buf, len, node, &child);
1044
1045 if (rv != 0) {
1046 hp_fini(node);
1047 nvlist_free(nvl);
1048 return (rv);
1049 }
1050
1051 if (prev_child) {
1052 prev_child->hp_sibling = child;
1053 } else {
1054 node->hp_child = child;
1055 }
1056 prev_child = child;
1057 }
1058 }
1059
1060 nvlist_free(nvl);
1061 *retp = node;
1062 return (0);
1063 }
1064
1065 /*
1066 * i_hp_unpack_node()
1067 *
1068 * Unpack an individual hotplug information node.
1069 */
1070 static int
i_hp_unpack_node(char * buf,size_t len,hp_node_t parent,hp_node_t * retp)1071 i_hp_unpack_node(char *buf, size_t len, hp_node_t parent, hp_node_t *retp)
1072 {
1073 hp_node_t node;
1074 nvlist_t *nvl;
1075 nvpair_t *nvp;
1076 uint32_t val_uint32;
1077 char *val_string;
1078 int rv = 0;
1079
1080 /* Initialize results */
1081 *retp = NULL;
1082
1083 /* Unpack node into an nvlist */
1084 if ((nvlist_unpack(buf, len, &nvl, 0) != 0))
1085 return (EINVAL);
1086
1087 /* Allocate the new node */
1088 if ((node = (hp_node_t)calloc(1, sizeof (struct hp_node))) == NULL) {
1089 nvlist_free(nvl);
1090 return (ENOMEM);
1091 }
1092
1093 /* Iterate through nvlist, unpacking each field */
1094 for (nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) {
1095
1096 if ((strcmp(nvpair_name(nvp), HP_INFO_TYPE) == 0) &&
1097 (nvpair_type(nvp) == DATA_TYPE_UINT32)) {
1098
1099 (void) nvpair_value_uint32(nvp, &val_uint32);
1100 node->hp_type = val_uint32;
1101
1102 } else if ((strcmp(nvpair_name(nvp), HP_INFO_NAME) == 0) &&
1103 (nvpair_type(nvp) == DATA_TYPE_STRING)) {
1104
1105 (void) nvpair_value_string(nvp, &val_string);
1106 if ((node->hp_name = strdup(val_string)) == NULL) {
1107 rv = ENOMEM;
1108 break;
1109 }
1110
1111 } else if ((strcmp(nvpair_name(nvp), HP_INFO_STATE) == 0) &&
1112 (nvpair_type(nvp) == DATA_TYPE_UINT32)) {
1113
1114 (void) nvpair_value_uint32(nvp, &val_uint32);
1115 node->hp_state = val_uint32;
1116
1117 } else if ((strcmp(nvpair_name(nvp), HP_INFO_USAGE) == 0) &&
1118 (nvpair_type(nvp) == DATA_TYPE_STRING)) {
1119
1120 (void) nvpair_value_string(nvp, &val_string);
1121 if ((node->hp_usage = strdup(val_string)) == NULL) {
1122 rv = ENOMEM;
1123 break;
1124 }
1125
1126 } else if ((strcmp(nvpair_name(nvp), HP_INFO_DESC) == 0) &&
1127 (nvpair_type(nvp) == DATA_TYPE_STRING)) {
1128
1129 (void) nvpair_value_string(nvp, &val_string);
1130 if ((node->hp_description = strdup(val_string))
1131 == NULL) {
1132 rv = ENOMEM;
1133 break;
1134 }
1135
1136 } else if ((strcmp(nvpair_name(nvp), HP_INFO_TIME) == 0) &&
1137 (nvpair_type(nvp) == DATA_TYPE_UINT32)) {
1138
1139 (void) nvpair_value_uint32(nvp, &val_uint32);
1140 node->hp_last_change = (time_t)val_uint32;
1141
1142 } else {
1143 i_hp_dprintf("i_hp_unpack_node: unrecognized: '%s'\n",
1144 nvpair_name(nvp));
1145 }
1146 }
1147
1148 /* Unpacked nvlist no longer needed */
1149 nvlist_free(nvl);
1150
1151 /* Check for errors */
1152 if (rv != 0) {
1153 hp_fini(node);
1154 return (rv);
1155 }
1156
1157 /* Success */
1158 node->hp_parent = parent;
1159 *retp = node;
1160 return (0);
1161 }
1162
1163 /*
1164 * i_hp_call_hotplugd()
1165 *
1166 * Perform a door call to the hotplug daemon.
1167 */
1168 static int
i_hp_call_hotplugd(nvlist_t * args,nvlist_t ** resultsp)1169 i_hp_call_hotplugd(nvlist_t *args, nvlist_t **resultsp)
1170 {
1171 door_arg_t door_arg;
1172 nvlist_t *results = NULL;
1173 char *buf = NULL;
1174 size_t len = 0;
1175 uint64_t seqnum;
1176 int door_fd;
1177 int rv;
1178
1179 /* Initialize results */
1180 *resultsp = NULL;
1181
1182 /* Open door */
1183 if ((door_fd = open(HOTPLUGD_DOOR, O_RDONLY)) < 0) {
1184 i_hp_dprintf("i_hp_call_hotplugd: cannot open door (%s)\n",
1185 strerror(errno));
1186 return (EBADF);
1187 }
1188
1189 /* Pack the nvlist of arguments */
1190 if ((rv = nvlist_pack(args, &buf, &len, NV_ENCODE_NATIVE, 0)) != 0) {
1191 i_hp_dprintf("i_hp_call_hotplugd: cannot pack arguments (%s)\n",
1192 strerror(rv));
1193 return (rv);
1194 }
1195
1196 /* Set the door argument using the packed arguments */
1197 door_arg.data_ptr = buf;
1198 door_arg.data_size = len;
1199 door_arg.desc_ptr = NULL;
1200 door_arg.desc_num = 0;
1201 door_arg.rbuf = (char *)(uintptr_t)&rv;
1202 door_arg.rsize = sizeof (rv);
1203
1204 /* Attempt the door call */
1205 if (door_call(door_fd, &door_arg) != 0) {
1206 rv = errno;
1207 i_hp_dprintf("i_hp_call_hotplugd: door call failed (%s)\n",
1208 strerror(rv));
1209 (void) close(door_fd);
1210 free(buf);
1211 return (rv);
1212 }
1213
1214 /* The arguments are no longer needed */
1215 free(buf);
1216
1217 /*
1218 * If results are not in the original buffer provided,
1219 * then check and process the new results buffer.
1220 */
1221 if (door_arg.rbuf != (char *)(uintptr_t)&rv) {
1222
1223 /*
1224 * First check that the buffer is valid. Then check for
1225 * the simple case where a short result code was sent.
1226 * The last case is a packed nvlist was returned, which
1227 * needs to be unpacked.
1228 */
1229 if ((door_arg.rbuf == NULL) ||
1230 (door_arg.data_size < sizeof (rv))) {
1231 i_hp_dprintf("i_hp_call_hotplugd: invalid results.\n");
1232 rv = EFAULT;
1233
1234 } else if (door_arg.data_size == sizeof (rv)) {
1235 rv = *(int *)(uintptr_t)door_arg.rbuf;
1236
1237 } else if ((rv = nvlist_unpack(door_arg.rbuf,
1238 door_arg.data_size, &results, 0)) != 0) {
1239 i_hp_dprintf("i_hp_call_hotplugd: "
1240 "cannot unpack results (%s).\n", strerror(rv));
1241 results = NULL;
1242 rv = EFAULT;
1243 }
1244
1245 /* Unmap the results buffer */
1246 if (door_arg.rbuf != NULL)
1247 (void) munmap(door_arg.rbuf, door_arg.rsize);
1248
1249 /*
1250 * In the case of a packed nvlist, notify the daemon
1251 * that it can free the result buffer from its heap.
1252 */
1253 if ((results != NULL) &&
1254 (nvlist_lookup_uint64(results, HPD_SEQNUM, &seqnum) == 0)) {
1255 door_arg.data_ptr = (char *)(uintptr_t)&seqnum;
1256 door_arg.data_size = sizeof (seqnum);
1257 door_arg.desc_ptr = NULL;
1258 door_arg.desc_num = 0;
1259 door_arg.rbuf = NULL;
1260 door_arg.rsize = 0;
1261 (void) door_call(door_fd, &door_arg);
1262 if (door_arg.rbuf != NULL)
1263 (void) munmap(door_arg.rbuf, door_arg.rsize);
1264 }
1265
1266 *resultsp = results;
1267 }
1268
1269 (void) close(door_fd);
1270 return (rv);
1271 }
1272
1273 /*
1274 * i_hp_set_args()
1275 *
1276 * Construct an nvlist of arguments for a hotplugd door call.
1277 */
1278 static nvlist_t *
i_hp_set_args(hp_cmd_t cmd,const char * path,const char * connection,uint_t flags,const char * options,int state)1279 i_hp_set_args(hp_cmd_t cmd, const char *path, const char *connection,
1280 uint_t flags, const char *options, int state)
1281 {
1282 nvlist_t *args;
1283
1284 /* Allocate a new nvlist */
1285 if (nvlist_alloc(&args, NV_UNIQUE_NAME_TYPE, 0) != 0)
1286 return (NULL);
1287
1288 /* Add common arguments */
1289 if ((nvlist_add_int32(args, HPD_CMD, cmd) != 0) ||
1290 (nvlist_add_string(args, HPD_PATH, path) != 0)) {
1291 nvlist_free(args);
1292 return (NULL);
1293 }
1294
1295 /* Add connection, but only if defined */
1296 if ((connection != NULL) && (connection[0] != '\0') &&
1297 (nvlist_add_string(args, HPD_CONNECTION, connection) != 0)) {
1298 nvlist_free(args);
1299 return (NULL);
1300 }
1301
1302 /* Add flags, but only if defined */
1303 if ((flags != 0) && (nvlist_add_uint32(args, HPD_FLAGS, flags) != 0)) {
1304 nvlist_free(args);
1305 return (NULL);
1306 }
1307
1308 /* Add options, but only if defined */
1309 if ((options != NULL) &&
1310 (nvlist_add_string(args, HPD_OPTIONS, options) != 0)) {
1311 nvlist_free(args);
1312 return (NULL);
1313 }
1314
1315 /* Add state, but only for CHANGESTATE command */
1316 if ((cmd == HP_CMD_CHANGESTATE) &&
1317 (nvlist_add_int32(args, HPD_STATE, state) != 0)) {
1318 nvlist_free(args);
1319 return (NULL);
1320 }
1321
1322 return (args);
1323 }
1324
1325 /*
1326 * i_hp_parse_results()
1327 *
1328 * Parse out individual fields of an nvlist of results from
1329 * a hotplugd door call.
1330 */
1331 static int
i_hp_parse_results(nvlist_t * results,hp_node_t * rootp,char ** optionsp)1332 i_hp_parse_results(nvlist_t *results, hp_node_t *rootp, char **optionsp)
1333 {
1334 int rv;
1335
1336 /* Parse an information snapshot */
1337 if (rootp) {
1338 char *buf = NULL;
1339 size_t len = 0;
1340
1341 *rootp = NULL;
1342 if (nvlist_lookup_byte_array(results, HPD_INFO,
1343 (uchar_t **)&buf, (uint_t *)&len) == 0) {
1344 if ((rv = hp_unpack(buf, len, rootp)) != 0)
1345 return (rv);
1346 }
1347 }
1348
1349 /* Parse a bus private option string */
1350 if (optionsp) {
1351 char *str;
1352
1353 *optionsp = NULL;
1354 if ((nvlist_lookup_string(results, HPD_OPTIONS, &str) == 0) &&
1355 ((*optionsp = strdup(str)) == NULL)) {
1356 return (ENOMEM);
1357 }
1358 }
1359
1360 /* Parse result code of the operation */
1361 if (nvlist_lookup_int32(results, HPD_STATUS, &rv) != 0) {
1362 i_hp_dprintf("i_hp_call_hotplugd: missing status.\n");
1363 return (EFAULT);
1364 }
1365
1366 return (rv);
1367 }
1368