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