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