126947304SEvan Yan /*
226947304SEvan Yan  * CDDL HEADER START
326947304SEvan Yan  *
426947304SEvan Yan  * The contents of this file are subject to the terms of the
526947304SEvan Yan  * Common Development and Distribution License (the "License").
626947304SEvan Yan  * You may not use this file except in compliance with the License.
726947304SEvan Yan  *
826947304SEvan Yan  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
926947304SEvan Yan  * or http://www.opensolaris.org/os/licensing.
1026947304SEvan Yan  * See the License for the specific language governing permissions
1126947304SEvan Yan  * and limitations under the License.
1226947304SEvan Yan  *
1326947304SEvan Yan  * When distributing Covered Code, include this CDDL HEADER in each
1426947304SEvan Yan  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1526947304SEvan Yan  * If applicable, add the following below this CDDL HEADER, with the
1626947304SEvan Yan  * fields enclosed by brackets "[]" replaced with your own identifying
1726947304SEvan Yan  * information: Portions Copyright [yyyy] [name of copyright owner]
1826947304SEvan Yan  *
1926947304SEvan Yan  * CDDL HEADER END
2026947304SEvan Yan  */
2126947304SEvan Yan /*
2226947304SEvan Yan  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2326947304SEvan Yan  * Use is subject to license terms.
2426947304SEvan Yan  */
2526947304SEvan Yan 
2626947304SEvan Yan #include <stdio.h>
2726947304SEvan Yan #include <stdlib.h>
2826947304SEvan Yan #include <stdarg.h>
2926947304SEvan Yan #include <unistd.h>
3026947304SEvan Yan #include <fcntl.h>
3126947304SEvan Yan #include <errno.h>
3226947304SEvan Yan #include <string.h>
3326947304SEvan Yan #include <door.h>
3426947304SEvan Yan #include <libnvpair.h>
3526947304SEvan Yan #include <libhotplug.h>
3626947304SEvan Yan #include <libhotplug_impl.h>
3726947304SEvan Yan #include <sys/sunddi.h>
3826947304SEvan Yan #include <sys/ddi_hp.h>
3926947304SEvan Yan 
4026947304SEvan Yan static void	i_hp_dprintf(const char *fmt, ...);
4126947304SEvan Yan static int	i_hp_pack_branch(hp_node_t, char **, size_t *);
4226947304SEvan Yan static int	i_hp_pack_node(hp_node_t, char **, size_t *);
4326947304SEvan Yan static int	i_hp_unpack_node(char *, size_t, hp_node_t, hp_node_t *);
4426947304SEvan Yan static int	i_hp_unpack_branch(char *, size_t, hp_node_t, hp_node_t *);
4526947304SEvan Yan static int	i_hp_call_hotplugd(nvlist_t *, nvlist_t **);
4626947304SEvan Yan static nvlist_t	*i_hp_set_args(hp_cmd_t, const char *, const char *, uint_t,
4726947304SEvan Yan 		    const char *, int);
4826947304SEvan Yan static int	i_hp_parse_results(nvlist_t *, hp_node_t *, char **);
4926947304SEvan Yan 
5026947304SEvan Yan /*
5126947304SEvan Yan  * Global flag to enable debug features.
5226947304SEvan Yan  */
5326947304SEvan Yan int	libhotplug_debug = 0;
5426947304SEvan Yan 
5526947304SEvan Yan /*
5626947304SEvan Yan  * hp_init()
5726947304SEvan Yan  *
5826947304SEvan Yan  *	Initialize a hotplug information snapshot.
5926947304SEvan Yan  */
6026947304SEvan Yan hp_node_t
hp_init(const char * path,const char * connection,uint_t flags)6126947304SEvan Yan hp_init(const char *path, const char *connection, uint_t flags)
6226947304SEvan Yan {
6326947304SEvan Yan 	nvlist_t	*args;
6426947304SEvan Yan 	nvlist_t	*results;
6526947304SEvan Yan 	hp_node_t	root = NULL;
6626947304SEvan Yan 	int		rv;
6726947304SEvan Yan 
6826947304SEvan Yan 	i_hp_dprintf("hp_init: path=%p, connection=%p, flags=0x%x\n",
6926947304SEvan Yan 	    (void *)path, (void *)connection, flags);
7026947304SEvan Yan 
7126947304SEvan Yan 	/* Check arguments */
7226947304SEvan Yan 	if ((path == NULL) || !HP_INIT_FLAGS_VALID(flags)) {
7326947304SEvan Yan 		i_hp_dprintf("hp_init: invalid arguments.\n");
7426947304SEvan Yan 		errno = EINVAL;
7526947304SEvan Yan 		return (NULL);
7626947304SEvan Yan 	}
7726947304SEvan Yan 
7826947304SEvan Yan 	/* Build arguments for door call */
7926947304SEvan Yan 	if ((args = i_hp_set_args(HP_CMD_GETINFO, path, connection, flags,
8026947304SEvan Yan 	    NULL, 0)) == NULL) {
8126947304SEvan Yan 		i_hp_dprintf("hp_init: cannot build arguments nvlist.\n");
8226947304SEvan Yan 		errno = ENOMEM;
8326947304SEvan Yan 		return (NULL);
8426947304SEvan Yan 	}
8526947304SEvan Yan 
8626947304SEvan Yan 	/* Make the door call to hotplugd */
8726947304SEvan Yan 	rv = i_hp_call_hotplugd(args, &results);
8826947304SEvan Yan 
8926947304SEvan Yan 	/* Arguments no longer needed */
9026947304SEvan Yan 	nvlist_free(args);
9126947304SEvan Yan 
9226947304SEvan Yan 	/* Parse additional results, if any */
9326947304SEvan Yan 	if ((rv == 0) && (results != NULL)) {
9426947304SEvan Yan 		rv = i_hp_parse_results(results, &root, NULL);
9526947304SEvan Yan 		nvlist_free(results);
9626947304SEvan Yan 	}
9726947304SEvan Yan 
9826947304SEvan Yan 	/* Check for errors */
9926947304SEvan Yan 	if (rv != 0) {
10026947304SEvan Yan 		i_hp_dprintf("hp_init: failure (%s).\n", strerror(rv));
10126947304SEvan Yan 		if (root)
10226947304SEvan Yan 			hp_fini(root);
10326947304SEvan Yan 		errno = rv;
10426947304SEvan Yan 		return (NULL);
10526947304SEvan Yan 	}
10626947304SEvan Yan 
10726947304SEvan Yan 	/* Success requires an info snapshot */
10826947304SEvan Yan 	if (root == NULL) {
10926947304SEvan Yan 		i_hp_dprintf("hp_init: missing info snapshot.\n");
11026947304SEvan Yan 		errno = EFAULT;
11126947304SEvan Yan 		return (NULL);
11226947304SEvan Yan 	}
11326947304SEvan Yan 
11426947304SEvan Yan 	/* Success */
11526947304SEvan Yan 	return (root);
11626947304SEvan Yan }
11726947304SEvan Yan 
11826947304SEvan Yan /*
11926947304SEvan Yan  * hp_fini()
12026947304SEvan Yan  *
12126947304SEvan Yan  *	Terminate and clean-up a hotplug information snapshot.
12226947304SEvan Yan  */
12326947304SEvan Yan void
hp_fini(hp_node_t root)12426947304SEvan Yan hp_fini(hp_node_t root)
12526947304SEvan Yan {
12626947304SEvan Yan 	hp_node_t	node;
12726947304SEvan Yan 	hp_node_t	sibling;
12826947304SEvan Yan 	char		*basepath;
12926947304SEvan Yan 
13026947304SEvan Yan 	i_hp_dprintf("hp_fini: root=%p\n", (void *)root);
13126947304SEvan Yan 
13226947304SEvan Yan 	if (root == NULL) {
13326947304SEvan Yan 		i_hp_dprintf("hp_fini: invalid arguments.\n");
13426947304SEvan Yan 		return;
13526947304SEvan Yan 	}
13626947304SEvan Yan 
13726947304SEvan Yan 	/* Extract and free base path */
13826947304SEvan Yan 	if (root->hp_basepath) {
13926947304SEvan Yan 		basepath = root->hp_basepath;
14026947304SEvan Yan 		for (node = root; node != NULL; node = node->hp_sibling)
14126947304SEvan Yan 			node->hp_basepath = NULL;
14226947304SEvan Yan 		free(basepath);
14326947304SEvan Yan 	}
14426947304SEvan Yan 
14526947304SEvan Yan 	/* Destroy the nodes */
14626947304SEvan Yan 	node = root;
14726947304SEvan Yan 	while (node) {
14826947304SEvan Yan 		sibling = node->hp_sibling;
14926947304SEvan Yan 		if (node->hp_child)
15026947304SEvan Yan 			hp_fini(node->hp_child);
15126947304SEvan Yan 		if (node->hp_name)
15226947304SEvan Yan 			free(node->hp_name);
15326947304SEvan Yan 		if (node->hp_usage)
15426947304SEvan Yan 			free(node->hp_usage);
15526947304SEvan Yan 		if (node->hp_description)
15626947304SEvan Yan 			free(node->hp_description);
15726947304SEvan Yan 		free(node);
15826947304SEvan Yan 		node = sibling;
15926947304SEvan Yan 	}
16026947304SEvan Yan }
16126947304SEvan Yan 
16226947304SEvan Yan /*
16326947304SEvan Yan  * hp_traverse()
16426947304SEvan Yan  *
16526947304SEvan Yan  *	Walk a graph of hotplug nodes, executing a callback on each node.
16626947304SEvan Yan  */
16726947304SEvan Yan int
hp_traverse(hp_node_t root,void * arg,int (* hp_callback)(hp_node_t,void * arg))16826947304SEvan Yan hp_traverse(hp_node_t root, void *arg, int (*hp_callback)(hp_node_t, void *arg))
16926947304SEvan Yan {
17026947304SEvan Yan 	int		rv;
17126947304SEvan Yan 	hp_node_t	node;
17226947304SEvan Yan 
17326947304SEvan Yan 	i_hp_dprintf("hp_traverse: root=%p, arg=%p, hp_callback=%p\n",
17426947304SEvan Yan 	    (void *)root, arg, (void *)hp_callback);
17526947304SEvan Yan 
17626947304SEvan Yan 	/* Check arguments */
17726947304SEvan Yan 	if ((root == NULL) || (hp_callback == NULL)) {
17826947304SEvan Yan 		i_hp_dprintf("hp_traverse: invalid arguments.\n");
17926947304SEvan Yan 		errno = EINVAL;
18026947304SEvan Yan 		return (-1);
18126947304SEvan Yan 	}
18226947304SEvan Yan 
18326947304SEvan Yan 	for (node = root; node; node = node->hp_sibling) {
18426947304SEvan Yan 		rv = hp_callback(node, arg);
18526947304SEvan Yan 
18626947304SEvan Yan 		if (rv == HP_WALK_TERMINATE) {
18726947304SEvan Yan 			i_hp_dprintf("hp_traverse: walk terminated.\n");
18826947304SEvan Yan 			return (HP_WALK_TERMINATE);
18926947304SEvan Yan 		}
19026947304SEvan Yan 
19126947304SEvan Yan 		if (node->hp_child && (rv != HP_WALK_PRUNECHILD))
19226947304SEvan Yan 			if (hp_traverse(node->hp_child, arg, hp_callback) ==
19326947304SEvan Yan 			    HP_WALK_TERMINATE) {
19426947304SEvan Yan 				i_hp_dprintf("hp_traverse: walk terminated.\n");
19526947304SEvan Yan 				return (HP_WALK_TERMINATE);
19626947304SEvan Yan 			}
19726947304SEvan Yan 
19826947304SEvan Yan 		if (rv == HP_WALK_PRUNESIBLING)
19926947304SEvan Yan 			break;
20026947304SEvan Yan 	}
20126947304SEvan Yan 
20226947304SEvan Yan 	return (0);
20326947304SEvan Yan }
20426947304SEvan Yan 
20526947304SEvan Yan /*
20626947304SEvan Yan  * hp_type()
20726947304SEvan Yan  *
20826947304SEvan Yan  *	Return a node's type.
20926947304SEvan Yan  */
21026947304SEvan Yan int
hp_type(hp_node_t node)21126947304SEvan Yan hp_type(hp_node_t node)
21226947304SEvan Yan {
21326947304SEvan Yan 	i_hp_dprintf("hp_type: node=%p\n", (void *)node);
21426947304SEvan Yan 
21526947304SEvan Yan 	if (node == NULL) {
21626947304SEvan Yan 		i_hp_dprintf("hp_type: invalid arguments.\n");
21726947304SEvan Yan 		errno = EINVAL;
21826947304SEvan Yan 		return (-1);
21926947304SEvan Yan 	}
22026947304SEvan Yan 
22126947304SEvan Yan 	return (node->hp_type);
22226947304SEvan Yan }
22326947304SEvan Yan 
22426947304SEvan Yan /*
22526947304SEvan Yan  * hp_name()
22626947304SEvan Yan  *
22726947304SEvan Yan  *	Return a node's name.
22826947304SEvan Yan  */
22926947304SEvan Yan char *
hp_name(hp_node_t node)23026947304SEvan Yan hp_name(hp_node_t node)
23126947304SEvan Yan {
23226947304SEvan Yan 	i_hp_dprintf("hp_name: node=%p\n", (void *)node);
23326947304SEvan Yan 
23426947304SEvan Yan 	if (node == NULL) {
23526947304SEvan Yan 		i_hp_dprintf("hp_name: invalid arguments.\n");
23626947304SEvan Yan 		errno = EINVAL;
23726947304SEvan Yan 		return (NULL);
23826947304SEvan Yan 	}
23926947304SEvan Yan 
24026947304SEvan Yan 	if (node->hp_name == NULL) {
24126947304SEvan Yan 		i_hp_dprintf("hp_name: missing name value.\n");
24226947304SEvan Yan 		errno = EFAULT;
24326947304SEvan Yan 	}
24426947304SEvan Yan 
24526947304SEvan Yan 	return (node->hp_name);
24626947304SEvan Yan }
24726947304SEvan Yan 
24826947304SEvan Yan /*
24926947304SEvan Yan  * hp_state()
25026947304SEvan Yan  *
25126947304SEvan Yan  *	Return a node's current state.
25226947304SEvan Yan  */
25326947304SEvan Yan int
hp_state(hp_node_t node)25426947304SEvan Yan hp_state(hp_node_t node)
25526947304SEvan Yan {
25626947304SEvan Yan 	i_hp_dprintf("hp_state: node=%p\n", (void *)node);
25726947304SEvan Yan 
25826947304SEvan Yan 	if (node == NULL) {
25926947304SEvan Yan 		i_hp_dprintf("hp_state: invalid arguments.\n");
26026947304SEvan Yan 		errno = EINVAL;
26126947304SEvan Yan 		return (-1);
26226947304SEvan Yan 	}
26326947304SEvan Yan 
26426947304SEvan Yan 	if ((node->hp_type != HP_NODE_CONNECTOR) &&
26526947304SEvan Yan 	    (node->hp_type != HP_NODE_PORT)) {
26626947304SEvan Yan 		i_hp_dprintf("hp_state: operation not supported.\n");
26726947304SEvan Yan 		errno = ENOTSUP;
26826947304SEvan Yan 		return (-1);
26926947304SEvan Yan 	}
27026947304SEvan Yan 
27126947304SEvan Yan 	return (node->hp_state);
27226947304SEvan Yan }
27326947304SEvan Yan 
27426947304SEvan Yan /*
27526947304SEvan Yan  * hp_usage()
27626947304SEvan Yan  *
27726947304SEvan Yan  *	Return a usage description for usage nodes.
27826947304SEvan Yan  */
27926947304SEvan Yan char *
hp_usage(hp_node_t node)28026947304SEvan Yan hp_usage(hp_node_t node)
28126947304SEvan Yan {
28226947304SEvan Yan 	i_hp_dprintf("hp_usage: node=%p\n", (void *)node);
28326947304SEvan Yan 
28426947304SEvan Yan 	if (node == NULL) {
28526947304SEvan Yan 		i_hp_dprintf("hp_usage: invalid arguments.\n");
28626947304SEvan Yan 		errno = EINVAL;
28726947304SEvan Yan 		return (NULL);
28826947304SEvan Yan 	}
28926947304SEvan Yan 
29026947304SEvan Yan 	if (node->hp_type != HP_NODE_USAGE) {
29126947304SEvan Yan 		i_hp_dprintf("hp_usage: operation not supported.\n");
29226947304SEvan Yan 		errno = ENOTSUP;
29326947304SEvan Yan 		return (NULL);
29426947304SEvan Yan 	}
29526947304SEvan Yan 
29626947304SEvan Yan 	if (node->hp_usage == NULL) {
29726947304SEvan Yan 		i_hp_dprintf("hp_usage: missing usage value.\n");
29826947304SEvan Yan 		errno = EFAULT;
29926947304SEvan Yan 	}
30026947304SEvan Yan 
30126947304SEvan Yan 	return (node->hp_usage);
30226947304SEvan Yan }
30326947304SEvan Yan 
30426947304SEvan Yan /*
30526947304SEvan Yan  * hp_description()
30626947304SEvan Yan  *
30726947304SEvan Yan  *	Return a type description (e.g. "PCI slot") for connection nodes.
30826947304SEvan Yan  */
30926947304SEvan Yan char *
hp_description(hp_node_t node)31026947304SEvan Yan hp_description(hp_node_t node)
31126947304SEvan Yan {
31226947304SEvan Yan 	i_hp_dprintf("hp_description: node=%p\n", (void *)node);
31326947304SEvan Yan 
31426947304SEvan Yan 	if (node == NULL) {
31526947304SEvan Yan 		i_hp_dprintf("hp_description: invalid arguments.\n");
31626947304SEvan Yan 		errno = EINVAL;
31726947304SEvan Yan 		return (NULL);
31826947304SEvan Yan 	}
31926947304SEvan Yan 
32026947304SEvan Yan 	if ((node->hp_type != HP_NODE_CONNECTOR) &&
32126947304SEvan Yan 	    (node->hp_type != HP_NODE_PORT)) {
32226947304SEvan Yan 		i_hp_dprintf("hp_description: operation not supported.\n");
32326947304SEvan Yan 		errno = ENOTSUP;
32426947304SEvan Yan 		return (NULL);
32526947304SEvan Yan 	}
32626947304SEvan Yan 
32726947304SEvan Yan 	if (node->hp_description == NULL) {
32826947304SEvan Yan 		i_hp_dprintf("hp_description: missing description value.\n");
32926947304SEvan Yan 		errno = EFAULT;
33026947304SEvan Yan 	}
33126947304SEvan Yan 
33226947304SEvan Yan 	return (node->hp_description);
33326947304SEvan Yan }
33426947304SEvan Yan 
33526947304SEvan Yan /*
33626947304SEvan Yan  * hp_last_change()
33726947304SEvan Yan  *
33826947304SEvan Yan  *	Return when the state of a connection was last changed.
33926947304SEvan Yan  */
34026947304SEvan Yan time_t
hp_last_change(hp_node_t node)34126947304SEvan Yan hp_last_change(hp_node_t node)
34226947304SEvan Yan {
34326947304SEvan Yan 	i_hp_dprintf("hp_last_change: node=%p\n", (void *)node);
34426947304SEvan Yan 
34526947304SEvan Yan 	if (node == NULL) {
34626947304SEvan Yan 		i_hp_dprintf("hp_last_change: invalid arguments.\n");
34726947304SEvan Yan 		errno = EINVAL;
348*ae7ff7d6SToomas Soome 		return (0);
34926947304SEvan Yan 	}
35026947304SEvan Yan 
35126947304SEvan Yan 	if ((node->hp_type != HP_NODE_CONNECTOR) &&
35226947304SEvan Yan 	    (node->hp_type != HP_NODE_PORT)) {
35326947304SEvan Yan 		i_hp_dprintf("hp_last_change: operation not supported.\n");
35426947304SEvan Yan 		errno = ENOTSUP;
355*ae7ff7d6SToomas Soome 		return (0);
35626947304SEvan Yan 	}
35726947304SEvan Yan 
35826947304SEvan Yan 	return (node->hp_last_change);
35926947304SEvan Yan }
36026947304SEvan Yan 
36126947304SEvan Yan /*
36226947304SEvan Yan  * hp_parent()
36326947304SEvan Yan  *
36426947304SEvan Yan  *	Return a node's parent node.
36526947304SEvan Yan  */
36626947304SEvan Yan hp_node_t
hp_parent(hp_node_t node)36726947304SEvan Yan hp_parent(hp_node_t node)
36826947304SEvan Yan {
36926947304SEvan Yan 	i_hp_dprintf("hp_parent: node=%p\n", (void *)node);
37026947304SEvan Yan 
37126947304SEvan Yan 	if (node == NULL) {
37226947304SEvan Yan 		i_hp_dprintf("hp_parent: invalid arguments.\n");
37326947304SEvan Yan 		errno = EINVAL;
37426947304SEvan Yan 		return (NULL);
37526947304SEvan Yan 	}
37626947304SEvan Yan 
37726947304SEvan Yan 	if (node->hp_parent == NULL) {
37826947304SEvan Yan 		i_hp_dprintf("hp_parent: node has no parent.\n");
37926947304SEvan Yan 		errno = ENXIO;
38026947304SEvan Yan 	}
38126947304SEvan Yan 
38226947304SEvan Yan 	return (node->hp_parent);
38326947304SEvan Yan }
38426947304SEvan Yan 
38526947304SEvan Yan /*
38626947304SEvan Yan  * hp_child()
38726947304SEvan Yan  *
38826947304SEvan Yan  *	Return a node's first child node.
38926947304SEvan Yan  */
39026947304SEvan Yan hp_node_t
hp_child(hp_node_t node)39126947304SEvan Yan hp_child(hp_node_t node)
39226947304SEvan Yan {
39326947304SEvan Yan 	i_hp_dprintf("hp_child: node=%p\n", (void *)node);
39426947304SEvan Yan 
39526947304SEvan Yan 	if (node == NULL) {
39626947304SEvan Yan 		i_hp_dprintf("hp_child: invalid arguments.\n");
39726947304SEvan Yan 		errno = EINVAL;
39826947304SEvan Yan 		return (NULL);
39926947304SEvan Yan 	}
40026947304SEvan Yan 
40126947304SEvan Yan 	if (node->hp_child == NULL) {
40226947304SEvan Yan 		i_hp_dprintf("hp_child: node has no child.\n");
40326947304SEvan Yan 		errno = ENXIO;
40426947304SEvan Yan 	}
40526947304SEvan Yan 
40626947304SEvan Yan 	return (node->hp_child);
40726947304SEvan Yan }
40826947304SEvan Yan 
40926947304SEvan Yan /*
41026947304SEvan Yan  * hp_sibling()
41126947304SEvan Yan  *
41226947304SEvan Yan  *	Return a node's next sibling node.
41326947304SEvan Yan  */
41426947304SEvan Yan hp_node_t
hp_sibling(hp_node_t node)41526947304SEvan Yan hp_sibling(hp_node_t node)
41626947304SEvan Yan {
41726947304SEvan Yan 	i_hp_dprintf("hp_sibling: node=%p\n", (void *)node);
41826947304SEvan Yan 
41926947304SEvan Yan 	if (node == NULL) {
42026947304SEvan Yan 		i_hp_dprintf("hp_sibling: invalid arguments.\n");
42126947304SEvan Yan 		errno = EINVAL;
42226947304SEvan Yan 		return (NULL);
42326947304SEvan Yan 	}
42426947304SEvan Yan 
42526947304SEvan Yan 	if (node->hp_sibling == NULL) {
42626947304SEvan Yan 		i_hp_dprintf("hp_sibling: node has no sibling.\n");
42726947304SEvan Yan 		errno = ENXIO;
42826947304SEvan Yan 	}
42926947304SEvan Yan 
43026947304SEvan Yan 	return (node->hp_sibling);
43126947304SEvan Yan }
43226947304SEvan Yan 
43326947304SEvan Yan /*
43426947304SEvan Yan  * hp_path()
43526947304SEvan Yan  *
43626947304SEvan Yan  *	Return the path (and maybe connection name) of a node.
43726947304SEvan Yan  *	The caller must supply two buffers, each MAXPATHLEN size.
43826947304SEvan Yan  */
43926947304SEvan Yan int
hp_path(hp_node_t node,char * path,char * connection)44026947304SEvan Yan hp_path(hp_node_t node, char *path, char *connection)
44126947304SEvan Yan {
442d598ea2fSToomas Soome 	hp_node_t	root = NULL;
44326947304SEvan Yan 	hp_node_t	parent;
44426947304SEvan Yan 	int		i;
44526947304SEvan Yan 	char		*s;
44626947304SEvan Yan 	char		components[MAXPATHLEN];
44726947304SEvan Yan 
44826947304SEvan Yan 	i_hp_dprintf("hp_path: node=%p, path=%p, connection=%p\n", (void *)node,
44926947304SEvan Yan 	    (void *)path, (void *)connection);
45026947304SEvan Yan 
45126947304SEvan Yan 	if ((node == NULL) || (path == NULL) || (connection == NULL)) {
45226947304SEvan Yan 		i_hp_dprintf("hp_path: invalid arguments.\n");
45326947304SEvan Yan 		return (EINVAL);
45426947304SEvan Yan 	}
45526947304SEvan Yan 
45626947304SEvan Yan 	(void) memset(path, 0, MAXPATHLEN);
45726947304SEvan Yan 	(void) memset(connection, 0, MAXPATHLEN);
45826947304SEvan Yan 	(void) memset(components, 0, MAXPATHLEN);
45926947304SEvan Yan 
46026947304SEvan Yan 	/*  Set 'connection' only for connectors and ports */
46126947304SEvan Yan 	if ((node->hp_type == HP_NODE_CONNECTOR) ||
46226947304SEvan Yan 	    (node->hp_type == HP_NODE_PORT))
46326947304SEvan Yan 		(void) strlcpy(connection, node->hp_name, MAXPATHLEN);
46426947304SEvan Yan 
46526947304SEvan Yan 	/* Trace back to the root node, accumulating components */
46626947304SEvan Yan 	for (parent = node; parent != NULL; parent = parent->hp_parent) {
46726947304SEvan Yan 		if (parent->hp_type == HP_NODE_DEVICE) {
46826947304SEvan Yan 			(void) strlcat(components, "/", MAXPATHLEN);
46926947304SEvan Yan 			(void) strlcat(components, parent->hp_name, MAXPATHLEN);
47026947304SEvan Yan 		}
47126947304SEvan Yan 		if (parent->hp_parent == NULL)
47226947304SEvan Yan 			root = parent;
47326947304SEvan Yan 	}
47426947304SEvan Yan 
47526947304SEvan Yan 	/* Ensure the snapshot actually contains a base path */
47626947304SEvan Yan 	if (root->hp_basepath == NULL) {
47726947304SEvan Yan 		i_hp_dprintf("hp_path: missing base pathname.\n");
47826947304SEvan Yan 		return (EFAULT);
47926947304SEvan Yan 	}
48026947304SEvan Yan 
48126947304SEvan Yan 	/*
48226947304SEvan Yan 	 * Construct the path.  Start with the base path from the root
48326947304SEvan Yan 	 * node, then append the accumulated components in reverse order.
48426947304SEvan Yan 	 */
48526947304SEvan Yan 	if (strcmp(root->hp_basepath, "/") != 0) {
48626947304SEvan Yan 		(void) strlcat(path, root->hp_basepath, MAXPATHLEN);
48726947304SEvan Yan 		if ((root->hp_type == HP_NODE_DEVICE) &&
48826947304SEvan Yan 		    ((s = strrchr(path, '/')) != NULL))
48926947304SEvan Yan 			*s = '\0';
49026947304SEvan Yan 	}
49126947304SEvan Yan 	for (i = strlen(components) - 1; i >= 0; i--) {
49226947304SEvan Yan 		if (components[i] == '/') {
49326947304SEvan Yan 			(void) strlcat(path, &components[i], MAXPATHLEN);
49426947304SEvan Yan 			components[i] = '\0';
49526947304SEvan Yan 		}
49626947304SEvan Yan 	}
49726947304SEvan Yan 
49826947304SEvan Yan 	return (0);
49926947304SEvan Yan }
50026947304SEvan Yan 
50126947304SEvan Yan /*
50226947304SEvan Yan  * hp_set_state()
50326947304SEvan Yan  *
50426947304SEvan Yan  *	Initiate a state change operation on a node.
50526947304SEvan Yan  */
50626947304SEvan Yan int
hp_set_state(hp_node_t node,uint_t flags,int state,hp_node_t * resultsp)50726947304SEvan Yan hp_set_state(hp_node_t node, uint_t flags, int state, hp_node_t *resultsp)
50826947304SEvan Yan {
50926947304SEvan Yan 	hp_node_t	root = NULL;
51026947304SEvan Yan 	nvlist_t	*args;
51126947304SEvan Yan 	nvlist_t	*results;
51226947304SEvan Yan 	int		rv;
51326947304SEvan Yan 	char		path[MAXPATHLEN];
51426947304SEvan Yan 	char		connection[MAXPATHLEN];
51526947304SEvan Yan 
51626947304SEvan Yan 	i_hp_dprintf("hp_set_state: node=%p, flags=0x%x, state=0x%x, "
51726947304SEvan Yan 	    "resultsp=%p\n", (void *)node, flags, state, (void *)resultsp);
51826947304SEvan Yan 
51926947304SEvan Yan 	/* Check arguments */
52026947304SEvan Yan 	if ((node == NULL) || (resultsp == NULL) ||
52126947304SEvan Yan 	    !HP_SET_STATE_FLAGS_VALID(flags)) {
52226947304SEvan Yan 		i_hp_dprintf("hp_set_state: invalid arguments.\n");
52326947304SEvan Yan 		return (EINVAL);
52426947304SEvan Yan 	}
52526947304SEvan Yan 
52626947304SEvan Yan 	/* Check node type */
52726947304SEvan Yan 	if ((node->hp_type != HP_NODE_CONNECTOR) &&
52826947304SEvan Yan 	    (node->hp_type != HP_NODE_PORT)) {
52926947304SEvan Yan 		i_hp_dprintf("hp_set_state: operation not supported.\n");
53026947304SEvan Yan 		return (ENOTSUP);
53126947304SEvan Yan 	}
53226947304SEvan Yan 
53326947304SEvan Yan 	/* Check that target state is valid */
53426947304SEvan Yan 	switch (state) {
53526947304SEvan Yan 	case DDI_HP_CN_STATE_PRESENT:
53626947304SEvan Yan 	case DDI_HP_CN_STATE_POWERED:
53726947304SEvan Yan 	case DDI_HP_CN_STATE_ENABLED:
53826947304SEvan Yan 		if (node->hp_type != HP_NODE_CONNECTOR) {
53926947304SEvan Yan 			i_hp_dprintf("hp_set_state: mismatched target.\n");
54026947304SEvan Yan 			return (ENOTSUP);
54126947304SEvan Yan 		}
54226947304SEvan Yan 		break;
54326947304SEvan Yan 	case DDI_HP_CN_STATE_PORT_PRESENT:
54426947304SEvan Yan 	case DDI_HP_CN_STATE_OFFLINE:
54526947304SEvan Yan 	case DDI_HP_CN_STATE_ONLINE:
54626947304SEvan Yan 		if (node->hp_type != HP_NODE_PORT) {
54726947304SEvan Yan 			i_hp_dprintf("hp_set_state: mismatched target.\n");
54826947304SEvan Yan 			return (ENOTSUP);
54926947304SEvan Yan 		}
55026947304SEvan Yan 		break;
55126947304SEvan Yan 	default:
55226947304SEvan Yan 		i_hp_dprintf("hp_set_state: invalid target state.\n");
55326947304SEvan Yan 		return (EINVAL);
55426947304SEvan Yan 	}
55526947304SEvan Yan 
55626947304SEvan Yan 	/* Get path and connection of specified node */
55726947304SEvan Yan 	if ((rv = hp_path(node, path, connection)) != 0)
55826947304SEvan Yan 		return (rv);
55926947304SEvan Yan 
56026947304SEvan Yan 	/* Build arguments for door call */
56126947304SEvan Yan 	if ((args = i_hp_set_args(HP_CMD_CHANGESTATE, path, connection, flags,
56226947304SEvan Yan 	    NULL, state)) == NULL)
56326947304SEvan Yan 		return (ENOMEM);
56426947304SEvan Yan 
56526947304SEvan Yan 	/* Make the door call to hotplugd */
56626947304SEvan Yan 	rv = i_hp_call_hotplugd(args, &results);
56726947304SEvan Yan 
56826947304SEvan Yan 	/* Arguments no longer needed */
56926947304SEvan Yan 	nvlist_free(args);
57026947304SEvan Yan 
57126947304SEvan Yan 	/* Parse additional results, if any */
57226947304SEvan Yan 	if ((rv == 0) && (results != NULL)) {
57326947304SEvan Yan 		rv = i_hp_parse_results(results, &root, NULL);
57426947304SEvan Yan 		nvlist_free(results);
57526947304SEvan Yan 		*resultsp = root;
57626947304SEvan Yan 	}
57726947304SEvan Yan 
57826947304SEvan Yan 	/* Done */
57926947304SEvan Yan 	return (rv);
58026947304SEvan Yan }
58126947304SEvan Yan 
58226947304SEvan Yan /*
58326947304SEvan Yan  * hp_set_private()
58426947304SEvan Yan  *
58526947304SEvan Yan  *	Set bus private options on the hotplug connection
58626947304SEvan Yan  *	indicated by the given hotplug information node.
58726947304SEvan Yan  */
58826947304SEvan Yan int
hp_set_private(hp_node_t node,const char * options,char ** resultsp)58926947304SEvan Yan hp_set_private(hp_node_t node, const char *options, char **resultsp)
59026947304SEvan Yan {
59126947304SEvan Yan 	int		rv;
59226947304SEvan Yan 	nvlist_t	*args;
59326947304SEvan Yan 	nvlist_t	*results;
59426947304SEvan Yan 	char		*values = NULL;
59526947304SEvan Yan 	char		path[MAXPATHLEN];
59626947304SEvan Yan 	char		connection[MAXPATHLEN];
59726947304SEvan Yan 
59826947304SEvan Yan 	i_hp_dprintf("hp_set_private: node=%p, options=%p, resultsp=%p\n",
59926947304SEvan Yan 	    (void *)node, (void *)options, (void *)resultsp);
60026947304SEvan Yan 
60126947304SEvan Yan 	/* Check arguments */
60226947304SEvan Yan 	if ((node == NULL) || (options == NULL) || (resultsp == NULL)) {
60326947304SEvan Yan 		i_hp_dprintf("hp_set_private: invalid arguments.\n");
60426947304SEvan Yan 		return (EINVAL);
60526947304SEvan Yan 	}
60626947304SEvan Yan 
60726947304SEvan Yan 	/* Check node type */
60826947304SEvan Yan 	if (node->hp_type != HP_NODE_CONNECTOR) {
60926947304SEvan Yan 		i_hp_dprintf("hp_set_private: operation not supported.\n");
61026947304SEvan Yan 		return (ENOTSUP);
61126947304SEvan Yan 	}
61226947304SEvan Yan 
61326947304SEvan Yan 	/* Initialize results */
61426947304SEvan Yan 	*resultsp = NULL;
61526947304SEvan Yan 
61626947304SEvan Yan 	/* Get path and connection of specified node */
61726947304SEvan Yan 	if ((rv = hp_path(node, path, connection)) != 0)
61826947304SEvan Yan 		return (rv);
61926947304SEvan Yan 
62026947304SEvan Yan 	/* Build arguments for door call */
62126947304SEvan Yan 	if ((args = i_hp_set_args(HP_CMD_SETPRIVATE, path, connection, 0,
62226947304SEvan Yan 	    options, 0)) == NULL)
62326947304SEvan Yan 		return (ENOMEM);
62426947304SEvan Yan 
62526947304SEvan Yan 	/* Make the door call to hotplugd */
62626947304SEvan Yan 	rv = i_hp_call_hotplugd(args, &results);
62726947304SEvan Yan 
62826947304SEvan Yan 	/* Arguments no longer needed */
62926947304SEvan Yan 	nvlist_free(args);
63026947304SEvan Yan 
63126947304SEvan Yan 	/* Parse additional results, if any */
63226947304SEvan Yan 	if ((rv == 0) && (results != NULL)) {
63326947304SEvan Yan 		rv = i_hp_parse_results(results, NULL, &values);
63426947304SEvan Yan 		nvlist_free(results);
63526947304SEvan Yan 		*resultsp = values;
63626947304SEvan Yan 	}
63726947304SEvan Yan 
63826947304SEvan Yan 	/* Done */
63926947304SEvan Yan 	return (rv);
64026947304SEvan Yan }
64126947304SEvan Yan 
64226947304SEvan Yan /*
64326947304SEvan Yan  * hp_get_private()
64426947304SEvan Yan  *
64526947304SEvan Yan  *	Get bus private options on the hotplug connection
64626947304SEvan Yan  *	indicated by the given hotplug information node.
64726947304SEvan Yan  */
64826947304SEvan Yan int
hp_get_private(hp_node_t node,const char * options,char ** resultsp)64926947304SEvan Yan hp_get_private(hp_node_t node, const char *options, char **resultsp)
65026947304SEvan Yan {
65126947304SEvan Yan 	int		rv;
65226947304SEvan Yan 	nvlist_t	*args;
65326947304SEvan Yan 	nvlist_t	*results;
65426947304SEvan Yan 	char		*values = NULL;
65526947304SEvan Yan 	char		path[MAXPATHLEN];
65626947304SEvan Yan 	char		connection[MAXPATHLEN];
65726947304SEvan Yan 
65826947304SEvan Yan 	i_hp_dprintf("hp_get_private: node=%p, options=%p, resultsp=%p\n",
65926947304SEvan Yan 	    (void *)node, (void *)options, (void *)resultsp);
66026947304SEvan Yan 
66126947304SEvan Yan 	/* Check arguments */
66226947304SEvan Yan 	if ((node == NULL) || (options == NULL) || (resultsp == NULL)) {
66326947304SEvan Yan 		i_hp_dprintf("hp_get_private: invalid arguments.\n");
66426947304SEvan Yan 		return (EINVAL);
66526947304SEvan Yan 	}
66626947304SEvan Yan 
66726947304SEvan Yan 	/* Check node type */
66826947304SEvan Yan 	if (node->hp_type != HP_NODE_CONNECTOR) {
66926947304SEvan Yan 		i_hp_dprintf("hp_get_private: operation not supported.\n");
67026947304SEvan Yan 		return (ENOTSUP);
67126947304SEvan Yan 	}
67226947304SEvan Yan 
67326947304SEvan Yan 	/* Initialize results */
67426947304SEvan Yan 	*resultsp = NULL;
67526947304SEvan Yan 
67626947304SEvan Yan 	/* Get path and connection of specified node */
67726947304SEvan Yan 	if ((rv = hp_path(node, path, connection)) != 0)
67826947304SEvan Yan 		return (rv);
67926947304SEvan Yan 
68026947304SEvan Yan 	/* Build arguments for door call */
68126947304SEvan Yan 	if ((args = i_hp_set_args(HP_CMD_GETPRIVATE, path, connection, 0,
68226947304SEvan Yan 	    options, 0)) == NULL)
68326947304SEvan Yan 		return (ENOMEM);
68426947304SEvan Yan 
68526947304SEvan Yan 	/* Make the door call to hotplugd */
68626947304SEvan Yan 	rv = i_hp_call_hotplugd(args, &results);
68726947304SEvan Yan 
68826947304SEvan Yan 	/* Arguments no longer needed */
68926947304SEvan Yan 	nvlist_free(args);
69026947304SEvan Yan 
69126947304SEvan Yan 	/* Parse additional results, if any */
69226947304SEvan Yan 	if ((rv == 0) && (results != NULL)) {
69326947304SEvan Yan 		rv = i_hp_parse_results(results, NULL, &values);
69426947304SEvan Yan 		nvlist_free(results);
69526947304SEvan Yan 		*resultsp = values;
69626947304SEvan Yan 	}
69726947304SEvan Yan 
69826947304SEvan Yan 	/* Done */
69926947304SEvan Yan 	return (rv);
70026947304SEvan Yan }
70126947304SEvan Yan 
70226947304SEvan Yan /*
70326947304SEvan Yan  * hp_pack()
70426947304SEvan Yan  *
70526947304SEvan Yan  *	Given the root of a hotplug information snapshot, pack
70626947304SEvan Yan  *	it into a contiguous byte array so that it is suitable
70726947304SEvan Yan  *	for network transport.
70826947304SEvan Yan  */
70926947304SEvan Yan int
hp_pack(hp_node_t root,char ** bufp,size_t * lenp)71026947304SEvan Yan hp_pack(hp_node_t root, char **bufp, size_t *lenp)
71126947304SEvan Yan {
71226947304SEvan Yan 	hp_node_t	node;
71326947304SEvan Yan 	nvlist_t	*nvl;
71426947304SEvan Yan 	char		*buf;
71526947304SEvan Yan 	size_t		len;
71626947304SEvan Yan 	int		rv;
71726947304SEvan Yan 
71826947304SEvan Yan 	i_hp_dprintf("hp_pack: root=%p, bufp=%p, lenp=%p\n", (void *)root,
71926947304SEvan Yan 	    (void *)bufp, (void *)lenp);
72026947304SEvan Yan 
72126947304SEvan Yan 	if ((root == NULL) || (bufp == NULL) || (lenp == NULL)) {
72226947304SEvan Yan 		i_hp_dprintf("hp_pack: invalid arguments.\n");
72326947304SEvan Yan 		return (EINVAL);
72426947304SEvan Yan 	}
72526947304SEvan Yan 
72626947304SEvan Yan 	*lenp = 0;
72726947304SEvan Yan 	*bufp = NULL;
72826947304SEvan Yan 
72926947304SEvan Yan 	if (nvlist_alloc(&nvl, 0, 0) != 0) {
73026947304SEvan Yan 		i_hp_dprintf("hp_pack: nvlist_alloc() failed (%s).\n",
73126947304SEvan Yan 		    strerror(errno));
73226947304SEvan Yan 		return (ENOMEM);
73326947304SEvan Yan 	}
73426947304SEvan Yan 
73526947304SEvan Yan 	if (root->hp_basepath != NULL) {
73626947304SEvan Yan 		rv = nvlist_add_string(nvl, HP_INFO_BASE, root->hp_basepath);
73726947304SEvan Yan 		if (rv != 0) {
73826947304SEvan Yan 			nvlist_free(nvl);
73926947304SEvan Yan 			return (rv);
74026947304SEvan Yan 		}
74126947304SEvan Yan 	}
74226947304SEvan Yan 
74326947304SEvan Yan 	for (node = root; node != NULL; node = node->hp_sibling) {
74426947304SEvan Yan 		if ((rv = i_hp_pack_branch(node, &buf, &len)) == 0) {
74526947304SEvan Yan 			rv = nvlist_add_byte_array(nvl, HP_INFO_BRANCH,
74626947304SEvan Yan 			    (uchar_t *)buf, len);
74726947304SEvan Yan 			free(buf);
74826947304SEvan Yan 		}
74926947304SEvan Yan 		if (rv != 0) {
75026947304SEvan Yan 			nvlist_free(nvl);
75126947304SEvan Yan 			return (rv);
75226947304SEvan Yan 		}
75326947304SEvan Yan 	}
75426947304SEvan Yan 
75526947304SEvan Yan 	len = 0;
75626947304SEvan Yan 	buf = NULL;
75726947304SEvan Yan 	if ((rv = nvlist_pack(nvl, &buf, &len, NV_ENCODE_NATIVE, 0)) == 0) {
75826947304SEvan Yan 		*lenp = len;
75926947304SEvan Yan 		*bufp = buf;
76026947304SEvan Yan 	}
76126947304SEvan Yan 
76226947304SEvan Yan 	nvlist_free(nvl);
76326947304SEvan Yan 
76426947304SEvan Yan 	return (rv);
76526947304SEvan Yan }
76626947304SEvan Yan 
76726947304SEvan Yan /*
76826947304SEvan Yan  * hp_unpack()
76926947304SEvan Yan  *
77026947304SEvan Yan  *	Unpack a hotplug information snapshot for normal usage.
77126947304SEvan Yan  */
77226947304SEvan Yan int
hp_unpack(char * packed_buf,size_t packed_len,hp_node_t * retp)77326947304SEvan Yan hp_unpack(char *packed_buf, size_t packed_len, hp_node_t *retp)
77426947304SEvan Yan {
77526947304SEvan Yan 	hp_node_t	root;
77626947304SEvan Yan 	hp_node_t	root_list = NULL;
77726947304SEvan Yan 	hp_node_t	prev_root = NULL;
77826947304SEvan Yan 	nvlist_t	*nvl = NULL;
77926947304SEvan Yan 	nvpair_t	*nvp;
78026947304SEvan Yan 	char		*basepath = NULL;
78126947304SEvan Yan 	int		rv;
78226947304SEvan Yan 
78326947304SEvan Yan 	i_hp_dprintf("hp_unpack: packed_buf=%p, packed_len=%u, retp=%p\n",
78426947304SEvan Yan 	    (void *)packed_buf, (uint32_t)packed_len, (void *)retp);
78526947304SEvan Yan 
78626947304SEvan Yan 	if ((packed_buf == NULL) || (packed_len == 0) || (retp == NULL)) {
78726947304SEvan Yan 		i_hp_dprintf("hp_unpack: invalid arguments.\n");
78826947304SEvan Yan 		return (EINVAL);
78926947304SEvan Yan 	}
79026947304SEvan Yan 
79126947304SEvan Yan 	if ((rv = nvlist_unpack(packed_buf, packed_len, &nvl, 0)) != 0)
79226947304SEvan Yan 		return (rv);
79326947304SEvan Yan 
79426947304SEvan Yan 	if (nvlist_next_nvpair(nvl, NULL) == NULL) {
79526947304SEvan Yan 		nvlist_free(nvl);
79626947304SEvan Yan 		errno = EINVAL;
797*ae7ff7d6SToomas Soome 		return (0);
79826947304SEvan Yan 	}
79926947304SEvan Yan 
80026947304SEvan Yan 	for (nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) {
80126947304SEvan Yan 
80226947304SEvan Yan 		rv = EINVAL;
80326947304SEvan Yan 
80426947304SEvan Yan 		if (strcmp(nvpair_name(nvp), HP_INFO_BASE) == 0) {
80526947304SEvan Yan 			char	*val_string;
80626947304SEvan Yan 
80726947304SEvan Yan 			if ((rv = nvpair_value_string(nvp, &val_string)) == 0) {
80826947304SEvan Yan 				if ((basepath = strdup(val_string)) == NULL)
80926947304SEvan Yan 					rv = ENOMEM;
81026947304SEvan Yan 			}
81126947304SEvan Yan 
81226947304SEvan Yan 		} else if (strcmp(nvpair_name(nvp), HP_INFO_BRANCH) == 0) {
81326947304SEvan Yan 			size_t		len = 0;
81426947304SEvan Yan 			char		*buf = NULL;
81526947304SEvan Yan 
81626947304SEvan Yan 			if ((rv = nvpair_value_byte_array(nvp,
81726947304SEvan Yan 			    (uchar_t **)&buf, (uint_t *)&len)) == 0) {
81826947304SEvan Yan 				rv = i_hp_unpack_branch(buf, len, NULL, &root);
81926947304SEvan Yan 			}
82026947304SEvan Yan 
82126947304SEvan Yan 			if (rv == 0) {
82226947304SEvan Yan 				if (prev_root) {
82326947304SEvan Yan 					prev_root->hp_sibling = root;
82426947304SEvan Yan 				} else {
82526947304SEvan Yan 					root_list = root;
82626947304SEvan Yan 				}
82726947304SEvan Yan 				prev_root = root;
82826947304SEvan Yan 			}
82926947304SEvan Yan 		}
83026947304SEvan Yan 
83126947304SEvan Yan 		if (rv != 0) {
83226947304SEvan Yan 			if (basepath)
83326947304SEvan Yan 				free(basepath);
83426947304SEvan Yan 			nvlist_free(nvl);
83526947304SEvan Yan 			hp_fini(root_list);
83626947304SEvan Yan 			*retp = NULL;
83726947304SEvan Yan 			return (rv);
83826947304SEvan Yan 		}
83926947304SEvan Yan 	}
84026947304SEvan Yan 
84126947304SEvan Yan 	/* Store the base path in each root node */
84226947304SEvan Yan 	if (basepath) {
84326947304SEvan Yan 		for (root = root_list; root; root = root->hp_sibling)
84426947304SEvan Yan 			root->hp_basepath = basepath;
84526947304SEvan Yan 	}
84626947304SEvan Yan 
84726947304SEvan Yan 	nvlist_free(nvl);
84826947304SEvan Yan 	*retp = root_list;
84926947304SEvan Yan 	return (0);
85026947304SEvan Yan }
85126947304SEvan Yan 
85226947304SEvan Yan /*
85326947304SEvan Yan  * i_hp_dprintf()
85426947304SEvan Yan  *
85526947304SEvan Yan  *	Print debug messages to stderr, but only when the debug flag
85626947304SEvan Yan  *	(libhotplug_debug) is set.
85726947304SEvan Yan  */
85826947304SEvan Yan /*PRINTFLIKE1*/
85926947304SEvan Yan static void
i_hp_dprintf(const char * fmt,...)86026947304SEvan Yan i_hp_dprintf(const char *fmt, ...)
86126947304SEvan Yan {
86226947304SEvan Yan 	va_list	ap;
86326947304SEvan Yan 
86426947304SEvan Yan 	if (libhotplug_debug) {
86526947304SEvan Yan 		va_start(ap, fmt);
86626947304SEvan Yan 		(void) vfprintf(stderr, fmt, ap);
86726947304SEvan Yan 		va_end(ap);
86826947304SEvan Yan 	}
86926947304SEvan Yan }
87026947304SEvan Yan 
87126947304SEvan Yan /*
87226947304SEvan Yan  * i_hp_pack_branch()
87326947304SEvan Yan  *
87426947304SEvan Yan  *	Pack an individual branch of a hotplug information snapshot.
87526947304SEvan Yan  */
87626947304SEvan Yan static int
i_hp_pack_branch(hp_node_t root,char ** bufp,size_t * lenp)87726947304SEvan Yan i_hp_pack_branch(hp_node_t root, char **bufp, size_t *lenp)
87826947304SEvan Yan {
87926947304SEvan Yan 	hp_node_t	child;
88026947304SEvan Yan 	nvlist_t	*nvl;
88126947304SEvan Yan 	char		*buf;
88226947304SEvan Yan 	size_t		len;
88326947304SEvan Yan 	int		rv;
88426947304SEvan Yan 
88526947304SEvan Yan 	*lenp = 0;
88626947304SEvan Yan 	*bufp = NULL;
88726947304SEvan Yan 
88826947304SEvan Yan 	/* Allocate an nvlist for this branch */
88926947304SEvan Yan 	if (nvlist_alloc(&nvl, 0, 0) != 0)
89026947304SEvan Yan 		return (ENOMEM);
89126947304SEvan Yan 
89226947304SEvan Yan 	/* Pack the root of the branch and add it to the nvlist */
89326947304SEvan Yan 	if ((rv = i_hp_pack_node(root, &buf, &len)) == 0) {
89426947304SEvan Yan 		rv = nvlist_add_byte_array(nvl, HP_INFO_NODE,
89526947304SEvan Yan 		    (uchar_t *)buf, len);
89626947304SEvan Yan 		free(buf);
89726947304SEvan Yan 	}
89826947304SEvan Yan 	if (rv != 0) {
89926947304SEvan Yan 		nvlist_free(nvl);
90026947304SEvan Yan 		return (rv);
90126947304SEvan Yan 	}
90226947304SEvan Yan 
90326947304SEvan Yan 	/* Pack each subordinate branch, and add it to the nvlist */
90426947304SEvan Yan 	for (child = root->hp_child; child != NULL; child = child->hp_sibling) {
90526947304SEvan Yan 		if ((rv = i_hp_pack_branch(child, &buf, &len)) == 0) {
90626947304SEvan Yan 			rv = nvlist_add_byte_array(nvl, HP_INFO_BRANCH,
90726947304SEvan Yan 			    (uchar_t *)buf, len);
90826947304SEvan Yan 			free(buf);
90926947304SEvan Yan 		}
91026947304SEvan Yan 		if (rv != 0) {
91126947304SEvan Yan 			nvlist_free(nvl);
91226947304SEvan Yan 			return (rv);
91326947304SEvan Yan 		}
91426947304SEvan Yan 	}
91526947304SEvan Yan 
91626947304SEvan Yan 	/* Pack the resulting nvlist into a single buffer */
91726947304SEvan Yan 	len = 0;
91826947304SEvan Yan 	buf = NULL;
91926947304SEvan Yan 	if ((rv = nvlist_pack(nvl, &buf, &len, NV_ENCODE_NATIVE, 0)) == 0) {
92026947304SEvan Yan 		*lenp = len;
92126947304SEvan Yan 		*bufp = buf;
92226947304SEvan Yan 	}
92326947304SEvan Yan 
92426947304SEvan Yan 	/* Free the nvlist */
92526947304SEvan Yan 	nvlist_free(nvl);
92626947304SEvan Yan 
92726947304SEvan Yan 	return (rv);
92826947304SEvan Yan }
92926947304SEvan Yan 
93026947304SEvan Yan /*
93126947304SEvan Yan  * i_hp_pack_node()
93226947304SEvan Yan  *
93326947304SEvan Yan  *	Pack an individual node of a hotplug information snapshot.
93426947304SEvan Yan  */
93526947304SEvan Yan static int
i_hp_pack_node(hp_node_t node,char ** bufp,size_t * lenp)93626947304SEvan Yan i_hp_pack_node(hp_node_t node, char **bufp, size_t *lenp)
93726947304SEvan Yan {
93826947304SEvan Yan 	nvlist_t	*nvl;
93926947304SEvan Yan 	char		*buf = NULL;
94026947304SEvan Yan 	size_t		len = 0;
94126947304SEvan Yan 	int		rv;
94226947304SEvan Yan 
94326947304SEvan Yan 	if (nvlist_alloc(&nvl, 0, 0) != 0)
94426947304SEvan Yan 		return (ENOMEM);
94526947304SEvan Yan 
94626947304SEvan Yan 	if ((rv = nvlist_add_uint32(nvl, HP_INFO_TYPE,
94726947304SEvan Yan 	    (uint32_t)node->hp_type)) != 0)
94826947304SEvan Yan 		goto fail;
94926947304SEvan Yan 
95026947304SEvan Yan 	if ((node->hp_name) &&
95126947304SEvan Yan 	    ((rv = nvlist_add_string(nvl, HP_INFO_NAME, node->hp_name)) != 0))
95226947304SEvan Yan 		goto fail;
95326947304SEvan Yan 
95426947304SEvan Yan 	if ((node->hp_usage) &&
95526947304SEvan Yan 	    ((rv = nvlist_add_string(nvl, HP_INFO_USAGE, node->hp_usage)) != 0))
95626947304SEvan Yan 		goto fail;
95726947304SEvan Yan 
95826947304SEvan Yan 	if ((node->hp_description) &&
95926947304SEvan Yan 	    ((rv = nvlist_add_string(nvl, HP_INFO_DESC,
96026947304SEvan Yan 	    node->hp_description)) != 0))
96126947304SEvan Yan 		goto fail;
96226947304SEvan Yan 
96326947304SEvan Yan 	if ((rv = nvlist_add_uint32(nvl, HP_INFO_STATE, node->hp_state)) != 0)
96426947304SEvan Yan 		goto fail;
96526947304SEvan Yan 
96626947304SEvan Yan 	if ((node->hp_last_change != 0) &&
96726947304SEvan Yan 	    ((rv = nvlist_add_uint32(nvl, HP_INFO_TIME,
96826947304SEvan Yan 	    node->hp_last_change)) != 0))
96926947304SEvan Yan 		goto fail;
97026947304SEvan Yan 
97126947304SEvan Yan 	if ((rv = nvlist_pack(nvl, &buf, &len, NV_ENCODE_NATIVE, 0)) != 0)
97226947304SEvan Yan 		goto fail;
97326947304SEvan Yan 
97426947304SEvan Yan 	*bufp = buf;
97526947304SEvan Yan 	*lenp = len;
97626947304SEvan Yan 	nvlist_free(nvl);
97726947304SEvan Yan 	return (0);
97826947304SEvan Yan 
97926947304SEvan Yan fail:
98026947304SEvan Yan 	*bufp = NULL;
98126947304SEvan Yan 	*lenp = 0;
98226947304SEvan Yan 	nvlist_free(nvl);
98326947304SEvan Yan 	return (rv);
98426947304SEvan Yan }
98526947304SEvan Yan 
98626947304SEvan Yan /*
98726947304SEvan Yan  * i_hp_unpack_branch()
98826947304SEvan Yan  *
98926947304SEvan Yan  *	Unpack a branch of hotplug information nodes.
99026947304SEvan Yan  */
99126947304SEvan Yan static int
i_hp_unpack_branch(char * packed_buf,size_t packed_len,hp_node_t parent,hp_node_t * retp)99226947304SEvan Yan i_hp_unpack_branch(char *packed_buf, size_t packed_len, hp_node_t parent,
99326947304SEvan Yan     hp_node_t *retp)
99426947304SEvan Yan {
99526947304SEvan Yan 	hp_node_t	node = NULL;
99626947304SEvan Yan 	hp_node_t	child;
99726947304SEvan Yan 	hp_node_t	prev_child = NULL;
99826947304SEvan Yan 	nvlist_t	*nvl = NULL;
99926947304SEvan Yan 	nvpair_t	*nvp;
100026947304SEvan Yan 	char		*buf;
100126947304SEvan Yan 	size_t		len;
100226947304SEvan Yan 	int		rv;
100326947304SEvan Yan 
100426947304SEvan Yan 	/* Initialize results */
100526947304SEvan Yan 	*retp = NULL;
100626947304SEvan Yan 
100726947304SEvan Yan 	/* Unpack the nvlist for this branch */
100826947304SEvan Yan 	if ((rv = nvlist_unpack(packed_buf, packed_len, &nvl, 0)) != 0)
100926947304SEvan Yan 		return (rv);
101026947304SEvan Yan 
101126947304SEvan Yan 	/*
101226947304SEvan Yan 	 * Unpack the branch.  The first item in the nvlist is
101326947304SEvan Yan 	 * always the root node.  And zero or more subordinate
101426947304SEvan Yan 	 * branches may be packed afterward.
101526947304SEvan Yan 	 */
101626947304SEvan Yan 	for (nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) {
101726947304SEvan Yan 
101826947304SEvan Yan 		len = 0;
101926947304SEvan Yan 		buf = NULL;
102026947304SEvan Yan 
102126947304SEvan Yan 		if (strcmp(nvpair_name(nvp), HP_INFO_NODE) == 0) {
102226947304SEvan Yan 
102326947304SEvan Yan 			/* Check that there is only one root node */
102426947304SEvan Yan 			if (node != NULL) {
102526947304SEvan Yan 				hp_fini(node);
102626947304SEvan Yan 				nvlist_free(nvl);
102726947304SEvan Yan 				return (EFAULT);
102826947304SEvan Yan 			}
102926947304SEvan Yan 
103026947304SEvan Yan 			if ((rv = nvpair_value_byte_array(nvp, (uchar_t **)&buf,
103126947304SEvan Yan 			    (uint_t *)&len)) == 0)
103226947304SEvan Yan 				rv = i_hp_unpack_node(buf, len, parent, &node);
103326947304SEvan Yan 
103426947304SEvan Yan 			if (rv != 0) {
103526947304SEvan Yan 				nvlist_free(nvl);
103626947304SEvan Yan 				return (rv);
103726947304SEvan Yan 			}
103826947304SEvan Yan 
103926947304SEvan Yan 		} else if (strcmp(nvpair_name(nvp), HP_INFO_BRANCH) == 0) {
104026947304SEvan Yan 
104126947304SEvan Yan 			if ((rv = nvpair_value_byte_array(nvp, (uchar_t **)&buf,
104226947304SEvan Yan 			    (uint_t *)&len)) == 0)
104326947304SEvan Yan 				rv = i_hp_unpack_branch(buf, len, node, &child);
104426947304SEvan Yan 
104526947304SEvan Yan 			if (rv != 0) {
104626947304SEvan Yan 				hp_fini(node);
104726947304SEvan Yan 				nvlist_free(nvl);
104826947304SEvan Yan 				return (rv);
104926947304SEvan Yan 			}
105026947304SEvan Yan 
105126947304SEvan Yan 			if (prev_child) {
105226947304SEvan Yan 				prev_child->hp_sibling = child;
105326947304SEvan Yan 			} else {
105426947304SEvan Yan 				node->hp_child = child;
105526947304SEvan Yan 			}
105626947304SEvan Yan 			prev_child = child;
105726947304SEvan Yan 		}
105826947304SEvan Yan 	}
105926947304SEvan Yan 
106026947304SEvan Yan 	nvlist_free(nvl);
106126947304SEvan Yan 	*retp = node;
106226947304SEvan Yan 	return (0);
106326947304SEvan Yan }
106426947304SEvan Yan 
106526947304SEvan Yan /*
106626947304SEvan Yan  * i_hp_unpack_node()
106726947304SEvan Yan  *
106826947304SEvan Yan  *	Unpack an individual hotplug information node.
106926947304SEvan Yan  */
107026947304SEvan Yan static int
i_hp_unpack_node(char * buf,size_t len,hp_node_t parent,hp_node_t * retp)107126947304SEvan Yan i_hp_unpack_node(char *buf, size_t len, hp_node_t parent, hp_node_t *retp)
107226947304SEvan Yan {
107326947304SEvan Yan 	hp_node_t	node;
107426947304SEvan Yan 	nvlist_t	*nvl;
107526947304SEvan Yan 	nvpair_t	*nvp;
107626947304SEvan Yan 	uint32_t	val_uint32;
107726947304SEvan Yan 	char		*val_string;
107826947304SEvan Yan 	int		rv = 0;
107926947304SEvan Yan 
108026947304SEvan Yan 	/* Initialize results */
108126947304SEvan Yan 	*retp = NULL;
108226947304SEvan Yan 
108326947304SEvan Yan 	/* Unpack node into an nvlist */
108426947304SEvan Yan 	if ((nvlist_unpack(buf, len, &nvl, 0) != 0))
108526947304SEvan Yan 		return (EINVAL);
108626947304SEvan Yan 
108726947304SEvan Yan 	/* Allocate the new node */
108826947304SEvan Yan 	if ((node = (hp_node_t)calloc(1, sizeof (struct hp_node))) == NULL) {
108926947304SEvan Yan 		nvlist_free(nvl);
109026947304SEvan Yan 		return (ENOMEM);
109126947304SEvan Yan 	}
109226947304SEvan Yan 
109326947304SEvan Yan 	/* Iterate through nvlist, unpacking each field */
109426947304SEvan Yan 	for (nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) {
109526947304SEvan Yan 
109626947304SEvan Yan 		if ((strcmp(nvpair_name(nvp), HP_INFO_TYPE) == 0) &&
109726947304SEvan Yan 		    (nvpair_type(nvp) == DATA_TYPE_UINT32)) {
109826947304SEvan Yan 
109926947304SEvan Yan 			(void) nvpair_value_uint32(nvp, &val_uint32);
110026947304SEvan Yan 			node->hp_type = val_uint32;
110126947304SEvan Yan 
110226947304SEvan Yan 		} else if ((strcmp(nvpair_name(nvp), HP_INFO_NAME) == 0) &&
110326947304SEvan Yan 		    (nvpair_type(nvp) == DATA_TYPE_STRING)) {
110426947304SEvan Yan 
110526947304SEvan Yan 			(void) nvpair_value_string(nvp, &val_string);
110626947304SEvan Yan 			if ((node->hp_name = strdup(val_string)) == NULL) {
110726947304SEvan Yan 				rv = ENOMEM;
110826947304SEvan Yan 				break;
110926947304SEvan Yan 			}
111026947304SEvan Yan 
111126947304SEvan Yan 		} else if ((strcmp(nvpair_name(nvp), HP_INFO_STATE) == 0) &&
111226947304SEvan Yan 		    (nvpair_type(nvp) == DATA_TYPE_UINT32)) {
111326947304SEvan Yan 
111426947304SEvan Yan 			(void) nvpair_value_uint32(nvp, &val_uint32);
111526947304SEvan Yan 			node->hp_state = val_uint32;
111626947304SEvan Yan 
111726947304SEvan Yan 		} else if ((strcmp(nvpair_name(nvp), HP_INFO_USAGE) == 0) &&
111826947304SEvan Yan 		    (nvpair_type(nvp) == DATA_TYPE_STRING)) {
111926947304SEvan Yan 
112026947304SEvan Yan 			(void) nvpair_value_string(nvp, &val_string);
112126947304SEvan Yan 			if ((node->hp_usage = strdup(val_string)) == NULL) {
112226947304SEvan Yan 				rv = ENOMEM;
112326947304SEvan Yan 				break;
112426947304SEvan Yan 			}
112526947304SEvan Yan 
112626947304SEvan Yan 		} else if ((strcmp(nvpair_name(nvp), HP_INFO_DESC) == 0) &&
112726947304SEvan Yan 		    (nvpair_type(nvp) == DATA_TYPE_STRING)) {
112826947304SEvan Yan 
112926947304SEvan Yan 			(void) nvpair_value_string(nvp, &val_string);
113026947304SEvan Yan 			if ((node->hp_description = strdup(val_string))
113126947304SEvan Yan 			    == NULL) {
113226947304SEvan Yan 				rv = ENOMEM;
113326947304SEvan Yan 				break;
113426947304SEvan Yan 			}
113526947304SEvan Yan 
113626947304SEvan Yan 		} else if ((strcmp(nvpair_name(nvp), HP_INFO_TIME) == 0) &&
113726947304SEvan Yan 		    (nvpair_type(nvp) == DATA_TYPE_UINT32)) {
113826947304SEvan Yan 
113926947304SEvan Yan 			(void) nvpair_value_uint32(nvp, &val_uint32);
114026947304SEvan Yan 			node->hp_last_change = (time_t)val_uint32;
114126947304SEvan Yan 
114226947304SEvan Yan 		} else {
114326947304SEvan Yan 			i_hp_dprintf("i_hp_unpack_node: unrecognized: '%s'\n",
114426947304SEvan Yan 			    nvpair_name(nvp));
114526947304SEvan Yan 		}
114626947304SEvan Yan 	}
114726947304SEvan Yan 
114826947304SEvan Yan 	/* Unpacked nvlist no longer needed */
114926947304SEvan Yan 	nvlist_free(nvl);
115026947304SEvan Yan 
115126947304SEvan Yan 	/* Check for errors */
115226947304SEvan Yan 	if (rv != 0) {
115326947304SEvan Yan 		hp_fini(node);
115426947304SEvan Yan 		return (rv);
115526947304SEvan Yan 	}
115626947304SEvan Yan 
115726947304SEvan Yan 	/* Success */
115826947304SEvan Yan 	node->hp_parent = parent;
115926947304SEvan Yan 	*retp = node;
116026947304SEvan Yan 	return (0);
116126947304SEvan Yan }
116226947304SEvan Yan 
116326947304SEvan Yan /*
116426947304SEvan Yan  * i_hp_call_hotplugd()
116526947304SEvan Yan  *
116626947304SEvan Yan  *	Perform a door call to the hotplug daemon.
116726947304SEvan Yan  */
116826947304SEvan Yan static int
i_hp_call_hotplugd(nvlist_t * args,nvlist_t ** resultsp)116926947304SEvan Yan i_hp_call_hotplugd(nvlist_t *args, nvlist_t **resultsp)
117026947304SEvan Yan {
117126947304SEvan Yan 	door_arg_t	door_arg;
117226947304SEvan Yan 	nvlist_t	*results = NULL;
117326947304SEvan Yan 	char		*buf = NULL;
117426947304SEvan Yan 	size_t		len = 0;
117526947304SEvan Yan 	uint64_t	seqnum;
117626947304SEvan Yan 	int		door_fd;
117726947304SEvan Yan 	int		rv;
117826947304SEvan Yan 
117926947304SEvan Yan 	/* Initialize results */
118026947304SEvan Yan 	*resultsp = NULL;
118126947304SEvan Yan 
118226947304SEvan Yan 	/* Open door */
118326947304SEvan Yan 	if ((door_fd = open(HOTPLUGD_DOOR, O_RDONLY)) < 0) {
118426947304SEvan Yan 		i_hp_dprintf("i_hp_call_hotplugd: cannot open door (%s)\n",
118526947304SEvan Yan 		    strerror(errno));
118626947304SEvan Yan 		return (EBADF);
118726947304SEvan Yan 	}
118826947304SEvan Yan 
118926947304SEvan Yan 	/* Pack the nvlist of arguments */
119026947304SEvan Yan 	if ((rv = nvlist_pack(args, &buf, &len, NV_ENCODE_NATIVE, 0)) != 0) {
119126947304SEvan Yan 		i_hp_dprintf("i_hp_call_hotplugd: cannot pack arguments (%s)\n",
119226947304SEvan Yan 		    strerror(rv));
119326947304SEvan Yan 		return (rv);
119426947304SEvan Yan 	}
119526947304SEvan Yan 
119626947304SEvan Yan 	/* Set the door argument using the packed arguments */
119726947304SEvan Yan 	door_arg.data_ptr = buf;
119826947304SEvan Yan 	door_arg.data_size = len;
119926947304SEvan Yan 	door_arg.desc_ptr = NULL;
120026947304SEvan Yan 	door_arg.desc_num = 0;
120126947304SEvan Yan 	door_arg.rbuf = (char *)(uintptr_t)&rv;
120226947304SEvan Yan 	door_arg.rsize = sizeof (rv);
120326947304SEvan Yan 
120426947304SEvan Yan 	/* Attempt the door call */
120526947304SEvan Yan 	if (door_call(door_fd, &door_arg) != 0) {
120626947304SEvan Yan 		rv = errno;
120726947304SEvan Yan 		i_hp_dprintf("i_hp_call_hotplugd: door call failed (%s)\n",
120826947304SEvan Yan 		    strerror(rv));
120926947304SEvan Yan 		(void) close(door_fd);
121026947304SEvan Yan 		free(buf);
121126947304SEvan Yan 		return (rv);
121226947304SEvan Yan 	}
121326947304SEvan Yan 
121426947304SEvan Yan 	/* The arguments are no longer needed */
121526947304SEvan Yan 	free(buf);
121626947304SEvan Yan 
121726947304SEvan Yan 	/*
121826947304SEvan Yan 	 * If results are not in the original buffer provided,
121926947304SEvan Yan 	 * then check and process the new results buffer.
122026947304SEvan Yan 	 */
122126947304SEvan Yan 	if (door_arg.rbuf != (char *)(uintptr_t)&rv) {
122226947304SEvan Yan 
122326947304SEvan Yan 		/*
122426947304SEvan Yan 		 * First check that the buffer is valid.  Then check for
122526947304SEvan Yan 		 * the simple case where a short result code was sent.
122626947304SEvan Yan 		 * The last case is a packed nvlist was returned, which
122726947304SEvan Yan 		 * needs to be unpacked.
122826947304SEvan Yan 		 */
122926947304SEvan Yan 		if ((door_arg.rbuf == NULL) ||
123026947304SEvan Yan 		    (door_arg.data_size < sizeof (rv))) {
123126947304SEvan Yan 			i_hp_dprintf("i_hp_call_hotplugd: invalid results.\n");
123226947304SEvan Yan 			rv = EFAULT;
123326947304SEvan Yan 
123426947304SEvan Yan 		} else if (door_arg.data_size == sizeof (rv)) {
123526947304SEvan Yan 			rv = *(int *)(uintptr_t)door_arg.rbuf;
123626947304SEvan Yan 
123726947304SEvan Yan 		} else if ((rv = nvlist_unpack(door_arg.rbuf,
123826947304SEvan Yan 		    door_arg.data_size, &results, 0)) != 0) {
123926947304SEvan Yan 			i_hp_dprintf("i_hp_call_hotplugd: "
124026947304SEvan Yan 			    "cannot unpack results (%s).\n", strerror(rv));
124126947304SEvan Yan 			results = NULL;
124226947304SEvan Yan 			rv = EFAULT;
124326947304SEvan Yan 		}
124426947304SEvan Yan 
124526947304SEvan Yan 		/* Unmap the results buffer */
124626947304SEvan Yan 		if (door_arg.rbuf != NULL)
124726947304SEvan Yan 			(void) munmap(door_arg.rbuf, door_arg.rsize);
124826947304SEvan Yan 
124926947304SEvan Yan 		/*
125026947304SEvan Yan 		 * In the case of a packed nvlist, notify the daemon
125126947304SEvan Yan 		 * that it can free the result buffer from its heap.
125226947304SEvan Yan 		 */
125326947304SEvan Yan 		if ((results != NULL) &&
125426947304SEvan Yan 		    (nvlist_lookup_uint64(results, HPD_SEQNUM, &seqnum) == 0)) {
125526947304SEvan Yan 			door_arg.data_ptr = (char *)(uintptr_t)&seqnum;
125626947304SEvan Yan 			door_arg.data_size = sizeof (seqnum);
125726947304SEvan Yan 			door_arg.desc_ptr = NULL;
125826947304SEvan Yan 			door_arg.desc_num = 0;
125926947304SEvan Yan 			door_arg.rbuf = NULL;
126026947304SEvan Yan 			door_arg.rsize = 0;
126126947304SEvan Yan 			(void) door_call(door_fd, &door_arg);
126226947304SEvan Yan 			if (door_arg.rbuf != NULL)
126326947304SEvan Yan 				(void) munmap(door_arg.rbuf, door_arg.rsize);
126426947304SEvan Yan 		}
126526947304SEvan Yan 
126626947304SEvan Yan 		*resultsp = results;
126726947304SEvan Yan 	}
126826947304SEvan Yan 
126926947304SEvan Yan 	(void) close(door_fd);
127026947304SEvan Yan 	return (rv);
127126947304SEvan Yan }
127226947304SEvan Yan 
127326947304SEvan Yan /*
127426947304SEvan Yan  * i_hp_set_args()
127526947304SEvan Yan  *
127626947304SEvan Yan  *	Construct an nvlist of arguments for a hotplugd door call.
127726947304SEvan Yan  */
127826947304SEvan Yan 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)127926947304SEvan Yan i_hp_set_args(hp_cmd_t cmd, const char *path, const char *connection,
128026947304SEvan Yan     uint_t flags, const char *options, int state)
128126947304SEvan Yan {
128226947304SEvan Yan 	nvlist_t	*args;
128326947304SEvan Yan 
128426947304SEvan Yan 	/* Allocate a new nvlist */
128526947304SEvan Yan 	if (nvlist_alloc(&args, NV_UNIQUE_NAME_TYPE, 0) != 0)
128626947304SEvan Yan 		return (NULL);
128726947304SEvan Yan 
128826947304SEvan Yan 	/* Add common arguments */
128926947304SEvan Yan 	if ((nvlist_add_int32(args, HPD_CMD, cmd) != 0) ||
129026947304SEvan Yan 	    (nvlist_add_string(args, HPD_PATH, path) != 0)) {
129126947304SEvan Yan 		nvlist_free(args);
129226947304SEvan Yan 		return (NULL);
129326947304SEvan Yan 	}
129426947304SEvan Yan 
129526947304SEvan Yan 	/* Add connection, but only if defined */
129626947304SEvan Yan 	if ((connection != NULL) && (connection[0] != '\0') &&
129726947304SEvan Yan 	    (nvlist_add_string(args, HPD_CONNECTION, connection) != 0)) {
129826947304SEvan Yan 		nvlist_free(args);
129926947304SEvan Yan 		return (NULL);
130026947304SEvan Yan 	}
130126947304SEvan Yan 
130226947304SEvan Yan 	/* Add flags, but only if defined */
130326947304SEvan Yan 	if ((flags != 0) && (nvlist_add_uint32(args, HPD_FLAGS, flags) != 0)) {
130426947304SEvan Yan 		nvlist_free(args);
130526947304SEvan Yan 		return (NULL);
130626947304SEvan Yan 	}
130726947304SEvan Yan 
130826947304SEvan Yan 	/* Add options, but only if defined */
130926947304SEvan Yan 	if ((options != NULL) &&
131026947304SEvan Yan 	    (nvlist_add_string(args, HPD_OPTIONS, options) != 0)) {
131126947304SEvan Yan 		nvlist_free(args);
131226947304SEvan Yan 		return (NULL);
131326947304SEvan Yan 	}
131426947304SEvan Yan 
131526947304SEvan Yan 	/* Add state, but only for CHANGESTATE command */
131626947304SEvan Yan 	if ((cmd == HP_CMD_CHANGESTATE) &&
131726947304SEvan Yan 	    (nvlist_add_int32(args, HPD_STATE, state) != 0)) {
131826947304SEvan Yan 		nvlist_free(args);
131926947304SEvan Yan 		return (NULL);
132026947304SEvan Yan 	}
132126947304SEvan Yan 
132226947304SEvan Yan 	return (args);
132326947304SEvan Yan }
132426947304SEvan Yan 
132526947304SEvan Yan /*
132626947304SEvan Yan  * i_hp_parse_results()
132726947304SEvan Yan  *
132826947304SEvan Yan  *	Parse out individual fields of an nvlist of results from
132926947304SEvan Yan  *	a hotplugd door call.
133026947304SEvan Yan  */
133126947304SEvan Yan static int
i_hp_parse_results(nvlist_t * results,hp_node_t * rootp,char ** optionsp)133226947304SEvan Yan i_hp_parse_results(nvlist_t *results, hp_node_t *rootp, char **optionsp)
133326947304SEvan Yan {
133426947304SEvan Yan 	int	rv;
133526947304SEvan Yan 
133626947304SEvan Yan 	/* Parse an information snapshot */
133726947304SEvan Yan 	if (rootp) {
133826947304SEvan Yan 		char	*buf = NULL;
133926947304SEvan Yan 		size_t	len = 0;
134026947304SEvan Yan 
134126947304SEvan Yan 		*rootp = NULL;
134226947304SEvan Yan 		if (nvlist_lookup_byte_array(results, HPD_INFO,
134326947304SEvan Yan 		    (uchar_t **)&buf, (uint_t *)&len) == 0) {
134426947304SEvan Yan 			if ((rv = hp_unpack(buf, len, rootp)) != 0)
134526947304SEvan Yan 				return (rv);
134626947304SEvan Yan 		}
134726947304SEvan Yan 	}
134826947304SEvan Yan 
134926947304SEvan Yan 	/* Parse a bus private option string */
135026947304SEvan Yan 	if (optionsp) {
135126947304SEvan Yan 		char	*str;
135226947304SEvan Yan 
135326947304SEvan Yan 		*optionsp = NULL;
135426947304SEvan Yan 		if ((nvlist_lookup_string(results, HPD_OPTIONS, &str) == 0) &&
135526947304SEvan Yan 		    ((*optionsp = strdup(str)) == NULL)) {
135626947304SEvan Yan 			return (ENOMEM);
135726947304SEvan Yan 		}
135826947304SEvan Yan 	}
135926947304SEvan Yan 
136026947304SEvan Yan 	/* Parse result code of the operation */
136126947304SEvan Yan 	if (nvlist_lookup_int32(results, HPD_STATUS, &rv) != 0) {
136226947304SEvan Yan 		i_hp_dprintf("i_hp_call_hotplugd: missing status.\n");
136326947304SEvan Yan 		return (EFAULT);
136426947304SEvan Yan 	}
136526947304SEvan Yan 
136626947304SEvan Yan 	return (rv);
136726947304SEvan Yan }
1368