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