xref: /titanic_52/usr/src/lib/libcfgadm/common/config_admin.c (revision 49d3bc91e27cd871b950d56c01398fa2f2e12ab4)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /* Portions Copyright 2005 Cyril Plisko */
23 
24 /*
25  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
26  * Use is subject to license terms.
27  */
28 
29 #include <errno.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <locale.h>
34 #include <langinfo.h>
35 #include <time.h>
36 
37 #if	!defined(DEBUG)
38 #define	NDEBUG	1
39 #else
40 #undef	NDEBUG
41 #endif
42 
43 #include <assert.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <sys/param.h>
47 #include <dlfcn.h>
48 #include <synch.h>
49 #include <sys/systeminfo.h>
50 #include <sys/sunddi.h>
51 #include <libdevinfo.h>
52 #include <unistd.h>
53 #include <stdarg.h>
54 #include <limits.h>
55 #include <ftw.h>
56 #include <ctype.h>
57 
58 #define	CFGA_PLUGIN_LIB
59 #include <config_admin.h>
60 
61 /* Limit size of sysinfo return */
62 #define	SYSINFO_LENGTH	256
63 
64 /*
65  * Attachment point specifier types.
66  */
67 typedef enum {
68 	UNKNOWN_AP,
69 	LOGICAL_LINK_AP,
70 	LOGICAL_DRV_AP,
71 	PHYSICAL_AP,
72 	AP_TYPE
73 } cfga_ap_types_t;
74 
75 static char *listopt_array[] = {
76 
77 #define	LISTOPT_CLASS	0
78 	"class",
79 	NULL
80 };
81 
82 typedef struct {
83 	int v_min;	/* Min acceptable version */
84 	int v_max;	/* Max acceptable version */
85 } vers_req_t;
86 
87 #define	INVALID_VERSION		-1
88 #define	VALID_HSL_VERS(v)	(((v) >= CFGA_HSL_V1) && \
89 				((v) <= CFGA_HSL_VERS))
90 
91 /*
92  * Incomplete definition
93  */
94 struct cfga_vers_ops;
95 
96 /*
97  * Structure that contains plugin library information.
98  */
99 typedef struct plugin_lib {
100 	struct	plugin_lib *next;	/* pointer to next */
101 	mutex_t	lock;			/* protects refcnt */
102 	int	refcnt;			/* reference count */
103 	void	*handle;		/* handle from dlopen */
104 	cfga_err_t	(*cfga_change_state_p)();
105 	cfga_err_t	(*cfga_private_func_p)();
106 	cfga_err_t	(*cfga_test_p)();
107 	cfga_err_t	(*cfga_stat_p)();
108 	cfga_err_t	(*cfga_list_p)();
109 	cfga_err_t	(*cfga_help_p)();
110 	int		(*cfga_ap_id_cmp_p)();
111 	cfga_err_t	(*cfga_list_ext_p)();	/* For V2 plug-ins only */
112 
113 	int		plugin_vers;	/* actual plugin version */
114 	struct cfga_vers_ops *vers_ops;	/* version dependant routines */
115 	char	libpath[MAXPATHLEN];	/* full pathname to lib */
116 } plugin_lib_t;
117 
118 static plugin_lib_t plugin_list;
119 
120 typedef struct lib_cache {
121 	struct lib_cache *lc_next;
122 	plugin_lib_t *lc_libp;
123 	char *lc_ap_id;
124 	char *lc_ap_physical;	/* physical ap_id */
125 	char *lc_ap_logical;	/* logical ap_id */
126 } lib_cache_t;
127 
128 static lib_cache_t *lib_cache;
129 static mutex_t lib_cache_lock;
130 
131 /*
132  * Library locator data struct - used to pass down through the device
133  * tree walking code.
134  */
135 typedef struct lib_locator {
136 	char	ap_base[MAXPATHLEN];
137 	char	ap_logical[CFGA_LOG_EXT_LEN];
138 	char	ap_physical[CFGA_PHYS_EXT_LEN];
139 	char	ap_class[CFGA_CLASS_LEN];
140 	char	pathname[MAXPATHLEN];
141 	plugin_lib_t *libp;
142 	cfga_err_t status;
143 	vers_req_t vers_req;	/* plug-in version required */
144 } lib_loc_t;
145 
146 /*
147  * linked list of cfga_stat_data structs - used for
148  * config_list
149  */
150 typedef struct stat_data_list {
151 	struct stat_data_list	*next;
152 	cfga_stat_data_t	stat_data;
153 } stat_data_list_t;
154 
155 /*
156  * linked list of arrays. Each array represents a bunch
157  * of list_data_t structures returned by a single call
158  * to a plugin's cfga_list_ext() routine.
159  */
160 typedef struct array_list {
161 	struct array_list	*next;
162 	cfga_list_data_t	*array;
163 	int			nelem;
164 } array_list_t;
165 
166 /*
167  * encapsulate config_list args to get them through the tree
168  * walking code
169  */
170 typedef struct list_stat {
171 	const char *opts;	/* Hardware specific options */
172 	char **errstr;
173 	cfga_flags_t flags;
174 	int	*countp;	/* Total number of list and stat structures */
175 	stat_data_list_t *sdl;	/* Linked list of stat structures */
176 	array_list_t *al;	/* Linked list of arrays of list structures */
177 	vers_req_t use_vers;	/* plugin versions to be stat'ed */
178 	char *shp_errstr;	/* only for shp plugin */
179 } list_stat_t;
180 
181 /*
182  * Internal operations for libcfgadm which are version dependant
183  */
184 struct cfga_vers_ops {
185 	cfga_err_t (*resolve_lib)(plugin_lib_t *libp);
186 	cfga_err_t (*stat_plugin)(list_stat_t *, lib_loc_t *, char **errstring);
187 	cfga_err_t (*mklog)(di_node_t, di_minor_t, plugin_lib_t *,
188 	    lib_loc_t *liblocp);
189 	cfga_err_t (*get_cond)(lib_loc_t *, cfga_cond_t *, char **);
190 };
191 
192 
193 /*
194  * Lock to protect list of libraries
195  */
196 static mutex_t plugin_list_lock;
197 
198 /*
199  * Forward declarations
200  */
201 
202 static const char *__config_strerror(cfga_err_t);
203 static void *config_calloc_check(size_t, size_t, char **);
204 static cfga_err_t resolve_lib_ref(plugin_lib_t *, lib_loc_t *);
205 static cfga_err_t config_get_lib(const char *, lib_loc_t *, char **);
206 static int check_ap(di_node_t, di_minor_t, void *);
207 static int check_ap_hp(di_node_t, di_hp_t, void *);
208 static int check_ap_impl(di_node_t, di_minor_t, di_hp_t, void *);
209 static int check_ap_phys(di_node_t, di_minor_t, void *);
210 static int check_ap_phys_hp(di_node_t, di_hp_t, void *);
211 static int check_ap_phys_impl(di_node_t, di_minor_t, di_hp_t, void *);
212 
213 static cfga_err_t find_ap_common(lib_loc_t *libloc_p, const char *rootpath,
214     int (*fcn)(di_node_t node, di_minor_t minor, void *arg),
215     int (*fcn_hp)(di_node_t node, di_hp_t hp, void *arg),
216     char **errstring);
217 
218 static plugin_lib_t *lib_in_list(char *);
219 static cfga_err_t find_lib(di_node_t, di_minor_t, lib_loc_t *);
220 static cfga_err_t find_lib_hp(di_node_t, di_hp_t, lib_loc_t *);
221 static cfga_err_t find_lib_impl(char *, lib_loc_t *);
222 static cfga_err_t load_lib(di_node_t, di_minor_t, lib_loc_t *);
223 static cfga_err_t load_lib_hp(di_node_t, di_hp_t, lib_loc_t *);
224 static cfga_err_t load_lib_impl(di_node_t, di_minor_t, di_hp_t, lib_loc_t *);
225 extern void bcopy(const void *, void *, size_t);
226 static void config_err(int, int, char **);
227 static void hold_lib(plugin_lib_t *);
228 static void rele_lib(plugin_lib_t *);
229 
230 static cfga_err_t parse_listopt(char *listopts, char **classpp,
231     char **errstring);
232 
233 static cfga_err_t list_common(list_stat_t *lstatp, const char *class);
234 static int do_list_common(di_node_t node, di_minor_t minor, void *arg);
235 static int do_list_common_hp(di_node_t node, di_hp_t hp, void *arg);
236 static int do_list_common_impl(di_node_t node, di_minor_t minor,
237     di_hp_t hp, void *arg);
238 static cfga_err_t stat_common(int num_ap_ids, char *const *ap_ids,
239     const char *class, list_stat_t *lstatp);
240 
241 static cfga_err_t null_resolve(plugin_lib_t *libp);
242 static cfga_err_t resolve_v1(plugin_lib_t *libp);
243 static cfga_err_t resolve_v2(plugin_lib_t *libp);
244 
245 static cfga_err_t mklog_common(di_node_t node, di_minor_t minor,
246     lib_loc_t *liblocp, size_t len);
247 
248 static cfga_err_t null_mklog(di_node_t node, di_minor_t minor,
249     plugin_lib_t *libp, lib_loc_t *liblocp);
250 static cfga_err_t mklog_v1(di_node_t node, di_minor_t minor,
251     plugin_lib_t *libp, lib_loc_t *liblocp);
252 static cfga_err_t mklog_v2(di_node_t node, di_minor_t minor,
253     plugin_lib_t *libp, lib_loc_t *liblocp);
254 
255 static cfga_err_t null_stat_plugin(list_stat_t *lstatp, lib_loc_t *libloc_p,
256     char **errstring);
257 static cfga_err_t stat_plugin_v2(list_stat_t *lstat, lib_loc_t *libloc_p,
258     char **errstring);
259 static cfga_err_t stat_plugin_v1(list_stat_t *lstat, lib_loc_t *libloc_p,
260     char **errstring);
261 
262 static cfga_err_t null_get_cond(lib_loc_t *liblocp, cfga_cond_t *condp,
263     char **errstring);
264 static cfga_err_t get_cond_v1(lib_loc_t *liblocp, cfga_cond_t *condp,
265     char **errstring);
266 static cfga_err_t get_cond_v2(lib_loc_t *liblocp, cfga_cond_t *condp,
267     char **errstring);
268 
269 static cfga_err_t realloc_data(cfga_stat_data_t **ap_id_list,
270     int *nlistp, list_stat_t *lstatp);
271 static cfga_err_t realloc_data_ext(cfga_list_data_t **ap_id_list,
272     int *nlistp, list_stat_t *lstatp);
273 
274 static void stat_to_list(cfga_list_data_t *lp, cfga_stat_data_t *statp);
275 static void lstat_free(list_stat_t *lstatp);
276 static cfga_ap_types_t find_arg_type(const char *ap_id);
277 static int compat_plugin(vers_req_t *reqp, int plugin_vers);
278 
279 static cfga_err_t check_flags(cfga_flags_t flags, cfga_flags_t mask,
280     char **errstring);
281 static cfga_err_t check_apids(int num_ap_ids, char *const *ap_ids,
282     char **errstring);
283 
284 static char *get_class(di_minor_t minor);
285 static cfga_err_t split_apid(char *ap_id, char **dyncompp, char **errstring);
286 static void append_dyn(char *buf, const char *dyncomp, size_t blen);
287 static int default_ap_id_cmp(const char *ap_id1, const char *ap_id2);
288 static void destroy_cache();
289 
290 /*
291  * Plugin library search path helpers
292  */
293 #define	LIB_PATH_BASE1	"/usr/platform/"
294 #define	LIB_PATH_BASE2	"/usr"
295 #if defined(__sparcv9)
296 #define	LIB_PATH_MIDDLE	"/lib/cfgadm/sparcv9/"
297 #elif defined(__amd64)
298 #define	LIB_PATH_MIDDLE "/lib/cfgadm/amd64/"
299 #else
300 #define	LIB_PATH_MIDDLE	"/lib/cfgadm/"
301 #endif
302 #define	LIB_PATH_TAIL	".so.1"
303 
304 
305 #if !defined(TEXT_DOMAIN)
306 #define	TEXT_DOMAIN	"SYS_TEST"
307 #endif
308 
309 /*
310  * Defined constants
311  */
312 #define	DEVICES_DIR		"/devices"
313 #define	DOT_DOT_DEVICES		"../devices"
314 #define	CFGA_DEV_DIR		"/dev/cfg"
315 #define	SLASH			"/"
316 #define	S_FREE(x)	(((x) != NULL) ? (free(x), (x) = NULL) : (void *)0)
317 #define	GET_DYN(a)	(strstr((a), CFGA_DYN_SEP))
318 
319 #define	CFGA_NO_CLASS		"none"
320 
321 /*
322  * Error strings
323  */
324 #define	DI_INIT_FAILED	1
325 #define	ALLOC_FAILED	2
326 #define	INVALID_ARGS	3
327 
328 static char *
329 err_strings[] = {
330 	NULL,
331 	"Device library initialize failed",
332 	"Memory allocation failed",
333 	"Invalid argument(s)"
334 };
335 
336 static const char err_sep[] = ": ";
337 
338 
339 /*
340  * Table of version dependant routines
341  */
342 static struct cfga_vers_ops cfga_vers_ops[CFGA_HSL_VERS + 1] = {
343 
344 {null_resolve,	null_stat_plugin,	null_mklog,	null_get_cond	},
345 {resolve_v1,	stat_plugin_v1,		mklog_v1,	get_cond_v1	},
346 {resolve_v2,	stat_plugin_v2,		mklog_v2,	get_cond_v2	}
347 
348 };
349 #define	VERS_ARRAY_SZ	(sizeof (cfga_vers_ops)/sizeof (cfga_vers_ops[0]))
350 
351 
352 /*
353  * Public interfaces for libcfgadm, as documented in config_admin.3x
354  */
355 
356 /*
357  * config_change_state
358  */
359 
360 cfga_err_t
361 config_change_state(
362 	cfga_cmd_t state_change_cmd,
363 	int num_ap_ids,
364 	char *const *ap_id,
365 	const char *options,
366 	struct cfga_confirm *confp,
367 	struct cfga_msg *msgp,
368 	char **errstring,
369 	cfga_flags_t flags)
370 {
371 	/*
372 	 * for each arg -
373 	 *  load hs library,
374 	 *  if force
375 	 *    call cfga_state_change_func
376 	 *    return status
377 	 *  else
378 	 *    call it's cfga_stat
379 	 *    check condition
380 	 *    call cfga_state_change_func
381 	 *    return status
382 	 */
383 	int i;
384 	lib_loc_t libloc;
385 	plugin_lib_t *libp;
386 	cfga_cond_t cond;
387 
388 	cfga_err_t retval = CFGA_OK;
389 
390 	/* Sanity checks */
391 	if (state_change_cmd == CFGA_CMD_NONE)
392 		return (retval);
393 
394 	if ((state_change_cmd < CFGA_CMD_NONE) ||
395 	    (state_change_cmd > CFGA_CMD_UNCONFIGURE))
396 		return (CFGA_INVAL);
397 
398 	if (errstring != NULL) {
399 		*errstring = NULL;
400 	}
401 
402 	if (check_flags(flags, CFGA_FLAG_FORCE | CFGA_FLAG_VERBOSE, errstring)
403 	    != CFGA_OK) {
404 		return (CFGA_ERROR);
405 	}
406 
407 	if (check_apids(num_ap_ids, ap_id, errstring) != CFGA_OK) {
408 		return (CFGA_ERROR);
409 	}
410 
411 	/*
412 	 * operate on each ap_id
413 	 */
414 	for (i = 0; (i < num_ap_ids) && (retval == CFGA_OK); i++) {
415 		libloc.libp = NULL;
416 		if ((retval = config_get_lib(ap_id[i], &libloc, errstring)) !=
417 		    CFGA_OK) {
418 			break;
419 		}
420 
421 		libp = libloc.libp;
422 		if ((flags & CFGA_FLAG_FORCE) ||
423 		    (state_change_cmd == CFGA_CMD_UNLOAD) ||
424 		    (state_change_cmd == CFGA_CMD_DISCONNECT) ||
425 		    (state_change_cmd == CFGA_CMD_UNCONFIGURE)) {
426 			errno = 0;
427 			retval = (*libp->cfga_change_state_p)
428 			    (state_change_cmd, libloc.ap_physical, options,
429 			    confp, msgp, errstring, flags);
430 		} else {
431 			/*
432 			 * Need to check condition before proceeding in
433 			 * the "configure direction"
434 			 */
435 			if ((retval = libp->vers_ops->get_cond(&libloc, &cond,
436 			    errstring)) != CFGA_OK) {
437 				break;
438 			}
439 
440 			if (cond == CFGA_COND_OK || cond == CFGA_COND_UNKNOWN) {
441 				errno = 0;
442 				retval =
443 				    (*libp->cfga_change_state_p)(
444 				    state_change_cmd,
445 				    libloc.ap_physical, options,
446 				    confp, msgp, errstring,
447 				    flags);
448 			} else {
449 				retval = CFGA_INSUFFICENT_CONDITION;
450 			}
451 		}
452 		rele_lib(libp);
453 	}
454 
455 	return (retval);
456 }
457 
458 /*
459  * config_private_func
460  */
461 
462 cfga_err_t
463 config_private_func(
464 	const char *function,
465 	int num_ap_ids,
466 	char *const *ap_ids,
467 	const char *options,
468 	struct cfga_confirm *confp,
469 	struct cfga_msg *msgp,
470 	char **errstring,
471 	cfga_flags_t flags)
472 {
473 	int i;
474 	lib_loc_t libloc;
475 	cfga_err_t retval = CFGA_OK;
476 
477 
478 	if (errstring != NULL) {
479 		*errstring = NULL;
480 	}
481 
482 	if (check_flags(flags, CFGA_FLAG_FORCE | CFGA_FLAG_VERBOSE, errstring)
483 	    != CFGA_OK) {
484 		return (CFGA_ERROR);
485 	}
486 
487 	if (check_apids(num_ap_ids, ap_ids, errstring) != CFGA_OK) {
488 		return (CFGA_ERROR);
489 	}
490 
491 	/*
492 	 * operate on each ap_id
493 	 */
494 	for (i = 0; (i < num_ap_ids) && (retval == CFGA_OK); i++) {
495 		libloc.libp = NULL;
496 		if ((retval = config_get_lib(ap_ids[i], &libloc, errstring)) !=
497 		    CFGA_OK)  {
498 			return (retval);
499 		}
500 
501 		errno = 0;
502 		retval = (*libloc.libp->cfga_private_func_p)(function,
503 		    libloc.ap_physical, options, confp, msgp, errstring,
504 		    flags);
505 		rele_lib(libloc.libp);
506 	}
507 
508 	return (retval);
509 }
510 
511 
512 /*
513  * config_test
514  */
515 
516 cfga_err_t
517 config_test(
518 	int num_ap_ids,
519 	char *const *ap_ids,
520 	const char *options,
521 	struct cfga_msg *msgp,
522 	char **errstring,
523 	cfga_flags_t flags)
524 {
525 	int i;
526 	lib_loc_t libloc;
527 	cfga_err_t retval = CFGA_OK;
528 
529 	if (errstring != NULL) {
530 		*errstring = NULL;
531 	}
532 
533 	if (check_flags(flags, CFGA_FLAG_FORCE | CFGA_FLAG_VERBOSE, errstring)
534 	    != CFGA_OK) {
535 		return (CFGA_ERROR);
536 	}
537 
538 	if (check_apids(num_ap_ids, ap_ids, errstring) != CFGA_OK) {
539 		return (CFGA_ERROR);
540 	}
541 
542 	/*
543 	 * operate on each ap_id
544 	 */
545 	for (i = 0; (i < num_ap_ids) && (retval == CFGA_OK); i++) {
546 		libloc.libp = NULL;
547 		if ((retval = config_get_lib(ap_ids[i], &libloc, errstring)) !=
548 		    CFGA_OK) {
549 			return (retval);
550 		}
551 
552 		errno = 0;
553 		retval = (*libloc.libp->cfga_test_p)(libloc.ap_physical,
554 		    options, msgp, errstring, flags);
555 		rele_lib(libloc.libp);
556 	}
557 
558 	return (retval);
559 }
560 
561 cfga_err_t
562 config_stat(
563 	int num_ap_ids,
564 	char *const *ap_ids,
565 	struct cfga_stat_data *buf,
566 	const char *options,
567 	char **errstring)
568 {
569 	int nstat, n, i;
570 	list_stat_t lstat = {NULL};
571 	cfga_err_t rc = CFGA_OK;
572 
573 	if (check_apids(num_ap_ids, ap_ids, errstring) != CFGA_OK) {
574 		return (CFGA_ERROR);
575 	}
576 
577 	/*
578 	 * V1 entry points don't support dynamic attachment points
579 	 */
580 	for (i = 0; i < num_ap_ids; i++) {
581 		if (GET_DYN(ap_ids[i]) != NULL) {
582 			return (CFGA_APID_NOEXIST);
583 		}
584 	}
585 
586 
587 	nstat = n = 0;
588 	lstat.countp = &nstat;
589 	lstat.opts = options;
590 	lstat.errstr = errstring;
591 	lstat.shp_errstr = NULL;
592 	/*
593 	 * This is a V1 interface which can use only V1 plugins
594 	 */
595 	lstat.use_vers.v_max = lstat.use_vers.v_min = CFGA_HSL_V1;
596 
597 	rc = stat_common(num_ap_ids, ap_ids, NULL, &lstat);
598 	if (rc == CFGA_OK) {
599 		assert(*lstat.countp == num_ap_ids);
600 		rc = realloc_data(&buf, &n, &lstat);
601 	}
602 
603 	return (rc);
604 }
605 
606 /*
607  * config_list
608  */
609 cfga_err_t
610 config_list(
611 	struct cfga_stat_data **ap_id_list,
612 	int *nlistp,
613 	const char *options,
614 	char **errstring)
615 {
616 	int nstat;
617 	list_stat_t lstat = {NULL};
618 	cfga_err_t retval = CFGA_ERROR;
619 
620 	if (errstring != NULL) {
621 		*errstring = NULL;
622 	}
623 
624 	nstat = 0;
625 	lstat.countp = &nstat;
626 	lstat.opts = options;
627 	lstat.errstr = errstring;
628 	lstat.shp_errstr = NULL;
629 	/*
630 	 * This is a V1 interface which can use only V1 plugins
631 	 */
632 	lstat.use_vers.v_max = lstat.use_vers.v_min = CFGA_HSL_V1;
633 
634 
635 	*ap_id_list = NULL;
636 	*nlistp = 0;
637 
638 	/*
639 	 * V1 interfaces don't support prefiltering, no class
640 	 * specified.
641 	 */
642 	retval = list_common(&lstat, NULL);
643 	if (retval == CFGA_OK) {
644 		retval = realloc_data(ap_id_list, nlistp, &lstat);
645 	}
646 
647 	assert((ap_id_list != NULL && *nlistp != 0) ||
648 	    (ap_id_list == NULL && *nlistp == 0));
649 
650 	if (retval == CFGA_OK && *nlistp == 0) {
651 		return (CFGA_NOTSUPP);
652 	} else {
653 		return (retval);
654 	}
655 }
656 
657 
658 /*
659  * config_list_ext
660  */
661 cfga_err_t
662 config_list_ext(
663 	int num_ap_ids,
664 	char *const *ap_ids,
665 	struct cfga_list_data **ap_id_list,
666 	int *nlistp,
667 	const char *options,
668 	const char *listopts,
669 	char **errstring,
670 	cfga_flags_t flags)
671 {
672 	int nstat, list, prefilter;
673 	list_stat_t lstat = {NULL};
674 	char *class;
675 
676 	cfga_err_t rc = CFGA_ERROR;
677 
678 	*nlistp = 0;
679 	*ap_id_list = NULL;
680 
681 	if (errstring != NULL) {
682 		*errstring = NULL;
683 	}
684 
685 	if (check_flags(flags, CFGA_FLAG_LIST_ALL, errstring) != CFGA_OK) {
686 		return (CFGA_ERROR);
687 	}
688 
689 	class = NULL;
690 	if ((rc = parse_listopt((char *)listopts, &class, errstring))
691 	    != CFGA_OK) {
692 		return (rc);
693 	}
694 
695 	prefilter = (class == NULL) ? 0 : 1;
696 
697 	nstat = 0;
698 	lstat.countp = &nstat;
699 	lstat.opts = options;
700 	lstat.errstr = errstring;
701 	lstat.shp_errstr = NULL;
702 	lstat.flags = flags;
703 	/*
704 	 * We support both V1 and V2 plugins through this entry
705 	 * point.
706 	 */
707 	lstat.use_vers.v_min = CFGA_HSL_V1;
708 	lstat.use_vers.v_max = CFGA_HSL_V2;
709 
710 	list = 0;
711 	if (num_ap_ids == 0 && ap_ids == NULL) {
712 		/*
713 		 * discover and stat all attachment points
714 		 */
715 		list = 1;
716 		rc = list_common(&lstat, class);
717 	} else if (num_ap_ids > 0 && ap_ids != NULL) {
718 		/*
719 		 * Stat specified attachment points. With dynamic expansion
720 		 * more data may be returned than was specified by user.
721 		 */
722 		rc = stat_common(num_ap_ids, ap_ids, class, &lstat);
723 	} else {
724 		rc = CFGA_ERROR;
725 	}
726 
727 	S_FREE(class);
728 
729 	if (rc != CFGA_OK) {
730 		return (rc);
731 	}
732 
733 	rc = realloc_data_ext(ap_id_list, nlistp, &lstat);
734 
735 	assert((ap_id_list != NULL && *nlistp != 0) ||
736 	    (ap_id_list == NULL && *nlistp == 0));
737 
738 	/*
739 	 * For the list command notify user if no attachment
740 	 * point is found in the system.
741 	 *
742 	 */
743 	if (list && rc == CFGA_OK && *nlistp == 0) {
744 		/*
745 		 * If attachment points are being prefiltered, absence of data
746 		 * does not imply that config. admin. is not
747 		 * supported by the system.
748 		 */
749 		if (prefilter) {
750 			/*
751 			 * Prefiltering: requested class is absent
752 			 */
753 			return (CFGA_APID_NOEXIST);
754 		} else {
755 			/*
756 			 * No attachment points in system
757 			 */
758 			return (CFGA_NOTSUPP);
759 		}
760 	} else {
761 		return (rc);
762 	}
763 }
764 
765 
766 /*
767  * config_unload_libs
768  *
769  * Attempts to remove all libs on the plugin list.
770  */
771 void
772 config_unload_libs()
773 {
774 	plugin_lib_t *libp, *prev = &plugin_list, *next = NULL;
775 
776 	/* destroy cache entries to remove refcnt agains plugins */
777 	destroy_cache();
778 
779 	(void) mutex_lock(&plugin_list_lock);
780 	for (libp = plugin_list.next; libp != NULL; libp = next) {
781 		next = libp->next;
782 		(void) mutex_lock(&libp->lock);
783 		if (libp->refcnt) {
784 			(void) mutex_unlock(&libp->lock);
785 			prev = libp;
786 			continue;
787 		}
788 		(void) mutex_unlock(&libp->lock);
789 		prev->next = next;
790 		(void) dlclose(libp->handle);
791 		(void) mutex_destroy(&libp->lock);
792 		free(libp);
793 	}
794 	(void) mutex_unlock(&plugin_list_lock);
795 }
796 
797 /*
798  * config_ap_id_cmp
799  */
800 int
801 config_ap_id_cmp(
802 	const cfga_ap_log_id_t ap1,
803 	const cfga_ap_log_id_t ap2)
804 {
805 	int ret;
806 	lib_loc_t libloc;
807 	char apstat1[CFGA_PHYS_EXT_LEN];
808 	char apstat2[CFGA_PHYS_EXT_LEN];
809 	char *sep1, *sep2;
810 
811 	/*
812 	 * Extract static ap_ids
813 	 */
814 	(void) strlcpy(apstat1, ap1, sizeof (apstat1));
815 	(void) strlcpy(apstat2, ap2, sizeof (apstat2));
816 
817 	sep1 = GET_DYN(apstat1);
818 	sep2 = GET_DYN(apstat2);
819 
820 	if (sep1)
821 		*sep1 = '\0';
822 	if (sep2)
823 		*sep2 = '\0';
824 
825 	/*
826 	 * Use the default comparator for static ap_ids
827 	 */
828 	ret = default_ap_id_cmp(apstat1, apstat2);
829 	if (ret)
830 		return (ret);
831 
832 	/*
833 	 * static components match. They belong to
834 	 * the same static ap_id. Check if both are dynamic
835 	 * If not, static < dynamic.
836 	 */
837 	if ((sep1 == NULL) ^ (sep2 == NULL))
838 		return (sep1 ? 1 : -1);
839 
840 	/*
841 	 * If both are static, then ap1 = ap2
842 	 */
843 	if (sep1 == NULL)
844 		return (0);
845 
846 	/*
847 	 * Both are dynamic and belong to same static ap_id.
848 	 * Use the plugin comparator
849 	 */
850 	libloc.libp = NULL;
851 	if (config_get_lib(ap1, &libloc, NULL) != CFGA_OK) {
852 		return (strncmp(sep1, sep2, CFGA_PHYS_EXT_LEN));
853 	}
854 
855 	ret = (*libloc.libp->cfga_ap_id_cmp_p)(ap1, ap2);
856 
857 	rele_lib(libloc.libp);
858 
859 	return (ret);
860 }
861 
862 /*
863  * config_strerror
864  */
865 
866 const char *
867 config_strerror(cfga_err_t cfgerrnum)
868 {
869 	const char *ep = NULL;
870 
871 	if ((cfgerrnum < CFGA_OK) || (cfgerrnum > CFGA_ATTR_INVAL))
872 		return (NULL);
873 
874 	ep = __config_strerror(cfgerrnum);
875 
876 	return ((ep != NULL) ? dgettext(TEXT_DOMAIN, ep) : NULL);
877 }
878 
879 /*
880  * config_help
881  */
882 cfga_err_t
883 config_help(
884 	int num_ap_ids,
885 	char *const *ap_ids,
886 	struct cfga_msg *msgp,
887 	const char *options,
888 	cfga_flags_t flags)
889 {
890 	int i;
891 	lib_loc_t libloc;
892 	cfga_err_t retval = CFGA_OK;
893 
894 	if (check_flags(flags, CFGA_FLAG_FORCE | CFGA_FLAG_VERBOSE, NULL)
895 	    != CFGA_OK) {
896 		return (CFGA_ERROR);
897 	}
898 
899 	if (num_ap_ids < 0) {
900 		return (CFGA_ERROR);
901 	}
902 
903 	if (num_ap_ids > 0 && ap_ids == NULL) {
904 		return (CFGA_ERROR);
905 	}
906 
907 	/*
908 	 * operate on each ap_id
909 	 */
910 	for (i = 0; (i < num_ap_ids) && (retval == CFGA_OK); i++) {
911 		libloc.libp = NULL;
912 		if ((retval = config_get_lib(ap_ids[i], &libloc,
913 		    NULL)) != CFGA_OK) {
914 			return (retval);
915 		}
916 
917 		errno = 0;
918 		retval = (*libloc.libp->cfga_help_p)(msgp, options, flags);
919 		rele_lib(libloc.libp);
920 	}
921 	return (retval);
922 }
923 
924 /*
925  * Private support routines for the public interfaces
926  */
927 
928 static const char *
929 __config_strerror(cfga_err_t cfgerrnum)
930 {
931 	const char *ep = NULL;
932 
933 	switch (cfgerrnum) {
934 	case CFGA_OK:
935 		ep = "Configuration operation succeeded";
936 		break;
937 	case CFGA_NACK:
938 		ep = "Configuration operation cancelled";
939 		break;
940 	case CFGA_INVAL:
941 		ep = "Configuration operation invalid";
942 		break;
943 	case CFGA_NOTSUPP:
944 		ep = "Configuration administration not supported";
945 		break;
946 	case CFGA_OPNOTSUPP:
947 		ep = "Configuration operation not supported";
948 		break;
949 	case CFGA_PRIV:
950 		ep = "Insufficient privileges";
951 		break;
952 	case CFGA_BUSY:
953 		ep = "Component system is busy, try again";
954 		break;
955 	case CFGA_SYSTEM_BUSY:
956 		ep = "System is busy, try again";
957 		break;
958 	case CFGA_DATA_ERROR:
959 		ep = "Data error";
960 		break;
961 	case CFGA_LIB_ERROR:
962 		ep = "Library error";
963 		break;
964 	case CFGA_NO_LIB:
965 		ep = "No Library found";
966 		break;
967 	case CFGA_INSUFFICENT_CONDITION:
968 		ep = "Insufficient condition";
969 		break;
970 	case CFGA_ERROR:
971 		ep = "Hardware specific failure";
972 		break;
973 	case CFGA_APID_NOEXIST:
974 		ep = "Attachment point not found";
975 		break;
976 	case CFGA_ATTR_INVAL:
977 		ep = "No attachment point with specified attributes found";
978 		break;
979 	default:
980 		ep = NULL;
981 		break;
982 	}
983 	return (ep);
984 }
985 
986 /*
987  * listopts is a string in the getsubopt(3C) style:
988  *	name1=value1,name2=value2,
989  */
990 static cfga_err_t
991 parse_listopt(char *listopts, char **classpp, char **errstring)
992 {
993 	char *bufp, *optp, *val = NULL;
994 	cfga_err_t rc = CFGA_ERROR;
995 
996 	*classpp = NULL;
997 
998 	/*
999 	 * NULL is a legal value for listopts
1000 	 */
1001 	if (listopts == NULL) {
1002 		return (CFGA_OK);
1003 	}
1004 
1005 	if ((bufp = config_calloc_check(1, strlen(listopts) + 1, errstring))
1006 	    == NULL) {
1007 		return (CFGA_LIB_ERROR);
1008 	}
1009 	(void) strcpy(bufp, listopts);
1010 
1011 	optp = bufp; /* getsubopt() modifies its argument */
1012 	while (*optp != '\0') {
1013 		switch (getsubopt(&optp, listopt_array, &val)) {
1014 		case LISTOPT_CLASS:
1015 			if (val == NULL || *classpp != NULL) {
1016 				rc = CFGA_ERROR;
1017 				goto out;
1018 			}
1019 			if ((*classpp = config_calloc_check(1, strlen(val) + 1,
1020 			    errstring)) == NULL) {
1021 				rc = CFGA_LIB_ERROR;
1022 				goto out;
1023 			}
1024 			(void) strcpy(*classpp, val);
1025 			break;
1026 		default:
1027 			rc = CFGA_ERROR;
1028 			goto out;
1029 		}
1030 	}
1031 
1032 	rc = CFGA_OK;
1033 	/*FALLTHRU*/
1034 out:
1035 	S_FREE(bufp);
1036 	if (rc != CFGA_OK) {
1037 		S_FREE(*classpp);
1038 	}
1039 	return (rc);
1040 }
1041 
1042 /*ARGSUSED*/
1043 static cfga_err_t
1044 null_mklog(
1045 	di_node_t node,
1046 	di_minor_t minor,
1047 	plugin_lib_t *libp,
1048 	lib_loc_t *liblocp)
1049 {
1050 	return (CFGA_OK);
1051 }
1052 
1053 static cfga_err_t
1054 mklog_v1(
1055 	di_node_t node,
1056 	di_minor_t minor,
1057 	plugin_lib_t *libp,
1058 	lib_loc_t *liblocp)
1059 {
1060 	const size_t len = CFGA_AP_LOG_ID_LEN;
1061 
1062 	assert(len <=  sizeof (liblocp->ap_logical));
1063 
1064 	if (libp->plugin_vers != CFGA_HSL_V1) {
1065 		return (CFGA_LIB_ERROR);
1066 	}
1067 
1068 	return (mklog_common(node, minor, liblocp, len));
1069 }
1070 
1071 
1072 /*
1073  * Obtain the devlink from a /devices path
1074  */
1075 static int
1076 get_link(di_devlink_t devlink, void *arg)
1077 {
1078 	char *linkp = (char *)arg;
1079 
1080 	(void) snprintf(linkp, CFGA_LOG_EXT_LEN, "%s",
1081 	    di_devlink_path(devlink));
1082 	return (DI_WALK_TERMINATE);
1083 }
1084 
1085 static cfga_err_t
1086 mklog_v2(
1087 	di_node_t node,
1088 	di_minor_t minor,
1089 	plugin_lib_t *libp,
1090 	lib_loc_t *liblocp)
1091 {
1092 	const size_t len = CFGA_LOG_EXT_LEN;
1093 	di_devlink_handle_t hdl;
1094 
1095 	assert(len <=  sizeof (liblocp->ap_logical));
1096 
1097 	if (libp->plugin_vers != CFGA_HSL_V2) {
1098 		return (CFGA_LIB_ERROR);
1099 	}
1100 
1101 	/* open devlink database */
1102 	if ((hdl = di_devlink_init(NULL, 0)) == NULL) {
1103 		return (CFGA_LIB_ERROR);
1104 	}
1105 
1106 	liblocp->ap_logical[0] = '\0';
1107 	(void) di_devlink_walk(hdl, NULL,
1108 	    liblocp->ap_physical + strlen(DEVICES_DIR),
1109 	    DI_PRIMARY_LINK, (void *)liblocp->ap_logical, get_link);
1110 
1111 	(void) di_devlink_fini(&hdl);
1112 
1113 	if (liblocp->ap_logical[0] != '\0')
1114 		return (CFGA_OK);
1115 	return (mklog_common(node, minor, liblocp, len));
1116 }
1117 
1118 /*
1119  * mklog_common - make a logical name from the driver and instance
1120  */
1121 static cfga_err_t
1122 mklog_common(
1123 	di_node_t node,
1124 	di_minor_t minor,
1125 	lib_loc_t *libloc_p,
1126 	size_t len)
1127 {
1128 	int inst;
1129 	char *drv, *minor_name;
1130 
1131 	drv = di_driver_name(node);
1132 	inst = di_instance(node);
1133 	minor_name = di_minor_name(minor);
1134 
1135 	errno = 0;
1136 	if (drv != NULL && inst != -1 && minor_name != NULL &&
1137 	    snprintf(libloc_p->ap_logical, len, "%s%d:%s", drv, inst,
1138 	    minor_name) < len) {	/* snprintf returns strlen */
1139 		return (CFGA_OK);
1140 	}
1141 
1142 	return (CFGA_LIB_ERROR);
1143 }
1144 
1145 /*
1146  * mklog_common - make a logical name from the driver and instance
1147  */
1148 /*ARGSUSED*/
1149 static cfga_err_t
1150 mklog_hp(
1151 	di_node_t node,
1152 	di_hp_t hp,
1153 	plugin_lib_t *libp,
1154 	lib_loc_t *liblocp)
1155 {
1156 	const size_t len = CFGA_LOG_EXT_LEN;
1157 	int inst;
1158 	char *drv, *hp_name;
1159 
1160 	drv = di_driver_name(node);
1161 	inst = di_instance(node);
1162 	hp_name = di_hp_name(hp);
1163 
1164 	errno = 0;
1165 	if (drv != NULL && inst != -1 && hp_name != NULL &&
1166 	    snprintf(liblocp->ap_logical, len, "%s%d:%s", drv, inst,
1167 	    hp_name) < len) {	/* snprintf returns strlen */
1168 		return (CFGA_OK);
1169 	}
1170 
1171 	return (CFGA_LIB_ERROR);
1172 }
1173 
1174 /*
1175  * resolve_lib_ref - relocate to use plugin lib
1176  */
1177 static cfga_err_t
1178 resolve_lib_ref(
1179 	plugin_lib_t *libp,
1180 	lib_loc_t *libloc_p)
1181 {
1182 	void *sym;
1183 	void *libhdlp = libp->handle;
1184 	int plug_vers;
1185 
1186 	if ((sym = dlsym(libhdlp, "cfga_version")) == NULL) {
1187 		/*
1188 		 * Version symbol not defined, must be the first version
1189 		 */
1190 		plug_vers = CFGA_HSL_V1;
1191 	} else {
1192 		plug_vers =   *((int *)sym);
1193 	}
1194 
1195 	/*
1196 	 * Check if plugin version matches request.
1197 	 */
1198 	if (!compat_plugin(&libloc_p->vers_req, plug_vers)) {
1199 		return (CFGA_NO_LIB);
1200 	}
1201 
1202 	/*
1203 	 * Record the plugin version and setup version dependant routines
1204 	 */
1205 	assert(plug_vers < VERS_ARRAY_SZ);
1206 	libp->plugin_vers = plug_vers;
1207 	libp->vers_ops = &cfga_vers_ops[plug_vers];
1208 
1209 	/* resolve symbols common to all versions */
1210 	if ((sym = dlsym(libhdlp, "cfga_change_state")) == NULL) {
1211 		perror("dlsym: cfga_change_state");
1212 		return (CFGA_LIB_ERROR);
1213 	} else
1214 		libp->cfga_change_state_p = (cfga_err_t (*)(cfga_cmd_t,
1215 		    const char *, const char *, struct cfga_confirm *,
1216 		    struct cfga_msg *, char **, cfga_flags_t)) sym;
1217 
1218 	if ((sym = dlsym(libhdlp, "cfga_private_func")) == NULL) {
1219 		perror("dlsym: cfga_private_func");
1220 		return (CFGA_LIB_ERROR);
1221 	} else
1222 		libp->cfga_private_func_p = (cfga_err_t (*)(const char *,
1223 		    const char *, const char *, struct cfga_confirm *,
1224 		    struct cfga_msg *, char **, cfga_flags_t))sym;
1225 
1226 	if ((sym = dlsym(libhdlp, "cfga_test")) == NULL) {
1227 		perror("dlsym: cfga_test");
1228 		return (CFGA_LIB_ERROR);
1229 	} else
1230 		libp->cfga_test_p = (cfga_err_t (*)(const char *, const char *,
1231 		    struct cfga_msg *, char **, cfga_flags_t))sym;
1232 
1233 	if ((sym = dlsym(libhdlp, "cfga_help")) == NULL) {
1234 		perror("dlsym: cfga_help");
1235 		return (CFGA_LIB_ERROR);
1236 	} else
1237 		libp->cfga_help_p = (cfga_err_t (*)(struct cfga_msg *,
1238 		    const char *, cfga_flags_t))sym;
1239 
1240 	if ((sym = dlsym(libhdlp, "cfga_ap_id_cmp")) == NULL) {
1241 		libp->cfga_ap_id_cmp_p = default_ap_id_cmp;
1242 	} else
1243 		libp->cfga_ap_id_cmp_p = (int (*)(const
1244 		    cfga_ap_log_id_t, const cfga_ap_log_id_t))sym;
1245 
1246 	/* Resolve version specific symbols */
1247 	return (libp->vers_ops->resolve_lib(libp));
1248 }
1249 
1250 /*ARGSUSED*/
1251 static cfga_err_t
1252 null_resolve(plugin_lib_t *libp)
1253 {
1254 	return (CFGA_OK);
1255 }
1256 
1257 static cfga_err_t
1258 resolve_v1(plugin_lib_t *libp)
1259 {
1260 	void *sym, *libhdlp = libp->handle;
1261 
1262 
1263 	if (libp->plugin_vers != CFGA_HSL_V1) {
1264 		return (CFGA_NO_LIB);
1265 	}
1266 
1267 	if ((sym = dlsym(libhdlp, "cfga_stat")) == NULL) {
1268 		perror("dlsym: cfga_stat");
1269 		return (CFGA_LIB_ERROR);
1270 	} else
1271 		libp->cfga_stat_p = (cfga_err_t (*)(const char *,
1272 		    struct cfga_stat_data *, const char *,
1273 		    char **))sym;
1274 
1275 	if ((sym = dlsym(libhdlp, "cfga_list")) == NULL) {
1276 		perror("dlsym: cfga_list");
1277 		return (CFGA_LIB_ERROR);
1278 	} else
1279 		libp->cfga_list_p = (cfga_err_t (*)(struct cfga_stat_data **,
1280 		    int *, const char *, char **))sym;
1281 
1282 	return (CFGA_OK);
1283 }
1284 
1285 static cfga_err_t
1286 resolve_v2(plugin_lib_t *libp)
1287 {
1288 	void *sym;
1289 
1290 
1291 	if (libp->plugin_vers != CFGA_HSL_V2) {
1292 		return (CFGA_NO_LIB);
1293 	}
1294 
1295 	if ((sym = dlsym(libp->handle, "cfga_list_ext")) == NULL) {
1296 		perror("dlsym: cfga_list_ext");
1297 		return (CFGA_LIB_ERROR);
1298 	} else {
1299 		libp->cfga_list_ext_p = (cfga_err_t (*)(const char *,
1300 		    struct cfga_list_data **, int *, const char *,
1301 		    const char *, char **, cfga_flags_t))sym;
1302 		return (CFGA_OK);
1303 	}
1304 }
1305 
1306 /*
1307  * config_calloc_check - perform allocation, check result and
1308  * set error string
1309  */
1310 static void *
1311 config_calloc_check(
1312 	size_t nelem,
1313 	size_t elsize,
1314 	char **errstring)
1315 {
1316 	void *p;
1317 
1318 	p = calloc(nelem, elsize);
1319 	if (p == NULL) {
1320 		config_err(0, ALLOC_FAILED, errstring);
1321 	}
1322 
1323 	return (p);
1324 }
1325 
1326 
1327 /*
1328  * config_get_lib - given an ap_id find the library name
1329  *	If successful, the plugin library is held.
1330  */
1331 static cfga_err_t
1332 config_get_lib(
1333 	const char *ap_id,
1334 	lib_loc_t *lib_loc_p,
1335 	char **errstring)
1336 {
1337 	char *dyncomp, path[PATH_MAX];
1338 	char *apdup;
1339 	cfga_ap_types_t type = UNKNOWN_AP;
1340 	cfga_err_t ret = CFGA_ERROR;
1341 
1342 	if (ap_id == NULL) {
1343 		config_err(0, INVALID_ARGS, errstring);
1344 		return (ret);
1345 	}
1346 
1347 	lib_loc_p->libp = NULL;
1348 
1349 	if ((apdup = config_calloc_check(1, strlen(ap_id) + 1, errstring))
1350 	    == NULL) {
1351 		return (CFGA_LIB_ERROR);
1352 	}
1353 	(void) strcpy(apdup, ap_id);
1354 
1355 	/*
1356 	 * Separate into base and dynamic components
1357 	 */
1358 	if ((ret = split_apid(apdup, &dyncomp, errstring)) != CFGA_OK) {
1359 		goto out;
1360 	}
1361 
1362 	/*
1363 	 * No upper limit on version
1364 	 */
1365 	lib_loc_p->vers_req.v_max = CFGA_HSL_VERS;
1366 	if (dyncomp != NULL) {
1367 		/*
1368 		 * We need atleast version 2 of the plug-in library
1369 		 * interface since the ap_id has a dynamic component.
1370 		 */
1371 
1372 		lib_loc_p->vers_req.v_min = CFGA_HSL_V2;
1373 	} else {
1374 		lib_loc_p->vers_req.v_min = CFGA_HSL_V1;
1375 	}
1376 
1377 	/*
1378 	 * If the ap_id is a devlink in CFGA_DEV_DIR, follow link
1379 	 * to get the physical ap_id.
1380 	 */
1381 	if ((type = find_arg_type(apdup)) == LOGICAL_LINK_AP) {
1382 		(void) snprintf(lib_loc_p->ap_base, sizeof (lib_loc_p->ap_base),
1383 		    "%s%s", CFGA_DEV_DIR SLASH, apdup);
1384 	}
1385 
1386 	path[sizeof (path) - 1] = '\0';
1387 	if (type == LOGICAL_LINK_AP && realpath(lib_loc_p->ap_base, path)
1388 	    != NULL) {
1389 		(void) snprintf(lib_loc_p->ap_base, sizeof (lib_loc_p->ap_base),
1390 		    "%s", path);
1391 	} else {
1392 		(void) snprintf(lib_loc_p->ap_base, sizeof (lib_loc_p->ap_base),
1393 		    "%s", apdup);
1394 	}
1395 
1396 
1397 	/*
1398 	 * find and load the library
1399 	 * The base component of the ap_id is used to locate the plug-in
1400 	 *
1401 	 * NOTE that PCIE/PCISHPC connectors also have minor nodes &
1402 	 * dev links created for now.
1403 	 */
1404 	if ((type = find_arg_type(lib_loc_p->ap_base)) == PHYSICAL_AP) {
1405 		/*
1406 		 * physical ap_id: Use ap_base as root for tree walk
1407 		 * A link based apid (logical) will resolve to a physical
1408 		 * ap_id.
1409 		 */
1410 		ret = find_ap_common(lib_loc_p, lib_loc_p->ap_base,
1411 		    check_ap_phys, check_ap_phys_hp, errstring);
1412 	} else if ((type == LOGICAL_DRV_AP) ||
1413 	    (type == AP_TYPE && dyncomp == NULL)) {
1414 		/*
1415 		 * logical ap_id or ap_type: Use "/" as root for tree walk
1416 		 * Note: an aptype cannot have a dynamic component
1417 		 */
1418 		ret = find_ap_common(lib_loc_p, "/", check_ap,
1419 		    check_ap_hp, errstring);
1420 	} else {
1421 		ret = CFGA_APID_NOEXIST;
1422 	}
1423 
1424 	if (ret == CFGA_OK) {
1425 #ifndef	NDEBUG
1426 		/*
1427 		 * variables used by assert() only which is disabled
1428 		 * by defining NDEBUG (see top of this file)
1429 		 */
1430 		plugin_lib_t *libp;
1431 
1432 		libp = lib_loc_p->libp;
1433 #endif	/* NDEBUG */
1434 
1435 		assert(strcmp(libp->libpath, lib_loc_p->pathname) == 0);
1436 		assert(VALID_HSL_VERS(libp->plugin_vers));
1437 
1438 		/*
1439 		 * If a dynamic component was present, v1 plug-ins are not
1440 		 * acceptable.
1441 		 */
1442 		assert(dyncomp == NULL || libp->plugin_vers >= CFGA_HSL_V2);
1443 
1444 		/*
1445 		 * ap_physical is passed to plugins as their ap_id argument.
1446 		 * Append dynamic component if any.
1447 		 */
1448 		append_dyn(lib_loc_p->ap_physical, dyncomp,
1449 		    sizeof (lib_loc_p->ap_physical));
1450 	}
1451 
1452 	/* cleanup */
1453 	lib_loc_p->vers_req.v_min = INVALID_VERSION;
1454 	lib_loc_p->vers_req.v_max = INVALID_VERSION;
1455 	*lib_loc_p->ap_base = '\0';
1456 
1457 	/*FALLTHRU*/
1458 out:
1459 	S_FREE(apdup);
1460 	S_FREE(dyncomp);
1461 	if (ret != CFGA_OK) {
1462 		lib_loc_p->libp = NULL;
1463 	}
1464 
1465 	assert(ret != CFGA_OK || lib_loc_p->libp != NULL);
1466 
1467 	return (ret);
1468 }
1469 
1470 /* load_lib - load library for non-SHP attachment point node */
1471 static cfga_err_t
1472 load_lib(
1473 	di_node_t node,
1474 	di_minor_t minor,
1475 	lib_loc_t *libloc_p)
1476 {
1477 	return (load_lib_impl(node, minor, NULL, libloc_p));
1478 }
1479 
1480 /* load_lib_hp - load library for SHP attachment point node */
1481 static cfga_err_t
1482 load_lib_hp(
1483 	di_node_t node,
1484 	di_hp_t hp,
1485 	lib_loc_t *libloc_p)
1486 {
1487 	return (load_lib_impl(node, NULL, hp, libloc_p));
1488 }
1489 
1490 /*
1491  * load_lib_impl - Given a library pathname, create a entry for it
1492  * in the library list, * if one does not already exist, and read
1493  * lock it to keep it there.
1494  */
1495 static cfga_err_t
1496 load_lib_impl(
1497 	di_node_t node,
1498 	di_minor_t minor,
1499 	di_hp_t hp,
1500 	lib_loc_t *libloc_p)
1501 {
1502 	plugin_lib_t *libp, *list_libp;
1503 	char *devfs_path;
1504 	char *name;
1505 
1506 	if (minor != DI_MINOR_NIL && hp != DI_HP_NIL)
1507 		return (CFGA_LIB_ERROR);
1508 
1509 	if (minor != DI_MINOR_NIL)
1510 		name = di_minor_name(minor);
1511 	else
1512 		name = di_hp_name(hp);
1513 
1514 	/*
1515 	 * lock the library list
1516 	 */
1517 	(void) mutex_lock(&plugin_list_lock);
1518 
1519 	/*
1520 	 * see if lib exist in list, if not, allocate a new one
1521 	 */
1522 	list_libp = lib_in_list(libloc_p->pathname);
1523 	if (list_libp != NULL) {
1524 		hold_lib(list_libp);
1525 		(void) mutex_unlock(&plugin_list_lock);
1526 
1527 		/* fill in logical and physical name in libloc_p */
1528 		libloc_p->libp = libp = list_libp;
1529 		if (minor != DI_MINOR_NIL) {
1530 			if (libp->vers_ops->mklog(node, minor, libp, libloc_p)
1531 			    != CFGA_OK) {
1532 				rele_lib(list_libp);
1533 				return (CFGA_LIB_ERROR);
1534 			}
1535 		} else {
1536 			if (mklog_hp(node, hp, libp, libloc_p) != CFGA_OK) {
1537 				rele_lib(list_libp);
1538 				return (CFGA_LIB_ERROR);
1539 			}
1540 		}
1541 
1542 		devfs_path = di_devfs_path(node);
1543 		(void) snprintf(libloc_p->ap_physical, MAXPATHLEN, "%s%s:%s",
1544 		    DEVICES_DIR, devfs_path, name);
1545 		di_devfs_path_free(devfs_path);
1546 
1547 		return (CFGA_OK);
1548 	}
1549 
1550 	/* allocate a new plugin_lib_t structure */
1551 	libp = config_calloc_check(1, sizeof (plugin_lib_t), NULL);
1552 	if (libp == NULL) {
1553 		(void) mutex_unlock(&plugin_list_lock);
1554 		return (CFGA_LIB_ERROR);
1555 	}
1556 
1557 	(void) snprintf(libp->libpath, sizeof (libp->libpath), "%s",
1558 	    libloc_p->pathname);
1559 
1560 	/*
1561 	 * ensure that the lib is open and linked in
1562 	 */
1563 	libp->handle = dlopen(libp->libpath, RTLD_NOW);
1564 	if (libp->handle == NULL) {
1565 		(void) mutex_unlock(&plugin_list_lock);
1566 		free(libp);
1567 		return (CFGA_NO_LIB);
1568 	}
1569 
1570 	if (minor != DI_MINOR_NIL) {
1571 		if (resolve_lib_ref(libp, libloc_p) != CFGA_OK ||
1572 		    libp->vers_ops->mklog(node, minor, libp, libloc_p)
1573 		    != CFGA_OK) {
1574 			(void) mutex_unlock(&plugin_list_lock);
1575 			(void) dlclose(libp->handle);
1576 			free(libp);
1577 			return (CFGA_NO_LIB);
1578 		}
1579 	} else {
1580 		if (resolve_lib_ref(libp, libloc_p) != CFGA_OK ||
1581 		    mklog_hp(node, hp, libp, libloc_p) != CFGA_OK) {
1582 			(void) mutex_unlock(&plugin_list_lock);
1583 			(void) dlclose(libp->handle);
1584 			free(libp);
1585 			return (CFGA_NO_LIB);
1586 		}
1587 	}
1588 
1589 	/*
1590 	 * link in new entry to the end of list
1591 	 */
1592 	list_libp = &plugin_list;
1593 	while (list_libp->next != NULL)
1594 		list_libp = list_libp->next;
1595 	libp->next = list_libp->next;
1596 	list_libp->next = libp;
1597 
1598 	/* Initialize refcnt to 1 */
1599 	libp->refcnt = 1;
1600 	(void) mutex_init(&libp->lock, USYNC_THREAD, NULL);
1601 
1602 	(void) mutex_unlock(&plugin_list_lock);
1603 
1604 	/*
1605 	 * record libp and physical node name in the libloc struct
1606 	 */
1607 	libloc_p->libp = libp;
1608 	devfs_path = di_devfs_path(node);
1609 	(void) snprintf(libloc_p->ap_physical, MAXPATHLEN, "%s%s:%s",
1610 	    DEVICES_DIR, devfs_path, name);
1611 	di_devfs_path_free(devfs_path);
1612 
1613 	return (CFGA_OK);
1614 }
1615 
1616 
1617 #define	NUM_LIB_NAMES   2
1618 
1619 /*
1620  * find_lib - find library for non-SHP attachment point node
1621  */
1622 static cfga_err_t
1623 find_lib(
1624 	di_node_t node,
1625 	di_minor_t minor,
1626 	lib_loc_t *libloc_p)
1627 {
1628 	char name[NUM_LIB_NAMES][MAXPATHLEN];
1629 	char *class = NULL, *drv = NULL;
1630 	int i;
1631 
1632 
1633 	/* Make sure pathname and class is null if we fail */
1634 	*libloc_p->ap_class = *libloc_p->pathname = '\0';
1635 
1636 	/*
1637 	 * Initialize possible library tags.
1638 	 */
1639 
1640 	drv = di_driver_name(node);
1641 	class = get_class(minor);
1642 
1643 	if (drv == NULL || class == NULL) {
1644 		return (CFGA_LIB_ERROR);
1645 	}
1646 
1647 	i = 0;
1648 	(void) snprintf(&name[i++][0], sizeof (name[0]), "%s", drv);
1649 	(void) snprintf(&name[i++][0], sizeof (name[0]), "%s", class);
1650 
1651 	/*
1652 	 * Cycle through the array of names to find the library.
1653 	 */
1654 	for (i = 0; i < NUM_LIB_NAMES; i++) {
1655 
1656 		/* Attachment points may not have a class (i.e. are generic) */
1657 		if (name[i][0] == '\0') {
1658 			continue;
1659 		}
1660 
1661 		if (find_lib_impl(name[i], libloc_p) == CFGA_OK)
1662 			goto found;
1663 	}
1664 
1665 	return (CFGA_NO_LIB);
1666 
1667 found:
1668 
1669 	/* Record class name (if any) */
1670 	(void) snprintf(libloc_p->ap_class, sizeof (libloc_p->ap_class), "%s",
1671 	    class);
1672 
1673 	return (CFGA_OK);
1674 }
1675 
1676 /*
1677  * find_lib_hp - find library for SHP attachment point
1678  */
1679 /*ARGSUSED*/
1680 static cfga_err_t
1681 find_lib_hp(
1682 	di_node_t node,
1683 	di_hp_t hp,
1684 	lib_loc_t *libloc_p)
1685 {
1686 	char name[MAXPATHLEN];
1687 	char *class = NULL;
1688 
1689 
1690 	/* Make sure pathname and class is null if we fail */
1691 	*libloc_p->ap_class = *libloc_p->pathname = '\0';
1692 
1693 	/*
1694 	 * Initialize possible library tags.
1695 	 *
1696 	 * Only support PCI class for now, this will need to be
1697 	 * changed as other plugins are migrated to SHP plugin.
1698 	 */
1699 	class = "pci";
1700 #if 0
1701 	/*
1702 	 * No type check for now as PCI is the only class SHP plugin
1703 	 * supports. In the future we'll need to enable the type check
1704 	 * and set class accordingly, when non PCI plugins are migrated
1705 	 * to SHP. In that case we'll probably need to add an additional
1706 	 * interface between libcfgadm and the plugins, and SHP plugin will
1707 	 * implement this interface which will translate the bus specific
1708 	 * strings to standard classes that libcfgadm can recognize, for
1709 	 * all the buses it supports, e.g. for pci/pcie it will translate
1710 	 * PCIE_NATIVE_HP_TYPE to string "pci". We'll also need to bump up
1711 	 * SHP plugin version to 3 to use the new interface.
1712 	 */
1713 	class = di_hp_type(hp);
1714 	if ((strcmp(class, PCIE_NATIVE_HP_TYPE) == 0) ||
1715 	    (strcmp(class, PCIE_ACPI_HP_TYPE) == 0) ||
1716 	    (strcmp(class, PCIE_PCI_HP_TYPE) == 0)) {
1717 		class = "pci";
1718 	} else {
1719 		goto fail;
1720 	}
1721 #endif
1722 	(void) snprintf(&name[0], sizeof (name), "%s", "shp");
1723 
1724 	if (find_lib_impl(name, libloc_p) == CFGA_OK)
1725 		goto found;
1726 fail:
1727 	return (CFGA_NO_LIB);
1728 
1729 found:
1730 
1731 	/* Record class name (if any) */
1732 	(void) snprintf(libloc_p->ap_class, sizeof (libloc_p->ap_class), "%s",
1733 	    class);
1734 
1735 	return (CFGA_OK);
1736 }
1737 
1738 /*
1739  * find_lib_impl - Given an attachment point node find it's library
1740  */
1741 static cfga_err_t
1742 find_lib_impl(
1743 	char *name,
1744 	lib_loc_t *libloc_p)
1745 {
1746 	char lib[MAXPATHLEN];
1747 	struct stat lib_stat;
1748 	void *dlhandle = NULL;
1749 	static char plat_name[SYSINFO_LENGTH];
1750 	static char machine_name[SYSINFO_LENGTH];
1751 	static char arch_name[SYSINFO_LENGTH];
1752 
1753 	/*
1754 	 * Initialize machine name and arch name
1755 	 */
1756 	if (strncmp("", machine_name, MAXPATHLEN) == 0) {
1757 		if (sysinfo(SI_PLATFORM, plat_name, SYSINFO_LENGTH) == -1) {
1758 			return (CFGA_ERROR);
1759 		}
1760 		if (sysinfo(SI_ARCHITECTURE, arch_name, SYSINFO_LENGTH) == -1) {
1761 			return (CFGA_ERROR);
1762 		}
1763 		if (sysinfo(SI_MACHINE, machine_name, SYSINFO_LENGTH) == -1) {
1764 			return (CFGA_ERROR);
1765 		}
1766 	}
1767 
1768 	/*
1769 	 * Try path based upon platform name
1770 	 */
1771 	(void) snprintf(lib, sizeof (lib), "%s%s%s%s%s",
1772 	    LIB_PATH_BASE1, plat_name, LIB_PATH_MIDDLE,
1773 	    name, LIB_PATH_TAIL);
1774 
1775 	if (stat(lib, &lib_stat) == 0) {
1776 		/* file exists, is it a lib */
1777 		dlhandle = dlopen(lib, RTLD_LAZY);
1778 		if (dlhandle != NULL) {
1779 			goto found;
1780 		}
1781 	}
1782 
1783 	/*
1784 	 * Try path based upon machine name
1785 	 */
1786 	(void) snprintf(lib, sizeof (lib), "%s%s%s%s%s",
1787 	    LIB_PATH_BASE1, machine_name, LIB_PATH_MIDDLE,
1788 	    name, LIB_PATH_TAIL);
1789 
1790 
1791 	if (stat(lib, &lib_stat) == 0) {
1792 		/* file exists, is it a lib */
1793 		dlhandle = dlopen(lib, RTLD_LAZY);
1794 		if (dlhandle != NULL) {
1795 			goto found;
1796 		}
1797 	}
1798 
1799 	/*
1800 	 * Try path based upon arch name
1801 	 */
1802 	(void) snprintf(lib, sizeof (lib), "%s%s%s%s%s",
1803 	    LIB_PATH_BASE1, arch_name, LIB_PATH_MIDDLE,
1804 	    name, LIB_PATH_TAIL);
1805 
1806 	if (stat(lib, &lib_stat) == 0) {
1807 		/* file exists, is it a lib */
1808 		dlhandle = dlopen(lib, RTLD_LAZY);
1809 		if (dlhandle != NULL) {
1810 			goto found;
1811 		}
1812 
1813 	}
1814 
1815 	/*
1816 	 * Try generic location
1817 	 */
1818 	(void) snprintf(lib, sizeof (lib), "%s%s%s%s",
1819 	    LIB_PATH_BASE2, LIB_PATH_MIDDLE, name, LIB_PATH_TAIL);
1820 
1821 	if (stat(lib, &lib_stat) == 0) {
1822 		/* file exists, is it a lib */
1823 		dlhandle = dlopen(lib, RTLD_LAZY);
1824 		if (dlhandle != NULL) {
1825 			goto found;
1826 		}
1827 
1828 	}
1829 	return (CFGA_NO_LIB);
1830 
1831 found:
1832 	/* we got one! */
1833 	(void) snprintf(libloc_p->pathname, sizeof (libloc_p->pathname), "%s",
1834 	    lib);
1835 
1836 	(void) dlclose(dlhandle);
1837 
1838 	return (CFGA_OK);
1839 }
1840 
1841 static cfga_err_t
1842 lookup_cache(lib_loc_t *libloc_p)
1843 {
1844 	lib_cache_t *entry;
1845 	(void) mutex_lock(&lib_cache_lock);
1846 	entry = lib_cache;
1847 	while (entry) {
1848 		if (strcmp(entry->lc_ap_id, libloc_p->ap_base) == 0) {
1849 			plugin_lib_t *libp = entry->lc_libp;
1850 			libloc_p->libp = libp;
1851 			hold_lib(libp);
1852 			(void) strcpy(libloc_p->pathname, libp->libpath);
1853 			(void) strcpy(libloc_p->ap_physical,
1854 			    entry->lc_ap_physical);
1855 			(void) strcpy(libloc_p->ap_logical,
1856 			    entry->lc_ap_logical);
1857 			(void) mutex_unlock(&lib_cache_lock);
1858 			return (CFGA_OK);
1859 		}
1860 		entry = entry->lc_next;
1861 	}
1862 	(void) mutex_unlock(&lib_cache_lock);
1863 
1864 	return (CFGA_ERROR);
1865 }
1866 
1867 static void
1868 update_cache(lib_loc_t *libloc_p)
1869 {
1870 	lib_cache_t *entry;
1871 	entry = config_calloc_check(1, sizeof (lib_cache_t), NULL);
1872 	if (entry == NULL)
1873 		return;
1874 
1875 	entry->lc_ap_id = strdup(libloc_p->ap_base);
1876 	entry->lc_ap_physical = strdup(libloc_p->ap_physical);
1877 	entry->lc_ap_logical = strdup(libloc_p->ap_logical);
1878 	if ((entry->lc_ap_id == NULL) || (entry->lc_ap_physical == NULL) ||
1879 	    (entry->lc_ap_logical == NULL)) {
1880 		free(entry->lc_ap_id);
1881 		free(entry->lc_ap_physical);
1882 		free(entry->lc_ap_logical);
1883 		free(entry);
1884 		return;
1885 	}
1886 
1887 	(void) mutex_lock(&lib_cache_lock);
1888 	entry->lc_libp = libloc_p->libp;
1889 	entry->lc_next = lib_cache;
1890 	lib_cache = entry;
1891 	hold_lib(entry->lc_libp);	/* prevent stale cache */
1892 	(void) mutex_unlock(&lib_cache_lock);
1893 }
1894 
1895 static void
1896 destroy_cache()
1897 {
1898 	lib_cache_t *entry, *next;
1899 	(void) mutex_lock(&lib_cache_lock);
1900 	entry = lib_cache;
1901 	while (entry) {
1902 		next = entry->lc_next;
1903 		rele_lib(entry->lc_libp);
1904 		free(entry->lc_ap_id);
1905 		free(entry->lc_ap_physical);
1906 		free(entry->lc_ap_logical);
1907 		free(entry);
1908 		entry = next;
1909 	}
1910 	(void) mutex_unlock(&lib_cache_lock);
1911 }
1912 
1913 /*
1914  * find_ap_common - locate a particular attachment point
1915  */
1916 static cfga_err_t
1917 find_ap_common(
1918 	lib_loc_t *libloc_p,
1919 	const char *physpath,
1920 	int (*fcn)(di_node_t node, di_minor_t minor, void *arg),
1921 	int (*fcn_hp)(di_node_t node, di_hp_t hp, void *arg),
1922 	char **errstring)
1923 {
1924 	di_node_t rnode, wnode;
1925 	char *cp, *rpath;
1926 	size_t len;
1927 
1928 	if (lookup_cache(libloc_p) == CFGA_OK)
1929 		return (CFGA_OK);
1930 
1931 	if ((rpath = config_calloc_check(1, strlen(physpath) + 1,
1932 	    errstring)) == NULL) {
1933 		return (CFGA_LIB_ERROR);
1934 	}
1935 
1936 	(void) strcpy(rpath, physpath);
1937 
1938 	/* Remove devices prefix (if any) */
1939 	len = strlen(DEVICES_DIR);
1940 	if (strncmp(rpath, DEVICES_DIR SLASH, len + strlen(SLASH)) == 0) {
1941 		(void) memmove(rpath, rpath + len,
1942 		    strlen(rpath + len) + 1);
1943 	}
1944 
1945 	/* Remove dynamic component if any */
1946 	if ((cp = GET_DYN(rpath)) != NULL) {
1947 		*cp = '\0';
1948 	}
1949 
1950 	/* Remove minor name (if any) */
1951 	if ((cp = strrchr(rpath, ':')) != NULL) {
1952 		*cp = '\0';
1953 	}
1954 
1955 	/*
1956 	 * begin walk of device tree
1957 	 *
1958 	 * Since we create minor nodes & dev links for both all PCI/PCIE
1959 	 * connectors, but only create hp nodes for PCIE/PCISHPC connectors
1960 	 * of the new framework, we should first match with hp nodes. If
1961 	 * the ap_id refers to a PCIE/PCISHPC connector, we'll be able to
1962 	 * find it here.
1963 	 */
1964 	rnode = di_init("/", DINFOSUBTREE | DINFOHP);
1965 	if (rnode)
1966 		wnode = di_lookup_node(rnode, rpath);
1967 	else
1968 		wnode = DI_NODE_NIL;
1969 
1970 	if (wnode == DI_NODE_NIL) {
1971 		if (rnode == DI_NODE_NIL) {
1972 			S_FREE(rpath);
1973 			config_err(errno, DI_INIT_FAILED, errstring);
1974 			return (CFGA_LIB_ERROR);
1975 		} else {
1976 			/*
1977 			 * di_lookup_node() may fail, either because the
1978 			 * ap_id does not exist, or because the ap_id refers
1979 			 * to a legacy PCI slot, thus we'll not able to
1980 			 * find node using DINFOHP, try to see if we can
1981 			 * find one using DINFOCACHE.
1982 			 */
1983 			di_fini(rnode);
1984 			goto find_minor;
1985 		}
1986 	}
1987 
1988 	libloc_p->libp = NULL;
1989 	libloc_p->status = CFGA_APID_NOEXIST;
1990 
1991 	(void) di_walk_hp(wnode, NULL, DI_HP_CONNECTOR,
1992 	    libloc_p, fcn_hp);
1993 
1994 	di_fini(rnode);
1995 
1996 	/*
1997 	 * Failed to find a matching hp node, try minor node.
1998 	 */
1999 	if (libloc_p->libp == NULL) {
2000 find_minor:
2001 		rnode = di_init("/", DINFOCACHE);
2002 		if (rnode)
2003 			wnode = di_lookup_node(rnode, rpath);
2004 		else
2005 			wnode = DI_NODE_NIL;
2006 
2007 		if (wnode == DI_NODE_NIL) {
2008 			if (rnode == DI_NODE_NIL) {
2009 				S_FREE(rpath);
2010 				config_err(errno, DI_INIT_FAILED, errstring);
2011 				return (CFGA_LIB_ERROR);
2012 			} else {
2013 				/*
2014 				 * di_lookup_node() may fail, because the
2015 				 * ap_id does not exist.
2016 				 */
2017 				S_FREE(rpath);
2018 				di_fini(rnode);
2019 				return (CFGA_APID_NOEXIST);
2020 			}
2021 		}
2022 
2023 		libloc_p->libp = NULL;
2024 		libloc_p->status = CFGA_APID_NOEXIST;
2025 
2026 		(void) di_walk_minor(wnode, "ddi_ctl:attachment_point",
2027 		    DI_CHECK_ALIAS|DI_CHECK_INTERNAL_PATH,
2028 		    libloc_p, fcn);
2029 
2030 		di_fini(rnode);
2031 	}
2032 
2033 	S_FREE(rpath);
2034 
2035 	if (libloc_p->libp != NULL) {
2036 		update_cache(libloc_p);
2037 		return (CFGA_OK);
2038 	} else {
2039 		return (libloc_p->status);
2040 	}
2041 }
2042 
2043 /*
2044  * check_ap - called for each non-SHP attachment point found
2045  */
2046 static int
2047 check_ap(
2048 	di_node_t node,
2049 	di_minor_t minor,
2050 	void *arg)
2051 {
2052 	return (check_ap_impl(node, minor, NULL, arg));
2053 }
2054 
2055 /*
2056  * check_ap_hp - called for each SHP attachment point found
2057  */
2058 static int
2059 check_ap_hp(
2060 	di_node_t node,
2061 	di_hp_t hp,
2062 	void *arg)
2063 {
2064 	return (check_ap_impl(node, NULL, hp, arg));
2065 }
2066 
2067 /*
2068  * check_ap_impl - called for each attachment point found
2069  *
2070  * This is used in cases where a particular attachment point
2071  * or type of attachment point is specified via a logical name or ap_type.
2072  * Not used for physical names or in the list case with no
2073  * ap's specified.
2074  */
2075 static int
2076 check_ap_impl(
2077 	di_node_t node,
2078 	di_minor_t minor,
2079 	di_hp_t hp,
2080 	void *arg)
2081 {
2082 	char *cp = NULL;
2083 	char aptype[MAXPATHLEN];
2084 	char *recep_id = NULL;
2085 	char *node_minor;
2086 	char *drv_name;
2087 	char inst[MAXPATHLEN];
2088 	char inst2[MAXPATHLEN];
2089 	lib_loc_t *libloc_p;
2090 	int comparison_test;
2091 	int instance;
2092 	cfga_ap_types_t type;
2093 
2094 	if (minor != DI_MINOR_NIL && hp != DI_HP_NIL)
2095 		return (DI_WALK_CONTINUE);
2096 
2097 	libloc_p = (lib_loc_t *)arg;
2098 
2099 	(void) snprintf(aptype, sizeof (aptype), "%s", libloc_p->ap_base);
2100 
2101 	/*
2102 	 * This routime handles only aptypes and driver based logical apids.
2103 	 */
2104 	type = find_arg_type(aptype);
2105 	if (type == LOGICAL_DRV_AP) {
2106 		cp = strchr(aptype, ':');
2107 		*cp = '\0';
2108 		recep_id =  cp+1;
2109 		cp--;
2110 		while (isdigit(*cp) && cp != aptype)
2111 			cp--;
2112 		cp++;
2113 
2114 		(void) snprintf(inst, sizeof (inst), "%s", cp);
2115 
2116 		*cp = '\0';
2117 	} else if (type != AP_TYPE) {
2118 		libloc_p->status = CFGA_APID_NOEXIST;
2119 		return (DI_WALK_CONTINUE);
2120 	}
2121 
2122 	if (minor != DI_MINOR_NIL)
2123 		node_minor = di_minor_name(minor);
2124 	else
2125 		node_minor = di_hp_name(hp);
2126 
2127 	drv_name = di_driver_name(node);
2128 	instance = di_instance(node);
2129 
2130 	if (node_minor == NULL || drv_name == NULL || instance == -1) {
2131 		libloc_p->status = CFGA_APID_NOEXIST;
2132 		return (DI_WALK_CONTINUE);
2133 	}
2134 
2135 	(void) sprintf(inst2, "%d", instance);
2136 
2137 	/*
2138 	 * If the base matches driver and instance try and find a lib for it,
2139 	 * then load it. On any failure we continue the walk.
2140 	 *
2141 	 * driver based logical ap_ids are derived from driver name + instance.
2142 	 * Ap_types are just partial driver names.
2143 	 *
2144 	 */
2145 
2146 	comparison_test = 0;
2147 	if (type == AP_TYPE) {
2148 		if (strncmp(aptype, drv_name, strlen(aptype)) == 0) {
2149 			comparison_test = 1;
2150 		}
2151 	} else {
2152 		if (strcmp(aptype, drv_name) == 0 &&
2153 		    strcmp(recep_id, node_minor) == 0 &&
2154 		    strcmp(inst, inst2) == 0) {
2155 			comparison_test = 1;
2156 		}
2157 	}
2158 
2159 	if (comparison_test) {
2160 		/*
2161 		 * save the correct type of error so user does not get confused
2162 		 */
2163 		if (minor != DI_MINOR_NIL) {
2164 			if (find_lib(node, minor, libloc_p) != CFGA_OK) {
2165 				libloc_p->status = CFGA_NO_LIB;
2166 				return (DI_WALK_CONTINUE);
2167 			}
2168 			if (load_lib(node, minor, libloc_p) != CFGA_OK) {
2169 				libloc_p->status = CFGA_LIB_ERROR;
2170 				return (DI_WALK_CONTINUE);
2171 			}
2172 		} else {
2173 			if (find_lib_hp(node, hp, libloc_p) != CFGA_OK) {
2174 				libloc_p->status = CFGA_NO_LIB;
2175 				return (DI_WALK_CONTINUE);
2176 			}
2177 			if (load_lib_hp(node, hp, libloc_p) != CFGA_OK) {
2178 				libloc_p->status = CFGA_LIB_ERROR;
2179 				return (DI_WALK_CONTINUE);
2180 			}
2181 		}
2182 		libloc_p->status = CFGA_OK;
2183 		return (DI_WALK_TERMINATE);
2184 	} else {
2185 		libloc_p->status = CFGA_APID_NOEXIST;
2186 		return (DI_WALK_CONTINUE);
2187 	}
2188 }
2189 
2190 
2191 /*
2192  * check_ap_phys - called for each non-SHP attachment point found
2193  */
2194 static int
2195 check_ap_phys(
2196 	di_node_t node,
2197 	di_minor_t minor,
2198 	void *arg)
2199 {
2200 	return (check_ap_phys_impl(node, minor, DI_HP_NIL, arg));
2201 }
2202 
2203 /*
2204  * check_ap_phys_hp - called for each SHP attachment point found
2205  */
2206 static int
2207 check_ap_phys_hp(
2208 	di_node_t node,
2209 	di_hp_t hp,
2210 	void *arg)
2211 {
2212 	return (check_ap_phys_impl(node, DI_HP_NIL, hp, arg));
2213 }
2214 
2215 /*
2216  * check_ap_phys_impl - called for each attachment point found
2217  *
2218  * This is used in cases where a particular attachment point
2219  * is specified via a physical name. If the name matches then
2220  * we try and find and load the library for it.
2221  */
2222 static int
2223 check_ap_phys_impl(
2224 	di_node_t node,
2225 	di_minor_t minor,
2226 	di_hp_t hp,
2227 	void *arg)
2228 {
2229 	lib_loc_t *libloc_p;
2230 	char phys_name[MAXPATHLEN];
2231 	char *devfs_path;
2232 	char *minor_name;
2233 
2234 	if (minor != DI_MINOR_NIL && hp != DI_HP_NIL)
2235 		return (DI_WALK_CONTINUE);
2236 
2237 	libloc_p = (lib_loc_t *)arg;
2238 	devfs_path = di_devfs_path(node);
2239 	if (minor != DI_MINOR_NIL)
2240 		minor_name = di_minor_name(minor);
2241 	else
2242 		minor_name = di_hp_name(hp);
2243 
2244 	if (devfs_path == NULL || minor_name == NULL) {
2245 		libloc_p->status = CFGA_APID_NOEXIST;
2246 		return (DI_WALK_CONTINUE);
2247 	}
2248 
2249 	(void) snprintf(phys_name, sizeof (phys_name), "%s%s:%s",
2250 	    DEVICES_DIR, devfs_path, minor_name);
2251 
2252 	di_devfs_path_free(devfs_path);
2253 
2254 	if (strcmp(phys_name, libloc_p->ap_base) == 0) {
2255 		if (minor != DI_MINOR_NIL) {
2256 			if (find_lib(node, minor, libloc_p) != CFGA_OK) {
2257 				libloc_p->status = CFGA_NO_LIB;
2258 				return (DI_WALK_CONTINUE);
2259 			}
2260 			if (load_lib(node, minor, libloc_p) != CFGA_OK) {
2261 				libloc_p->status = CFGA_LIB_ERROR;
2262 				return (DI_WALK_CONTINUE);
2263 			}
2264 		} else {
2265 			if (find_lib_hp(node, hp, libloc_p) != CFGA_OK) {
2266 				libloc_p->status = CFGA_NO_LIB;
2267 				return (DI_WALK_CONTINUE);
2268 			}
2269 			if (load_lib_hp(node, hp, libloc_p) != CFGA_OK) {
2270 				libloc_p->status = CFGA_LIB_ERROR;
2271 				return (DI_WALK_CONTINUE);
2272 			}
2273 		}
2274 
2275 		libloc_p->status = CFGA_OK;
2276 		return (DI_WALK_TERMINATE);
2277 	} else {
2278 		libloc_p->status = CFGA_APID_NOEXIST;
2279 		return (DI_WALK_CONTINUE);
2280 	}
2281 }
2282 
2283 /*
2284  * lib_in_list
2285  *
2286  * See if library, as specified by the full pathname and controller
2287  * instance number is already represented in the plugin library list.
2288  * If the instance number is -1 it is ignored.
2289  */
2290 static plugin_lib_t *
2291 lib_in_list(char *libpath)
2292 {
2293 	plugin_lib_t *libp = NULL;
2294 
2295 	for (libp = plugin_list.next; libp != NULL; libp = libp->next) {
2296 		if (strncmp(libpath, libp->libpath, MAXPATHLEN) == 0) {
2297 			return (libp);
2298 		}
2299 	}
2300 	return (NULL);
2301 }
2302 
2303 
2304 
2305 
2306 /*
2307  * Coalesce stat and list data into single array
2308  */
2309 static cfga_err_t
2310 realloc_data_ext(
2311 	cfga_list_data_t **ap_id_list,
2312 	int *nlistp,
2313 	list_stat_t *lstatp)
2314 {
2315 	int i, j;
2316 	stat_data_list_t *slp;
2317 	cfga_list_data_t *cldp;
2318 	array_list_t *alp;
2319 	cfga_err_t rc = CFGA_OK;
2320 
2321 
2322 	assert(*lstatp->countp >= 0);
2323 
2324 	if (*lstatp->countp == 0) {
2325 		*ap_id_list = NULL;
2326 		*nlistp = 0;
2327 		return (CFGA_OK);
2328 	}
2329 
2330 	/*
2331 	 * allocate the array
2332 	 */
2333 	if ((cldp = config_calloc_check(*lstatp->countp,
2334 	    sizeof (cfga_list_data_t), lstatp->errstr)) == NULL) {
2335 		rc = CFGA_LIB_ERROR;
2336 		goto out;
2337 	}
2338 
2339 	/*
2340 	 * copy all the stat elements (if any) into the array
2341 	 */
2342 	slp = lstatp->sdl;
2343 	for (i = 0; slp != NULL; i++) {
2344 		if (i >= *lstatp->countp) {
2345 			rc = CFGA_LIB_ERROR;
2346 			goto out;
2347 		}
2348 		stat_to_list(&cldp[i], &slp->stat_data);
2349 		slp = slp->next;
2350 	}
2351 
2352 	/*
2353 	 * copy all the list elements (if any) into the array
2354 	 */
2355 	alp = lstatp->al;
2356 	for (; alp != NULL; ) {
2357 		if (i + alp->nelem > *lstatp->countp) {
2358 			rc = CFGA_LIB_ERROR;
2359 			goto out;
2360 		}
2361 
2362 		for (j = 0; j < alp->nelem; i++, j++) {
2363 			cldp[i] = alp->array[j];
2364 		}
2365 		alp = alp->next;
2366 	}
2367 
2368 	if (i != *lstatp->countp) {
2369 		rc = CFGA_LIB_ERROR;
2370 	} else {
2371 		rc = CFGA_OK;
2372 	}
2373 
2374 	/*FALLTHRU*/
2375 
2376 out:
2377 	/* clean up */
2378 	lstat_free(lstatp);
2379 
2380 	if (rc == CFGA_OK) {
2381 		*ap_id_list = cldp;
2382 		*nlistp = *lstatp->countp;
2383 	} else {
2384 		S_FREE(cldp);
2385 		*ap_id_list = NULL;
2386 		*nlistp = 0;
2387 	}
2388 	return (rc);
2389 }
2390 
2391 /*
2392  * The caller of this routine may supply a buffer through
2393  * ap_id_list for returning data. Otherwise, this routine allocates the
2394  * buffer.
2395  */
2396 static cfga_err_t
2397 realloc_data(cfga_stat_data_t **ap_id_list, int *nlistp, list_stat_t *lstatp)
2398 {
2399 	int i;
2400 	stat_data_list_t *slp;
2401 	cfga_stat_data_t *csdp, *buf;
2402 	cfga_err_t rc;
2403 
2404 
2405 	assert(*lstatp->countp >= 0);
2406 
2407 	if (*lstatp->countp == 0) {
2408 		*nlistp = 0;
2409 		return (CFGA_OK);
2410 	}
2411 
2412 
2413 	/*
2414 	 * allocate the array if caller does not supply one.
2415 	 */
2416 	if (*ap_id_list == NULL) {
2417 		if ((buf = config_calloc_check(*lstatp->countp,
2418 		    sizeof (cfga_stat_data_t), lstatp->errstr)) == NULL) {
2419 			rc = CFGA_LIB_ERROR;
2420 			goto out;
2421 		}
2422 	} else {
2423 		buf = *ap_id_list;
2424 	}
2425 
2426 	/*
2427 	 * copy the stat elements into the array
2428 	 */
2429 	csdp = buf;
2430 	slp = lstatp->sdl;
2431 	for (i = 0; slp != NULL; i++) {
2432 		if (i >= *lstatp->countp) {
2433 			rc = CFGA_LIB_ERROR;
2434 			goto out;
2435 		}
2436 		*csdp++ = slp->stat_data;
2437 		slp = slp->next;
2438 	}
2439 
2440 	rc = CFGA_OK;
2441 
2442 out:
2443 	if (rc == CFGA_OK) {
2444 		*nlistp = *lstatp->countp;
2445 		*ap_id_list = buf;
2446 	} else {
2447 		/*
2448 		 * Free buffer only if we allocated it.
2449 		 */
2450 		if (*ap_id_list == NULL) {
2451 			free(buf);
2452 		}
2453 		*nlistp = 0;
2454 	}
2455 
2456 	assert(lstatp->al == NULL);
2457 	lstat_free(lstatp);
2458 
2459 	return (rc);
2460 }
2461 
2462 
2463 /*
2464  * list_common - walk the device tree and stat all attachment points.
2465  */
2466 static cfga_err_t
2467 list_common(list_stat_t *lstatp, const char *class)
2468 {
2469 	di_node_t rnode;
2470 	char nodetype[MAXPATHLEN];
2471 	const char *l_class, *l_sep;
2472 
2473 	/*
2474 	 * May walk a subset of all attachment points in the device tree if
2475 	 * a class is specified
2476 	 */
2477 	if (class != NULL) {
2478 		l_sep = ":";
2479 		l_class = class;
2480 	} else {
2481 		l_sep = l_class = "";
2482 	}
2483 
2484 	(void) snprintf(nodetype, sizeof (nodetype), "%s%s%s",
2485 	    DDI_NT_ATTACHMENT_POINT, l_sep, l_class);
2486 
2487 	/*
2488 	 * Walk all hp nodes
2489 	 */
2490 	if ((rnode = di_init("/", DINFOSUBTREE | DINFOHP)) == DI_NODE_NIL) {
2491 		config_err(errno, DI_INIT_FAILED, lstatp->errstr);
2492 		return (CFGA_LIB_ERROR);
2493 	}
2494 	/* No need to filter on class for now */
2495 	(void) di_walk_hp(rnode, NULL, DI_HP_CONNECTOR,
2496 	    lstatp, do_list_common_hp);
2497 
2498 	di_fini(rnode);
2499 
2500 	/*
2501 	 * Walk all minor nodes
2502 	 * but exclude PCIE/PCIESHPC connectors which have been walked above.
2503 	 */
2504 	if ((rnode = di_init("/", DINFOCACHE)) == DI_NODE_NIL) {
2505 		config_err(errno, DI_INIT_FAILED, lstatp->errstr);
2506 		return (CFGA_LIB_ERROR);
2507 	}
2508 	(void) di_walk_minor(rnode, nodetype,
2509 	    DI_CHECK_ALIAS|DI_CHECK_INTERNAL_PATH, lstatp, do_list_common);
2510 
2511 	di_fini(rnode);
2512 
2513 	if (lstatp->shp_errstr != NULL) {
2514 		*(lstatp->errstr) = strdup(lstatp->shp_errstr);
2515 		free(lstatp->shp_errstr);
2516 		lstatp->shp_errstr = NULL;
2517 	}
2518 
2519 	return (CFGA_OK);
2520 }
2521 
2522 static void
2523 config_err(int errnum, int err_type, char **errstring)
2524 {
2525 	char *p = NULL, *q = NULL;
2526 	char *syserr = NULL;
2527 	char syserr_num[20];
2528 	int len = 0;
2529 
2530 	/*
2531 	 * If errstring is null it means user in not interested in getting
2532 	 * error status. So we don't do all the work
2533 	 */
2534 	if (errstring == NULL) {
2535 		return;
2536 	}
2537 
2538 	if (errnum != 0) {
2539 		syserr = strerror(errnum);
2540 		if (syserr == NULL) {
2541 			(void) sprintf(syserr_num, "errno=%d", errnum);
2542 			syserr = syserr_num;
2543 		}
2544 	} else
2545 		syserr = NULL;
2546 
2547 	q = dgettext(TEXT_DOMAIN, err_strings[err_type]);
2548 
2549 	len = strlen(q);
2550 	if (syserr != NULL) {
2551 		len += strlen(err_sep) + strlen(syserr);
2552 	}
2553 
2554 	p = malloc(len + 1);
2555 	if (p == NULL) {
2556 		*errstring = NULL;
2557 		return;
2558 	}
2559 
2560 	(void) strcpy(p, q);
2561 	if (syserr != NULL) {
2562 		(void) strcat(p, err_sep);
2563 		(void) strcat(p, syserr);
2564 	}
2565 
2566 	*errstring  = p;
2567 }
2568 
2569 /*
2570  * do_list_common - list non-SHP attachment point
2571  */
2572 static int
2573 do_list_common(
2574 	di_node_t node,
2575 	di_minor_t minor,
2576 	void *arg)
2577 {
2578 	di_node_t rnode;
2579 	di_hp_t hp;
2580 	char *minor_name;
2581 
2582 	minor_name = di_minor_name(minor);
2583 
2584 	/*
2585 	 * since PCIE/PCIHSHPC connectors have both hp nodes and minor nodes
2586 	 * created for now, we need to specifically exclude these connectors
2587 	 * during walking minor nodes.
2588 	 */
2589 	if ((rnode = di_init(di_devfs_path(node), DINFOSUBTREE | DINFOHP))
2590 	    == DI_NODE_NIL) {
2591 		return (DI_WALK_CONTINUE);
2592 	}
2593 
2594 	for (hp = DI_HP_NIL; (hp = di_hp_next(rnode, hp)) != DI_HP_NIL; ) {
2595 		if (strcmp(di_hp_name(hp), minor_name) == 0) {
2596 			di_fini(rnode);
2597 			return (DI_WALK_CONTINUE);
2598 		}
2599 	}
2600 
2601 	di_fini(rnode);
2602 
2603 	return (do_list_common_impl(node, minor, NULL, arg));
2604 }
2605 
2606 /*
2607  * do_list_common_hp - list SHP attachment point
2608  */
2609 static int
2610 do_list_common_hp(
2611 	di_node_t node,
2612 	di_hp_t hp,
2613 	void *arg)
2614 {
2615 	return (do_list_common_impl(node, NULL, hp, arg));
2616 }
2617 
2618 /*
2619  * do_list_common_impl - Routine to list attachment point as part of
2620  * a config_list opertion. Used by both v1 and v2 interfaces.
2621  * This is somewhat similar to config_get_lib() and its helper routines
2622  * except that the ap_ids are always physical and don't have dynamic
2623  * components.
2624  */
2625 static int
2626 do_list_common_impl(
2627 	di_node_t node,
2628 	di_minor_t minor,
2629 	di_hp_t hp,
2630 	void *arg)
2631 {
2632 	lib_loc_t lib_loc;
2633 	plugin_lib_t *libp;
2634 	list_stat_t *lstatp = NULL;
2635 	cfga_err_t ret = CFGA_ERROR;
2636 
2637 	if (minor != DI_MINOR_NIL && hp != DI_HP_NIL)
2638 		return (DI_WALK_CONTINUE);
2639 
2640 	lstatp = (list_stat_t *)arg;
2641 
2642 	lib_loc.libp = NULL;
2643 	/*
2644 	 * try and find a lib for this node
2645 	 */
2646 	if (minor != DI_MINOR_NIL) {
2647 		ret = find_lib(node, minor, &lib_loc);
2648 	} else {
2649 		ret = find_lib_hp(node, hp, &lib_loc);
2650 	}
2651 	if (ret != CFGA_OK) {
2652 		return (DI_WALK_CONTINUE);
2653 	}
2654 
2655 	/*
2656 	 * Load all plugins. We will check compatibility later in this
2657 	 * routine.
2658 	 */
2659 	lib_loc.vers_req.v_min = CFGA_HSL_V1;
2660 	lib_loc.vers_req.v_max = CFGA_HSL_VERS;
2661 
2662 	if (minor != DI_MINOR_NIL) {
2663 		ret = load_lib(node, minor, &lib_loc);
2664 	} else {
2665 		ret = load_lib_hp(node, hp, &lib_loc);
2666 	}
2667 	if (ret != CFGA_OK) {
2668 		return (DI_WALK_CONTINUE);
2669 	}
2670 
2671 	libp = lib_loc.libp;
2672 	assert(libp != NULL);
2673 
2674 	/*
2675 	 * Note: For list type routines (list all attachment points in
2676 	 * device tree) we don't pass errstring to the plugin, nor do we
2677 	 * stop the walk if an error occurs in the plugin.
2678 	 */
2679 	if (compat_plugin(&lstatp->use_vers, libp->plugin_vers)) {
2680 		if (minor != DI_MINOR_NIL) {
2681 			(void) libp->vers_ops->stat_plugin(lstatp,
2682 			    &lib_loc, NULL);
2683 		} else {
2684 			/*
2685 			 * If the underlying hotplug daemon is not enabled,
2686 			 * the SHP attach points will not be shown, this
2687 			 * could confuse the uesrs. We specifically pass the
2688 			 * errstring to SHP plugin so that it can set the
2689 			 * errstring accordingly in this case, giving users
2690 			 * a hint.
2691 			 */
2692 			ret = libp->vers_ops->stat_plugin(lstatp,
2693 			    &lib_loc, lstatp->errstr);
2694 			if (ret == CFGA_NOTSUPP && *(lstatp->errstr) != NULL) {
2695 				if (lstatp->shp_errstr == NULL) {
2696 					lstatp->shp_errstr =
2697 					    strdup(*(lstatp->errstr));
2698 				}
2699 			}
2700 
2701 			if (*(lstatp->errstr) != NULL) {
2702 				free(*(lstatp->errstr));
2703 				*(lstatp->errstr) = NULL;
2704 			}
2705 		}
2706 	}
2707 	rele_lib(libp);
2708 
2709 	return (DI_WALK_CONTINUE);
2710 }
2711 
2712 /*
2713  * stat_common - stat a user specified set of attachment points.
2714  */
2715 static cfga_err_t
2716 stat_common(
2717 	int num_ap_ids,
2718 	char *const *ap_ids,
2719 	const char *class,
2720 	list_stat_t *lstatp)
2721 {
2722 	int i;
2723 	lib_loc_t libloc;
2724 	plugin_lib_t *libp;
2725 	cfga_err_t rc = CFGA_OK;
2726 
2727 
2728 	/*
2729 	 * operate on each ap_id
2730 	 */
2731 	for (i = 0; i < num_ap_ids; i++) {
2732 		libloc.libp = NULL;
2733 		if ((rc = config_get_lib(ap_ids[i], &libloc,
2734 		    lstatp->errstr)) != CFGA_OK) {
2735 			break;
2736 		}
2737 		assert(libloc.libp != NULL);
2738 		libp = libloc.libp;
2739 
2740 		/*
2741 		 * do pre-filtering if requested
2742 		 */
2743 		if (class != NULL && strcmp(libloc.ap_class, class)) {
2744 			rele_lib(libp);
2745 			continue;
2746 		}
2747 
2748 		/*
2749 		 * Unlike list type routines, while stat'ing specific
2750 		 * attachment points we pass errstring to the plugins
2751 		 * and halt if an error occurs in the plugin.
2752 		 */
2753 		rc = libp->vers_ops->stat_plugin(lstatp, &libloc,
2754 		    lstatp->errstr);
2755 		rele_lib(libp);
2756 		if (rc != CFGA_OK) {
2757 			break;
2758 		}
2759 	}
2760 
2761 	if (rc != CFGA_OK) {
2762 		lstat_free(lstatp);
2763 	}
2764 	return (rc);
2765 }
2766 
2767 /*ARGSUSED*/
2768 static cfga_err_t
2769 null_stat_plugin(list_stat_t *lstatp, lib_loc_t *libloc_p, char **errstring)
2770 {
2771 	return (CFGA_OK);
2772 }
2773 
2774 /*
2775  * Pass errstring as a separate argument. Some higher level routines need
2776  * it to be NULL.
2777  */
2778 static cfga_err_t
2779 stat_plugin_v1(list_stat_t *lstatp, lib_loc_t *libloc_p, char **errstring)
2780 {
2781 	stat_data_list_t *slp, *slp2 = NULL;
2782 	cfga_err_t rc;
2783 
2784 	/*
2785 	 * allocate stat data buffer and list element
2786 	 */
2787 	if ((slp = config_calloc_check(1, sizeof (stat_data_list_t),
2788 	    errstring)) == NULL) {
2789 		return (CFGA_LIB_ERROR);
2790 	}
2791 
2792 	/*
2793 	 * Do the stat
2794 	 */
2795 	errno = 0;
2796 	if ((rc = (*(libloc_p->libp->cfga_stat_p))(libloc_p->ap_physical,
2797 	    &slp->stat_data, lstatp->opts, errstring)) != CFGA_OK) {
2798 		S_FREE(slp);
2799 		return (rc);
2800 	}
2801 	slp->next = NULL;
2802 
2803 	/*
2804 	 * Set up the logical and physical id's.
2805 	 * For v1 interfaces, the generic library (libcfgadm) creates the
2806 	 * ap_ids. mklog() is assumed to have been called in
2807 	 * the caller of this routine.
2808 	 */
2809 	(void) snprintf(slp->stat_data.ap_log_id, CFGA_AP_LOG_ID_LEN, "%s",
2810 	    libloc_p->ap_logical);
2811 
2812 	(void) snprintf(slp->stat_data.ap_phys_id, CFGA_AP_PHYS_ID_LEN, "%s",
2813 	    libloc_p->ap_physical);
2814 
2815 	/*
2816 	 * link it in
2817 	 */
2818 	if ((slp2 = lstatp->sdl) == NULL) {
2819 		lstatp->sdl = slp;
2820 	} else {
2821 		while (slp2->next != NULL)
2822 			slp2 = slp2->next;
2823 		slp2->next = slp;
2824 	}
2825 
2826 	/* keep count */
2827 	(*lstatp->countp)++;
2828 
2829 	return (CFGA_OK);
2830 }
2831 
2832 static cfga_err_t
2833 stat_plugin_v2(list_stat_t *lstatp, lib_loc_t *libloc_p, char **errstring)
2834 {
2835 	int i;
2836 	array_list_t *alp, *alp2 = NULL;
2837 	cfga_err_t rc;
2838 	char *class;
2839 
2840 	/*
2841 	 * allocate array list
2842 	 */
2843 	if ((alp = config_calloc_check(1, sizeof (array_list_t),
2844 	    errstring)) == NULL) {
2845 		return (CFGA_LIB_ERROR);
2846 	}
2847 
2848 	alp->array = NULL;
2849 	alp->nelem = 0;
2850 
2851 	/*
2852 	 * The listopts argument is currently unused. Use NULL
2853 	 */
2854 	errno = 0;
2855 	if ((rc = (*(libloc_p->libp->cfga_list_ext_p))(
2856 	    libloc_p->ap_physical, &alp->array, &alp->nelem, lstatp->opts, NULL,
2857 	    errstring, lstatp->flags)) != CFGA_OK || alp->nelem <= 0) {
2858 		S_FREE(alp);
2859 		return (rc);
2860 	}
2861 	alp->next = NULL;
2862 
2863 	/*
2864 	 * Set up the logical and physical id's if necessary.
2865 	 * For v2 interfaces, the generic library (libcfgadm) creates the
2866 	 * ap_ids only if there are no dynamic attachment points and the
2867 	 * plug-in does not create the name itself.  mklog() is
2868 	 * assumed to have been called in the caller of this routine.
2869 	 */
2870 	if (alp->nelem == 1) {
2871 		char cphys, clog;
2872 
2873 		clog = (alp->array[0]).ap_log_id[0];
2874 		cphys = (alp->array[0]).ap_phys_id[0];
2875 
2876 		if (clog == '\0') {
2877 			(void) snprintf((alp->array[0]).ap_log_id,
2878 			    sizeof ((alp->array[0]).ap_log_id), "%s",
2879 			    libloc_p->ap_logical);
2880 		}
2881 
2882 		if (cphys == '\0') {
2883 			(void) snprintf((alp->array[0]).ap_phys_id,
2884 			    sizeof ((alp->array[0]).ap_phys_id), "%s",
2885 			    libloc_p->ap_physical);
2886 		}
2887 	}
2888 
2889 	if (libloc_p->ap_class[0] == '\0') {
2890 		class = CFGA_NO_CLASS;
2891 	} else {
2892 		class = libloc_p->ap_class;
2893 	}
2894 
2895 	/* Fill in the class information for all list elements */
2896 	for (i = 0; i < alp->nelem; i++) {
2897 		(void) snprintf((alp->array[i]).ap_class,
2898 		    sizeof ((alp->array[i]).ap_class), "%s", class);
2899 	}
2900 
2901 	/*
2902 	 * link it in
2903 	 */
2904 	if ((alp2 = lstatp->al) == NULL) {
2905 		lstatp->al = alp;
2906 	} else {
2907 		while (alp2->next != NULL)
2908 			alp2 = alp2->next;
2909 		alp2->next = alp;
2910 	}
2911 
2912 	/* keep count */
2913 	(*lstatp->countp) += alp->nelem;
2914 
2915 	return (CFGA_OK);
2916 }
2917 
2918 /*
2919  * Check if a plugin version is within requested limits.
2920  */
2921 static int
2922 compat_plugin(vers_req_t *reqp, int plugin_vers)
2923 {
2924 
2925 	if (!VALID_HSL_VERS(reqp->v_min) || !VALID_HSL_VERS(reqp->v_max) ||
2926 	    !VALID_HSL_VERS(plugin_vers)) {
2927 		return (0);
2928 	}
2929 
2930 	if (plugin_vers < reqp->v_min || plugin_vers > reqp->v_max) {
2931 		return (0);
2932 	}
2933 
2934 
2935 	return (1);
2936 }
2937 
2938 /*
2939  * find_arg_type - determine if an argument is an ap_id or an ap_type.
2940  * Adapted from cfgadm.c
2941  */
2942 static cfga_ap_types_t
2943 find_arg_type(const char *ap_id)
2944 {
2945 	struct stat sbuf;
2946 	cfga_ap_types_t type = UNKNOWN_AP;
2947 	char *mkr = NULL;
2948 	size_t len;
2949 	int size_ap = 0, size_mkr = 0, digit = 0, i = 0;
2950 	char *cp, path[MAXPATHLEN], ap_base[MAXPATHLEN];
2951 
2952 
2953 	/*
2954 	 * sanity checks
2955 	 */
2956 	if (ap_id == NULL || *ap_id == '\0') {
2957 
2958 		return (UNKNOWN_AP);
2959 	}
2960 
2961 	/*
2962 	 * Extract the base component
2963 	 */
2964 	if ((cp = GET_DYN(ap_id)) != NULL) {
2965 		len = cp - ap_id;
2966 	} else {
2967 		len = strlen(ap_id);
2968 	}
2969 
2970 	if (len >= sizeof (ap_base)) {
2971 		return (UNKNOWN_AP);
2972 	}
2973 
2974 	/* Copy only the first "len" chars */
2975 	(void) strncpy(ap_base, ap_id, len);
2976 	ap_base[len] = '\0';
2977 
2978 	/*
2979 	 * If it starts with a slash and is stat-able its a physical.
2980 	 */
2981 	if (*ap_base == '/' && stat(ap_base, &sbuf) == 0) {
2982 		return (PHYSICAL_AP);
2983 	}
2984 
2985 	/*
2986 	 * Is this a symlink in CFGA_DEV_DIR ?
2987 	 */
2988 	(void) snprintf(path, sizeof (path), "%s%s",
2989 	    CFGA_DEV_DIR SLASH, ap_base);
2990 
2991 	if (lstat(path, &sbuf) == 0 && S_ISLNK(sbuf.st_mode) &&
2992 	    stat(path, &sbuf) == 0) {
2993 		return (LOGICAL_LINK_AP);
2994 	}
2995 
2996 	/*
2997 	 * Check for ":" which is always present in an ap_id
2998 	 * but not in an ap_type.
2999 	 * we need to check that the characters right before the : are digits
3000 	 * since an ap_id is of the form <name><instance>:<specific ap name>
3001 	 */
3002 	if ((mkr = strchr(ap_base, ':')) == NULL)  {
3003 		type = AP_TYPE;
3004 	} else {
3005 		size_ap = strlen(ap_base);
3006 		size_mkr = strlen(mkr);
3007 		mkr = ap_base;
3008 
3009 		digit = 0;
3010 		for (i = size_ap - size_mkr - 1;  i > 0; i--) {
3011 			if ((int)isdigit(mkr[i])) {
3012 				digit++;
3013 				break;
3014 			}
3015 		}
3016 		if (digit == 0) {
3017 			type = AP_TYPE;
3018 		} else {
3019 			type = LOGICAL_DRV_AP;
3020 		}
3021 	}
3022 
3023 	return (type);
3024 }
3025 
3026 /*ARGSUSED*/
3027 static cfga_err_t
3028 null_get_cond(lib_loc_t *liblocp, cfga_cond_t *condp, char **errstring)
3029 {
3030 	return (CFGA_OK);
3031 }
3032 
3033 static cfga_err_t
3034 get_cond_v1(lib_loc_t *liblocp, cfga_cond_t *condp, char **errstring)
3035 {
3036 	plugin_lib_t *libp;
3037 	cfga_stat_data_t sdbuf;
3038 	cfga_err_t rc;
3039 
3040 
3041 	libp = liblocp->libp;
3042 	if (libp->plugin_vers != CFGA_HSL_V1) {
3043 		return (CFGA_LIB_ERROR);
3044 	}
3045 
3046 	errno = 0;
3047 	if ((rc = (*liblocp->libp->cfga_stat_p)(
3048 	    liblocp->ap_physical, &sdbuf, NULL, errstring))
3049 	    == CFGA_OK) {
3050 		*condp = sdbuf.ap_cond;
3051 	} else {
3052 		*condp = CFGA_COND_UNKNOWN;
3053 	}
3054 
3055 	return (rc);
3056 }
3057 
3058 static cfga_err_t
3059 get_cond_v2(lib_loc_t *liblocp, cfga_cond_t *condp, char **errstring)
3060 {
3061 	int nelem;
3062 	plugin_lib_t *libp;
3063 	cfga_list_data_t *ldbufp;
3064 	cfga_err_t rc;
3065 
3066 
3067 	libp = liblocp->libp;
3068 	if (libp->plugin_vers != CFGA_HSL_V2) {
3069 		return (CFGA_LIB_ERROR);
3070 	}
3071 
3072 	errno = 0;
3073 	nelem = 0;
3074 	ldbufp = NULL;
3075 	if ((rc = (*liblocp->libp->cfga_list_ext_p)(
3076 	    liblocp->ap_physical, &ldbufp, &nelem, NULL, NULL,
3077 	    errstring, 0)) == CFGA_OK) {
3078 		assert(nelem == 1 && ldbufp != NULL);
3079 
3080 		*condp = ldbufp->ap_cond;
3081 		S_FREE(ldbufp);
3082 	} else {
3083 		*condp = CFGA_COND_UNKNOWN;
3084 	}
3085 
3086 	return (rc);
3087 }
3088 
3089 /* mask represents the flags accepted */
3090 static cfga_err_t
3091 check_flags(cfga_flags_t flags, cfga_flags_t mask, char **errstring)
3092 {
3093 	if ((flags & ~mask) != 0) {
3094 		config_err(0, INVALID_ARGS, errstring);
3095 		return (CFGA_ERROR);
3096 	} else {
3097 		return (CFGA_OK);
3098 	}
3099 }
3100 
3101 static cfga_err_t
3102 check_apids(int num_ap_ids, char *const *ap_ids, char **errstring)
3103 {
3104 	if (num_ap_ids <= 0 || ap_ids == NULL) {
3105 		config_err(0, INVALID_ARGS, errstring);
3106 		return (CFGA_ERROR);
3107 	} else {
3108 		return (CFGA_OK);
3109 	}
3110 }
3111 
3112 /*
3113  * Returns the class or the empty string if attacment point has
3114  * no class.
3115  */
3116 static char *
3117 get_class(di_minor_t minor)
3118 {
3119 	char *cp, c;
3120 	size_t len;
3121 
3122 
3123 	if (minor == DI_MINOR_NIL) {
3124 		return (NULL);
3125 	}
3126 
3127 	cp = di_minor_nodetype(minor);
3128 	if (cp == NULL) {
3129 		return (NULL);
3130 	}
3131 
3132 	len = strlen(DDI_NT_ATTACHMENT_POINT);
3133 	if (strncmp(cp, DDI_NT_ATTACHMENT_POINT, len)) {
3134 		return (NULL);
3135 	}
3136 
3137 	cp += len;
3138 
3139 	c = *cp;
3140 	if (c != '\0' && c != ':') {
3141 		return (NULL);
3142 	}
3143 
3144 	if (c == ':') {
3145 		cp++;
3146 	}
3147 
3148 	return (cp);
3149 
3150 }
3151 
3152 /*
3153  * Transform stat data to list data
3154  */
3155 static void
3156 stat_to_list(cfga_list_data_t *lp, cfga_stat_data_t *statp)
3157 {
3158 
3159 	(void) snprintf(lp->ap_log_id, sizeof (lp->ap_log_id), "%s",
3160 	    statp->ap_log_id);
3161 
3162 	(void) snprintf(lp->ap_phys_id, sizeof (lp->ap_phys_id), "%s",
3163 	    statp->ap_phys_id);
3164 
3165 	(void) snprintf(lp->ap_class, sizeof (lp->ap_class), "%s",
3166 	    CFGA_NO_CLASS);
3167 
3168 	lp->ap_r_state = statp->ap_r_state;
3169 	lp->ap_o_state = statp->ap_o_state;
3170 	lp->ap_cond = statp->ap_cond;
3171 	lp->ap_busy = statp->ap_busy;
3172 	lp->ap_status_time = statp->ap_status_time;
3173 
3174 	(void) snprintf(lp->ap_info, sizeof (lp->ap_info), "%s",
3175 	    statp->ap_info);
3176 	(void) snprintf(lp->ap_type, sizeof (lp->ap_type), "%s",
3177 	    statp->ap_type);
3178 }
3179 
3180 static void
3181 lstat_free(list_stat_t *lstatp)
3182 {
3183 	stat_data_list_t *slp, *slp2;
3184 	array_list_t *ap, *ap2;
3185 
3186 	slp = lstatp->sdl;
3187 	while (slp != NULL) {
3188 		slp2 = slp->next;
3189 		S_FREE(slp);
3190 		slp = slp2;
3191 	}
3192 
3193 	lstatp->sdl = NULL;
3194 
3195 	ap = lstatp->al;
3196 	while (ap != NULL) {
3197 		ap2 = ap->next;
3198 		S_FREE(ap->array);
3199 		S_FREE(ap);
3200 		ap = ap2;
3201 	}
3202 
3203 	lstatp->al = NULL;
3204 }
3205 
3206 static cfga_err_t
3207 split_apid(char *ap_id, char **dyncompp, char **errstring)
3208 {
3209 	char *cp;
3210 
3211 	*dyncompp = NULL;
3212 
3213 	if (ap_id == NULL) {
3214 		return (CFGA_ERROR);
3215 	}
3216 
3217 	if ((cp = strstr(ap_id, CFGA_DYN_SEP)) == NULL) {
3218 		return (CFGA_OK);
3219 	}
3220 
3221 	*cp = '\0';
3222 	cp += strlen(CFGA_DYN_SEP);
3223 	if ((*dyncompp = config_calloc_check(1, strlen(cp) + 1,
3224 	    errstring)) == NULL) {
3225 		return (CFGA_LIB_ERROR);
3226 	}
3227 	(void) strcpy(*dyncompp, cp);
3228 
3229 	return (CFGA_OK);
3230 }
3231 
3232 static void
3233 append_dyn(char *buf, const char *dyncomp, size_t blen)
3234 {
3235 	if (dyncomp != NULL) {
3236 		char *cp = buf + strlen(buf);
3237 		size_t len = blen - strlen(buf);
3238 
3239 		(void) snprintf(cp, len, "%s%s", CFGA_DYN_SEP,
3240 		    dyncomp);
3241 	}
3242 }
3243 
3244 /*
3245  * Default implementation of cfga_ap_id_cmp. Works for most cases
3246  * except for long hex number sequences like world-wide-name.
3247  *
3248  * This function compares the ap's in a generic way.  It does so by
3249  * determining the place of difference between the 2 aps.  If the first
3250  * difference is a digit, it attempts to obtain the numbers and compare them
3251  * Otherwise it just compares the aps as strings
3252  */
3253 static int
3254 default_ap_id_cmp(const char *ap_id1, const char *ap_id2)
3255 {
3256 	int i = 0;
3257 
3258 	/*
3259 	 * Search for first different char
3260 	 */
3261 	while (ap_id1[i] == ap_id2[i] && ap_id1[i] != '\0')
3262 		i++;
3263 
3264 	/*
3265 	 * If one of the char is a digit, back up to where the
3266 	 * number started, compare the number.
3267 	 */
3268 	if (isdigit(ap_id1[i]) || isdigit(ap_id2[i])) {
3269 		while ((i > 0) && isdigit(ap_id1[i - 1]))
3270 			i--;
3271 
3272 		if (isdigit(ap_id1[i]) && isdigit(ap_id2[i]))
3273 			return (atoi(ap_id1 + i) - atoi(ap_id2 + i));
3274 	}
3275 
3276 	/* One of them isn't a number, compare the char */
3277 	return (ap_id1[i] - ap_id2[i]);
3278 }
3279 
3280 static void
3281 hold_lib(plugin_lib_t *libp)
3282 {
3283 	assert(libp->refcnt >= 0);
3284 	(void) mutex_lock(&libp->lock);
3285 	libp->refcnt++;
3286 	(void) mutex_unlock(&libp->lock);
3287 }
3288 
3289 static void
3290 rele_lib(plugin_lib_t *libp)
3291 {
3292 	assert(libp->refcnt > 0);
3293 	(void) mutex_lock(&libp->lock);
3294 	libp->refcnt--;
3295 	(void) mutex_unlock(&libp->lock);
3296 }
3297