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 */
67typedef enum {
68	UNKNOWN_AP,
69	LOGICAL_LINK_AP,
70	LOGICAL_DRV_AP,
71	PHYSICAL_AP,
72	AP_TYPE
73} cfga_ap_types_t;
74
75static char *listopt_array[] = {
76
77#define	LISTOPT_CLASS	0
78	"class",
79	NULL
80};
81
82typedef 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 */
94struct cfga_vers_ops;
95
96/*
97 * Structure that contains plugin library information.
98 */
99typedef 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
118static plugin_lib_t plugin_list;
119
120typedef 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
128static lib_cache_t *lib_cache;
129static mutex_t lib_cache_lock;
130
131/*
132 * Library locator data struct - used to pass down through the device
133 * tree walking code.
134 */
135typedef 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 */
150typedef 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 */
160typedef 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 */
170typedef 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 */
184struct 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 */
196static mutex_t plugin_list_lock;
197
198/*
199 * Forward declarations
200 */
201
202static const char *__config_strerror(cfga_err_t);
203static void *config_calloc_check(size_t, size_t, char **);
204static cfga_err_t resolve_lib_ref(plugin_lib_t *, lib_loc_t *);
205static cfga_err_t config_get_lib(const char *, lib_loc_t *, char **);
206static int check_ap(di_node_t, di_minor_t, void *);
207static int check_ap_hp(di_node_t, di_hp_t, void *);
208static int check_ap_impl(di_node_t, di_minor_t, di_hp_t, void *);
209static int check_ap_phys(di_node_t, di_minor_t, void *);
210static int check_ap_phys_hp(di_node_t, di_hp_t, void *);
211static int check_ap_phys_impl(di_node_t, di_minor_t, di_hp_t, void *);
212
213static 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
218static plugin_lib_t *lib_in_list(char *);
219static cfga_err_t find_lib(di_node_t, di_minor_t, lib_loc_t *);
220static cfga_err_t find_lib_hp(di_node_t, di_hp_t, lib_loc_t *);
221static cfga_err_t find_lib_impl(char *, lib_loc_t *);
222static cfga_err_t load_lib(di_node_t, di_minor_t, lib_loc_t *);
223static cfga_err_t load_lib_hp(di_node_t, di_hp_t, lib_loc_t *);
224static cfga_err_t load_lib_impl(di_node_t, di_minor_t, di_hp_t, lib_loc_t *);
225extern void bcopy(const void *, void *, size_t);
226static void config_err(int, int, char **);
227static void hold_lib(plugin_lib_t *);
228static void rele_lib(plugin_lib_t *);
229
230static cfga_err_t parse_listopt(char *listopts, char **classpp,
231    char **errstring);
232
233static cfga_err_t list_common(list_stat_t *lstatp, const char *class);
234static int do_list_common(di_node_t node, di_minor_t minor, void *arg);
235static int do_list_common_hp(di_node_t node, di_hp_t hp, void *arg);
236static int do_list_common_impl(di_node_t node, di_minor_t minor,
237    di_hp_t hp, void *arg);
238static cfga_err_t stat_common(int num_ap_ids, char *const *ap_ids,
239    const char *class, list_stat_t *lstatp);
240
241static cfga_err_t null_resolve(plugin_lib_t *libp);
242static cfga_err_t resolve_v1(plugin_lib_t *libp);
243static cfga_err_t resolve_v2(plugin_lib_t *libp);
244
245static cfga_err_t mklog_common(di_node_t node, di_minor_t minor,
246    lib_loc_t *liblocp, size_t len);
247
248static cfga_err_t null_mklog(di_node_t node, di_minor_t minor,
249    plugin_lib_t *libp, lib_loc_t *liblocp);
250static cfga_err_t mklog_v1(di_node_t node, di_minor_t minor,
251    plugin_lib_t *libp, lib_loc_t *liblocp);
252static cfga_err_t mklog_v2(di_node_t node, di_minor_t minor,
253    plugin_lib_t *libp, lib_loc_t *liblocp);
254
255static cfga_err_t null_stat_plugin(list_stat_t *lstatp, lib_loc_t *libloc_p,
256    char **errstring);
257static cfga_err_t stat_plugin_v2(list_stat_t *lstat, lib_loc_t *libloc_p,
258    char **errstring);
259static cfga_err_t stat_plugin_v1(list_stat_t *lstat, lib_loc_t *libloc_p,
260    char **errstring);
261
262static cfga_err_t null_get_cond(lib_loc_t *liblocp, cfga_cond_t *condp,
263    char **errstring);
264static cfga_err_t get_cond_v1(lib_loc_t *liblocp, cfga_cond_t *condp,
265    char **errstring);
266static cfga_err_t get_cond_v2(lib_loc_t *liblocp, cfga_cond_t *condp,
267    char **errstring);
268
269static cfga_err_t realloc_data(cfga_stat_data_t **ap_id_list,
270    int *nlistp, list_stat_t *lstatp);
271static cfga_err_t realloc_data_ext(cfga_list_data_t **ap_id_list,
272    int *nlistp, list_stat_t *lstatp);
273
274static void stat_to_list(cfga_list_data_t *lp, cfga_stat_data_t *statp);
275static void lstat_free(list_stat_t *lstatp);
276static cfga_ap_types_t find_arg_type(const char *ap_id);
277static int compat_plugin(vers_req_t *reqp, int plugin_vers);
278
279static cfga_err_t check_flags(cfga_flags_t flags, cfga_flags_t mask,
280    char **errstring);
281static cfga_err_t check_apids(int num_ap_ids, char *const *ap_ids,
282    char **errstring);
283
284static char *get_class(di_minor_t minor);
285static cfga_err_t split_apid(char *ap_id, char **dyncompp, char **errstring);
286static void append_dyn(char *buf, const char *dyncomp, size_t blen);
287static int default_ap_id_cmp(const char *ap_id1, const char *ap_id2);
288static 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
328static char *
329err_strings[] = {
330	NULL,
331	"Device library initialize failed",
332	"Memory allocation failed",
333	"Invalid argument(s)"
334};
335
336static const char err_sep[] = ": ";
337
338
339/*
340 * Table of version dependant routines
341 */
342static 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
360cfga_err_t
361config_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
462cfga_err_t
463config_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
516cfga_err_t
517config_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
561cfga_err_t
562config_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 */
609cfga_err_t
610config_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 */
661cfga_err_t
662config_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 */
771void
772config_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 */
800int
801config_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
866const char *
867config_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 */
882cfga_err_t
883config_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
928static 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 */
990static cfga_err_t
991parse_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*/
1034out:
1035	S_FREE(bufp);
1036	if (rc != CFGA_OK) {
1037		S_FREE(*classpp);
1038	}
1039	return (rc);
1040}
1041
1042/*ARGSUSED*/
1043static cfga_err_t
1044null_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
1053static cfga_err_t
1054mklog_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 */
1075static int
1076get_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
1085static cfga_err_t
1086mklog_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 */
1121static cfga_err_t
1122mklog_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*/
1149static cfga_err_t
1150mklog_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 */
1177static cfga_err_t
1178resolve_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*/
1251static cfga_err_t
1252null_resolve(plugin_lib_t *libp)
1253{
1254	return (CFGA_OK);
1255}
1256
1257static cfga_err_t
1258resolve_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
1285static cfga_err_t
1286resolve_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 */
1310static void *
1311config_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 */
1331static cfga_err_t
1332config_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*/
1458out:
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 */
1471static cfga_err_t
1472load_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 */
1481static cfga_err_t
1482load_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 */
1495static cfga_err_t
1496load_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 */
1622static cfga_err_t
1623find_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
1667found:
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*/
1680static cfga_err_t
1681find_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;
1726fail:
1727	return (CFGA_NO_LIB);
1728
1729found:
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 */
1741static cfga_err_t
1742find_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
1831found:
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
1841static cfga_err_t
1842lookup_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
1867static void
1868update_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
1895static void
1896destroy_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 */
1916static cfga_err_t
1917find_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) {
2000find_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 */
2046static int
2047check_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 */
2058static int
2059check_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 */
2075static int
2076check_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 */
2194static int
2195check_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 */
2206static int
2207check_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 */
2222static int
2223check_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 */
2290static plugin_lib_t *
2291lib_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 */
2309static cfga_err_t
2310realloc_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
2376out:
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 */
2396static cfga_err_t
2397realloc_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
2442out:
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 */
2466static cfga_err_t
2467list_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
2522static void
2523config_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 */
2572static int
2573do_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 */
2609static int
2610do_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 */
2625static int
2626do_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 */
2715static cfga_err_t
2716stat_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*/
2768static cfga_err_t
2769null_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 */
2778static cfga_err_t
2779stat_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
2832static cfga_err_t
2833stat_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 */
2921static int
2922compat_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 */
2942static cfga_ap_types_t
2943find_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*/
3027static cfga_err_t
3028null_get_cond(lib_loc_t *liblocp, cfga_cond_t *condp, char **errstring)
3029{
3030	return (CFGA_OK);
3031}
3032
3033static cfga_err_t
3034get_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
3058static cfga_err_t
3059get_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 */
3090static cfga_err_t
3091check_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
3101static cfga_err_t
3102check_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 */
3116static char *
3117get_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 */
3155static void
3156stat_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
3180static void
3181lstat_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
3206static cfga_err_t
3207split_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
3232static void
3233append_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 */
3253static int
3254default_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
3280static void
3281hold_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
3289static void
3290rele_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