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