2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/* Portions Copyright 2005 Cyril Plisko */
2N/A
2N/A#include <errno.h>
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <locale.h>
2N/A#include <langinfo.h>
2N/A#include <time.h>
2N/A
2N/A#if !defined(DEBUG)
2N/A#define NDEBUG 1
2N/A#else
2N/A#undef NDEBUG
2N/A#endif
2N/A
2N/A#include <assert.h>
2N/A#include <sys/types.h>
2N/A#include <sys/stat.h>
2N/A#include <sys/param.h>
2N/A#include <dlfcn.h>
2N/A#include <synch.h>
2N/A#include <sys/systeminfo.h>
2N/A#include <sys/sunddi.h>
2N/A#include <libdevinfo.h>
2N/A#include <unistd.h>
2N/A#include <stdarg.h>
2N/A#include <limits.h>
2N/A#include <ftw.h>
2N/A#include <ctype.h>
2N/A
2N/A#define CFGA_PLUGIN_LIB
2N/A#include <config_admin.h>
2N/A
2N/A/* Limit size of sysinfo return */
2N/A#define SYSINFO_LENGTH 256
2N/A
2N/A/*
2N/A * Attachment point specifier types.
2N/A */
2N/Atypedef enum {
2N/A UNKNOWN_AP,
2N/A LOGICAL_LINK_AP,
2N/A LOGICAL_DRV_AP,
2N/A PHYSICAL_AP,
2N/A AP_TYPE
2N/A} cfga_ap_types_t;
2N/A
2N/Astatic char *listopt_array[] = {
2N/A
2N/A#define LISTOPT_CLASS 0
2N/A "class",
2N/A NULL
2N/A};
2N/A
2N/Atypedef struct {
2N/A int v_min; /* Min acceptable version */
2N/A int v_max; /* Max acceptable version */
2N/A} vers_req_t;
2N/A
2N/A#define INVALID_VERSION -1
2N/A#define VALID_HSL_VERS(v) (((v) >= CFGA_HSL_V1) && \
2N/A ((v) <= CFGA_HSL_VERS))
2N/A
2N/A/*
2N/A * Incomplete definition
2N/A */
2N/Astruct cfga_vers_ops;
2N/A
2N/A/*
2N/A * Structure that contains plugin library information.
2N/A */
2N/Atypedef struct plugin_lib {
2N/A struct plugin_lib *next; /* pointer to next */
2N/A mutex_t lock; /* protects refcnt */
2N/A int refcnt; /* reference count */
2N/A void *handle; /* handle from dlopen */
2N/A cfga_err_t (*cfga_change_state_p)();
2N/A cfga_err_t (*cfga_private_func_p)();
2N/A cfga_err_t (*cfga_test_p)();
2N/A cfga_err_t (*cfga_stat_p)();
2N/A cfga_err_t (*cfga_list_p)();
2N/A cfga_err_t (*cfga_help_p)();
2N/A int (*cfga_ap_id_cmp_p)();
2N/A cfga_err_t (*cfga_list_ext_p)(); /* For V2 plug-ins only */
2N/A
2N/A int plugin_vers; /* actual plugin version */
2N/A struct cfga_vers_ops *vers_ops; /* version dependant routines */
2N/A char libpath[MAXPATHLEN]; /* full pathname to lib */
2N/A} plugin_lib_t;
2N/A
2N/Astatic plugin_lib_t plugin_list;
2N/A
2N/Atypedef struct lib_cache {
2N/A struct lib_cache *lc_next;
2N/A plugin_lib_t *lc_libp;
2N/A char *lc_ap_id;
2N/A char *lc_ap_physical; /* physical ap_id */
2N/A char *lc_ap_logical; /* logical ap_id */
2N/A} lib_cache_t;
2N/A
2N/Astatic lib_cache_t *lib_cache;
2N/Astatic mutex_t lib_cache_lock;
2N/A
2N/A/*
2N/A * Library locator data struct - used to pass down through the device
2N/A * tree walking code.
2N/A */
2N/Atypedef struct lib_locator {
2N/A char ap_base[MAXPATHLEN];
2N/A char ap_logical[CFGA_LOG_EXT_LEN];
2N/A char ap_physical[CFGA_PHYS_EXT_LEN];
2N/A char ap_class[CFGA_CLASS_LEN];
2N/A char pathname[MAXPATHLEN];
2N/A plugin_lib_t *libp;
2N/A cfga_err_t status;
2N/A vers_req_t vers_req; /* plug-in version required */
2N/A} lib_loc_t;
2N/A
2N/A/*
2N/A * linked list of cfga_stat_data structs - used for
2N/A * config_list
2N/A */
2N/Atypedef struct stat_data_list {
2N/A struct stat_data_list *next;
2N/A cfga_stat_data_t stat_data;
2N/A} stat_data_list_t;
2N/A
2N/A/*
2N/A * linked list of arrays. Each array represents a bunch
2N/A * of list_data_t structures returned by a single call
2N/A * to a plugin's cfga_list_ext() routine.
2N/A */
2N/Atypedef struct array_list {
2N/A struct array_list *next;
2N/A cfga_list_data_t *array;
2N/A int nelem;
2N/A} array_list_t;
2N/A
2N/A/*
2N/A * encapsulate config_list args to get them through the tree
2N/A * walking code
2N/A */
2N/Atypedef struct list_stat {
2N/A const char *opts; /* Hardware specific options */
2N/A char **errstr;
2N/A cfga_flags_t flags;
2N/A int *countp; /* Total number of list and stat structures */
2N/A stat_data_list_t *sdl; /* Linked list of stat structures */
2N/A array_list_t *al; /* Linked list of arrays of list structures */
2N/A vers_req_t use_vers; /* plugin versions to be stat'ed */
2N/A char *shp_errstr; /* only for shp plugin */
2N/A} list_stat_t;
2N/A
2N/A/*
2N/A * Internal operations for libcfgadm which are version dependant
2N/A */
2N/Astruct cfga_vers_ops {
2N/A cfga_err_t (*resolve_lib)(plugin_lib_t *libp);
2N/A cfga_err_t (*stat_plugin)(list_stat_t *, lib_loc_t *, char **errstring);
2N/A cfga_err_t (*mklog)(di_node_t, di_minor_t, plugin_lib_t *,
2N/A lib_loc_t *liblocp);
2N/A cfga_err_t (*get_cond)(lib_loc_t *, cfga_cond_t *, char **);
2N/A};
2N/A
2N/A
2N/A/*
2N/A * Lock to protect list of libraries
2N/A */
2N/Astatic mutex_t plugin_list_lock;
2N/A
2N/A/*
2N/A * Forward declarations
2N/A */
2N/A
2N/Astatic const char *__config_strerror(cfga_err_t);
2N/Astatic void *config_calloc_check(size_t, size_t, char **);
2N/Astatic cfga_err_t resolve_lib_ref(plugin_lib_t *, lib_loc_t *);
2N/Astatic cfga_err_t config_get_lib(const char *, lib_loc_t *, char **);
2N/Astatic int check_ap(di_node_t, di_minor_t, void *);
2N/Astatic int check_ap_hp(di_node_t, di_hp_t, void *);
2N/Astatic int check_ap_impl(di_node_t, di_minor_t, di_hp_t, void *);
2N/Astatic int check_ap_phys(di_node_t, di_minor_t, void *);
2N/Astatic int check_ap_phys_hp(di_node_t, di_hp_t, void *);
2N/Astatic int check_ap_phys_impl(di_node_t, di_minor_t, di_hp_t, void *);
2N/A
2N/Astatic cfga_err_t find_ap_common(lib_loc_t *libloc_p, const char *rootpath,
2N/A int (*fcn)(di_node_t node, di_minor_t minor, void *arg),
2N/A int (*fcn_hp)(di_node_t node, di_hp_t hp, void *arg),
2N/A char **errstring);
2N/A
2N/Astatic plugin_lib_t *lib_in_list(char *);
2N/Astatic cfga_err_t find_lib(di_node_t, di_minor_t, lib_loc_t *);
2N/Astatic cfga_err_t find_lib_hp(di_node_t, di_hp_t, lib_loc_t *);
2N/Astatic cfga_err_t find_lib_impl(char *, lib_loc_t *);
2N/Astatic cfga_err_t load_lib(di_node_t, di_minor_t, lib_loc_t *);
2N/Astatic cfga_err_t load_lib_hp(di_node_t, di_hp_t, lib_loc_t *);
2N/Astatic cfga_err_t load_lib_impl(di_node_t, di_minor_t, di_hp_t, lib_loc_t *);
2N/Aextern void bcopy(const void *, void *, size_t);
2N/Astatic void config_err(int, int, char **);
2N/Astatic void hold_lib(plugin_lib_t *);
2N/Astatic void rele_lib(plugin_lib_t *);
2N/A
2N/Astatic cfga_err_t parse_listopt(char *listopts, char **classpp,
2N/A char **errstring);
2N/A
2N/Astatic cfga_err_t list_common(list_stat_t *lstatp, const char *class);
2N/Astatic int do_list_common(di_node_t node, di_minor_t minor, void *arg);
2N/Astatic int do_list_common_hp(di_node_t node, di_hp_t hp, void *arg);
2N/Astatic int do_list_common_impl(di_node_t node, di_minor_t minor,
2N/A di_hp_t hp, void *arg);
2N/Astatic cfga_err_t stat_common(int num_ap_ids, char *const *ap_ids,
2N/A const char *class, list_stat_t *lstatp);
2N/A
2N/Astatic cfga_err_t null_resolve(plugin_lib_t *libp);
2N/Astatic cfga_err_t resolve_v1(plugin_lib_t *libp);
2N/Astatic cfga_err_t resolve_v2(plugin_lib_t *libp);
2N/A
2N/Astatic cfga_err_t mklog_common(di_node_t node, di_minor_t minor,
2N/A lib_loc_t *liblocp, size_t len);
2N/A
2N/Astatic cfga_err_t null_mklog(di_node_t node, di_minor_t minor,
2N/A plugin_lib_t *libp, lib_loc_t *liblocp);
2N/Astatic cfga_err_t mklog_v1(di_node_t node, di_minor_t minor,
2N/A plugin_lib_t *libp, lib_loc_t *liblocp);
2N/Astatic cfga_err_t mklog_v2(di_node_t node, di_minor_t minor,
2N/A plugin_lib_t *libp, lib_loc_t *liblocp);
2N/A
2N/Astatic cfga_err_t null_stat_plugin(list_stat_t *lstatp, lib_loc_t *libloc_p,
2N/A char **errstring);
2N/Astatic cfga_err_t stat_plugin_v2(list_stat_t *lstat, lib_loc_t *libloc_p,
2N/A char **errstring);
2N/Astatic cfga_err_t stat_plugin_v1(list_stat_t *lstat, lib_loc_t *libloc_p,
2N/A char **errstring);
2N/A
2N/Astatic cfga_err_t null_get_cond(lib_loc_t *liblocp, cfga_cond_t *condp,
2N/A char **errstring);
2N/Astatic cfga_err_t get_cond_v1(lib_loc_t *liblocp, cfga_cond_t *condp,
2N/A char **errstring);
2N/Astatic cfga_err_t get_cond_v2(lib_loc_t *liblocp, cfga_cond_t *condp,
2N/A char **errstring);
2N/A
2N/Astatic cfga_err_t realloc_data(cfga_stat_data_t **ap_id_list,
2N/A int *nlistp, list_stat_t *lstatp);
2N/Astatic cfga_err_t realloc_data_ext(cfga_list_data_t **ap_id_list,
2N/A int *nlistp, list_stat_t *lstatp);
2N/A
2N/Astatic void stat_to_list(cfga_list_data_t *lp, cfga_stat_data_t *statp);
2N/Astatic void lstat_free(list_stat_t *lstatp);
2N/Astatic cfga_ap_types_t find_arg_type(const char *ap_id);
2N/Astatic int compat_plugin(vers_req_t *reqp, int plugin_vers);
2N/A
2N/Astatic cfga_err_t check_flags(cfga_flags_t flags, cfga_flags_t mask,
2N/A char **errstring);
2N/Astatic cfga_err_t check_apids(int num_ap_ids, char *const *ap_ids,
2N/A char **errstring);
2N/A
2N/Astatic char *get_class(di_minor_t minor);
2N/Astatic cfga_err_t split_apid(char *ap_id, char **dyncompp, char **errstring);
2N/Astatic void append_dyn(char *buf, const char *dyncomp, size_t blen);
2N/Astatic int default_ap_id_cmp(const char *ap_id1, const char *ap_id2);
2N/Astatic void destroy_cache();
2N/A
2N/A/*
2N/A * Plugin library search path helpers
2N/A */
2N/A#define LIB_PATH_BASE1 "/usr/platform/"
2N/A#define LIB_PATH_BASE2 "/usr"
2N/A#if defined(__sparcv9)
2N/A#define LIB_PATH_MIDDLE "/lib/cfgadm/sparcv9/"
2N/A#elif defined(__amd64)
2N/A#define LIB_PATH_MIDDLE "/lib/cfgadm/amd64/"
2N/A#else
2N/A#define LIB_PATH_MIDDLE "/lib/cfgadm/"
2N/A#endif
2N/A#define LIB_PATH_TAIL ".so.1"
2N/A
2N/A
2N/A#if !defined(TEXT_DOMAIN)
2N/A#define TEXT_DOMAIN "SYS_TEST"
2N/A#endif
2N/A
2N/A/*
2N/A * Defined constants
2N/A */
2N/A#define DEVICES_DIR "/devices"
2N/A#define DOT_DOT_DEVICES "../devices"
2N/A#define CFGA_DEV_DIR "/dev/cfg"
2N/A#define SLASH "/"
2N/A#define S_FREE(x) (((x) != NULL) ? (free(x), (x) = NULL) : (void *)0)
2N/A#define GET_DYN(a) (strstr((a), CFGA_DYN_SEP))
2N/A
2N/A#define CFGA_NO_CLASS "none"
2N/A
2N/A/*
2N/A * Error strings
2N/A */
2N/A#define DI_INIT_FAILED 1
2N/A#define ALLOC_FAILED 2
2N/A#define INVALID_ARGS 3
2N/A
2N/Astatic char *
2N/Aerr_strings[] = {
2N/A NULL,
2N/A "Device library initialize failed",
2N/A "Memory allocation failed",
2N/A "Invalid argument(s)"
2N/A};
2N/A
2N/Astatic const char err_sep[] = ": ";
2N/A
2N/A
2N/A/*
2N/A * Table of version dependant routines
2N/A */
2N/Astatic struct cfga_vers_ops cfga_vers_ops[CFGA_HSL_VERS + 1] = {
2N/A
2N/A{null_resolve, null_stat_plugin, null_mklog, null_get_cond },
2N/A{resolve_v1, stat_plugin_v1, mklog_v1, get_cond_v1 },
2N/A{resolve_v2, stat_plugin_v2, mklog_v2, get_cond_v2 }
2N/A
2N/A};
2N/A#define VERS_ARRAY_SZ (sizeof (cfga_vers_ops)/sizeof (cfga_vers_ops[0]))
2N/A
2N/A
2N/A/*
2N/A * Public interfaces for libcfgadm, as documented in config_admin.3x
2N/A */
2N/A
2N/A/*
2N/A * config_change_state
2N/A */
2N/A
2N/Acfga_err_t
2N/Aconfig_change_state(
2N/A cfga_cmd_t state_change_cmd,
2N/A int num_ap_ids,
2N/A char *const *ap_id,
2N/A const char *options,
2N/A struct cfga_confirm *confp,
2N/A struct cfga_msg *msgp,
2N/A char **errstring,
2N/A cfga_flags_t flags)
2N/A{
2N/A /*
2N/A * for each arg -
2N/A * load hs library,
2N/A * if force
2N/A * call cfga_state_change_func
2N/A * return status
2N/A * else
2N/A * call it's cfga_stat
2N/A * check condition
2N/A * call cfga_state_change_func
2N/A * return status
2N/A */
2N/A int i;
2N/A lib_loc_t libloc;
2N/A plugin_lib_t *libp;
2N/A cfga_cond_t cond;
2N/A
2N/A cfga_err_t retval = CFGA_OK;
2N/A
2N/A /* Sanity checks */
2N/A if (state_change_cmd == CFGA_CMD_NONE)
2N/A return (retval);
2N/A
2N/A if ((state_change_cmd < CFGA_CMD_NONE) ||
2N/A (state_change_cmd > CFGA_CMD_UNCONFIGURE))
2N/A return (CFGA_INVAL);
2N/A
2N/A if (errstring != NULL) {
2N/A *errstring = NULL;
2N/A }
2N/A
2N/A if (check_flags(flags, CFGA_FLAG_FORCE | CFGA_FLAG_VERBOSE, errstring)
2N/A != CFGA_OK) {
2N/A return (CFGA_ERROR);
2N/A }
2N/A
2N/A if (check_apids(num_ap_ids, ap_id, errstring) != CFGA_OK) {
2N/A return (CFGA_ERROR);
2N/A }
2N/A
2N/A /*
2N/A * operate on each ap_id
2N/A */
2N/A for (i = 0; (i < num_ap_ids) && (retval == CFGA_OK); i++) {
2N/A libloc.libp = NULL;
2N/A if ((retval = config_get_lib(ap_id[i], &libloc, errstring)) !=
2N/A CFGA_OK) {
2N/A break;
2N/A }
2N/A
2N/A libp = libloc.libp;
2N/A if ((flags & CFGA_FLAG_FORCE) ||
2N/A (state_change_cmd == CFGA_CMD_UNLOAD) ||
2N/A (state_change_cmd == CFGA_CMD_DISCONNECT) ||
2N/A (state_change_cmd == CFGA_CMD_UNCONFIGURE)) {
2N/A errno = 0;
2N/A retval = (*libp->cfga_change_state_p)
2N/A (state_change_cmd, libloc.ap_physical, options,
2N/A confp, msgp, errstring, flags);
2N/A } else {
2N/A /*
2N/A * Need to check condition before proceeding in
2N/A * the "configure direction"
2N/A */
2N/A if ((retval = libp->vers_ops->get_cond(&libloc, &cond,
2N/A errstring)) != CFGA_OK) {
2N/A break;
2N/A }
2N/A
2N/A if (cond == CFGA_COND_OK || cond == CFGA_COND_UNKNOWN) {
2N/A errno = 0;
2N/A retval =
2N/A (*libp->cfga_change_state_p)(
2N/A state_change_cmd,
2N/A libloc.ap_physical, options,
2N/A confp, msgp, errstring,
2N/A flags);
2N/A } else {
2N/A retval = CFGA_INSUFFICENT_CONDITION;
2N/A }
2N/A }
2N/A rele_lib(libp);
2N/A }
2N/A
2N/A return (retval);
2N/A}
2N/A
2N/A/*
2N/A * config_private_func
2N/A */
2N/A
2N/Acfga_err_t
2N/Aconfig_private_func(
2N/A const char *function,
2N/A int num_ap_ids,
2N/A char *const *ap_ids,
2N/A const char *options,
2N/A struct cfga_confirm *confp,
2N/A struct cfga_msg *msgp,
2N/A char **errstring,
2N/A cfga_flags_t flags)
2N/A{
2N/A int i;
2N/A lib_loc_t libloc;
2N/A cfga_err_t retval = CFGA_OK;
2N/A
2N/A
2N/A if (errstring != NULL) {
2N/A *errstring = NULL;
2N/A }
2N/A
2N/A if (check_flags(flags, CFGA_FLAG_FORCE | CFGA_FLAG_VERBOSE, errstring)
2N/A != CFGA_OK) {
2N/A return (CFGA_ERROR);
2N/A }
2N/A
2N/A if (check_apids(num_ap_ids, ap_ids, errstring) != CFGA_OK) {
2N/A return (CFGA_ERROR);
2N/A }
2N/A
2N/A /*
2N/A * operate on each ap_id
2N/A */
2N/A for (i = 0; (i < num_ap_ids) && (retval == CFGA_OK); i++) {
2N/A libloc.libp = NULL;
2N/A if ((retval = config_get_lib(ap_ids[i], &libloc, errstring)) !=
2N/A CFGA_OK) {
2N/A return (retval);
2N/A }
2N/A
2N/A errno = 0;
2N/A retval = (*libloc.libp->cfga_private_func_p)(function,
2N/A libloc.ap_physical, options, confp, msgp, errstring,
2N/A flags);
2N/A rele_lib(libloc.libp);
2N/A }
2N/A
2N/A return (retval);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * config_test
2N/A */
2N/A
2N/Acfga_err_t
2N/Aconfig_test(
2N/A int num_ap_ids,
2N/A char *const *ap_ids,
2N/A const char *options,
2N/A struct cfga_msg *msgp,
2N/A char **errstring,
2N/A cfga_flags_t flags)
2N/A{
2N/A int i;
2N/A lib_loc_t libloc;
2N/A cfga_err_t retval = CFGA_OK;
2N/A
2N/A if (errstring != NULL) {
2N/A *errstring = NULL;
2N/A }
2N/A
2N/A if (check_flags(flags, CFGA_FLAG_FORCE | CFGA_FLAG_VERBOSE, errstring)
2N/A != CFGA_OK) {
2N/A return (CFGA_ERROR);
2N/A }
2N/A
2N/A if (check_apids(num_ap_ids, ap_ids, errstring) != CFGA_OK) {
2N/A return (CFGA_ERROR);
2N/A }
2N/A
2N/A /*
2N/A * operate on each ap_id
2N/A */
2N/A for (i = 0; (i < num_ap_ids) && (retval == CFGA_OK); i++) {
2N/A libloc.libp = NULL;
2N/A if ((retval = config_get_lib(ap_ids[i], &libloc, errstring)) !=
2N/A CFGA_OK) {
2N/A return (retval);
2N/A }
2N/A
2N/A errno = 0;
2N/A retval = (*libloc.libp->cfga_test_p)(libloc.ap_physical,
2N/A options, msgp, errstring, flags);
2N/A rele_lib(libloc.libp);
2N/A }
2N/A
2N/A return (retval);
2N/A}
2N/A
2N/Acfga_err_t
2N/Aconfig_stat(
2N/A int num_ap_ids,
2N/A char *const *ap_ids,
2N/A struct cfga_stat_data *buf,
2N/A const char *options,
2N/A char **errstring)
2N/A{
2N/A int nstat, n, i;
2N/A list_stat_t lstat = {NULL};
2N/A cfga_err_t rc = CFGA_OK;
2N/A
2N/A if (check_apids(num_ap_ids, ap_ids, errstring) != CFGA_OK) {
2N/A return (CFGA_ERROR);
2N/A }
2N/A
2N/A /*
2N/A * V1 entry points don't support dynamic attachment points
2N/A */
2N/A for (i = 0; i < num_ap_ids; i++) {
2N/A if (GET_DYN(ap_ids[i]) != NULL) {
2N/A return (CFGA_APID_NOEXIST);
2N/A }
2N/A }
2N/A
2N/A
2N/A nstat = n = 0;
2N/A lstat.countp = &nstat;
2N/A lstat.opts = options;
2N/A lstat.errstr = errstring;
2N/A lstat.shp_errstr = NULL;
2N/A /*
2N/A * This is a V1 interface which can use only V1 plugins
2N/A */
2N/A lstat.use_vers.v_max = lstat.use_vers.v_min = CFGA_HSL_V1;
2N/A
2N/A rc = stat_common(num_ap_ids, ap_ids, NULL, &lstat);
2N/A if (rc == CFGA_OK) {
2N/A assert(*lstat.countp == num_ap_ids);
2N/A rc = realloc_data(&buf, &n, &lstat);
2N/A }
2N/A
2N/A return (rc);
2N/A}
2N/A
2N/A/*
2N/A * config_list
2N/A */
2N/Acfga_err_t
2N/Aconfig_list(
2N/A struct cfga_stat_data **ap_id_list,
2N/A int *nlistp,
2N/A const char *options,
2N/A char **errstring)
2N/A{
2N/A int nstat;
2N/A list_stat_t lstat = {NULL};
2N/A cfga_err_t retval = CFGA_ERROR;
2N/A
2N/A if (errstring != NULL) {
2N/A *errstring = NULL;
2N/A }
2N/A
2N/A nstat = 0;
2N/A lstat.countp = &nstat;
2N/A lstat.opts = options;
2N/A lstat.errstr = errstring;
2N/A lstat.shp_errstr = NULL;
2N/A /*
2N/A * This is a V1 interface which can use only V1 plugins
2N/A */
2N/A lstat.use_vers.v_max = lstat.use_vers.v_min = CFGA_HSL_V1;
2N/A
2N/A
2N/A *ap_id_list = NULL;
2N/A *nlistp = 0;
2N/A
2N/A /*
2N/A * V1 interfaces don't support prefiltering, no class
2N/A * specified.
2N/A */
2N/A retval = list_common(&lstat, NULL);
2N/A if (retval == CFGA_OK) {
2N/A retval = realloc_data(ap_id_list, nlistp, &lstat);
2N/A }
2N/A
2N/A assert((ap_id_list != NULL && *nlistp != 0) ||
2N/A (ap_id_list == NULL && *nlistp == 0));
2N/A
2N/A if (retval == CFGA_OK && *nlistp == 0) {
2N/A return (CFGA_NOTSUPP);
2N/A } else {
2N/A return (retval);
2N/A }
2N/A}
2N/A
2N/A
2N/A/*
2N/A * config_list_ext
2N/A */
2N/Acfga_err_t
2N/Aconfig_list_ext(
2N/A int num_ap_ids,
2N/A char *const *ap_ids,
2N/A struct cfga_list_data **ap_id_list,
2N/A int *nlistp,
2N/A const char *options,
2N/A const char *listopts,
2N/A char **errstring,
2N/A cfga_flags_t flags)
2N/A{
2N/A int nstat, list, prefilter;
2N/A list_stat_t lstat = {NULL};
2N/A char *class;
2N/A
2N/A cfga_err_t rc = CFGA_ERROR;
2N/A
2N/A *nlistp = 0;
2N/A *ap_id_list = NULL;
2N/A
2N/A if (errstring != NULL) {
2N/A *errstring = NULL;
2N/A }
2N/A
2N/A if (check_flags(flags, CFGA_FLAG_LIST_ALL, errstring) != CFGA_OK) {
2N/A return (CFGA_ERROR);
2N/A }
2N/A
2N/A class = NULL;
2N/A if ((rc = parse_listopt((char *)listopts, &class, errstring))
2N/A != CFGA_OK) {
2N/A return (rc);
2N/A }
2N/A
2N/A prefilter = (class == NULL) ? 0 : 1;
2N/A
2N/A nstat = 0;
2N/A lstat.countp = &nstat;
2N/A lstat.opts = options;
2N/A lstat.errstr = errstring;
2N/A lstat.shp_errstr = NULL;
2N/A lstat.flags = flags;
2N/A /*
2N/A * We support both V1 and V2 plugins through this entry
2N/A * point.
2N/A */
2N/A lstat.use_vers.v_min = CFGA_HSL_V1;
2N/A lstat.use_vers.v_max = CFGA_HSL_V2;
2N/A
2N/A list = 0;
2N/A if (num_ap_ids == 0 && ap_ids == NULL) {
2N/A /*
2N/A * discover and stat all attachment points
2N/A */
2N/A list = 1;
2N/A rc = list_common(&lstat, class);
2N/A } else if (num_ap_ids > 0 && ap_ids != NULL) {
2N/A /*
2N/A * Stat specified attachment points. With dynamic expansion
2N/A * more data may be returned than was specified by user.
2N/A */
2N/A rc = stat_common(num_ap_ids, ap_ids, class, &lstat);
2N/A } else {
2N/A rc = CFGA_ERROR;
2N/A }
2N/A
2N/A S_FREE(class);
2N/A
2N/A if (rc != CFGA_OK) {
2N/A return (rc);
2N/A }
2N/A
2N/A rc = realloc_data_ext(ap_id_list, nlistp, &lstat);
2N/A
2N/A assert((ap_id_list != NULL && *nlistp != 0) ||
2N/A (ap_id_list == NULL && *nlistp == 0));
2N/A
2N/A /*
2N/A * For the list command notify user if no attachment
2N/A * point is found in the system.
2N/A *
2N/A */
2N/A if (list && rc == CFGA_OK && *nlistp == 0) {
2N/A /*
2N/A * If attachment points are being prefiltered, absence of data
2N/A * does not imply that config. admin. is not
2N/A * supported by the system.
2N/A */
2N/A if (prefilter) {
2N/A /*
2N/A * Prefiltering: requested class is absent
2N/A */
2N/A return (CFGA_APID_NOEXIST);
2N/A } else {
2N/A /*
2N/A * No attachment points in system
2N/A */
2N/A return (CFGA_NOTSUPP);
2N/A }
2N/A } else {
2N/A return (rc);
2N/A }
2N/A}
2N/A
2N/A
2N/A/*
2N/A * config_unload_libs
2N/A *
2N/A * Attempts to remove all libs on the plugin list.
2N/A */
2N/Avoid
2N/Aconfig_unload_libs(void)
2N/A{
2N/A plugin_lib_t *libp, *prev = &plugin_list, *next = NULL;
2N/A
2N/A /* destroy cache entries to remove refcnt agains plugins */
2N/A destroy_cache();
2N/A
2N/A (void) mutex_lock(&plugin_list_lock);
2N/A for (libp = plugin_list.next; libp != NULL; libp = next) {
2N/A next = libp->next;
2N/A (void) mutex_lock(&libp->lock);
2N/A if (libp->refcnt) {
2N/A (void) mutex_unlock(&libp->lock);
2N/A prev = libp;
2N/A continue;
2N/A }
2N/A (void) mutex_unlock(&libp->lock);
2N/A prev->next = next;
2N/A (void) dlclose(libp->handle);
2N/A (void) mutex_destroy(&libp->lock);
2N/A free(libp);
2N/A }
2N/A (void) mutex_unlock(&plugin_list_lock);
2N/A}
2N/A
2N/A/*
2N/A * config_ap_id_cmp
2N/A */
2N/Aint
2N/Aconfig_ap_id_cmp(
2N/A const cfga_ap_log_id_t ap1,
2N/A const cfga_ap_log_id_t ap2)
2N/A{
2N/A int ret;
2N/A lib_loc_t libloc;
2N/A char apstat1[CFGA_PHYS_EXT_LEN];
2N/A char apstat2[CFGA_PHYS_EXT_LEN];
2N/A char *sep1, *sep2;
2N/A
2N/A /*
2N/A * Extract static ap_ids
2N/A */
2N/A (void) strlcpy(apstat1, ap1, sizeof (apstat1));
2N/A (void) strlcpy(apstat2, ap2, sizeof (apstat2));
2N/A
2N/A sep1 = GET_DYN(apstat1);
2N/A sep2 = GET_DYN(apstat2);
2N/A
2N/A if (sep1)
2N/A *sep1 = '\0';
2N/A if (sep2)
2N/A *sep2 = '\0';
2N/A
2N/A /*
2N/A * Use the default comparator for static ap_ids
2N/A */
2N/A ret = default_ap_id_cmp(apstat1, apstat2);
2N/A if (ret)
2N/A return (ret);
2N/A
2N/A /*
2N/A * static components match. They belong to
2N/A * the same static ap_id. Check if both are dynamic
2N/A * If not, static < dynamic.
2N/A */
2N/A if ((sep1 == NULL) ^ (sep2 == NULL))
2N/A return (sep1 ? 1 : -1);
2N/A
2N/A /*
2N/A * If both are static, then ap1 = ap2
2N/A */
2N/A if (sep1 == NULL)
2N/A return (0);
2N/A
2N/A /*
2N/A * Both are dynamic and belong to same static ap_id.
2N/A * Use the plugin comparator
2N/A */
2N/A libloc.libp = NULL;
2N/A if (config_get_lib(ap1, &libloc, NULL) != CFGA_OK) {
2N/A return (strncmp(sep1, sep2, CFGA_PHYS_EXT_LEN));
2N/A }
2N/A
2N/A ret = (*libloc.libp->cfga_ap_id_cmp_p)(ap1, ap2);
2N/A
2N/A rele_lib(libloc.libp);
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * config_strerror
2N/A */
2N/A
2N/Aconst char *
2N/Aconfig_strerror(cfga_err_t cfgerrnum)
2N/A{
2N/A const char *ep = NULL;
2N/A
2N/A if ((cfgerrnum < CFGA_OK) || (cfgerrnum > CFGA_ATTR_INVAL))
2N/A return (NULL);
2N/A
2N/A ep = __config_strerror(cfgerrnum);
2N/A
2N/A return ((ep != NULL) ? dgettext(TEXT_DOMAIN, ep) : NULL);
2N/A}
2N/A
2N/A/*
2N/A * config_help
2N/A */
2N/Acfga_err_t
2N/Aconfig_help(
2N/A int num_ap_ids,
2N/A char *const *ap_ids,
2N/A struct cfga_msg *msgp,
2N/A const char *options,
2N/A cfga_flags_t flags)
2N/A{
2N/A int i;
2N/A lib_loc_t libloc;
2N/A cfga_err_t retval = CFGA_OK;
2N/A
2N/A if (check_flags(flags, CFGA_FLAG_FORCE | CFGA_FLAG_VERBOSE, NULL)
2N/A != CFGA_OK) {
2N/A return (CFGA_ERROR);
2N/A }
2N/A
2N/A if (num_ap_ids < 0) {
2N/A return (CFGA_ERROR);
2N/A }
2N/A
2N/A if (num_ap_ids > 0 && ap_ids == NULL) {
2N/A return (CFGA_ERROR);
2N/A }
2N/A
2N/A /*
2N/A * operate on each ap_id
2N/A */
2N/A for (i = 0; (i < num_ap_ids) && (retval == CFGA_OK); i++) {
2N/A libloc.libp = NULL;
2N/A if ((retval = config_get_lib(ap_ids[i], &libloc,
2N/A NULL)) != CFGA_OK) {
2N/A return (retval);
2N/A }
2N/A
2N/A errno = 0;
2N/A retval = (*libloc.libp->cfga_help_p)(msgp, options, flags);
2N/A rele_lib(libloc.libp);
2N/A }
2N/A return (retval);
2N/A}
2N/A
2N/A/*
2N/A * Private support routines for the public interfaces
2N/A */
2N/A
2N/Astatic const char *
2N/A__config_strerror(cfga_err_t cfgerrnum)
2N/A{
2N/A const char *ep = NULL;
2N/A
2N/A switch (cfgerrnum) {
2N/A case CFGA_OK:
2N/A ep = "Configuration operation succeeded";
2N/A break;
2N/A case CFGA_NACK:
2N/A ep = "Configuration operation cancelled";
2N/A break;
2N/A case CFGA_INVAL:
2N/A ep = "Configuration operation invalid";
2N/A break;
2N/A case CFGA_NOTSUPP:
2N/A ep = "Configuration administration not supported";
2N/A break;
2N/A case CFGA_OPNOTSUPP:
2N/A ep = "Configuration operation not supported";
2N/A break;
2N/A case CFGA_PRIV:
2N/A ep = "Insufficient privileges";
2N/A break;
2N/A case CFGA_BUSY:
2N/A ep = "Component system is busy, try again";
2N/A break;
2N/A case CFGA_SYSTEM_BUSY:
2N/A ep = "System is busy, try again";
2N/A break;
2N/A case CFGA_DATA_ERROR:
2N/A ep = "Data error";
2N/A break;
2N/A case CFGA_LIB_ERROR:
2N/A ep = "Library error";
2N/A break;
2N/A case CFGA_NO_LIB:
2N/A ep = "No Library found";
2N/A break;
2N/A case CFGA_INSUFFICENT_CONDITION:
2N/A ep = "Insufficient condition";
2N/A break;
2N/A case CFGA_ERROR:
2N/A ep = "Hardware specific failure";
2N/A break;
2N/A case CFGA_APID_NOEXIST:
2N/A ep = "Attachment point not found";
2N/A break;
2N/A case CFGA_ATTR_INVAL:
2N/A ep = "No attachment point with specified attributes found";
2N/A break;
2N/A default:
2N/A ep = NULL;
2N/A break;
2N/A }
2N/A return (ep);
2N/A}
2N/A
2N/A/*
2N/A * listopts is a string in the getsubopt(3C) style:
2N/A * name1=value1,name2=value2,
2N/A */
2N/Astatic cfga_err_t
2N/Aparse_listopt(char *listopts, char **classpp, char **errstring)
2N/A{
2N/A char *bufp, *optp, *val = NULL;
2N/A cfga_err_t rc = CFGA_ERROR;
2N/A
2N/A *classpp = NULL;
2N/A
2N/A /*
2N/A * NULL is a legal value for listopts
2N/A */
2N/A if (listopts == NULL) {
2N/A return (CFGA_OK);
2N/A }
2N/A
2N/A if ((bufp = config_calloc_check(1, strlen(listopts) + 1, errstring))
2N/A == NULL) {
2N/A return (CFGA_LIB_ERROR);
2N/A }
2N/A (void) strcpy(bufp, listopts);
2N/A
2N/A optp = bufp; /* getsubopt() modifies its argument */
2N/A while (*optp != '\0') {
2N/A switch (getsubopt(&optp, listopt_array, &val)) {
2N/A case LISTOPT_CLASS:
2N/A if (val == NULL || *classpp != NULL) {
2N/A rc = CFGA_ERROR;
2N/A goto out;
2N/A }
2N/A if ((*classpp = config_calloc_check(1, strlen(val) + 1,
2N/A errstring)) == NULL) {
2N/A rc = CFGA_LIB_ERROR;
2N/A goto out;
2N/A }
2N/A (void) strcpy(*classpp, val);
2N/A break;
2N/A default:
2N/A rc = CFGA_ERROR;
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A rc = CFGA_OK;
2N/A /*FALLTHRU*/
2N/Aout:
2N/A S_FREE(bufp);
2N/A if (rc != CFGA_OK) {
2N/A S_FREE(*classpp);
2N/A }
2N/A return (rc);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic cfga_err_t
2N/Anull_mklog(
2N/A di_node_t node,
2N/A di_minor_t minor,
2N/A plugin_lib_t *libp,
2N/A lib_loc_t *liblocp)
2N/A{
2N/A return (CFGA_OK);
2N/A}
2N/A
2N/Astatic cfga_err_t
2N/Amklog_v1(
2N/A di_node_t node,
2N/A di_minor_t minor,
2N/A plugin_lib_t *libp,
2N/A lib_loc_t *liblocp)
2N/A{
2N/A const size_t len = CFGA_AP_LOG_ID_LEN;
2N/A
2N/A assert(len <= sizeof (liblocp->ap_logical));
2N/A
2N/A if (libp->plugin_vers != CFGA_HSL_V1) {
2N/A return (CFGA_LIB_ERROR);
2N/A }
2N/A
2N/A return (mklog_common(node, minor, liblocp, len));
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Obtain the devlink from a /devices path
2N/A */
2N/Astatic int
2N/Aget_link(di_devlink_t devlink, void *arg)
2N/A{
2N/A char *linkp = (char *)arg;
2N/A
2N/A (void) snprintf(linkp, CFGA_LOG_EXT_LEN, "%s",
2N/A di_devlink_path(devlink));
2N/A return (DI_WALK_TERMINATE);
2N/A}
2N/A
2N/Astatic cfga_err_t
2N/Amklog_v2(
2N/A di_node_t node,
2N/A di_minor_t minor,
2N/A plugin_lib_t *libp,
2N/A lib_loc_t *liblocp)
2N/A{
2N/A const size_t len = CFGA_LOG_EXT_LEN;
2N/A di_devlink_handle_t hdl;
2N/A
2N/A assert(len <= sizeof (liblocp->ap_logical));
2N/A
2N/A if (libp->plugin_vers != CFGA_HSL_V2) {
2N/A return (CFGA_LIB_ERROR);
2N/A }
2N/A
2N/A /* open devlink database */
2N/A if ((hdl = di_devlink_init(NULL, 0)) == NULL) {
2N/A return (CFGA_LIB_ERROR);
2N/A }
2N/A
2N/A liblocp->ap_logical[0] = '\0';
2N/A (void) di_devlink_walk(hdl, NULL,
2N/A liblocp->ap_physical + strlen(DEVICES_DIR),
2N/A DI_PRIMARY_LINK, (void *)liblocp->ap_logical, get_link);
2N/A
2N/A (void) di_devlink_fini(&hdl);
2N/A
2N/A if (liblocp->ap_logical[0] != '\0')
2N/A return (CFGA_OK);
2N/A return (mklog_common(node, minor, liblocp, len));
2N/A}
2N/A
2N/A/*
2N/A * mklog_common - make a logical name from the driver and instance
2N/A */
2N/Astatic cfga_err_t
2N/Amklog_common(
2N/A di_node_t node,
2N/A di_minor_t minor,
2N/A lib_loc_t *libloc_p,
2N/A size_t len)
2N/A{
2N/A int inst;
2N/A char *drv, *minor_name;
2N/A
2N/A drv = di_driver_name(node);
2N/A inst = di_instance(node);
2N/A minor_name = di_minor_name(minor);
2N/A
2N/A errno = 0;
2N/A if (drv != NULL && inst != -1 && minor_name != NULL &&
2N/A snprintf(libloc_p->ap_logical, len, "%s%d:%s", drv, inst,
2N/A minor_name) < len) { /* snprintf returns strlen */
2N/A return (CFGA_OK);
2N/A }
2N/A
2N/A return (CFGA_LIB_ERROR);
2N/A}
2N/A
2N/A/*
2N/A * mklog_common - make a logical name from the driver and instance
2N/A */
2N/A/*ARGSUSED*/
2N/Astatic cfga_err_t
2N/Amklog_hp(
2N/A di_node_t node,
2N/A di_hp_t hp,
2N/A plugin_lib_t *libp,
2N/A lib_loc_t *liblocp)
2N/A{
2N/A const size_t len = CFGA_LOG_EXT_LEN;
2N/A int inst;
2N/A char *drv, *hp_name;
2N/A
2N/A drv = di_driver_name(node);
2N/A inst = di_instance(node);
2N/A hp_name = di_hp_name(hp);
2N/A
2N/A errno = 0;
2N/A if (drv != NULL && inst != -1 && hp_name != NULL &&
2N/A snprintf(liblocp->ap_logical, len, "%s%d:%s", drv, inst,
2N/A hp_name) < len) { /* snprintf returns strlen */
2N/A return (CFGA_OK);
2N/A }
2N/A
2N/A return (CFGA_LIB_ERROR);
2N/A}
2N/A
2N/A/*
2N/A * resolve_lib_ref - relocate to use plugin lib
2N/A */
2N/Astatic cfga_err_t
2N/Aresolve_lib_ref(
2N/A plugin_lib_t *libp,
2N/A lib_loc_t *libloc_p)
2N/A{
2N/A void *sym;
2N/A void *libhdlp = libp->handle;
2N/A int plug_vers;
2N/A
2N/A if ((sym = dlsym(libhdlp, "cfga_version")) == NULL) {
2N/A /*
2N/A * Version symbol not defined, must be the first version
2N/A */
2N/A plug_vers = CFGA_HSL_V1;
2N/A } else {
2N/A plug_vers = *((int *)sym);
2N/A }
2N/A
2N/A /*
2N/A * Check if plugin version matches request.
2N/A */
2N/A if (!compat_plugin(&libloc_p->vers_req, plug_vers)) {
2N/A return (CFGA_NO_LIB);
2N/A }
2N/A
2N/A /*
2N/A * Record the plugin version and setup version dependant routines
2N/A */
2N/A assert(plug_vers < VERS_ARRAY_SZ);
2N/A libp->plugin_vers = plug_vers;
2N/A libp->vers_ops = &cfga_vers_ops[plug_vers];
2N/A
2N/A /* resolve symbols common to all versions */
2N/A if ((sym = dlsym(libhdlp, "cfga_change_state")) == NULL) {
2N/A perror("dlsym: cfga_change_state");
2N/A return (CFGA_LIB_ERROR);
2N/A } else
2N/A libp->cfga_change_state_p = (cfga_err_t (*)(cfga_cmd_t,
2N/A const char *, const char *, struct cfga_confirm *,
2N/A struct cfga_msg *, char **, cfga_flags_t)) sym;
2N/A
2N/A if ((sym = dlsym(libhdlp, "cfga_private_func")) == NULL) {
2N/A perror("dlsym: cfga_private_func");
2N/A return (CFGA_LIB_ERROR);
2N/A } else
2N/A libp->cfga_private_func_p = (cfga_err_t (*)(const char *,
2N/A const char *, const char *, struct cfga_confirm *,
2N/A struct cfga_msg *, char **, cfga_flags_t))sym;
2N/A
2N/A if ((sym = dlsym(libhdlp, "cfga_test")) == NULL) {
2N/A perror("dlsym: cfga_test");
2N/A return (CFGA_LIB_ERROR);
2N/A } else
2N/A libp->cfga_test_p = (cfga_err_t (*)(const char *, const char *,
2N/A struct cfga_msg *, char **, cfga_flags_t))sym;
2N/A
2N/A if ((sym = dlsym(libhdlp, "cfga_help")) == NULL) {
2N/A perror("dlsym: cfga_help");
2N/A return (CFGA_LIB_ERROR);
2N/A } else
2N/A libp->cfga_help_p = (cfga_err_t (*)(struct cfga_msg *,
2N/A const char *, cfga_flags_t))sym;
2N/A
2N/A if ((sym = dlsym(libhdlp, "cfga_ap_id_cmp")) == NULL) {
2N/A libp->cfga_ap_id_cmp_p = default_ap_id_cmp;
2N/A } else
2N/A libp->cfga_ap_id_cmp_p = (int (*)(const
2N/A cfga_ap_log_id_t, const cfga_ap_log_id_t))sym;
2N/A
2N/A /* Resolve version specific symbols */
2N/A return (libp->vers_ops->resolve_lib(libp));
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic cfga_err_t
2N/Anull_resolve(plugin_lib_t *libp)
2N/A{
2N/A return (CFGA_OK);
2N/A}
2N/A
2N/Astatic cfga_err_t
2N/Aresolve_v1(plugin_lib_t *libp)
2N/A{
2N/A void *sym, *libhdlp = libp->handle;
2N/A
2N/A
2N/A if (libp->plugin_vers != CFGA_HSL_V1) {
2N/A return (CFGA_NO_LIB);
2N/A }
2N/A
2N/A if ((sym = dlsym(libhdlp, "cfga_stat")) == NULL) {
2N/A perror("dlsym: cfga_stat");
2N/A return (CFGA_LIB_ERROR);
2N/A } else
2N/A libp->cfga_stat_p = (cfga_err_t (*)(const char *,
2N/A struct cfga_stat_data *, const char *,
2N/A char **))sym;
2N/A
2N/A if ((sym = dlsym(libhdlp, "cfga_list")) == NULL) {
2N/A perror("dlsym: cfga_list");
2N/A return (CFGA_LIB_ERROR);
2N/A } else
2N/A libp->cfga_list_p = (cfga_err_t (*)(struct cfga_stat_data **,
2N/A int *, const char *, char **))sym;
2N/A
2N/A return (CFGA_OK);
2N/A}
2N/A
2N/Astatic cfga_err_t
2N/Aresolve_v2(plugin_lib_t *libp)
2N/A{
2N/A void *sym;
2N/A
2N/A
2N/A if (libp->plugin_vers != CFGA_HSL_V2) {
2N/A return (CFGA_NO_LIB);
2N/A }
2N/A
2N/A if ((sym = dlsym(libp->handle, "cfga_list_ext")) == NULL) {
2N/A perror("dlsym: cfga_list_ext");
2N/A return (CFGA_LIB_ERROR);
2N/A } else {
2N/A libp->cfga_list_ext_p = (cfga_err_t (*)(const char *,
2N/A struct cfga_list_data **, int *, const char *,
2N/A const char *, char **, cfga_flags_t))sym;
2N/A return (CFGA_OK);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * config_calloc_check - perform allocation, check result and
2N/A * set error string
2N/A */
2N/Astatic void *
2N/Aconfig_calloc_check(
2N/A size_t nelem,
2N/A size_t elsize,
2N/A char **errstring)
2N/A{
2N/A void *p;
2N/A
2N/A p = calloc(nelem, elsize);
2N/A if (p == NULL) {
2N/A config_err(0, ALLOC_FAILED, errstring);
2N/A }
2N/A
2N/A return (p);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * config_get_lib - given an ap_id find the library name
2N/A * If successful, the plugin library is held.
2N/A */
2N/Astatic cfga_err_t
2N/Aconfig_get_lib(
2N/A const char *ap_id,
2N/A lib_loc_t *lib_loc_p,
2N/A char **errstring)
2N/A{
2N/A char *dyncomp, path[PATH_MAX];
2N/A char *apdup;
2N/A cfga_ap_types_t type = UNKNOWN_AP;
2N/A cfga_err_t ret = CFGA_ERROR;
2N/A
2N/A if (ap_id == NULL) {
2N/A config_err(0, INVALID_ARGS, errstring);
2N/A return (ret);
2N/A }
2N/A
2N/A lib_loc_p->libp = NULL;
2N/A
2N/A if ((apdup = config_calloc_check(1, strlen(ap_id) + 1, errstring))
2N/A == NULL) {
2N/A return (CFGA_LIB_ERROR);
2N/A }
2N/A (void) strcpy(apdup, ap_id);
2N/A
2N/A /*
2N/A * Separate into base and dynamic components
2N/A */
2N/A if ((ret = split_apid(apdup, &dyncomp, errstring)) != CFGA_OK) {
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * No upper limit on version
2N/A */
2N/A lib_loc_p->vers_req.v_max = CFGA_HSL_VERS;
2N/A if (dyncomp != NULL) {
2N/A /*
2N/A * We need atleast version 2 of the plug-in library
2N/A * interface since the ap_id has a dynamic component.
2N/A */
2N/A
2N/A lib_loc_p->vers_req.v_min = CFGA_HSL_V2;
2N/A } else {
2N/A lib_loc_p->vers_req.v_min = CFGA_HSL_V1;
2N/A }
2N/A
2N/A /*
2N/A * If the ap_id is a devlink in CFGA_DEV_DIR, follow link
2N/A * to get the physical ap_id.
2N/A */
2N/A if ((type = find_arg_type(apdup)) == LOGICAL_LINK_AP) {
2N/A (void) snprintf(lib_loc_p->ap_base, sizeof (lib_loc_p->ap_base),
2N/A "%s%s", CFGA_DEV_DIR SLASH, apdup);
2N/A }
2N/A
2N/A path[sizeof (path) - 1] = '\0';
2N/A if (type == LOGICAL_LINK_AP && realpath(lib_loc_p->ap_base, path)
2N/A != NULL) {
2N/A (void) snprintf(lib_loc_p->ap_base, sizeof (lib_loc_p->ap_base),
2N/A "%s", path);
2N/A } else {
2N/A (void) snprintf(lib_loc_p->ap_base, sizeof (lib_loc_p->ap_base),
2N/A "%s", apdup);
2N/A }
2N/A
2N/A
2N/A /*
2N/A * find and load the library
2N/A * The base component of the ap_id is used to locate the plug-in
2N/A *
2N/A * NOTE that PCIE/PCISHPC connectors also have minor nodes &
2N/A * dev links created for now.
2N/A */
2N/A if ((type = find_arg_type(lib_loc_p->ap_base)) == PHYSICAL_AP) {
2N/A /*
2N/A * physical ap_id: Use ap_base as root for tree walk
2N/A * A link based apid (logical) will resolve to a physical
2N/A * ap_id.
2N/A */
2N/A ret = find_ap_common(lib_loc_p, lib_loc_p->ap_base,
2N/A check_ap_phys, check_ap_phys_hp, errstring);
2N/A } else if ((type == LOGICAL_DRV_AP) ||
2N/A (type == AP_TYPE && dyncomp == NULL)) {
2N/A /*
2N/A * logical ap_id or ap_type: Use "/" as root for tree walk
2N/A * Note: an aptype cannot have a dynamic component
2N/A */
2N/A ret = find_ap_common(lib_loc_p, "/", check_ap,
2N/A check_ap_hp, errstring);
2N/A } else {
2N/A ret = CFGA_APID_NOEXIST;
2N/A }
2N/A
2N/A if (ret == CFGA_OK) {
2N/A#ifndef NDEBUG
2N/A /*
2N/A * variables used by assert() only which is disabled
2N/A * by defining NDEBUG (see top of this file)
2N/A */
2N/A plugin_lib_t *libp;
2N/A
2N/A libp = lib_loc_p->libp;
2N/A#endif /* NDEBUG */
2N/A
2N/A assert(strcmp(libp->libpath, lib_loc_p->pathname) == 0);
2N/A assert(VALID_HSL_VERS(libp->plugin_vers));
2N/A
2N/A /*
2N/A * If a dynamic component was present, v1 plug-ins are not
2N/A * acceptable.
2N/A */
2N/A assert(dyncomp == NULL || libp->plugin_vers >= CFGA_HSL_V2);
2N/A
2N/A /*
2N/A * ap_physical is passed to plugins as their ap_id argument.
2N/A * Append dynamic component if any.
2N/A */
2N/A append_dyn(lib_loc_p->ap_physical, dyncomp,
2N/A sizeof (lib_loc_p->ap_physical));
2N/A }
2N/A
2N/A /* cleanup */
2N/A lib_loc_p->vers_req.v_min = INVALID_VERSION;
2N/A lib_loc_p->vers_req.v_max = INVALID_VERSION;
2N/A *lib_loc_p->ap_base = '\0';
2N/A
2N/A /*FALLTHRU*/
2N/Aout:
2N/A S_FREE(apdup);
2N/A S_FREE(dyncomp);
2N/A if (ret != CFGA_OK) {
2N/A lib_loc_p->libp = NULL;
2N/A }
2N/A
2N/A assert(ret != CFGA_OK || lib_loc_p->libp != NULL);
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/A/* load_lib - load library for non-SHP attachment point node */
2N/Astatic cfga_err_t
2N/Aload_lib(
2N/A di_node_t node,
2N/A di_minor_t minor,
2N/A lib_loc_t *libloc_p)
2N/A{
2N/A return (load_lib_impl(node, minor, NULL, libloc_p));
2N/A}
2N/A
2N/A/* load_lib_hp - load library for SHP attachment point node */
2N/Astatic cfga_err_t
2N/Aload_lib_hp(
2N/A di_node_t node,
2N/A di_hp_t hp,
2N/A lib_loc_t *libloc_p)
2N/A{
2N/A return (load_lib_impl(node, NULL, hp, libloc_p));
2N/A}
2N/A
2N/A/*
2N/A * load_lib_impl - Given a library pathname, create a entry for it
2N/A * in the library list, * if one does not already exist, and read
2N/A * lock it to keep it there.
2N/A */
2N/Astatic cfga_err_t
2N/Aload_lib_impl(
2N/A di_node_t node,
2N/A di_minor_t minor,
2N/A di_hp_t hp,
2N/A lib_loc_t *libloc_p)
2N/A{
2N/A plugin_lib_t *libp, *list_libp;
2N/A char *devfs_path;
2N/A char *name;
2N/A
2N/A if (minor != DI_MINOR_NIL && hp != DI_HP_NIL)
2N/A return (CFGA_LIB_ERROR);
2N/A
2N/A if (minor != DI_MINOR_NIL)
2N/A name = di_minor_name(minor);
2N/A else
2N/A name = di_hp_name(hp);
2N/A
2N/A /*
2N/A * lock the library list
2N/A */
2N/A (void) mutex_lock(&plugin_list_lock);
2N/A
2N/A /*
2N/A * see if lib exist in list, if not, allocate a new one
2N/A */
2N/A list_libp = lib_in_list(libloc_p->pathname);
2N/A if (list_libp != NULL) {
2N/A hold_lib(list_libp);
2N/A (void) mutex_unlock(&plugin_list_lock);
2N/A
2N/A /* fill in logical and physical name in libloc_p */
2N/A libloc_p->libp = libp = list_libp;
2N/A if (minor != DI_MINOR_NIL) {
2N/A if (libp->vers_ops->mklog(node, minor, libp, libloc_p)
2N/A != CFGA_OK) {
2N/A rele_lib(list_libp);
2N/A return (CFGA_LIB_ERROR);
2N/A }
2N/A } else {
2N/A if (mklog_hp(node, hp, libp, libloc_p) != CFGA_OK) {
2N/A rele_lib(list_libp);
2N/A return (CFGA_LIB_ERROR);
2N/A }
2N/A }
2N/A
2N/A devfs_path = di_devfs_path(node);
2N/A (void) snprintf(libloc_p->ap_physical, MAXPATHLEN, "%s%s:%s",
2N/A DEVICES_DIR, devfs_path, name);
2N/A di_devfs_path_free(devfs_path);
2N/A
2N/A return (CFGA_OK);
2N/A }
2N/A
2N/A /* allocate a new plugin_lib_t structure */
2N/A libp = config_calloc_check(1, sizeof (plugin_lib_t), NULL);
2N/A if (libp == NULL) {
2N/A (void) mutex_unlock(&plugin_list_lock);
2N/A return (CFGA_LIB_ERROR);
2N/A }
2N/A
2N/A (void) snprintf(libp->libpath, sizeof (libp->libpath), "%s",
2N/A libloc_p->pathname);
2N/A
2N/A /*
2N/A * ensure that the lib is open and linked in
2N/A */
2N/A libp->handle = dlopen(libp->libpath, RTLD_NOW);
2N/A if (libp->handle == NULL) {
2N/A (void) mutex_unlock(&plugin_list_lock);
2N/A free(libp);
2N/A return (CFGA_NO_LIB);
2N/A }
2N/A
2N/A if (minor != DI_MINOR_NIL) {
2N/A if (resolve_lib_ref(libp, libloc_p) != CFGA_OK ||
2N/A libp->vers_ops->mklog(node, minor, libp, libloc_p)
2N/A != CFGA_OK) {
2N/A (void) mutex_unlock(&plugin_list_lock);
2N/A (void) dlclose(libp->handle);
2N/A free(libp);
2N/A return (CFGA_NO_LIB);
2N/A }
2N/A } else {
2N/A if (resolve_lib_ref(libp, libloc_p) != CFGA_OK ||
2N/A mklog_hp(node, hp, libp, libloc_p) != CFGA_OK) {
2N/A (void) mutex_unlock(&plugin_list_lock);
2N/A (void) dlclose(libp->handle);
2N/A free(libp);
2N/A return (CFGA_NO_LIB);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * link in new entry to the end of list
2N/A */
2N/A list_libp = &plugin_list;
2N/A while (list_libp->next != NULL)
2N/A list_libp = list_libp->next;
2N/A libp->next = list_libp->next;
2N/A list_libp->next = libp;
2N/A
2N/A /* Initialize refcnt to 1 */
2N/A libp->refcnt = 1;
2N/A (void) mutex_init(&libp->lock, USYNC_THREAD, NULL);
2N/A
2N/A (void) mutex_unlock(&plugin_list_lock);
2N/A
2N/A /*
2N/A * record libp and physical node name in the libloc struct
2N/A */
2N/A libloc_p->libp = libp;
2N/A devfs_path = di_devfs_path(node);
2N/A (void) snprintf(libloc_p->ap_physical, MAXPATHLEN, "%s%s:%s",
2N/A DEVICES_DIR, devfs_path, name);
2N/A di_devfs_path_free(devfs_path);
2N/A
2N/A return (CFGA_OK);
2N/A}
2N/A
2N/A
2N/A#define NUM_LIB_NAMES 2
2N/A
2N/A/*
2N/A * find_lib - find library for non-SHP attachment point node
2N/A */
2N/Astatic cfga_err_t
2N/Afind_lib(
2N/A di_node_t node,
2N/A di_minor_t minor,
2N/A lib_loc_t *libloc_p)
2N/A{
2N/A char name[NUM_LIB_NAMES][MAXPATHLEN];
2N/A char *class = NULL, *drv = NULL;
2N/A int i;
2N/A
2N/A
2N/A /* Make sure pathname and class is null if we fail */
2N/A *libloc_p->ap_class = *libloc_p->pathname = '\0';
2N/A
2N/A /*
2N/A * Initialize possible library tags.
2N/A */
2N/A
2N/A drv = di_driver_name(node);
2N/A class = get_class(minor);
2N/A
2N/A if (drv == NULL || class == NULL) {
2N/A return (CFGA_LIB_ERROR);
2N/A }
2N/A
2N/A i = 0;
2N/A (void) snprintf(&name[i++][0], sizeof (name[0]), "%s", drv);
2N/A (void) snprintf(&name[i++][0], sizeof (name[0]), "%s", class);
2N/A
2N/A /*
2N/A * Cycle through the array of names to find the library.
2N/A */
2N/A for (i = 0; i < NUM_LIB_NAMES; i++) {
2N/A
2N/A /* Attachment points may not have a class (i.e. are generic) */
2N/A if (name[i][0] == '\0') {
2N/A continue;
2N/A }
2N/A
2N/A if (find_lib_impl(name[i], libloc_p) == CFGA_OK)
2N/A goto found;
2N/A }
2N/A
2N/A return (CFGA_NO_LIB);
2N/A
2N/Afound:
2N/A
2N/A /* Record class name (if any) */
2N/A (void) snprintf(libloc_p->ap_class, sizeof (libloc_p->ap_class), "%s",
2N/A class);
2N/A
2N/A return (CFGA_OK);
2N/A}
2N/A
2N/A/*
2N/A * find_lib_hp - find library for SHP attachment point
2N/A */
2N/A/*ARGSUSED*/
2N/Astatic cfga_err_t
2N/Afind_lib_hp(
2N/A di_node_t node,
2N/A di_hp_t hp,
2N/A lib_loc_t *libloc_p)
2N/A{
2N/A char name[MAXPATHLEN];
2N/A char *class = NULL;
2N/A
2N/A
2N/A /* Make sure pathname and class is null if we fail */
2N/A *libloc_p->ap_class = *libloc_p->pathname = '\0';
2N/A
2N/A /*
2N/A * Initialize possible library tags.
2N/A *
2N/A * Only support PCI class for now, this will need to be
2N/A * changed as other plugins are migrated to SHP plugin.
2N/A */
2N/A class = "pci";
2N/A#if 0
2N/A /*
2N/A * No type check for now as PCI is the only class SHP plugin
2N/A * supports. In the future we'll need to enable the type check
2N/A * and set class accordingly, when non PCI plugins are migrated
2N/A * to SHP. In that case we'll probably need to add an additional
2N/A * interface between libcfgadm and the plugins, and SHP plugin will
2N/A * implement this interface which will translate the bus specific
2N/A * strings to standard classes that libcfgadm can recognize, for
2N/A * all the buses it supports, e.g. for pci/pcie it will translate
2N/A * PCIE_NATIVE_HP_TYPE to string "pci". We'll also need to bump up
2N/A * SHP plugin version to 3 to use the new interface.
2N/A */
2N/A class = di_hp_type(hp);
2N/A if ((strcmp(class, PCIE_NATIVE_HP_TYPE) == 0) ||
2N/A (strcmp(class, PCIE_ACPI_HP_TYPE) == 0) ||
2N/A (strcmp(class, PCIE_PCI_HP_TYPE) == 0)) {
2N/A class = "pci";
2N/A } else {
2N/A goto fail;
2N/A }
2N/A#endif
2N/A (void) snprintf(&name[0], sizeof (name), "%s", "shp");
2N/A
2N/A if (find_lib_impl(name, libloc_p) == CFGA_OK)
2N/A goto found;
2N/Afail:
2N/A return (CFGA_NO_LIB);
2N/A
2N/Afound:
2N/A
2N/A /* Record class name (if any) */
2N/A (void) snprintf(libloc_p->ap_class, sizeof (libloc_p->ap_class), "%s",
2N/A class);
2N/A
2N/A return (CFGA_OK);
2N/A}
2N/A
2N/A/*
2N/A * find_lib_impl - Given an attachment point node find it's library
2N/A */
2N/Astatic cfga_err_t
2N/Afind_lib_impl(
2N/A char *name,
2N/A lib_loc_t *libloc_p)
2N/A{
2N/A char lib[MAXPATHLEN];
2N/A struct stat lib_stat;
2N/A void *dlhandle = NULL;
2N/A static char plat_name[SYSINFO_LENGTH];
2N/A static char machine_name[SYSINFO_LENGTH];
2N/A static char arch_name[SYSINFO_LENGTH];
2N/A
2N/A /*
2N/A * Initialize machine name and arch name
2N/A */
2N/A if (strncmp("", machine_name, MAXPATHLEN) == 0) {
2N/A if (sysinfo(SI_PLATFORM, plat_name, SYSINFO_LENGTH) == -1) {
2N/A return (CFGA_ERROR);
2N/A }
2N/A if (sysinfo(SI_ARCHITECTURE, arch_name, SYSINFO_LENGTH) == -1) {
2N/A return (CFGA_ERROR);
2N/A }
2N/A if (sysinfo(SI_MACHINE, machine_name, SYSINFO_LENGTH) == -1) {
2N/A return (CFGA_ERROR);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Try path based upon platform name
2N/A */
2N/A (void) snprintf(lib, sizeof (lib), "%s%s%s%s%s",
2N/A LIB_PATH_BASE1, plat_name, LIB_PATH_MIDDLE,
2N/A name, LIB_PATH_TAIL);
2N/A
2N/A if (stat(lib, &lib_stat) == 0) {
2N/A /* file exists, is it a lib */
2N/A dlhandle = dlopen(lib, RTLD_LAZY);
2N/A if (dlhandle != NULL) {
2N/A goto found;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Try path based upon machine name
2N/A */
2N/A (void) snprintf(lib, sizeof (lib), "%s%s%s%s%s",
2N/A LIB_PATH_BASE1, machine_name, LIB_PATH_MIDDLE,
2N/A name, LIB_PATH_TAIL);
2N/A
2N/A
2N/A if (stat(lib, &lib_stat) == 0) {
2N/A /* file exists, is it a lib */
2N/A dlhandle = dlopen(lib, RTLD_LAZY);
2N/A if (dlhandle != NULL) {
2N/A goto found;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Try path based upon arch name
2N/A */
2N/A (void) snprintf(lib, sizeof (lib), "%s%s%s%s%s",
2N/A LIB_PATH_BASE1, arch_name, LIB_PATH_MIDDLE,
2N/A name, LIB_PATH_TAIL);
2N/A
2N/A if (stat(lib, &lib_stat) == 0) {
2N/A /* file exists, is it a lib */
2N/A dlhandle = dlopen(lib, RTLD_LAZY);
2N/A if (dlhandle != NULL) {
2N/A goto found;
2N/A }
2N/A
2N/A }
2N/A
2N/A /*
2N/A * Try generic location
2N/A */
2N/A (void) snprintf(lib, sizeof (lib), "%s%s%s%s",
2N/A LIB_PATH_BASE2, LIB_PATH_MIDDLE, name, LIB_PATH_TAIL);
2N/A
2N/A if (stat(lib, &lib_stat) == 0) {
2N/A /* file exists, is it a lib */
2N/A dlhandle = dlopen(lib, RTLD_LAZY);
2N/A if (dlhandle != NULL) {
2N/A goto found;
2N/A }
2N/A
2N/A }
2N/A return (CFGA_NO_LIB);
2N/A
2N/Afound:
2N/A /* we got one! */
2N/A (void) snprintf(libloc_p->pathname, sizeof (libloc_p->pathname), "%s",
2N/A lib);
2N/A
2N/A (void) dlclose(dlhandle);
2N/A
2N/A return (CFGA_OK);
2N/A}
2N/A
2N/Astatic cfga_err_t
2N/Alookup_cache(lib_loc_t *libloc_p)
2N/A{
2N/A lib_cache_t *entry;
2N/A (void) mutex_lock(&lib_cache_lock);
2N/A entry = lib_cache;
2N/A while (entry) {
2N/A if (strcmp(entry->lc_ap_id, libloc_p->ap_base) == 0) {
2N/A plugin_lib_t *libp = entry->lc_libp;
2N/A libloc_p->libp = libp;
2N/A hold_lib(libp);
2N/A (void) strcpy(libloc_p->pathname, libp->libpath);
2N/A (void) strcpy(libloc_p->ap_physical,
2N/A entry->lc_ap_physical);
2N/A (void) strcpy(libloc_p->ap_logical,
2N/A entry->lc_ap_logical);
2N/A (void) mutex_unlock(&lib_cache_lock);
2N/A return (CFGA_OK);
2N/A }
2N/A entry = entry->lc_next;
2N/A }
2N/A (void) mutex_unlock(&lib_cache_lock);
2N/A
2N/A return (CFGA_ERROR);
2N/A}
2N/A
2N/Astatic void
2N/Aupdate_cache(lib_loc_t *libloc_p)
2N/A{
2N/A lib_cache_t *entry;
2N/A entry = config_calloc_check(1, sizeof (lib_cache_t), NULL);
2N/A if (entry == NULL)
2N/A return;
2N/A
2N/A entry->lc_ap_id = strdup(libloc_p->ap_base);
2N/A entry->lc_ap_physical = strdup(libloc_p->ap_physical);
2N/A entry->lc_ap_logical = strdup(libloc_p->ap_logical);
2N/A if ((entry->lc_ap_id == NULL) || (entry->lc_ap_physical == NULL) ||
2N/A (entry->lc_ap_logical == NULL)) {
2N/A free(entry->lc_ap_id);
2N/A free(entry->lc_ap_physical);
2N/A free(entry->lc_ap_logical);
2N/A free(entry);
2N/A return;
2N/A }
2N/A
2N/A (void) mutex_lock(&lib_cache_lock);
2N/A entry->lc_libp = libloc_p->libp;
2N/A entry->lc_next = lib_cache;
2N/A lib_cache = entry;
2N/A hold_lib(entry->lc_libp); /* prevent stale cache */
2N/A (void) mutex_unlock(&lib_cache_lock);
2N/A}
2N/A
2N/Astatic void
2N/Adestroy_cache()
2N/A{
2N/A lib_cache_t *entry, *next;
2N/A (void) mutex_lock(&lib_cache_lock);
2N/A entry = lib_cache;
2N/A while (entry) {
2N/A next = entry->lc_next;
2N/A rele_lib(entry->lc_libp);
2N/A free(entry->lc_ap_id);
2N/A free(entry->lc_ap_physical);
2N/A free(entry->lc_ap_logical);
2N/A free(entry);
2N/A entry = next;
2N/A }
2N/A (void) mutex_unlock(&lib_cache_lock);
2N/A}
2N/A
2N/A/*
2N/A * find_ap_common - locate a particular attachment point
2N/A */
2N/Astatic cfga_err_t
2N/Afind_ap_common(
2N/A lib_loc_t *libloc_p,
2N/A const char *physpath,
2N/A int (*fcn)(di_node_t node, di_minor_t minor, void *arg),
2N/A int (*fcn_hp)(di_node_t node, di_hp_t hp, void *arg),
2N/A char **errstring)
2N/A{
2N/A di_node_t rnode, wnode;
2N/A char *cp, *rpath;
2N/A size_t len;
2N/A
2N/A if (lookup_cache(libloc_p) == CFGA_OK)
2N/A return (CFGA_OK);
2N/A
2N/A if ((rpath = config_calloc_check(1, strlen(physpath) + 1,
2N/A errstring)) == NULL) {
2N/A return (CFGA_LIB_ERROR);
2N/A }
2N/A
2N/A (void) strcpy(rpath, physpath);
2N/A
2N/A /* Remove devices prefix (if any) */
2N/A len = strlen(DEVICES_DIR);
2N/A if (strncmp(rpath, DEVICES_DIR SLASH, len + strlen(SLASH)) == 0) {
2N/A (void) memmove(rpath, rpath + len,
2N/A strlen(rpath + len) + 1);
2N/A }
2N/A
2N/A /* Remove dynamic component if any */
2N/A if ((cp = GET_DYN(rpath)) != NULL) {
2N/A *cp = '\0';
2N/A }
2N/A
2N/A /* Remove minor name (if any) */
2N/A if ((cp = strrchr(rpath, ':')) != NULL) {
2N/A *cp = '\0';
2N/A }
2N/A
2N/A /*
2N/A * begin walk of device tree
2N/A *
2N/A * Since we create minor nodes & dev links for both all PCI/PCIE
2N/A * connectors, but only create hp nodes for PCIE/PCISHPC connectors
2N/A * of the new framework, we should first match with hp nodes. If
2N/A * the ap_id refers to a PCIE/PCISHPC connector, we'll be able to
2N/A * find it here.
2N/A */
2N/A rnode = di_init("/", DINFOSUBTREE | DINFOHP);
2N/A if (rnode)
2N/A wnode = di_lookup_node(rnode, rpath);
2N/A else
2N/A wnode = DI_NODE_NIL;
2N/A
2N/A if (wnode == DI_NODE_NIL) {
2N/A if (rnode == DI_NODE_NIL) {
2N/A S_FREE(rpath);
2N/A config_err(errno, DI_INIT_FAILED, errstring);
2N/A return (CFGA_LIB_ERROR);
2N/A } else {
2N/A /*
2N/A * di_lookup_node() may fail, either because the
2N/A * ap_id does not exist, or because the ap_id refers
2N/A * to a legacy PCI slot, thus we'll not able to
2N/A * find node using DINFOHP, try to see if we can
2N/A * find one using DINFOCACHE.
2N/A */
2N/A di_fini(rnode);
2N/A goto find_minor;
2N/A }
2N/A }
2N/A
2N/A libloc_p->libp = NULL;
2N/A libloc_p->status = CFGA_APID_NOEXIST;
2N/A
2N/A (void) di_walk_hp(wnode, NULL, DI_HP_CONNECTOR,
2N/A libloc_p, fcn_hp);
2N/A
2N/A di_fini(rnode);
2N/A
2N/A /*
2N/A * Failed to find a matching hp node, try minor node.
2N/A */
2N/A if (libloc_p->libp == NULL) {
2N/Afind_minor:
2N/A rnode = di_init("/", DINFOCACHE);
2N/A if (rnode)
2N/A wnode = di_lookup_node(rnode, rpath);
2N/A else
2N/A wnode = DI_NODE_NIL;
2N/A
2N/A if (wnode == DI_NODE_NIL) {
2N/A if (rnode == DI_NODE_NIL) {
2N/A S_FREE(rpath);
2N/A config_err(errno, DI_INIT_FAILED, errstring);
2N/A return (CFGA_LIB_ERROR);
2N/A } else {
2N/A /*
2N/A * di_lookup_node() may fail, because the
2N/A * ap_id does not exist.
2N/A */
2N/A S_FREE(rpath);
2N/A di_fini(rnode);
2N/A return (CFGA_APID_NOEXIST);
2N/A }
2N/A }
2N/A
2N/A libloc_p->libp = NULL;
2N/A libloc_p->status = CFGA_APID_NOEXIST;
2N/A
2N/A (void) di_walk_minor(wnode, "ddi_ctl:attachment_point",
2N/A DI_CHECK_ALIAS|DI_CHECK_INTERNAL_PATH,
2N/A libloc_p, fcn);
2N/A
2N/A di_fini(rnode);
2N/A }
2N/A
2N/A S_FREE(rpath);
2N/A
2N/A if (libloc_p->libp != NULL) {
2N/A update_cache(libloc_p);
2N/A return (CFGA_OK);
2N/A } else {
2N/A return (libloc_p->status);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * check_ap - called for each non-SHP attachment point found
2N/A */
2N/Astatic int
2N/Acheck_ap(
2N/A di_node_t node,
2N/A di_minor_t minor,
2N/A void *arg)
2N/A{
2N/A return (check_ap_impl(node, minor, NULL, arg));
2N/A}
2N/A
2N/A/*
2N/A * check_ap_hp - called for each SHP attachment point found
2N/A */
2N/Astatic int
2N/Acheck_ap_hp(
2N/A di_node_t node,
2N/A di_hp_t hp,
2N/A void *arg)
2N/A{
2N/A return (check_ap_impl(node, NULL, hp, arg));
2N/A}
2N/A
2N/A/*
2N/A * check_ap_impl - called for each attachment point found
2N/A *
2N/A * This is used in cases where a particular attachment point
2N/A * or type of attachment point is specified via a logical name or ap_type.
2N/A * Not used for physical names or in the list case with no
2N/A * ap's specified.
2N/A */
2N/Astatic int
2N/Acheck_ap_impl(
2N/A di_node_t node,
2N/A di_minor_t minor,
2N/A di_hp_t hp,
2N/A void *arg)
2N/A{
2N/A char *cp = NULL;
2N/A char aptype[MAXPATHLEN];
2N/A char *recep_id = NULL;
2N/A char *node_minor;
2N/A char *drv_name;
2N/A char inst[MAXPATHLEN];
2N/A char inst2[MAXPATHLEN];
2N/A lib_loc_t *libloc_p;
2N/A int comparison_test;
2N/A int instance;
2N/A cfga_ap_types_t type;
2N/A
2N/A if (minor != DI_MINOR_NIL && hp != DI_HP_NIL)
2N/A return (DI_WALK_CONTINUE);
2N/A
2N/A libloc_p = (lib_loc_t *)arg;
2N/A
2N/A (void) snprintf(aptype, sizeof (aptype), "%s", libloc_p->ap_base);
2N/A
2N/A /*
2N/A * This routime handles only aptypes and driver based logical apids.
2N/A */
2N/A type = find_arg_type(aptype);
2N/A if (type == LOGICAL_DRV_AP) {
2N/A cp = strchr(aptype, ':');
2N/A *cp = '\0';
2N/A recep_id = cp+1;
2N/A cp--;
2N/A while (isdigit(*cp) && cp != aptype)
2N/A cp--;
2N/A cp++;
2N/A
2N/A (void) snprintf(inst, sizeof (inst), "%s", cp);
2N/A
2N/A *cp = '\0';
2N/A } else if (type != AP_TYPE) {
2N/A libloc_p->status = CFGA_APID_NOEXIST;
2N/A return (DI_WALK_CONTINUE);
2N/A }
2N/A
2N/A if (minor != DI_MINOR_NIL)
2N/A node_minor = di_minor_name(minor);
2N/A else
2N/A node_minor = di_hp_name(hp);
2N/A
2N/A drv_name = di_driver_name(node);
2N/A instance = di_instance(node);
2N/A
2N/A if (node_minor == NULL || drv_name == NULL || instance == -1) {
2N/A libloc_p->status = CFGA_APID_NOEXIST;
2N/A return (DI_WALK_CONTINUE);
2N/A }
2N/A
2N/A (void) sprintf(inst2, "%d", instance);
2N/A
2N/A /*
2N/A * If the base matches driver and instance try and find a lib for it,
2N/A * then load it. On any failure we continue the walk.
2N/A *
2N/A * driver based logical ap_ids are derived from driver name + instance.
2N/A * Ap_types are just partial driver names.
2N/A *
2N/A */
2N/A
2N/A comparison_test = 0;
2N/A if (type == AP_TYPE) {
2N/A if (strncmp(aptype, drv_name, strlen(aptype)) == 0) {
2N/A comparison_test = 1;
2N/A }
2N/A } else {
2N/A if (strcmp(aptype, drv_name) == 0 &&
2N/A strcmp(recep_id, node_minor) == 0 &&
2N/A strcmp(inst, inst2) == 0) {
2N/A comparison_test = 1;
2N/A }
2N/A }
2N/A
2N/A if (comparison_test) {
2N/A /*
2N/A * save the correct type of error so user does not get confused
2N/A */
2N/A if (minor != DI_MINOR_NIL) {
2N/A if (find_lib(node, minor, libloc_p) != CFGA_OK) {
2N/A libloc_p->status = CFGA_NO_LIB;
2N/A return (DI_WALK_CONTINUE);
2N/A }
2N/A if (load_lib(node, minor, libloc_p) != CFGA_OK) {
2N/A libloc_p->status = CFGA_LIB_ERROR;
2N/A return (DI_WALK_CONTINUE);
2N/A }
2N/A } else {
2N/A if (find_lib_hp(node, hp, libloc_p) != CFGA_OK) {
2N/A libloc_p->status = CFGA_NO_LIB;
2N/A return (DI_WALK_CONTINUE);
2N/A }
2N/A if (load_lib_hp(node, hp, libloc_p) != CFGA_OK) {
2N/A libloc_p->status = CFGA_LIB_ERROR;
2N/A return (DI_WALK_CONTINUE);
2N/A }
2N/A }
2N/A libloc_p->status = CFGA_OK;
2N/A return (DI_WALK_TERMINATE);
2N/A } else {
2N/A libloc_p->status = CFGA_APID_NOEXIST;
2N/A return (DI_WALK_CONTINUE);
2N/A }
2N/A}
2N/A
2N/A
2N/A/*
2N/A * check_ap_phys - called for each non-SHP attachment point found
2N/A */
2N/Astatic int
2N/Acheck_ap_phys(
2N/A di_node_t node,
2N/A di_minor_t minor,
2N/A void *arg)
2N/A{
2N/A return (check_ap_phys_impl(node, minor, DI_HP_NIL, arg));
2N/A}
2N/A
2N/A/*
2N/A * check_ap_phys_hp - called for each SHP attachment point found
2N/A */
2N/Astatic int
2N/Acheck_ap_phys_hp(
2N/A di_node_t node,
2N/A di_hp_t hp,
2N/A void *arg)
2N/A{
2N/A return (check_ap_phys_impl(node, DI_HP_NIL, hp, arg));
2N/A}
2N/A
2N/A/*
2N/A * check_ap_phys_impl - called for each attachment point found
2N/A *
2N/A * This is used in cases where a particular attachment point
2N/A * is specified via a physical name. If the name matches then
2N/A * we try and find and load the library for it.
2N/A */
2N/Astatic int
2N/Acheck_ap_phys_impl(
2N/A di_node_t node,
2N/A di_minor_t minor,
2N/A di_hp_t hp,
2N/A void *arg)
2N/A{
2N/A lib_loc_t *libloc_p;
2N/A char phys_name[MAXPATHLEN];
2N/A char *devfs_path;
2N/A char *minor_name;
2N/A
2N/A if (minor != DI_MINOR_NIL && hp != DI_HP_NIL)
2N/A return (DI_WALK_CONTINUE);
2N/A
2N/A libloc_p = (lib_loc_t *)arg;
2N/A devfs_path = di_devfs_path(node);
2N/A if (minor != DI_MINOR_NIL)
2N/A minor_name = di_minor_name(minor);
2N/A else
2N/A minor_name = di_hp_name(hp);
2N/A
2N/A if (devfs_path == NULL || minor_name == NULL) {
2N/A libloc_p->status = CFGA_APID_NOEXIST;
2N/A return (DI_WALK_CONTINUE);
2N/A }
2N/A
2N/A (void) snprintf(phys_name, sizeof (phys_name), "%s%s:%s",
2N/A DEVICES_DIR, devfs_path, minor_name);
2N/A
2N/A di_devfs_path_free(devfs_path);
2N/A
2N/A if (strcmp(phys_name, libloc_p->ap_base) == 0) {
2N/A if (minor != DI_MINOR_NIL) {
2N/A if (find_lib(node, minor, libloc_p) != CFGA_OK) {
2N/A libloc_p->status = CFGA_NO_LIB;
2N/A return (DI_WALK_CONTINUE);
2N/A }
2N/A if (load_lib(node, minor, libloc_p) != CFGA_OK) {
2N/A libloc_p->status = CFGA_LIB_ERROR;
2N/A return (DI_WALK_CONTINUE);
2N/A }
2N/A } else {
2N/A if (find_lib_hp(node, hp, libloc_p) != CFGA_OK) {
2N/A libloc_p->status = CFGA_NO_LIB;
2N/A return (DI_WALK_CONTINUE);
2N/A }
2N/A if (load_lib_hp(node, hp, libloc_p) != CFGA_OK) {
2N/A libloc_p->status = CFGA_LIB_ERROR;
2N/A return (DI_WALK_CONTINUE);
2N/A }
2N/A }
2N/A
2N/A libloc_p->status = CFGA_OK;
2N/A return (DI_WALK_TERMINATE);
2N/A } else {
2N/A libloc_p->status = CFGA_APID_NOEXIST;
2N/A return (DI_WALK_CONTINUE);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * lib_in_list
2N/A *
2N/A * See if library, as specified by the full pathname and controller
2N/A * instance number is already represented in the plugin library list.
2N/A * If the instance number is -1 it is ignored.
2N/A */
2N/Astatic plugin_lib_t *
2N/Alib_in_list(char *libpath)
2N/A{
2N/A plugin_lib_t *libp = NULL;
2N/A
2N/A for (libp = plugin_list.next; libp != NULL; libp = libp->next) {
2N/A if (strncmp(libpath, libp->libpath, MAXPATHLEN) == 0) {
2N/A return (libp);
2N/A }
2N/A }
2N/A return (NULL);
2N/A}
2N/A
2N/A
2N/A
2N/A
2N/A/*
2N/A * Coalesce stat and list data into single array
2N/A */
2N/Astatic cfga_err_t
2N/Arealloc_data_ext(
2N/A cfga_list_data_t **ap_id_list,
2N/A int *nlistp,
2N/A list_stat_t *lstatp)
2N/A{
2N/A int i, j;
2N/A stat_data_list_t *slp;
2N/A cfga_list_data_t *cldp;
2N/A array_list_t *alp;
2N/A cfga_err_t rc = CFGA_OK;
2N/A
2N/A
2N/A assert(*lstatp->countp >= 0);
2N/A
2N/A if (*lstatp->countp == 0) {
2N/A *ap_id_list = NULL;
2N/A *nlistp = 0;
2N/A return (CFGA_OK);
2N/A }
2N/A
2N/A /*
2N/A * allocate the array
2N/A */
2N/A if ((cldp = config_calloc_check(*lstatp->countp,
2N/A sizeof (cfga_list_data_t), lstatp->errstr)) == NULL) {
2N/A rc = CFGA_LIB_ERROR;
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * copy all the stat elements (if any) into the array
2N/A */
2N/A slp = lstatp->sdl;
2N/A for (i = 0; slp != NULL; i++) {
2N/A if (i >= *lstatp->countp) {
2N/A rc = CFGA_LIB_ERROR;
2N/A goto out;
2N/A }
2N/A stat_to_list(&cldp[i], &slp->stat_data);
2N/A slp = slp->next;
2N/A }
2N/A
2N/A /*
2N/A * copy all the list elements (if any) into the array
2N/A */
2N/A alp = lstatp->al;
2N/A for (; alp != NULL; ) {
2N/A if (i + alp->nelem > *lstatp->countp) {
2N/A rc = CFGA_LIB_ERROR;
2N/A goto out;
2N/A }
2N/A
2N/A for (j = 0; j < alp->nelem; i++, j++) {
2N/A cldp[i] = alp->array[j];
2N/A }
2N/A alp = alp->next;
2N/A }
2N/A
2N/A if (i != *lstatp->countp) {
2N/A rc = CFGA_LIB_ERROR;
2N/A } else {
2N/A rc = CFGA_OK;
2N/A }
2N/A
2N/A /*FALLTHRU*/
2N/A
2N/Aout:
2N/A /* clean up */
2N/A lstat_free(lstatp);
2N/A
2N/A if (rc == CFGA_OK) {
2N/A *ap_id_list = cldp;
2N/A *nlistp = *lstatp->countp;
2N/A } else {
2N/A S_FREE(cldp);
2N/A *ap_id_list = NULL;
2N/A *nlistp = 0;
2N/A }
2N/A return (rc);
2N/A}
2N/A
2N/A/*
2N/A * The caller of this routine may supply a buffer through
2N/A * ap_id_list for returning data. Otherwise, this routine allocates the
2N/A * buffer.
2N/A */
2N/Astatic cfga_err_t
2N/Arealloc_data(cfga_stat_data_t **ap_id_list, int *nlistp, list_stat_t *lstatp)
2N/A{
2N/A int i;
2N/A stat_data_list_t *slp;
2N/A cfga_stat_data_t *csdp, *buf;
2N/A cfga_err_t rc;
2N/A
2N/A
2N/A assert(*lstatp->countp >= 0);
2N/A
2N/A if (*lstatp->countp == 0) {
2N/A *nlistp = 0;
2N/A return (CFGA_OK);
2N/A }
2N/A
2N/A
2N/A /*
2N/A * allocate the array if caller does not supply one.
2N/A */
2N/A if (*ap_id_list == NULL) {
2N/A if ((buf = config_calloc_check(*lstatp->countp,
2N/A sizeof (cfga_stat_data_t), lstatp->errstr)) == NULL) {
2N/A rc = CFGA_LIB_ERROR;
2N/A goto out;
2N/A }
2N/A } else {
2N/A buf = *ap_id_list;
2N/A }
2N/A
2N/A /*
2N/A * copy the stat elements into the array
2N/A */
2N/A csdp = buf;
2N/A slp = lstatp->sdl;
2N/A for (i = 0; slp != NULL; i++) {
2N/A if (i >= *lstatp->countp) {
2N/A rc = CFGA_LIB_ERROR;
2N/A goto out;
2N/A }
2N/A *csdp++ = slp->stat_data;
2N/A slp = slp->next;
2N/A }
2N/A
2N/A rc = CFGA_OK;
2N/A
2N/Aout:
2N/A if (rc == CFGA_OK) {
2N/A *nlistp = *lstatp->countp;
2N/A *ap_id_list = buf;
2N/A } else {
2N/A /*
2N/A * Free buffer only if we allocated it.
2N/A */
2N/A if (*ap_id_list == NULL) {
2N/A free(buf);
2N/A }
2N/A *nlistp = 0;
2N/A }
2N/A
2N/A assert(lstatp->al == NULL);
2N/A lstat_free(lstatp);
2N/A
2N/A return (rc);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * list_common - walk the device tree and stat all attachment points.
2N/A */
2N/Astatic cfga_err_t
2N/Alist_common(list_stat_t *lstatp, const char *class)
2N/A{
2N/A di_node_t rnode;
2N/A char nodetype[MAXPATHLEN];
2N/A const char *l_class, *l_sep;
2N/A
2N/A /*
2N/A * May walk a subset of all attachment points in the device tree if
2N/A * a class is specified
2N/A */
2N/A if (class != NULL) {
2N/A l_sep = ":";
2N/A l_class = class;
2N/A } else {
2N/A l_sep = l_class = "";
2N/A }
2N/A
2N/A (void) snprintf(nodetype, sizeof (nodetype), "%s%s%s",
2N/A DDI_NT_ATTACHMENT_POINT, l_sep, l_class);
2N/A
2N/A /*
2N/A * Walk all hp nodes
2N/A */
2N/A if ((rnode = di_init("/", DINFOSUBTREE | DINFOHP)) == DI_NODE_NIL) {
2N/A config_err(errno, DI_INIT_FAILED, lstatp->errstr);
2N/A return (CFGA_LIB_ERROR);
2N/A }
2N/A /* No need to filter on class for now */
2N/A (void) di_walk_hp(rnode, NULL, DI_HP_CONNECTOR,
2N/A lstatp, do_list_common_hp);
2N/A
2N/A di_fini(rnode);
2N/A
2N/A /*
2N/A * Walk all minor nodes
2N/A * but exclude PCIE/PCIESHPC connectors which have been walked above.
2N/A */
2N/A if ((rnode = di_init("/", DINFOCACHE)) == DI_NODE_NIL) {
2N/A config_err(errno, DI_INIT_FAILED, lstatp->errstr);
2N/A return (CFGA_LIB_ERROR);
2N/A }
2N/A (void) di_walk_minor(rnode, nodetype,
2N/A DI_CHECK_ALIAS|DI_CHECK_INTERNAL_PATH, lstatp, do_list_common);
2N/A
2N/A di_fini(rnode);
2N/A
2N/A if (lstatp->shp_errstr != NULL) {
2N/A *(lstatp->errstr) = strdup(lstatp->shp_errstr);
2N/A free(lstatp->shp_errstr);
2N/A lstatp->shp_errstr = NULL;
2N/A }
2N/A
2N/A return (CFGA_OK);
2N/A}
2N/A
2N/Astatic void
2N/Aconfig_err(int errnum, int err_type, char **errstring)
2N/A{
2N/A char *p = NULL, *q = NULL;
2N/A char *syserr = NULL;
2N/A char syserr_num[20];
2N/A int len = 0;
2N/A
2N/A /*
2N/A * If errstring is null it means user in not interested in getting
2N/A * error status. So we don't do all the work
2N/A */
2N/A if (errstring == NULL) {
2N/A return;
2N/A }
2N/A
2N/A if (errnum != 0) {
2N/A syserr = strerror(errnum);
2N/A if (syserr == NULL) {
2N/A (void) sprintf(syserr_num, "errno=%d", errnum);
2N/A syserr = syserr_num;
2N/A }
2N/A } else
2N/A syserr = NULL;
2N/A
2N/A q = dgettext(TEXT_DOMAIN, err_strings[err_type]);
2N/A
2N/A len = strlen(q);
2N/A if (syserr != NULL) {
2N/A len += strlen(err_sep) + strlen(syserr);
2N/A }
2N/A
2N/A p = malloc(len + 1);
2N/A if (p == NULL) {
2N/A *errstring = NULL;
2N/A return;
2N/A }
2N/A
2N/A (void) strcpy(p, q);
2N/A if (syserr != NULL) {
2N/A (void) strcat(p, err_sep);
2N/A (void) strcat(p, syserr);
2N/A }
2N/A
2N/A *errstring = p;
2N/A}
2N/A
2N/A/*
2N/A * do_list_common - list non-SHP attachment point
2N/A */
2N/Astatic int
2N/Ado_list_common(
2N/A di_node_t node,
2N/A di_minor_t minor,
2N/A void *arg)
2N/A{
2N/A di_node_t rnode;
2N/A di_hp_t hp;
2N/A char *minor_name;
2N/A
2N/A minor_name = di_minor_name(minor);
2N/A
2N/A /*
2N/A * since PCIE/PCIHSHPC connectors have both hp nodes and minor nodes
2N/A * created for now, we need to specifically exclude these connectors
2N/A * during walking minor nodes.
2N/A */
2N/A if ((rnode = di_init(di_devfs_path(node), DINFOSUBTREE | DINFOHP))
2N/A == DI_NODE_NIL) {
2N/A return (DI_WALK_CONTINUE);
2N/A }
2N/A
2N/A for (hp = DI_HP_NIL; (hp = di_hp_next(rnode, hp)) != DI_HP_NIL; ) {
2N/A if (strcmp(di_hp_name(hp), minor_name) == 0) {
2N/A di_fini(rnode);
2N/A return (DI_WALK_CONTINUE);
2N/A }
2N/A }
2N/A
2N/A di_fini(rnode);
2N/A
2N/A return (do_list_common_impl(node, minor, NULL, arg));
2N/A}
2N/A
2N/A/*
2N/A * do_list_common_hp - list SHP attachment point
2N/A */
2N/Astatic int
2N/Ado_list_common_hp(
2N/A di_node_t node,
2N/A di_hp_t hp,
2N/A void *arg)
2N/A{
2N/A return (do_list_common_impl(node, NULL, hp, arg));
2N/A}
2N/A
2N/A/*
2N/A * do_list_common_impl - Routine to list attachment point as part of
2N/A * a config_list opertion. Used by both v1 and v2 interfaces.
2N/A * This is somewhat similar to config_get_lib() and its helper routines
2N/A * except that the ap_ids are always physical and don't have dynamic
2N/A * components.
2N/A */
2N/Astatic int
2N/Ado_list_common_impl(
2N/A di_node_t node,
2N/A di_minor_t minor,
2N/A di_hp_t hp,
2N/A void *arg)
2N/A{
2N/A lib_loc_t lib_loc;
2N/A plugin_lib_t *libp;
2N/A list_stat_t *lstatp = NULL;
2N/A cfga_err_t ret = CFGA_ERROR;
2N/A
2N/A if (minor != DI_MINOR_NIL && hp != DI_HP_NIL)
2N/A return (DI_WALK_CONTINUE);
2N/A
2N/A lstatp = (list_stat_t *)arg;
2N/A
2N/A lib_loc.libp = NULL;
2N/A /*
2N/A * try and find a lib for this node
2N/A */
2N/A if (minor != DI_MINOR_NIL) {
2N/A ret = find_lib(node, minor, &lib_loc);
2N/A } else {
2N/A ret = find_lib_hp(node, hp, &lib_loc);
2N/A }
2N/A if (ret != CFGA_OK) {
2N/A return (DI_WALK_CONTINUE);
2N/A }
2N/A
2N/A /*
2N/A * Load all plugins. We will check compatibility later in this
2N/A * routine.
2N/A */
2N/A lib_loc.vers_req.v_min = CFGA_HSL_V1;
2N/A lib_loc.vers_req.v_max = CFGA_HSL_VERS;
2N/A
2N/A if (minor != DI_MINOR_NIL) {
2N/A ret = load_lib(node, minor, &lib_loc);
2N/A } else {
2N/A ret = load_lib_hp(node, hp, &lib_loc);
2N/A }
2N/A if (ret != CFGA_OK) {
2N/A return (DI_WALK_CONTINUE);
2N/A }
2N/A
2N/A libp = lib_loc.libp;
2N/A assert(libp != NULL);
2N/A
2N/A /*
2N/A * Note: For list type routines (list all attachment points in
2N/A * device tree) we don't pass errstring to the plugin, nor do we
2N/A * stop the walk if an error occurs in the plugin.
2N/A */
2N/A if (compat_plugin(&lstatp->use_vers, libp->plugin_vers)) {
2N/A if (minor != DI_MINOR_NIL) {
2N/A (void) libp->vers_ops->stat_plugin(lstatp,
2N/A &lib_loc, NULL);
2N/A } else {
2N/A /*
2N/A * If the underlying hotplug daemon is not enabled,
2N/A * the SHP attach points will not be shown, this
2N/A * could confuse the uesrs. We specifically pass the
2N/A * errstring to SHP plugin so that it can set the
2N/A * errstring accordingly in this case, giving users
2N/A * a hint.
2N/A */
2N/A ret = libp->vers_ops->stat_plugin(lstatp,
2N/A &lib_loc, lstatp->errstr);
2N/A if (ret == CFGA_NOTSUPP && *(lstatp->errstr) != NULL) {
2N/A if (lstatp->shp_errstr == NULL) {
2N/A lstatp->shp_errstr =
2N/A strdup(*(lstatp->errstr));
2N/A }
2N/A }
2N/A
2N/A if (*(lstatp->errstr) != NULL) {
2N/A free(*(lstatp->errstr));
2N/A *(lstatp->errstr) = NULL;
2N/A }
2N/A }
2N/A }
2N/A rele_lib(libp);
2N/A
2N/A return (DI_WALK_CONTINUE);
2N/A}
2N/A
2N/A/*
2N/A * stat_common - stat a user specified set of attachment points.
2N/A */
2N/Astatic cfga_err_t
2N/Astat_common(
2N/A int num_ap_ids,
2N/A char *const *ap_ids,
2N/A const char *class,
2N/A list_stat_t *lstatp)
2N/A{
2N/A int i;
2N/A lib_loc_t libloc;
2N/A plugin_lib_t *libp;
2N/A cfga_err_t rc = CFGA_OK;
2N/A
2N/A
2N/A /*
2N/A * operate on each ap_id
2N/A */
2N/A for (i = 0; i < num_ap_ids; i++) {
2N/A libloc.libp = NULL;
2N/A if ((rc = config_get_lib(ap_ids[i], &libloc,
2N/A lstatp->errstr)) != CFGA_OK) {
2N/A break;
2N/A }
2N/A assert(libloc.libp != NULL);
2N/A libp = libloc.libp;
2N/A
2N/A /*
2N/A * do pre-filtering if requested
2N/A */
2N/A if (class != NULL && strcmp(libloc.ap_class, class)) {
2N/A rele_lib(libp);
2N/A continue;
2N/A }
2N/A
2N/A /*
2N/A * Unlike list type routines, while stat'ing specific
2N/A * attachment points we pass errstring to the plugins
2N/A * and halt if an error occurs in the plugin.
2N/A */
2N/A rc = libp->vers_ops->stat_plugin(lstatp, &libloc,
2N/A lstatp->errstr);
2N/A rele_lib(libp);
2N/A if (rc != CFGA_OK) {
2N/A break;
2N/A }
2N/A }
2N/A
2N/A if (rc != CFGA_OK) {
2N/A lstat_free(lstatp);
2N/A }
2N/A return (rc);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic cfga_err_t
2N/Anull_stat_plugin(list_stat_t *lstatp, lib_loc_t *libloc_p, char **errstring)
2N/A{
2N/A return (CFGA_OK);
2N/A}
2N/A
2N/A/*
2N/A * Pass errstring as a separate argument. Some higher level routines need
2N/A * it to be NULL.
2N/A */
2N/Astatic cfga_err_t
2N/Astat_plugin_v1(list_stat_t *lstatp, lib_loc_t *libloc_p, char **errstring)
2N/A{
2N/A stat_data_list_t *slp, *slp2 = NULL;
2N/A cfga_err_t rc;
2N/A
2N/A /*
2N/A * allocate stat data buffer and list element
2N/A */
2N/A if ((slp = config_calloc_check(1, sizeof (stat_data_list_t),
2N/A errstring)) == NULL) {
2N/A return (CFGA_LIB_ERROR);
2N/A }
2N/A
2N/A /*
2N/A * Do the stat
2N/A */
2N/A errno = 0;
2N/A if ((rc = (*(libloc_p->libp->cfga_stat_p))(libloc_p->ap_physical,
2N/A &slp->stat_data, lstatp->opts, errstring)) != CFGA_OK) {
2N/A S_FREE(slp);
2N/A return (rc);
2N/A }
2N/A slp->next = NULL;
2N/A
2N/A /*
2N/A * Set up the logical and physical id's.
2N/A * For v1 interfaces, the generic library (libcfgadm) creates the
2N/A * ap_ids. mklog() is assumed to have been called in
2N/A * the caller of this routine.
2N/A */
2N/A (void) snprintf(slp->stat_data.ap_log_id, CFGA_AP_LOG_ID_LEN, "%s",
2N/A libloc_p->ap_logical);
2N/A
2N/A (void) snprintf(slp->stat_data.ap_phys_id, CFGA_AP_PHYS_ID_LEN, "%s",
2N/A libloc_p->ap_physical);
2N/A
2N/A /*
2N/A * link it in
2N/A */
2N/A if ((slp2 = lstatp->sdl) == NULL) {
2N/A lstatp->sdl = slp;
2N/A } else {
2N/A while (slp2->next != NULL)
2N/A slp2 = slp2->next;
2N/A slp2->next = slp;
2N/A }
2N/A
2N/A /* keep count */
2N/A (*lstatp->countp)++;
2N/A
2N/A return (CFGA_OK);
2N/A}
2N/A
2N/Astatic cfga_err_t
2N/Astat_plugin_v2(list_stat_t *lstatp, lib_loc_t *libloc_p, char **errstring)
2N/A{
2N/A int i;
2N/A array_list_t *alp, *alp2 = NULL;
2N/A cfga_err_t rc;
2N/A char *class;
2N/A
2N/A /*
2N/A * allocate array list
2N/A */
2N/A if ((alp = config_calloc_check(1, sizeof (array_list_t),
2N/A errstring)) == NULL) {
2N/A return (CFGA_LIB_ERROR);
2N/A }
2N/A
2N/A alp->array = NULL;
2N/A alp->nelem = 0;
2N/A
2N/A /*
2N/A * The listopts argument is currently unused. Use NULL
2N/A */
2N/A errno = 0;
2N/A if ((rc = (*(libloc_p->libp->cfga_list_ext_p))(
2N/A libloc_p->ap_physical, &alp->array, &alp->nelem, lstatp->opts, NULL,
2N/A errstring, lstatp->flags)) != CFGA_OK || alp->nelem <= 0) {
2N/A S_FREE(alp);
2N/A return (rc);
2N/A }
2N/A alp->next = NULL;
2N/A
2N/A /*
2N/A * Set up the logical and physical id's if necessary.
2N/A * For v2 interfaces, the generic library (libcfgadm) creates the
2N/A * ap_ids only if there are no dynamic attachment points and the
2N/A * plug-in does not create the name itself. mklog() is
2N/A * assumed to have been called in the caller of this routine.
2N/A */
2N/A if (alp->nelem == 1) {
2N/A char cphys, clog;
2N/A
2N/A clog = (alp->array[0]).ap_log_id[0];
2N/A cphys = (alp->array[0]).ap_phys_id[0];
2N/A
2N/A if (clog == '\0') {
2N/A (void) snprintf((alp->array[0]).ap_log_id,
2N/A sizeof ((alp->array[0]).ap_log_id), "%s",
2N/A libloc_p->ap_logical);
2N/A }
2N/A
2N/A if (cphys == '\0') {
2N/A (void) snprintf((alp->array[0]).ap_phys_id,
2N/A sizeof ((alp->array[0]).ap_phys_id), "%s",
2N/A libloc_p->ap_physical);
2N/A }
2N/A }
2N/A
2N/A if (libloc_p->ap_class[0] == '\0') {
2N/A class = CFGA_NO_CLASS;
2N/A } else {
2N/A class = libloc_p->ap_class;
2N/A }
2N/A
2N/A /* Fill in the class information for all list elements */
2N/A for (i = 0; i < alp->nelem; i++) {
2N/A (void) snprintf((alp->array[i]).ap_class,
2N/A sizeof ((alp->array[i]).ap_class), "%s", class);
2N/A }
2N/A
2N/A /*
2N/A * link it in
2N/A */
2N/A if ((alp2 = lstatp->al) == NULL) {
2N/A lstatp->al = alp;
2N/A } else {
2N/A while (alp2->next != NULL)
2N/A alp2 = alp2->next;
2N/A alp2->next = alp;
2N/A }
2N/A
2N/A /* keep count */
2N/A (*lstatp->countp) += alp->nelem;
2N/A
2N/A return (CFGA_OK);
2N/A}
2N/A
2N/A/*
2N/A * Check if a plugin version is within requested limits.
2N/A */
2N/Astatic int
2N/Acompat_plugin(vers_req_t *reqp, int plugin_vers)
2N/A{
2N/A
2N/A if (!VALID_HSL_VERS(reqp->v_min) || !VALID_HSL_VERS(reqp->v_max) ||
2N/A !VALID_HSL_VERS(plugin_vers)) {
2N/A return (0);
2N/A }
2N/A
2N/A if (plugin_vers < reqp->v_min || plugin_vers > reqp->v_max) {
2N/A return (0);
2N/A }
2N/A
2N/A
2N/A return (1);
2N/A}
2N/A
2N/A/*
2N/A * find_arg_type - determine if an argument is an ap_id or an ap_type.
2N/A * Adapted from cfgadm.c
2N/A */
2N/Astatic cfga_ap_types_t
2N/Afind_arg_type(const char *ap_id)
2N/A{
2N/A struct stat sbuf;
2N/A cfga_ap_types_t type = UNKNOWN_AP;
2N/A char *mkr = NULL;
2N/A size_t len;
2N/A int size_ap = 0, size_mkr = 0, digit = 0, i = 0;
2N/A char *cp, path[MAXPATHLEN], ap_base[MAXPATHLEN];
2N/A
2N/A
2N/A /*
2N/A * sanity checks
2N/A */
2N/A if (ap_id == NULL || *ap_id == '\0') {
2N/A
2N/A return (UNKNOWN_AP);
2N/A }
2N/A
2N/A /*
2N/A * Extract the base component
2N/A */
2N/A if ((cp = GET_DYN(ap_id)) != NULL) {
2N/A len = cp - ap_id;
2N/A } else {
2N/A len = strlen(ap_id);
2N/A }
2N/A
2N/A if (len >= sizeof (ap_base)) {
2N/A return (UNKNOWN_AP);
2N/A }
2N/A
2N/A /* Copy only the first "len" chars */
2N/A (void) strncpy(ap_base, ap_id, len);
2N/A ap_base[len] = '\0';
2N/A
2N/A /*
2N/A * If it starts with a slash and is stat-able its a physical.
2N/A */
2N/A if (*ap_base == '/' && stat(ap_base, &sbuf) == 0) {
2N/A return (PHYSICAL_AP);
2N/A }
2N/A
2N/A /*
2N/A * Is this a symlink in CFGA_DEV_DIR ?
2N/A */
2N/A (void) snprintf(path, sizeof (path), "%s%s",
2N/A CFGA_DEV_DIR SLASH, ap_base);
2N/A
2N/A if (lstat(path, &sbuf) == 0 && S_ISLNK(sbuf.st_mode) &&
2N/A stat(path, &sbuf) == 0) {
2N/A return (LOGICAL_LINK_AP);
2N/A }
2N/A
2N/A /*
2N/A * Check for ":" which is always present in an ap_id
2N/A * but not in an ap_type.
2N/A * we need to check that the characters right before the : are digits
2N/A * since an ap_id is of the form <name><instance>:<specific ap name>
2N/A */
2N/A if ((mkr = strchr(ap_base, ':')) == NULL) {
2N/A type = AP_TYPE;
2N/A } else {
2N/A size_ap = strlen(ap_base);
2N/A size_mkr = strlen(mkr);
2N/A mkr = ap_base;
2N/A
2N/A digit = 0;
2N/A for (i = size_ap - size_mkr - 1; i > 0; i--) {
2N/A if ((int)isdigit(mkr[i])) {
2N/A digit++;
2N/A break;
2N/A }
2N/A }
2N/A if (digit == 0) {
2N/A type = AP_TYPE;
2N/A } else {
2N/A type = LOGICAL_DRV_AP;
2N/A }
2N/A }
2N/A
2N/A return (type);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic cfga_err_t
2N/Anull_get_cond(lib_loc_t *liblocp, cfga_cond_t *condp, char **errstring)
2N/A{
2N/A return (CFGA_OK);
2N/A}
2N/A
2N/Astatic cfga_err_t
2N/Aget_cond_v1(lib_loc_t *liblocp, cfga_cond_t *condp, char **errstring)
2N/A{
2N/A plugin_lib_t *libp;
2N/A cfga_stat_data_t sdbuf;
2N/A cfga_err_t rc;
2N/A
2N/A
2N/A libp = liblocp->libp;
2N/A if (libp->plugin_vers != CFGA_HSL_V1) {
2N/A return (CFGA_LIB_ERROR);
2N/A }
2N/A
2N/A errno = 0;
2N/A if ((rc = (*liblocp->libp->cfga_stat_p)(
2N/A liblocp->ap_physical, &sdbuf, NULL, errstring))
2N/A == CFGA_OK) {
2N/A *condp = sdbuf.ap_cond;
2N/A } else {
2N/A *condp = CFGA_COND_UNKNOWN;
2N/A }
2N/A
2N/A return (rc);
2N/A}
2N/A
2N/Astatic cfga_err_t
2N/Aget_cond_v2(lib_loc_t *liblocp, cfga_cond_t *condp, char **errstring)
2N/A{
2N/A int nelem;
2N/A plugin_lib_t *libp;
2N/A cfga_list_data_t *ldbufp;
2N/A cfga_err_t rc;
2N/A
2N/A
2N/A libp = liblocp->libp;
2N/A if (libp->plugin_vers != CFGA_HSL_V2) {
2N/A return (CFGA_LIB_ERROR);
2N/A }
2N/A
2N/A errno = 0;
2N/A nelem = 0;
2N/A ldbufp = NULL;
2N/A if ((rc = (*liblocp->libp->cfga_list_ext_p)(
2N/A liblocp->ap_physical, &ldbufp, &nelem, NULL, NULL,
2N/A errstring, 0)) == CFGA_OK) {
2N/A assert(nelem == 1 && ldbufp != NULL);
2N/A
2N/A *condp = ldbufp->ap_cond;
2N/A S_FREE(ldbufp);
2N/A } else {
2N/A *condp = CFGA_COND_UNKNOWN;
2N/A }
2N/A
2N/A return (rc);
2N/A}
2N/A
2N/A/* mask represents the flags accepted */
2N/Astatic cfga_err_t
2N/Acheck_flags(cfga_flags_t flags, cfga_flags_t mask, char **errstring)
2N/A{
2N/A if ((flags & ~mask) != 0) {
2N/A config_err(0, INVALID_ARGS, errstring);
2N/A return (CFGA_ERROR);
2N/A } else {
2N/A return (CFGA_OK);
2N/A }
2N/A}
2N/A
2N/Astatic cfga_err_t
2N/Acheck_apids(int num_ap_ids, char *const *ap_ids, char **errstring)
2N/A{
2N/A if (num_ap_ids <= 0 || ap_ids == NULL) {
2N/A config_err(0, INVALID_ARGS, errstring);
2N/A return (CFGA_ERROR);
2N/A } else {
2N/A return (CFGA_OK);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Returns the class or the empty string if attacment point has
2N/A * no class.
2N/A */
2N/Astatic char *
2N/Aget_class(di_minor_t minor)
2N/A{
2N/A char *cp, c;
2N/A size_t len;
2N/A
2N/A
2N/A if (minor == DI_MINOR_NIL) {
2N/A return (NULL);
2N/A }
2N/A
2N/A cp = di_minor_nodetype(minor);
2N/A if (cp == NULL) {
2N/A return (NULL);
2N/A }
2N/A
2N/A len = strlen(DDI_NT_ATTACHMENT_POINT);
2N/A if (strncmp(cp, DDI_NT_ATTACHMENT_POINT, len)) {
2N/A return (NULL);
2N/A }
2N/A
2N/A cp += len;
2N/A
2N/A c = *cp;
2N/A if (c != '\0' && c != ':') {
2N/A return (NULL);
2N/A }
2N/A
2N/A if (c == ':') {
2N/A cp++;
2N/A }
2N/A
2N/A return (cp);
2N/A
2N/A}
2N/A
2N/A/*
2N/A * Transform stat data to list data
2N/A */
2N/Astatic void
2N/Astat_to_list(cfga_list_data_t *lp, cfga_stat_data_t *statp)
2N/A{
2N/A
2N/A (void) snprintf(lp->ap_log_id, sizeof (lp->ap_log_id), "%s",
2N/A statp->ap_log_id);
2N/A
2N/A (void) snprintf(lp->ap_phys_id, sizeof (lp->ap_phys_id), "%s",
2N/A statp->ap_phys_id);
2N/A
2N/A (void) snprintf(lp->ap_class, sizeof (lp->ap_class), "%s",
2N/A CFGA_NO_CLASS);
2N/A
2N/A lp->ap_r_state = statp->ap_r_state;
2N/A lp->ap_o_state = statp->ap_o_state;
2N/A lp->ap_cond = statp->ap_cond;
2N/A lp->ap_busy = statp->ap_busy;
2N/A lp->ap_status_time = statp->ap_status_time;
2N/A
2N/A (void) snprintf(lp->ap_info, sizeof (lp->ap_info), "%s",
2N/A statp->ap_info);
2N/A (void) snprintf(lp->ap_type, sizeof (lp->ap_type), "%s",
2N/A statp->ap_type);
2N/A}
2N/A
2N/Astatic void
2N/Alstat_free(list_stat_t *lstatp)
2N/A{
2N/A stat_data_list_t *slp, *slp2;
2N/A array_list_t *ap, *ap2;
2N/A
2N/A slp = lstatp->sdl;
2N/A while (slp != NULL) {
2N/A slp2 = slp->next;
2N/A S_FREE(slp);
2N/A slp = slp2;
2N/A }
2N/A
2N/A lstatp->sdl = NULL;
2N/A
2N/A ap = lstatp->al;
2N/A while (ap != NULL) {
2N/A ap2 = ap->next;
2N/A S_FREE(ap->array);
2N/A S_FREE(ap);
2N/A ap = ap2;
2N/A }
2N/A
2N/A lstatp->al = NULL;
2N/A}
2N/A
2N/Astatic cfga_err_t
2N/Asplit_apid(char *ap_id, char **dyncompp, char **errstring)
2N/A{
2N/A char *cp;
2N/A
2N/A *dyncompp = NULL;
2N/A
2N/A if (ap_id == NULL) {
2N/A return (CFGA_ERROR);
2N/A }
2N/A
2N/A if ((cp = strstr(ap_id, CFGA_DYN_SEP)) == NULL) {
2N/A return (CFGA_OK);
2N/A }
2N/A
2N/A *cp = '\0';
2N/A cp += strlen(CFGA_DYN_SEP);
2N/A if ((*dyncompp = config_calloc_check(1, strlen(cp) + 1,
2N/A errstring)) == NULL) {
2N/A return (CFGA_LIB_ERROR);
2N/A }
2N/A (void) strcpy(*dyncompp, cp);
2N/A
2N/A return (CFGA_OK);
2N/A}
2N/A
2N/Astatic void
2N/Aappend_dyn(char *buf, const char *dyncomp, size_t blen)
2N/A{
2N/A if (dyncomp != NULL) {
2N/A char *cp = buf + strlen(buf);
2N/A size_t len = blen - strlen(buf);
2N/A
2N/A (void) snprintf(cp, len, "%s%s", CFGA_DYN_SEP,
2N/A dyncomp);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Default implementation of cfga_ap_id_cmp. Works for most cases
2N/A * except for long hex number sequences like world-wide-name.
2N/A *
2N/A * This function compares the ap's in a generic way. It does so by
2N/A * determining the place of difference between the 2 aps. If the first
2N/A * difference is a digit, it attempts to obtain the numbers and compare them
2N/A * Otherwise it just compares the aps as strings
2N/A */
2N/Astatic int
2N/Adefault_ap_id_cmp(const char *ap_id1, const char *ap_id2)
2N/A{
2N/A int i = 0;
2N/A
2N/A /*
2N/A * Search for first different char
2N/A */
2N/A while (ap_id1[i] == ap_id2[i] && ap_id1[i] != '\0')
2N/A i++;
2N/A
2N/A /*
2N/A * If one of the char is a digit, back up to where the
2N/A * number started, compare the number.
2N/A */
2N/A if (isdigit(ap_id1[i]) || isdigit(ap_id2[i])) {
2N/A while ((i > 0) && isdigit(ap_id1[i - 1]))
2N/A i--;
2N/A
2N/A if (isdigit(ap_id1[i]) && isdigit(ap_id2[i]))
2N/A return (atoi(ap_id1 + i) - atoi(ap_id2 + i));
2N/A }
2N/A
2N/A /* One of them isn't a number, compare the char */
2N/A return (ap_id1[i] - ap_id2[i]);
2N/A}
2N/A
2N/Astatic void
2N/Ahold_lib(plugin_lib_t *libp)
2N/A{
2N/A assert(libp->refcnt >= 0);
2N/A (void) mutex_lock(&libp->lock);
2N/A libp->refcnt++;
2N/A (void) mutex_unlock(&libp->lock);
2N/A}
2N/A
2N/Astatic void
2N/Arele_lib(plugin_lib_t *libp)
2N/A{
2N/A assert(libp->refcnt > 0);
2N/A (void) mutex_lock(&libp->lock);
2N/A libp->refcnt--;
2N/A (void) mutex_unlock(&libp->lock);
2N/A}