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