1*26947304SEvan Yan /*
2*26947304SEvan Yan  * CDDL HEADER START
3*26947304SEvan Yan  *
4*26947304SEvan Yan  * The contents of this file are subject to the terms of the
5*26947304SEvan Yan  * Common Development and Distribution License (the "License").
6*26947304SEvan Yan  * You may not use this file except in compliance with the License.
7*26947304SEvan Yan  *
8*26947304SEvan Yan  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*26947304SEvan Yan  * or http://www.opensolaris.org/os/licensing.
10*26947304SEvan Yan  * See the License for the specific language governing permissions
11*26947304SEvan Yan  * and limitations under the License.
12*26947304SEvan Yan  *
13*26947304SEvan Yan  * When distributing Covered Code, include this CDDL HEADER in each
14*26947304SEvan Yan  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*26947304SEvan Yan  * If applicable, add the following below this CDDL HEADER, with the
16*26947304SEvan Yan  * fields enclosed by brackets "[]" replaced with your own identifying
17*26947304SEvan Yan  * information: Portions Copyright [yyyy] [name of copyright owner]
18*26947304SEvan Yan  *
19*26947304SEvan Yan  * CDDL HEADER END
20*26947304SEvan Yan  */
21*26947304SEvan Yan 
22*26947304SEvan Yan /*
23*26947304SEvan Yan  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24*26947304SEvan Yan  * Use is subject to license terms.
25*26947304SEvan Yan  */
26*26947304SEvan Yan 
27*26947304SEvan Yan #include <stdio.h>
28*26947304SEvan Yan #include <stdlib.h>
29*26947304SEvan Yan #include <unistd.h>
30*26947304SEvan Yan #include <errno.h>
31*26947304SEvan Yan #include <string.h>
32*26947304SEvan Yan #include <librcm.h>
33*26947304SEvan Yan #include <libhotplug.h>
34*26947304SEvan Yan #include <libhotplug_impl.h>
35*26947304SEvan Yan #include <sys/sunddi.h>
36*26947304SEvan Yan #include <sys/ddi_hp.h>
37*26947304SEvan Yan #include "hotplugd_impl.h"
38*26947304SEvan Yan 
39*26947304SEvan Yan /*
40*26947304SEvan Yan  * Define structures for a path-to-usage lookup table.
41*26947304SEvan Yan  */
42*26947304SEvan Yan typedef struct info_entry {
43*26947304SEvan Yan 	char			*rsrc;
44*26947304SEvan Yan 	char			*usage;
45*26947304SEvan Yan 	struct info_entry	*next;
46*26947304SEvan Yan } info_entry_t;
47*26947304SEvan Yan 
48*26947304SEvan Yan typedef struct {
49*26947304SEvan Yan 	char		*path;
50*26947304SEvan Yan 	info_entry_t	*entries;
51*26947304SEvan Yan } info_table_t;
52*26947304SEvan Yan 
53*26947304SEvan Yan /*
54*26947304SEvan Yan  * Define callback argument used when getting resources.
55*26947304SEvan Yan  */
56*26947304SEvan Yan typedef struct {
57*26947304SEvan Yan 	int	error;
58*26947304SEvan Yan 	int	n_rsrcs;
59*26947304SEvan Yan 	char	**rsrcs;
60*26947304SEvan Yan 	char	path[MAXPATHLEN];
61*26947304SEvan Yan 	char	connection[MAXPATHLEN];
62*26947304SEvan Yan 	char	dev_path[MAXPATHLEN];
63*26947304SEvan Yan } resource_cb_arg_t;
64*26947304SEvan Yan 
65*26947304SEvan Yan /*
66*26947304SEvan Yan  * Define callback argument used when merging info.
67*26947304SEvan Yan  */
68*26947304SEvan Yan typedef struct {
69*26947304SEvan Yan 	int		error;
70*26947304SEvan Yan 	info_table_t	*table;
71*26947304SEvan Yan 	size_t		table_len;
72*26947304SEvan Yan 	char		path[MAXPATHLEN];
73*26947304SEvan Yan 	char		connection[MAXPATHLEN];
74*26947304SEvan Yan } merge_cb_arg_t;
75*26947304SEvan Yan 
76*26947304SEvan Yan /*
77*26947304SEvan Yan  * Local functions.
78*26947304SEvan Yan  */
79*26947304SEvan Yan static int	merge_rcm_info(hp_node_t root, rcm_info_t *info);
80*26947304SEvan Yan static int	get_rcm_usage(char **rsrcs, rcm_info_t **info_p);
81*26947304SEvan Yan static int	build_table(rcm_info_t *info, info_table_t **tablep,
82*26947304SEvan Yan 		    size_t *table_lenp);
83*26947304SEvan Yan static void	free_table(info_table_t *table, size_t table_len);
84*26947304SEvan Yan static int	resource_callback(hp_node_t node, void *argp);
85*26947304SEvan Yan static int	merge_callback(hp_node_t node, void *argp);
86*26947304SEvan Yan static int	rsrc2path(const char *rsrc, char *path);
87*26947304SEvan Yan static int	compare_info(const void *a, const void *b);
88*26947304SEvan Yan 
89*26947304SEvan Yan /*
90*26947304SEvan Yan  * copy_usage()
91*26947304SEvan Yan  *
92*26947304SEvan Yan  *	Given an information snapshot, get the corresponding
93*26947304SEvan Yan  *	RCM usage information and merge it into the snapshot.
94*26947304SEvan Yan  */
95*26947304SEvan Yan int
copy_usage(hp_node_t root)96*26947304SEvan Yan copy_usage(hp_node_t root)
97*26947304SEvan Yan {
98*26947304SEvan Yan 	rcm_info_t	*info = NULL;
99*26947304SEvan Yan 	char		**rsrcs = NULL;
100*26947304SEvan Yan 	int		rv;
101*26947304SEvan Yan 
102*26947304SEvan Yan 	/* Get resource names */
103*26947304SEvan Yan 	if ((rv = rcm_resources(root, &rsrcs)) != 0) {
104*26947304SEvan Yan 		log_err("Cannot get RCM resources (%s)\n", strerror(rv));
105*26947304SEvan Yan 		return (rv);
106*26947304SEvan Yan 	}
107*26947304SEvan Yan 
108*26947304SEvan Yan 	/* Do nothing if no resources */
109*26947304SEvan Yan 	if (rsrcs == NULL)
110*26947304SEvan Yan 		return (0);
111*26947304SEvan Yan 
112*26947304SEvan Yan 	/* Get RCM usage information */
113*26947304SEvan Yan 	if ((rv = get_rcm_usage(rsrcs, &info)) != 0) {
114*26947304SEvan Yan 		log_err("Cannot get RCM information (%s)\n", strerror(rv));
115*26947304SEvan Yan 		free_rcm_resources(rsrcs);
116*26947304SEvan Yan 		return (rv);
117*26947304SEvan Yan 	}
118*26947304SEvan Yan 
119*26947304SEvan Yan 	/* Done with resource names */
120*26947304SEvan Yan 	free_rcm_resources(rsrcs);
121*26947304SEvan Yan 
122*26947304SEvan Yan 	/* If there is RCM usage information, merge it in */
123*26947304SEvan Yan 	if (info != NULL) {
124*26947304SEvan Yan 		rv = merge_rcm_info(root, info);
125*26947304SEvan Yan 		rcm_free_info(info);
126*26947304SEvan Yan 		return (rv);
127*26947304SEvan Yan 	}
128*26947304SEvan Yan 
129*26947304SEvan Yan 	return (0);
130*26947304SEvan Yan }
131*26947304SEvan Yan 
132*26947304SEvan Yan /*
133*26947304SEvan Yan  * rcm_resources()
134*26947304SEvan Yan  *
135*26947304SEvan Yan  *	Given the root of a hotplug information snapshot,
136*26947304SEvan Yan  *	construct a list of RCM compatible resource names.
137*26947304SEvan Yan  */
138*26947304SEvan Yan int
rcm_resources(hp_node_t root,char *** rsrcsp)139*26947304SEvan Yan rcm_resources(hp_node_t root, char ***rsrcsp)
140*26947304SEvan Yan {
141*26947304SEvan Yan 	resource_cb_arg_t	arg;
142*26947304SEvan Yan 
143*26947304SEvan Yan 	/* Initialize results */
144*26947304SEvan Yan 	*rsrcsp = NULL;
145*26947304SEvan Yan 
146*26947304SEvan Yan 	/* Traverse snapshot to get resources */
147*26947304SEvan Yan 	(void) memset(&arg, 0, sizeof (resource_cb_arg_t));
148*26947304SEvan Yan 	(void) hp_traverse(root, &arg, resource_callback);
149*26947304SEvan Yan 
150*26947304SEvan Yan 	/* Check for errors */
151*26947304SEvan Yan 	if (arg.error != 0) {
152*26947304SEvan Yan 		free_rcm_resources(arg.rsrcs);
153*26947304SEvan Yan 		return (arg.error);
154*26947304SEvan Yan 	}
155*26947304SEvan Yan 
156*26947304SEvan Yan 	/* Success */
157*26947304SEvan Yan 	*rsrcsp = arg.rsrcs;
158*26947304SEvan Yan 	return (0);
159*26947304SEvan Yan }
160*26947304SEvan Yan 
161*26947304SEvan Yan /*
162*26947304SEvan Yan  * free_rcm_resources()
163*26947304SEvan Yan  *
164*26947304SEvan Yan  *	Free a table of RCM resource names.
165*26947304SEvan Yan  */
166*26947304SEvan Yan void
free_rcm_resources(char ** rsrcs)167*26947304SEvan Yan free_rcm_resources(char **rsrcs)
168*26947304SEvan Yan {
169*26947304SEvan Yan 	int	i;
170*26947304SEvan Yan 
171*26947304SEvan Yan 	if (rsrcs != NULL) {
172*26947304SEvan Yan 		for (i = 0; rsrcs[i] != NULL; i++)
173*26947304SEvan Yan 			free(rsrcs[i]);
174*26947304SEvan Yan 		free(rsrcs);
175*26947304SEvan Yan 	}
176*26947304SEvan Yan }
177*26947304SEvan Yan 
178*26947304SEvan Yan /*
179*26947304SEvan Yan  * rcm_offline()
180*26947304SEvan Yan  *
181*26947304SEvan Yan  *	Implement an RCM offline request.
182*26947304SEvan Yan  *
183*26947304SEvan Yan  *	NOTE: errors from RCM will be merged into the snapshot.
184*26947304SEvan Yan  */
185*26947304SEvan Yan int
rcm_offline(char ** rsrcs,uint_t flags,hp_node_t root)186*26947304SEvan Yan rcm_offline(char **rsrcs, uint_t flags, hp_node_t root)
187*26947304SEvan Yan {
188*26947304SEvan Yan 	rcm_handle_t	*handle;
189*26947304SEvan Yan 	rcm_info_t	*info = NULL;
190*26947304SEvan Yan 	uint_t		rcm_flags = 0;
191*26947304SEvan Yan 	int		rv = 0;
192*26947304SEvan Yan 
193*26947304SEvan Yan 	dprintf("rcm_offline()\n");
194*26947304SEvan Yan 
195*26947304SEvan Yan 	/* Set flags */
196*26947304SEvan Yan 	if (flags & HPFORCE)
197*26947304SEvan Yan 		rcm_flags |= RCM_FORCE;
198*26947304SEvan Yan 	if (flags & HPQUERY)
199*26947304SEvan Yan 		rcm_flags |= RCM_QUERY;
200*26947304SEvan Yan 
201*26947304SEvan Yan 	/* Allocate RCM handle */
202*26947304SEvan Yan 	if (rcm_alloc_handle(NULL, 0, NULL, &handle) != RCM_SUCCESS) {
203*26947304SEvan Yan 		log_err("Cannot allocate RCM handle (%s)\n", strerror(errno));
204*26947304SEvan Yan 		return (EFAULT);
205*26947304SEvan Yan 	}
206*26947304SEvan Yan 
207*26947304SEvan Yan 	/* Request RCM offline */
208*26947304SEvan Yan 	if (rcm_request_offline_list(handle, rsrcs, rcm_flags,
209*26947304SEvan Yan 	    &info) != RCM_SUCCESS)
210*26947304SEvan Yan 		rv = EBUSY;
211*26947304SEvan Yan 
212*26947304SEvan Yan 	/* RCM handle is no longer needed */
213*26947304SEvan Yan 	(void) rcm_free_handle(handle);
214*26947304SEvan Yan 
215*26947304SEvan Yan 	/*
216*26947304SEvan Yan 	 * Check if RCM returned any information tuples.  If so,
217*26947304SEvan Yan 	 * then also check if the RCM operation failed, and possibly
218*26947304SEvan Yan 	 * merge the RCM info into the caller's hotplug snapshot.
219*26947304SEvan Yan 	 */
220*26947304SEvan Yan 	if (info != NULL) {
221*26947304SEvan Yan 		if (rv != 0)
222*26947304SEvan Yan 			(void) merge_rcm_info(root, info);
223*26947304SEvan Yan 		rcm_free_info(info);
224*26947304SEvan Yan 	}
225*26947304SEvan Yan 
226*26947304SEvan Yan 	return (rv);
227*26947304SEvan Yan }
228*26947304SEvan Yan 
229*26947304SEvan Yan /*
230*26947304SEvan Yan  * rcm_online()
231*26947304SEvan Yan  *
232*26947304SEvan Yan  *	Implement an RCM online notification.
233*26947304SEvan Yan  */
234*26947304SEvan Yan void
rcm_online(char ** rsrcs)235*26947304SEvan Yan rcm_online(char **rsrcs)
236*26947304SEvan Yan {
237*26947304SEvan Yan 	rcm_handle_t	*handle;
238*26947304SEvan Yan 	rcm_info_t	*info = NULL;
239*26947304SEvan Yan 
240*26947304SEvan Yan 	dprintf("rcm_online()\n");
241*26947304SEvan Yan 
242*26947304SEvan Yan 	if (rcm_alloc_handle(NULL, 0, NULL, &handle) != RCM_SUCCESS) {
243*26947304SEvan Yan 		log_err("Cannot allocate RCM handle (%s)\n", strerror(errno));
244*26947304SEvan Yan 		return;
245*26947304SEvan Yan 	}
246*26947304SEvan Yan 
247*26947304SEvan Yan 	(void) rcm_notify_online_list(handle, rsrcs, 0, &info);
248*26947304SEvan Yan 
249*26947304SEvan Yan 	(void) rcm_free_handle(handle);
250*26947304SEvan Yan 
251*26947304SEvan Yan 	if (info != NULL)
252*26947304SEvan Yan 		rcm_free_info(info);
253*26947304SEvan Yan }
254*26947304SEvan Yan 
255*26947304SEvan Yan /*
256*26947304SEvan Yan  * rcm_remove()
257*26947304SEvan Yan  *
258*26947304SEvan Yan  *	Implement an RCM remove notification.
259*26947304SEvan Yan  */
260*26947304SEvan Yan void
rcm_remove(char ** rsrcs)261*26947304SEvan Yan rcm_remove(char **rsrcs)
262*26947304SEvan Yan {
263*26947304SEvan Yan 	rcm_handle_t	*handle;
264*26947304SEvan Yan 	rcm_info_t	*info = NULL;
265*26947304SEvan Yan 
266*26947304SEvan Yan 	dprintf("rcm_remove()\n");
267*26947304SEvan Yan 
268*26947304SEvan Yan 	if (rcm_alloc_handle(NULL, 0, NULL, &handle) != RCM_SUCCESS) {
269*26947304SEvan Yan 		log_err("Cannot allocate RCM handle (%s)\n", strerror(errno));
270*26947304SEvan Yan 		return;
271*26947304SEvan Yan 	}
272*26947304SEvan Yan 
273*26947304SEvan Yan 	(void) rcm_notify_remove_list(handle, rsrcs, 0, &info);
274*26947304SEvan Yan 
275*26947304SEvan Yan 	(void) rcm_free_handle(handle);
276*26947304SEvan Yan 
277*26947304SEvan Yan 	if (info != NULL)
278*26947304SEvan Yan 		rcm_free_info(info);
279*26947304SEvan Yan }
280*26947304SEvan Yan 
281*26947304SEvan Yan /*
282*26947304SEvan Yan  * get_rcm_usage()
283*26947304SEvan Yan  *
284*26947304SEvan Yan  *	Lookup usage information for a set of resources from RCM.
285*26947304SEvan Yan  */
286*26947304SEvan Yan static int
get_rcm_usage(char ** rsrcs,rcm_info_t ** info_p)287*26947304SEvan Yan get_rcm_usage(char **rsrcs, rcm_info_t **info_p)
288*26947304SEvan Yan {
289*26947304SEvan Yan 	rcm_handle_t	*handle;
290*26947304SEvan Yan 	rcm_info_t	*info = NULL;
291*26947304SEvan Yan 	int		rv = 0;
292*26947304SEvan Yan 
293*26947304SEvan Yan 	/* No-op if no RCM resources */
294*26947304SEvan Yan 	if (rsrcs == NULL)
295*26947304SEvan Yan 		return (0);
296*26947304SEvan Yan 
297*26947304SEvan Yan 	/* Allocate RCM handle */
298*26947304SEvan Yan 	if (rcm_alloc_handle(NULL, RCM_NOPID, NULL, &handle) != RCM_SUCCESS) {
299*26947304SEvan Yan 		log_err("Cannot allocate RCM handle (%s)\n", strerror(errno));
300*26947304SEvan Yan 		return (EFAULT);
301*26947304SEvan Yan 	}
302*26947304SEvan Yan 
303*26947304SEvan Yan 	/* Get usage information from RCM */
304*26947304SEvan Yan 	if (rcm_get_info_list(handle, rsrcs,
305*26947304SEvan Yan 	    RCM_INCLUDE_DEPENDENT | RCM_INCLUDE_SUBTREE,
306*26947304SEvan Yan 	    &info) != RCM_SUCCESS) {
307*26947304SEvan Yan 		log_err("Failed to get RCM information (%s)\n",
308*26947304SEvan Yan 		    strerror(errno));
309*26947304SEvan Yan 		rv = EFAULT;
310*26947304SEvan Yan 	}
311*26947304SEvan Yan 
312*26947304SEvan Yan 	/* RCM handle is no longer needed */
313*26947304SEvan Yan 	(void) rcm_free_handle(handle);
314*26947304SEvan Yan 
315*26947304SEvan Yan 	*info_p = info;
316*26947304SEvan Yan 	return (rv);
317*26947304SEvan Yan }
318*26947304SEvan Yan 
319*26947304SEvan Yan /*
320*26947304SEvan Yan  * merge_rcm_info()
321*26947304SEvan Yan  *
322*26947304SEvan Yan  *	Merge RCM information into a hotplug information snapshot.
323*26947304SEvan Yan  *	First a lookup table is built to map lists of RCM usage to
324*26947304SEvan Yan  *	pathnames.  Then during a full traversal of the snapshot,
325*26947304SEvan Yan  *	the lookup table is used for each node to find matching
326*26947304SEvan Yan  *	RCM info tuples for each path in the snapshot.
327*26947304SEvan Yan  */
328*26947304SEvan Yan static int
merge_rcm_info(hp_node_t root,rcm_info_t * info)329*26947304SEvan Yan merge_rcm_info(hp_node_t root, rcm_info_t *info)
330*26947304SEvan Yan {
331*26947304SEvan Yan 	merge_cb_arg_t		arg;
332*26947304SEvan Yan 	info_table_t		*table;
333*26947304SEvan Yan 	size_t			table_len;
334*26947304SEvan Yan 	int			rv;
335*26947304SEvan Yan 
336*26947304SEvan Yan 	/* Build a lookup table, mapping paths to usage information */
337*26947304SEvan Yan 	if ((rv = build_table(info, &table, &table_len)) != 0) {
338*26947304SEvan Yan 		log_err("Cannot build RCM lookup table (%s)\n", strerror(rv));
339*26947304SEvan Yan 		return (rv);
340*26947304SEvan Yan 	}
341*26947304SEvan Yan 
342*26947304SEvan Yan 	/* Stop if no valid entries were inserted in table */
343*26947304SEvan Yan 	if ((table == NULL) || (table_len == 0)) {
344*26947304SEvan Yan 		log_err("Unable to gather RCM usage.\n");
345*26947304SEvan Yan 		return (0);
346*26947304SEvan Yan 	}
347*26947304SEvan Yan 
348*26947304SEvan Yan 	/* Initialize callback argument */
349*26947304SEvan Yan 	(void) memset(&arg, 0, sizeof (merge_cb_arg_t));
350*26947304SEvan Yan 	arg.table = table;
351*26947304SEvan Yan 	arg.table_len = table_len;
352*26947304SEvan Yan 
353*26947304SEvan Yan 	/* Perform a merge traversal */
354*26947304SEvan Yan 	(void) hp_traverse(root, (void *)&arg, merge_callback);
355*26947304SEvan Yan 
356*26947304SEvan Yan 	/* Done with the table */
357*26947304SEvan Yan 	free_table(table, table_len);
358*26947304SEvan Yan 
359*26947304SEvan Yan 	/* Check for errors */
360*26947304SEvan Yan 	if (arg.error != 0) {
361*26947304SEvan Yan 		log_err("Cannot merge RCM information (%s)\n", strerror(rv));
362*26947304SEvan Yan 		return (rv);
363*26947304SEvan Yan 	}
364*26947304SEvan Yan 
365*26947304SEvan Yan 	return (0);
366*26947304SEvan Yan }
367*26947304SEvan Yan 
368*26947304SEvan Yan /*
369*26947304SEvan Yan  * resource_callback()
370*26947304SEvan Yan  *
371*26947304SEvan Yan  *	A callback function for hp_traverse() that builds an RCM
372*26947304SEvan Yan  *	compatible list of resource path names.  The array has
373*26947304SEvan Yan  *	been pre-allocated based on results from the related
374*26947304SEvan Yan  *	callback resource_count_callback().
375*26947304SEvan Yan  */
376*26947304SEvan Yan static int
resource_callback(hp_node_t node,void * argp)377*26947304SEvan Yan resource_callback(hp_node_t node, void *argp)
378*26947304SEvan Yan {
379*26947304SEvan Yan 	resource_cb_arg_t	*arg = (resource_cb_arg_t *)argp;
380*26947304SEvan Yan 	char			**new_rsrcs;
381*26947304SEvan Yan 	size_t			new_size;
382*26947304SEvan Yan 	int			type;
383*26947304SEvan Yan 
384*26947304SEvan Yan 	/* Get node type */
385*26947304SEvan Yan 	type = hp_type(node);
386*26947304SEvan Yan 
387*26947304SEvan Yan 	/* Prune OFFLINE ports */
388*26947304SEvan Yan 	if ((type == HP_NODE_PORT) && HP_IS_OFFLINE(hp_state(node)))
389*26947304SEvan Yan 		return (HP_WALK_PRUNECHILD);
390*26947304SEvan Yan 
391*26947304SEvan Yan 	/* Skip past non-devices */
392*26947304SEvan Yan 	if (type != HP_NODE_DEVICE)
393*26947304SEvan Yan 		return (HP_WALK_CONTINUE);
394*26947304SEvan Yan 
395*26947304SEvan Yan 	/* Lookup resource path */
396*26947304SEvan Yan 	if (hp_path(node, arg->path, arg->connection) != 0) {
397*26947304SEvan Yan 		log_err("Cannot get RCM resource path.\n");
398*26947304SEvan Yan 		arg->error = EFAULT;
399*26947304SEvan Yan 		return (HP_WALK_TERMINATE);
400*26947304SEvan Yan 	}
401*26947304SEvan Yan 
402*26947304SEvan Yan 	/* Insert "/devices" to path name */
403*26947304SEvan Yan 	(void) snprintf(arg->dev_path, MAXPATHLEN, "/devices%s", arg->path);
404*26947304SEvan Yan 
405*26947304SEvan Yan 	/*
406*26947304SEvan Yan 	 * Grow resource array to accomodate new /devices path.
407*26947304SEvan Yan 	 * NOTE: include an extra NULL pointer at end of array.
408*26947304SEvan Yan 	 */
409*26947304SEvan Yan 	new_size = (arg->n_rsrcs + 2) * sizeof (char *);
410*26947304SEvan Yan 	if (arg->rsrcs == NULL)
411*26947304SEvan Yan 		new_rsrcs = (char **)malloc(new_size);
412*26947304SEvan Yan 	else
413*26947304SEvan Yan 		new_rsrcs = (char **)realloc(arg->rsrcs, new_size);
414*26947304SEvan Yan 	if (new_rsrcs != NULL) {
415*26947304SEvan Yan 		arg->rsrcs = new_rsrcs;
416*26947304SEvan Yan 	} else {
417*26947304SEvan Yan 		log_err("Cannot allocate RCM resource array.\n");
418*26947304SEvan Yan 		arg->error = ENOMEM;
419*26947304SEvan Yan 		return (HP_WALK_TERMINATE);
420*26947304SEvan Yan 	}
421*26947304SEvan Yan 
422*26947304SEvan Yan 	/* Initialize new entries */
423*26947304SEvan Yan 	arg->rsrcs[arg->n_rsrcs] = strdup(arg->dev_path);
424*26947304SEvan Yan 	arg->rsrcs[arg->n_rsrcs + 1] = NULL;
425*26947304SEvan Yan 
426*26947304SEvan Yan 	/* Check for errors */
427*26947304SEvan Yan 	if (arg->rsrcs[arg->n_rsrcs] == NULL) {
428*26947304SEvan Yan 		log_err("Cannot allocate RCM resource path.\n");
429*26947304SEvan Yan 		arg->error = ENOMEM;
430*26947304SEvan Yan 		return (HP_WALK_TERMINATE);
431*26947304SEvan Yan 	}
432*26947304SEvan Yan 
433*26947304SEvan Yan 	/* Increment resource count */
434*26947304SEvan Yan 	arg->n_rsrcs += 1;
435*26947304SEvan Yan 
436*26947304SEvan Yan 	/* Do not visit children */
437*26947304SEvan Yan 	return (HP_WALK_PRUNECHILD);
438*26947304SEvan Yan }
439*26947304SEvan Yan 
440*26947304SEvan Yan /*
441*26947304SEvan Yan  * merge_callback()
442*26947304SEvan Yan  *
443*26947304SEvan Yan  *	A callback function for hp_traverse() that merges RCM information
444*26947304SEvan Yan  *	tuples into an existing hotplug information snapshot.  The RCM
445*26947304SEvan Yan  *	information will be turned into HP_NODE_USAGE nodes.
446*26947304SEvan Yan  */
447*26947304SEvan Yan static int
merge_callback(hp_node_t node,void * argp)448*26947304SEvan Yan merge_callback(hp_node_t node, void *argp)
449*26947304SEvan Yan {
450*26947304SEvan Yan 	merge_cb_arg_t	*arg = (merge_cb_arg_t *)argp;
451*26947304SEvan Yan 	hp_node_t	usage;
452*26947304SEvan Yan 	info_table_t	lookup;
453*26947304SEvan Yan 	info_table_t	*slot;
454*26947304SEvan Yan 	info_entry_t	*entry;
455*26947304SEvan Yan 	int		rv;
456*26947304SEvan Yan 
457*26947304SEvan Yan 	/* Only process device nodes (other nodes cannot have usage) */
458*26947304SEvan Yan 	if (hp_type(node) != HP_NODE_DEVICE)
459*26947304SEvan Yan 		return (HP_WALK_CONTINUE);
460*26947304SEvan Yan 
461*26947304SEvan Yan 	/* Get path of current node, using buffer provided in 'arg' */
462*26947304SEvan Yan 	if ((rv = hp_path(node, arg->path, arg->connection)) != 0) {
463*26947304SEvan Yan 		log_err("Cannot lookup hotplug path (%s)\n", strerror(rv));
464*26947304SEvan Yan 		arg->error = rv;
465*26947304SEvan Yan 		return (HP_WALK_TERMINATE);
466*26947304SEvan Yan 	}
467*26947304SEvan Yan 
468*26947304SEvan Yan 	/* Check the lookup table for associated usage */
469*26947304SEvan Yan 	lookup.path = arg->path;
470*26947304SEvan Yan 	if ((slot = bsearch(&lookup, arg->table, arg->table_len,
471*26947304SEvan Yan 	    sizeof (info_table_t), compare_info)) == NULL)
472*26947304SEvan Yan 		return (HP_WALK_CONTINUE);
473*26947304SEvan Yan 
474*26947304SEvan Yan 	/* Usage information was found.  Append HP_NODE_USAGE nodes. */
475*26947304SEvan Yan 	for (entry = slot->entries; entry != NULL; entry = entry->next) {
476*26947304SEvan Yan 
477*26947304SEvan Yan 		/* Allocate a new usage node */
478*26947304SEvan Yan 		usage = (hp_node_t)calloc(1, sizeof (struct hp_node));
479*26947304SEvan Yan 		if (usage == NULL) {
480*26947304SEvan Yan 			log_err("Cannot allocate hotplug usage node.\n");
481*26947304SEvan Yan 			arg->error = ENOMEM;
482*26947304SEvan Yan 			return (HP_WALK_TERMINATE);
483*26947304SEvan Yan 		}
484*26947304SEvan Yan 
485*26947304SEvan Yan 		/* Initialize the usage node's contents */
486*26947304SEvan Yan 		usage->hp_type = HP_NODE_USAGE;
487*26947304SEvan Yan 		if ((usage->hp_name = strdup(entry->rsrc)) == NULL) {
488*26947304SEvan Yan 			log_err("Cannot allocate hotplug usage node name.\n");
489*26947304SEvan Yan 			free(usage);
490*26947304SEvan Yan 			arg->error = ENOMEM;
491*26947304SEvan Yan 			return (HP_WALK_TERMINATE);
492*26947304SEvan Yan 		}
493*26947304SEvan Yan 		if ((usage->hp_usage = strdup(entry->usage)) == NULL) {
494*26947304SEvan Yan 			log_err("Cannot allocate hotplug usage node info.\n");
495*26947304SEvan Yan 			free(usage->hp_name);
496*26947304SEvan Yan 			free(usage);
497*26947304SEvan Yan 			arg->error = ENOMEM;
498*26947304SEvan Yan 			return (HP_WALK_TERMINATE);
499*26947304SEvan Yan 		}
500*26947304SEvan Yan 
501*26947304SEvan Yan 		/* Link the usage node as a child of the device node */
502*26947304SEvan Yan 		usage->hp_parent = node;
503*26947304SEvan Yan 		usage->hp_sibling = node->hp_child;
504*26947304SEvan Yan 		node->hp_child = usage;
505*26947304SEvan Yan 	}
506*26947304SEvan Yan 
507*26947304SEvan Yan 	return (HP_WALK_CONTINUE);
508*26947304SEvan Yan }
509*26947304SEvan Yan 
510*26947304SEvan Yan /*
511*26947304SEvan Yan  * build_table()
512*26947304SEvan Yan  *
513*26947304SEvan Yan  *	Build a lookup table that will be used to map paths to their
514*26947304SEvan Yan  *	corresponding RCM information tuples.
515*26947304SEvan Yan  */
516*26947304SEvan Yan static int
build_table(rcm_info_t * info,info_table_t ** tablep,size_t * table_lenp)517*26947304SEvan Yan build_table(rcm_info_t *info, info_table_t **tablep, size_t *table_lenp)
518*26947304SEvan Yan {
519*26947304SEvan Yan 	rcm_info_tuple_t	*tuple;
520*26947304SEvan Yan 	info_entry_t		*entry;
521*26947304SEvan Yan 	info_table_t		*slot;
522*26947304SEvan Yan 	info_table_t		*table;
523*26947304SEvan Yan 	size_t			table_len;
524*26947304SEvan Yan 	const char		*rsrc;
525*26947304SEvan Yan 	const char		*usage;
526*26947304SEvan Yan 	char			path[MAXPATHLEN];
527*26947304SEvan Yan 
528*26947304SEvan Yan 	/* Initialize results */
529*26947304SEvan Yan 	*tablep = NULL;
530*26947304SEvan Yan 	*table_lenp = 0;
531*26947304SEvan Yan 
532*26947304SEvan Yan 	/* Count the RCM info tuples to determine the table's size */
533*26947304SEvan Yan 	table_len = 0;
534*26947304SEvan Yan 	for (tuple = NULL; (tuple = rcm_info_next(info, tuple)) != NULL; )
535*26947304SEvan Yan 		table_len++;
536*26947304SEvan Yan 
537*26947304SEvan Yan 	/* If the table would be empty, then do nothing */
538*26947304SEvan Yan 	if (table_len == 0)
539*26947304SEvan Yan 		return (ENOENT);
540*26947304SEvan Yan 
541*26947304SEvan Yan 	/* Allocate the lookup table */
542*26947304SEvan Yan 	table = (info_table_t *)calloc(table_len, sizeof (info_table_t));
543*26947304SEvan Yan 	if (table == NULL)
544*26947304SEvan Yan 		return (ENOMEM);
545*26947304SEvan Yan 
546*26947304SEvan Yan 	/*
547*26947304SEvan Yan 	 * Fill in the lookup table.  Fill one slot in the table
548*26947304SEvan Yan 	 * for each device path that has a set of associated RCM
549*26947304SEvan Yan 	 * information tuples.  In some cases multiple tuples will
550*26947304SEvan Yan 	 * be joined together within the same slot.
551*26947304SEvan Yan 	 */
552*26947304SEvan Yan 	slot = NULL;
553*26947304SEvan Yan 	table_len = 0;
554*26947304SEvan Yan 	for (tuple = NULL; (tuple = rcm_info_next(info, tuple)) != NULL; ) {
555*26947304SEvan Yan 
556*26947304SEvan Yan 		/*
557*26947304SEvan Yan 		 * Extract RCM resource name and usage description.
558*26947304SEvan Yan 		 *
559*26947304SEvan Yan 		 * NOTE: skip invalid tuples to return as much as possible.
560*26947304SEvan Yan 		 */
561*26947304SEvan Yan 		if (((rsrc = rcm_info_rsrc(tuple)) == NULL) ||
562*26947304SEvan Yan 		    ((usage = rcm_info_info(tuple)) == NULL)) {
563*26947304SEvan Yan 			log_err("RCM returned invalid resource or usage.\n");
564*26947304SEvan Yan 			continue;
565*26947304SEvan Yan 		}
566*26947304SEvan Yan 
567*26947304SEvan Yan 		/*
568*26947304SEvan Yan 		 * Try to convert the RCM resource name to a hotplug path.
569*26947304SEvan Yan 		 * If conversion succeeds and this path differs from the
570*26947304SEvan Yan 		 * current slot in the table, then initialize the next
571*26947304SEvan Yan 		 * slot in the table.
572*26947304SEvan Yan 		 */
573*26947304SEvan Yan 		if ((rsrc2path(rsrc, path) == 0) &&
574*26947304SEvan Yan 		    ((slot == NULL) || (strcmp(slot->path, path) != 0))) {
575*26947304SEvan Yan 			slot = &table[table_len];
576*26947304SEvan Yan 			if ((slot->path = strdup(path)) == NULL) {
577*26947304SEvan Yan 				log_err("Cannot build info table slot.\n");
578*26947304SEvan Yan 				free_table(table, table_len);
579*26947304SEvan Yan 				return (ENOMEM);
580*26947304SEvan Yan 			}
581*26947304SEvan Yan 			table_len++;
582*26947304SEvan Yan 		}
583*26947304SEvan Yan 
584*26947304SEvan Yan 		/* Append current usage to entry list in the current slot */
585*26947304SEvan Yan 		if (slot != NULL) {
586*26947304SEvan Yan 
587*26947304SEvan Yan 			/* Allocate new entry */
588*26947304SEvan Yan 			entry = (info_entry_t *)malloc(sizeof (info_entry_t));
589*26947304SEvan Yan 			if (entry == NULL) {
590*26947304SEvan Yan 				log_err("Cannot allocate info table entry.\n");
591*26947304SEvan Yan 				free_table(table, table_len);
592*26947304SEvan Yan 				return (ENOMEM);
593*26947304SEvan Yan 			}
594*26947304SEvan Yan 
595*26947304SEvan Yan 			/* Link entry into current slot list */
596*26947304SEvan Yan 			entry->next = slot->entries;
597*26947304SEvan Yan 			slot->entries = entry;
598*26947304SEvan Yan 
599*26947304SEvan Yan 			/* Initialize entry values */
600*26947304SEvan Yan 			if (((entry->rsrc = strdup(rsrc)) == NULL) ||
601*26947304SEvan Yan 			    ((entry->usage = strdup(usage)) == NULL)) {
602*26947304SEvan Yan 				log_err("Cannot build info table entry.\n");
603*26947304SEvan Yan 				free_table(table, table_len);
604*26947304SEvan Yan 				return (ENOMEM);
605*26947304SEvan Yan 			}
606*26947304SEvan Yan 		}
607*26947304SEvan Yan 	}
608*26947304SEvan Yan 
609*26947304SEvan Yan 	/* Check if valid entries were inserted in table */
610*26947304SEvan Yan 	if (table_len == 0) {
611*26947304SEvan Yan 		free(table);
612*26947304SEvan Yan 		return (0);
613*26947304SEvan Yan 	}
614*26947304SEvan Yan 
615*26947304SEvan Yan 	/* Sort the lookup table by hotplug path */
616*26947304SEvan Yan 	qsort(table, table_len, sizeof (info_table_t), compare_info);
617*26947304SEvan Yan 
618*26947304SEvan Yan 	/* Done */
619*26947304SEvan Yan 	*tablep = table;
620*26947304SEvan Yan 	*table_lenp = table_len;
621*26947304SEvan Yan 	return (0);
622*26947304SEvan Yan }
623*26947304SEvan Yan 
624*26947304SEvan Yan /*
625*26947304SEvan Yan  * free_table()
626*26947304SEvan Yan  *
627*26947304SEvan Yan  *	Destroy a lookup table.
628*26947304SEvan Yan  */
629*26947304SEvan Yan static void
free_table(info_table_t * table,size_t table_len)630*26947304SEvan Yan free_table(info_table_t *table, size_t table_len)
631*26947304SEvan Yan {
632*26947304SEvan Yan 	info_entry_t	*entry;
633*26947304SEvan Yan 	int		index;
634*26947304SEvan Yan 
635*26947304SEvan Yan 	if (table != NULL) {
636*26947304SEvan Yan 		for (index = 0; index < table_len; index++) {
637*26947304SEvan Yan 			if (table[index].path != NULL)
638*26947304SEvan Yan 				free(table[index].path);
639*26947304SEvan Yan 			while (table[index].entries != NULL) {
640*26947304SEvan Yan 				entry = table[index].entries;
641*26947304SEvan Yan 				table[index].entries = entry->next;
642*26947304SEvan Yan 				if (entry->rsrc != NULL)
643*26947304SEvan Yan 					free(entry->rsrc);
644*26947304SEvan Yan 				if (entry->usage != NULL)
645*26947304SEvan Yan 					free(entry->usage);
646*26947304SEvan Yan 				free(entry);
647*26947304SEvan Yan 			}
648*26947304SEvan Yan 		}
649*26947304SEvan Yan 		free(table);
650*26947304SEvan Yan 	}
651*26947304SEvan Yan }
652*26947304SEvan Yan 
653*26947304SEvan Yan /*
654*26947304SEvan Yan  * rsrc2path()
655*26947304SEvan Yan  *
656*26947304SEvan Yan  *	Convert from an RCM resource name to a hotplug device path.
657*26947304SEvan Yan  */
658*26947304SEvan Yan static int
rsrc2path(const char * rsrc,char * path)659*26947304SEvan Yan rsrc2path(const char *rsrc, char *path)
660*26947304SEvan Yan {
661*26947304SEvan Yan 	char	*s;
662*26947304SEvan Yan 	char	tmp[MAXPATHLEN];
663*26947304SEvan Yan 
664*26947304SEvan Yan 	/* Only convert /dev and /devices paths */
665*26947304SEvan Yan 	if (strncmp(rsrc, "/dev", 4) == 0) {
666*26947304SEvan Yan 
667*26947304SEvan Yan 		/* Follow symbolic links for /dev paths */
668*26947304SEvan Yan 		if (realpath(rsrc, tmp) == NULL) {
669*26947304SEvan Yan 			log_err("Cannot resolve RCM resource (%s)\n",
670*26947304SEvan Yan 			    strerror(errno));
671*26947304SEvan Yan 			return (-1);
672*26947304SEvan Yan 		}
673*26947304SEvan Yan 
674*26947304SEvan Yan 		/* Remove the leading "/devices" part */
675*26947304SEvan Yan 		(void) strlcpy(path, &tmp[strlen(S_DEVICES)], MAXPATHLEN);
676*26947304SEvan Yan 
677*26947304SEvan Yan 		/* Remove any trailing minor node part */
678*26947304SEvan Yan 		if ((s = strrchr(path, ':')) != NULL)
679*26947304SEvan Yan 			*s = '\0';
680*26947304SEvan Yan 
681*26947304SEvan Yan 		/* Successfully converted */
682*26947304SEvan Yan 		return (0);
683*26947304SEvan Yan 	}
684*26947304SEvan Yan 
685*26947304SEvan Yan 	/* Not converted */
686*26947304SEvan Yan 	return (-1);
687*26947304SEvan Yan }
688*26947304SEvan Yan 
689*26947304SEvan Yan /*
690*26947304SEvan Yan  * compare_info()
691*26947304SEvan Yan  *
692*26947304SEvan Yan  *	Compare two slots in the lookup table that maps paths to usage.
693*26947304SEvan Yan  *
694*26947304SEvan Yan  *	NOTE: for use with qsort() and bsearch().
695*26947304SEvan Yan  */
696*26947304SEvan Yan static int
compare_info(const void * a,const void * b)697*26947304SEvan Yan compare_info(const void *a, const void *b)
698*26947304SEvan Yan {
699*26947304SEvan Yan 	info_table_t	*slot_a = (info_table_t *)a;
700*26947304SEvan Yan 	info_table_t	*slot_b = (info_table_t *)b;
701*26947304SEvan Yan 
702*26947304SEvan Yan 	return (strcmp(slot_a->path, slot_b->path));
703*26947304SEvan Yan }
704