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