/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 2020 Joyent, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Topology node properties and method operations may be accessed by FMRI. * The FMRI used to perform property look-ups and method operations is * the FMRI contained in the matching topology node's protocol property * grouping for the resource property. The full range of fmd(8) * scheme plugin operations are supported as long as a backend method is * supplied by a scheme-specific enumerator or the enumerator module that * created the matching topology node. Support for fmd scheme operations * include: * * - expand * - present * - replaced * - contains * - unusable * - service_state * - nvl2str * - retire * - unretire * * In addition, the following operations are supported per-FMRI: * * - str2nvl: convert string-based FMRI to nvlist * - compare: compare two FMRIs * - asru: lookup associated ASRU property by FMRI * - fru: lookup associated FRU by FMRI * - create: an FMRI nvlist by scheme type * - propery lookup * * These routines may only be called by consumers of a topology snapshot. * They may not be called by libtopo enumerator or method modules. */ /*ARGSUSED*/ static int set_error(topo_hdl_t *thp, int err, int *errp, char *method, nvlist_t *nvlp) { nvlist_free(nvlp); topo_dprintf(thp, TOPO_DBG_ERR, "%s failed: %s\n", method, topo_strerror(err)); *errp = err; return (-1); } /*ARGSUSED*/ static nvlist_t * set_nverror(topo_hdl_t *thp, int err, int *errp, char *method, nvlist_t *nvlp) { nvlist_free(nvlp); topo_dprintf(thp, TOPO_DBG_ERR, "%s failed: %s\n", method, topo_strerror(err)); *errp = err; return (NULL); } int topo_fmri_nvl2str(topo_hdl_t *thp, nvlist_t *fmri, char **fmristr, int *err) { char *scheme, *str; nvlist_t *out = NULL; tnode_t *rnode; if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0) return (set_error(thp, ETOPO_FMRI_MALFORM, err, TOPO_METH_NVL2STR, out)); if ((rnode = topo_hdl_root(thp, scheme)) == NULL) return (set_error(thp, ETOPO_METHOD_NOTSUP, err, TOPO_METH_NVL2STR, out)); if (topo_method_invoke(rnode, TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_VERSION, fmri, &out, err) != 0) return (set_error(thp, *err, err, TOPO_METH_NVL2STR, out)); if (out == NULL || nvlist_lookup_string(out, "fmri-string", &str) != 0) return (set_error(thp, ETOPO_METHOD_INVAL, err, TOPO_METH_NVL2STR, out)); if ((*fmristr = topo_hdl_strdup(thp, str)) == NULL) return (set_error(thp, ETOPO_NOMEM, err, TOPO_METH_NVL2STR, out)); nvlist_free(out); return (0); } int topo_fmri_str2nvl(topo_hdl_t *thp, const char *fmristr, nvlist_t **fmri, int *err) { char *f, buf[PATH_MAX], *method = TOPO_METH_STR2NVL; nvlist_t *out = NULL, *in = NULL; tnode_t *rnode; boolean_t is_path = B_FALSE; /* * For path FMRI's the scheme is encoded in the authority portion of * the FMRI - e.g. * * path://scheme=/... */ if (strncmp(fmristr, "path://", 7) == 0) { char *scheme_start, *scheme_end; is_path = B_TRUE; method = TOPO_METH_PATH_STR2NVL; if ((scheme_start = strchr(fmristr, '=')) == NULL) { return (set_error(thp, ETOPO_FMRI_MALFORM, err, TOPO_METH_STR2NVL, in)); } scheme_start++; if ((scheme_end = strchr(scheme_start, '/')) == NULL) { return (set_error(thp, ETOPO_FMRI_MALFORM, err, TOPO_METH_STR2NVL, in)); } (void) strlcpy(buf, scheme_start, (scheme_end - scheme_start) + 1); } else { (void) strlcpy(buf, fmristr, sizeof (buf)); if ((f = strchr(buf, ':')) == NULL) return (set_error(thp, ETOPO_FMRI_MALFORM, err, TOPO_METH_STR2NVL, in)); *f = '\0'; /* strip trailing FMRI path */ } if (is_path) { topo_digraph_t *tdg; if ((tdg = topo_digraph_get(thp, buf)) == NULL) { return (set_error(thp, ETOPO_METHOD_NOTSUP, err, TOPO_METH_STR2NVL, in)); } rnode = tdg->tdg_rootnode; } else if ((rnode = topo_hdl_root(thp, buf)) == NULL) { return (set_error(thp, ETOPO_METHOD_NOTSUP, err, TOPO_METH_STR2NVL, in)); } if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0) return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_STR2NVL, in)); if (nvlist_add_string(in, "fmri-string", fmristr) != 0) return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_STR2NVL, in)); if (topo_method_invoke(rnode, method, TOPO_METH_STR2NVL_VERSION, in, &out, err) != 0) return (set_error(thp, *err, err, TOPO_METH_STR2NVL, in)); nvlist_free(in); if (out == NULL || topo_hdl_nvdup(thp, out, fmri) != 0) return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_STR2NVL, out)); nvlist_free(out); return (0); } int topo_fmri_present(topo_hdl_t *thp, nvlist_t *fmri, int *err) { uint32_t present = 0; char *scheme; nvlist_t *out = NULL; tnode_t *rnode; if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0) return (set_error(thp, ETOPO_FMRI_MALFORM, err, TOPO_METH_PRESENT, out)); if ((rnode = topo_hdl_root(thp, scheme)) == NULL) return (set_error(thp, ETOPO_METHOD_NOTSUP, err, TOPO_METH_PRESENT, out)); if (topo_method_invoke(rnode, TOPO_METH_PRESENT, TOPO_METH_PRESENT_VERSION, fmri, &out, err) < 0) { (void) set_error(thp, *err, err, TOPO_METH_PRESENT, out); return (present); } (void) nvlist_lookup_uint32(out, TOPO_METH_PRESENT_RET, &present); nvlist_free(out); return (present); } int topo_fmri_replaced(topo_hdl_t *thp, nvlist_t *fmri, int *err) { uint32_t replaced = FMD_OBJ_STATE_NOT_PRESENT; char *scheme; nvlist_t *out = NULL; tnode_t *rnode; if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0) return (set_error(thp, ETOPO_FMRI_MALFORM, err, TOPO_METH_REPLACED, out)); if ((rnode = topo_hdl_root(thp, scheme)) == NULL) return (set_error(thp, ETOPO_METHOD_NOTSUP, err, TOPO_METH_REPLACED, out)); if (topo_method_invoke(rnode, TOPO_METH_REPLACED, TOPO_METH_REPLACED_VERSION, fmri, &out, err) < 0) { (void) set_error(thp, *err, err, TOPO_METH_REPLACED, out); return (FMD_OBJ_STATE_UNKNOWN); } (void) nvlist_lookup_uint32(out, TOPO_METH_REPLACED_RET, &replaced); nvlist_free(out); return (replaced); } int topo_fmri_contains(topo_hdl_t *thp, nvlist_t *fmri, nvlist_t *subfmri, int *err) { uint32_t contains; char *scheme; nvlist_t *in = NULL, *out = NULL; tnode_t *rnode; if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0) return (set_error(thp, ETOPO_FMRI_MALFORM, err, TOPO_METH_CONTAINS, NULL)); if ((rnode = topo_hdl_root(thp, scheme)) == NULL) return (set_error(thp, ETOPO_METHOD_NOTSUP, err, TOPO_METH_CONTAINS, NULL)); if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0) return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_CONTAINS, NULL)); if (nvlist_add_nvlist(in, TOPO_METH_FMRI_ARG_FMRI, fmri) != 0 || nvlist_add_nvlist(in, TOPO_METH_FMRI_ARG_SUBFMRI, subfmri) != 0) return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_CONTAINS, in)); if (topo_method_invoke(rnode, TOPO_METH_CONTAINS, TOPO_METH_CONTAINS_VERSION, in, &out, err) < 0) return (set_error(thp, *err, err, TOPO_METH_CONTAINS, in)); (void) nvlist_lookup_uint32(out, TOPO_METH_CONTAINS_RET, &contains); nvlist_free(in); nvlist_free(out); return (contains); } int topo_fmri_unusable(topo_hdl_t *thp, nvlist_t *fmri, int *err) { char *scheme; uint32_t unusable = 0; nvlist_t *out = NULL; tnode_t *rnode; if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0) return (set_error(thp, ETOPO_FMRI_MALFORM, err, TOPO_METH_UNUSABLE, out)); if ((rnode = topo_hdl_root(thp, scheme)) == NULL) return (set_error(thp, ETOPO_METHOD_NOTSUP, err, TOPO_METH_UNUSABLE, out)); if (topo_method_invoke(rnode, TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_VERSION, fmri, &out, err) < 0) return (set_error(thp, *err, err, TOPO_METH_UNUSABLE, out)); (void) nvlist_lookup_uint32(out, TOPO_METH_UNUSABLE_RET, &unusable); nvlist_free(out); return (unusable); } int topo_fmri_retire(topo_hdl_t *thp, nvlist_t *fmri, int *err) { char *scheme; uint32_t status; nvlist_t *out = NULL; tnode_t *rnode; if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0) return (set_error(thp, ETOPO_FMRI_MALFORM, err, TOPO_METH_RETIRE, out)); if ((rnode = topo_hdl_root(thp, scheme)) == NULL) return (set_error(thp, ETOPO_METHOD_NOTSUP, err, TOPO_METH_RETIRE, out)); if (topo_method_invoke(rnode, TOPO_METH_RETIRE, TOPO_METH_RETIRE_VERSION, fmri, &out, err) < 0) return (set_error(thp, *err, err, TOPO_METH_RETIRE, out)); if (nvlist_lookup_uint32(out, TOPO_METH_RETIRE_RET, &status) != 0) return (set_error(thp, ETOPO_METHOD_FAIL, err, TOPO_METH_RETIRE, out)); nvlist_free(out); return (status); } int topo_fmri_unretire(topo_hdl_t *thp, nvlist_t *fmri, int *err) { char *scheme; uint32_t status; nvlist_t *out = NULL; tnode_t *rnode; if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0) return (set_error(thp, ETOPO_FMRI_MALFORM, err, TOPO_METH_UNRETIRE, out)); if ((rnode = topo_hdl_root(thp, scheme)) == NULL) return (set_error(thp, ETOPO_METHOD_NOTSUP, err, TOPO_METH_UNRETIRE, out)); if (topo_method_invoke(rnode, TOPO_METH_UNRETIRE, TOPO_METH_UNRETIRE_VERSION, fmri, &out, err) < 0) return (set_error(thp, *err, err, TOPO_METH_UNRETIRE, out)); if (nvlist_lookup_uint32(out, TOPO_METH_UNRETIRE_RET, &status) != 0) { nvlist_free(out); return (set_error(thp, ETOPO_METHOD_FAIL, err, TOPO_METH_UNRETIRE, out)); } nvlist_free(out); return (status); } int topo_fmri_service_state(topo_hdl_t *thp, nvlist_t *fmri, int *err) { char *scheme; uint32_t service_state = FMD_SERVICE_STATE_UNKNOWN; nvlist_t *out = NULL; tnode_t *rnode; if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0) return (set_error(thp, ETOPO_FMRI_MALFORM, err, TOPO_METH_SERVICE_STATE, out)); if ((rnode = topo_hdl_root(thp, scheme)) == NULL) return (set_error(thp, ETOPO_METHOD_NOTSUP, err, TOPO_METH_SERVICE_STATE, out)); if (topo_method_invoke(rnode, TOPO_METH_SERVICE_STATE, TOPO_METH_SERVICE_STATE_VERSION, fmri, &out, err) < 0) return (set_error(thp, *err, err, TOPO_METH_SERVICE_STATE, out)); (void) nvlist_lookup_uint32(out, TOPO_METH_SERVICE_STATE_RET, &service_state); nvlist_free(out); return (service_state); } int topo_fmri_expand(topo_hdl_t *thp, nvlist_t *fmri, int *err) { char *scheme; nvlist_t *out = NULL; tnode_t *rnode; if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0) return (set_error(thp, ETOPO_FMRI_MALFORM, err, TOPO_METH_EXPAND, out)); if ((rnode = topo_hdl_root(thp, scheme)) == NULL) return (set_error(thp, ETOPO_METHOD_NOTSUP, err, TOPO_METH_EXPAND, out)); if (topo_method_invoke(rnode, TOPO_METH_EXPAND, TOPO_METH_EXPAND_VERSION, fmri, &out, err) != 0) return (set_error(thp, *err, err, TOPO_METH_EXPAND, out)); return (0); } static int fmri_prop(topo_hdl_t *thp, nvlist_t *rsrc, const char *pgname, const char *pname, nvlist_t *args, nvlist_t **prop, int *err) { int rv; nvlist_t *in = NULL; tnode_t *rnode; char *scheme; if (nvlist_lookup_string(rsrc, FM_FMRI_SCHEME, &scheme) != 0) return (set_error(thp, ETOPO_FMRI_MALFORM, err, TOPO_METH_PROP_GET, in)); if ((rnode = topo_hdl_root(thp, scheme)) == NULL) return (set_error(thp, ETOPO_METHOD_NOTSUP, err, TOPO_METH_PROP_GET, in)); if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0) return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_PROP_GET, in)); rv = nvlist_add_nvlist(in, TOPO_PROP_RESOURCE, rsrc); rv |= nvlist_add_string(in, TOPO_PROP_GROUP, pgname); rv |= nvlist_add_string(in, TOPO_PROP_VAL_NAME, pname); if (args != NULL) rv |= nvlist_add_nvlist(in, TOPO_PROP_PARGS, args); if (rv != 0) return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_PROP_GET, in)); *prop = NULL; rv = topo_method_invoke(rnode, TOPO_METH_PROP_GET, TOPO_METH_PROP_GET_VERSION, in, prop, err); nvlist_free(in); if (rv != 0) return (-1); /* *err is set for us */ if (*prop == NULL) return (set_error(thp, ETOPO_PROP_NOENT, err, TOPO_METH_PROP_GET, NULL)); return (0); } int topo_fmri_asru(topo_hdl_t *thp, nvlist_t *nvl, nvlist_t **asru, int *err) { nvlist_t *ap, *prop = NULL; if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, TOPO_PROP_ASRU, nvl, &prop, err) < 0) return (set_error(thp, *err, err, "topo_fmri_asru", NULL)); if (nvlist_lookup_nvlist(prop, TOPO_PROP_VAL_VAL, &ap) != 0) return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_asru", prop)); if (topo_hdl_nvdup(thp, ap, asru) < 0) return (set_error(thp, ETOPO_PROP_NOMEM, err, "topo_fmri_asru", prop)); nvlist_free(prop); return (0); } int topo_fmri_fru(topo_hdl_t *thp, nvlist_t *nvl, nvlist_t **fru, int *err) { nvlist_t *fp, *prop = NULL; if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, TOPO_PROP_FRU, nvl, &prop, err) < 0) return (set_error(thp, *err, err, "topo_fmri_fru", NULL)); if (nvlist_lookup_nvlist(prop, TOPO_PROP_VAL_VAL, &fp) != 0) return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_fru", prop)); if (topo_hdl_nvdup(thp, fp, fru) < 0) return (set_error(thp, ETOPO_PROP_NOMEM, err, "topo_fmri_fru", prop)); nvlist_free(prop); return (0); } int topo_fmri_label(topo_hdl_t *thp, nvlist_t *nvl, char **label, int *err) { nvlist_t *prop = NULL; char *lp; if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, TOPO_PROP_LABEL, NULL, &prop, err) < 0) return (set_error(thp, *err, err, "topo_fmri_label", NULL)); if (nvlist_lookup_string(prop, TOPO_PROP_VAL_VAL, &lp) != 0) return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_label", prop)); if ((*label = topo_hdl_strdup(thp, lp)) == NULL) return (set_error(thp, ETOPO_PROP_NOMEM, err, "topo_fmri_label", prop)); nvlist_free(prop); return (0); } int topo_fmri_serial(topo_hdl_t *thp, nvlist_t *nvl, char **serial, int *err) { nvlist_t *prop = NULL; char *sp; /* * If there is a serial id in the resource fmri, then use that. * Otherwise fall back to looking for a serial id property in the * protocol group. */ if (nvlist_lookup_string(nvl, FM_FMRI_HC_SERIAL_ID, &sp) == 0) { if ((*serial = topo_hdl_strdup(thp, sp)) == NULL) return (set_error(thp, ETOPO_PROP_NOMEM, err, "topo_fmri_serial", prop)); else return (0); } if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, FM_FMRI_HC_SERIAL_ID, NULL, &prop, err) < 0) return (set_error(thp, *err, err, "topo_fmri_serial", NULL)); if (nvlist_lookup_string(prop, TOPO_PROP_VAL_VAL, &sp) != 0) return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_serial", prop)); if ((*serial = topo_hdl_strdup(thp, sp)) == NULL) return (set_error(thp, ETOPO_PROP_NOMEM, err, "topo_fmri_serial", prop)); nvlist_free(prop); return (0); } int topo_fmri_getprop(topo_hdl_t *thp, nvlist_t *nvl, const char *pg, const char *pname, nvlist_t *args, nvlist_t **prop, int *err) { *prop = NULL; return (fmri_prop(thp, nvl, pg, pname, args, prop, err)); } int topo_fmri_setprop(topo_hdl_t *thp, nvlist_t *nvl, const char *pg, nvlist_t *prop, int flag, nvlist_t *args, int *err) { int rv; nvlist_t *in = NULL, *out = NULL; tnode_t *rnode; char *scheme; if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &scheme) != 0) return (set_error(thp, ETOPO_FMRI_MALFORM, err, TOPO_METH_PROP_SET, in)); if ((rnode = topo_hdl_root(thp, scheme)) == NULL) return (set_error(thp, ETOPO_METHOD_NOTSUP, err, TOPO_METH_PROP_SET, in)); if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0) return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_PROP_SET, in)); rv = nvlist_add_nvlist(in, TOPO_PROP_RESOURCE, nvl); rv |= nvlist_add_string(in, TOPO_PROP_GROUP, pg); rv |= nvlist_add_nvlist(in, TOPO_PROP_VAL, prop); rv |= nvlist_add_int32(in, TOPO_PROP_FLAG, (int32_t)flag); if (args != NULL) rv |= nvlist_add_nvlist(in, TOPO_PROP_PARGS, args); if (rv != 0) return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_PROP_SET, in)); rv = topo_method_invoke(rnode, TOPO_METH_PROP_SET, TOPO_METH_PROP_SET_VERSION, in, &out, err); nvlist_free(in); /* no return values */ nvlist_free(out); if (rv) return (-1); return (0); } int topo_fmri_getpgrp(topo_hdl_t *thp, nvlist_t *rsrc, const char *pgname, nvlist_t **pgroup, int *err) { int rv; nvlist_t *in = NULL; tnode_t *rnode; char *scheme; if (nvlist_lookup_string(rsrc, FM_FMRI_SCHEME, &scheme) != 0) return (set_error(thp, ETOPO_FMRI_MALFORM, err, TOPO_METH_PROP_GET, in)); if ((rnode = topo_hdl_root(thp, scheme)) == NULL) return (set_error(thp, ETOPO_METHOD_NOTSUP, err, TOPO_METH_PROP_GET, in)); if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0) return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_PROP_GET, in)); rv = nvlist_add_nvlist(in, TOPO_PROP_RESOURCE, rsrc); rv |= nvlist_add_string(in, TOPO_PROP_GROUP, pgname); if (rv != 0) return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_PROP_GET, in)); *pgroup = NULL; rv = topo_method_invoke(rnode, TOPO_METH_PGRP_GET, TOPO_METH_PGRP_GET_VERSION, in, pgroup, err); nvlist_free(in); if (rv != 0) return (-1); /* *err is set for us */ if (*pgroup == NULL) return (set_error(thp, ETOPO_PROP_NOENT, err, TOPO_METH_PROP_GET, NULL)); return (0); } int topo_fmri_compare(topo_hdl_t *thp, nvlist_t *f1, nvlist_t *f2, int *err) { uint32_t compare; char *scheme1, *scheme2; nvlist_t *in; nvlist_t *out = NULL; tnode_t *rnode; if (nvlist_lookup_string(f1, FM_FMRI_SCHEME, &scheme1) != 0) return (set_error(thp, ETOPO_FMRI_MALFORM, err, TOPO_METH_COMPARE, NULL)); if (nvlist_lookup_string(f2, FM_FMRI_SCHEME, &scheme2) != 0) return (set_error(thp, ETOPO_FMRI_MALFORM, err, TOPO_METH_COMPARE, NULL)); if (strcmp(scheme1, scheme2) != 0) return (0); if ((rnode = topo_hdl_root(thp, scheme1)) == NULL) return (set_error(thp, ETOPO_METHOD_NOTSUP, err, TOPO_METH_COMPARE, NULL)); if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0) return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_COMPARE, NULL)); if (nvlist_add_nvlist(in, TOPO_METH_FMRI_ARG_NV1, f1) != 0 || nvlist_add_nvlist(in, TOPO_METH_FMRI_ARG_NV2, f2) != 0) return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_COMPARE, in)); if (topo_method_invoke(rnode, TOPO_METH_COMPARE, TOPO_METH_COMPARE_VERSION, in, &out, err) < 0) return (set_error(thp, *err, err, TOPO_METH_COMPARE, in)); (void) nvlist_lookup_uint32(out, TOPO_METH_COMPARE_RET, &compare); nvlist_free(out); nvlist_free(in); return (compare); } /* * topo_fmri_create * * If possible, creates an FMRI of the requested version in the * requested scheme. Args are passed as part of the inputs to the * fmri-create method of the scheme. */ nvlist_t * topo_fmri_create(topo_hdl_t *thp, const char *scheme, const char *name, topo_instance_t inst, nvlist_t *nvl, int *err) { nvlist_t *ins; nvlist_t *out; tnode_t *rnode; ins = out = NULL; if ((rnode = topo_hdl_root(thp, scheme)) == NULL) return (set_nverror(thp, ETOPO_METHOD_NOTSUP, err, TOPO_METH_FMRI, NULL)); if ((*err = topo_hdl_nvalloc(thp, &ins, NV_UNIQUE_NAME)) != 0) return (set_nverror(thp, ETOPO_FMRI_NVL, err, TOPO_METH_FMRI, NULL)); if (nvlist_add_string(ins, TOPO_METH_FMRI_ARG_NAME, name) != 0 || nvlist_add_uint64(ins, TOPO_METH_FMRI_ARG_INST, inst) != 0) { return (set_nverror(thp, ETOPO_FMRI_NVL, err, TOPO_METH_FMRI, ins)); } if (nvl != NULL && nvlist_add_nvlist(ins, TOPO_METH_FMRI_ARG_NVL, nvl) != 0) { return (set_nverror(thp, ETOPO_FMRI_NVL, err, TOPO_METH_FMRI, ins)); } if (topo_method_invoke(rnode, TOPO_METH_FMRI, TOPO_METH_FMRI_VERSION, ins, &out, err) != 0) { return (set_nverror(thp, *err, err, TOPO_METH_FMRI, ins)); } nvlist_free(ins); return (out); } /* * These private utility functions are used by fmd to maintain its resource * cache. Because hc instance numbers are not guaranteed, it's possible to * have two different FMRI strings represent the same logical entity. These * functions hide this implementation detail from unknowing consumers such as * fmd. * * Ideally, we'd like to do a str2nvl() and then a full FMRI hash and * comparison, but these functions are designed to be fast and efficient. * Given that there is only a single hc node that has this property * (ses-enclosure), we hard-code this behavior here. If there are more * instances of this behavior in the future, this function could be made more * generic. * * This code also handles changes in the server-id or revision fields of the hc * FMRI, as these fields have no bearing on equivalence of FRUs. */ static ulong_t topo_fmri_strhash_one(const char *fmri, size_t len) { ulong_t g, h = 0; size_t i; for (i = 0; i < len; i++) { h = (h << 4) + fmri[i]; if ((g = (h & 0xf0000000)) != 0) { h ^= (g >> 24); h ^= g; } } return (h); } static const char * topo_fmri_next_auth(const char *auth) { const char *colon, *slash; colon = strchr(auth + 1, ':'); slash = strchr(auth, '/'); if (colon == NULL && slash == NULL) return (NULL); if (colon == NULL) return (slash); else if (slash < colon) return (slash); else return (colon); } /* * List of authority information we care about. Note that we explicitly ignore * things that are properties of the chassis and not the resource itself: * * FM_FMRI_AUTH_PRODUCT_SN "product-sn" * FM_FMRI_AUTH_PRODUCT "product-id" * FM_FMRI_AUTH_DOMAIN "domain-id" * FM_FMRI_AUTH_SERVER "server-id" * FM_FMRI_AUTH_HOST "host-id" * * We also ignore the "revision" authority member, as that typically indicates * the firmware revision and is not a static property of the FRU. This leaves * the following interesting members: * * FM_FMRI_AUTH_CHASSIS "chassis-id" * FM_FMRI_HC_SERIAL_ID "serial" * FM_FMRI_HC_PART "part" */ typedef enum { HC_AUTH_CHASSIS, HC_AUTH_SERIAL, HC_AUTH_PART, HC_AUTH_MAX } hc_auth_type_t; static char *hc_auth_table[] = { FM_FMRI_AUTH_CHASSIS, FM_FMRI_HC_SERIAL_ID, FM_FMRI_HC_PART }; /* * Takes an authority member, with leading ":" and trailing "=", and returns * one of the above types if it's one of the things we care about. If * 'authlen' is specified, it is filled in with the length of the authority * member, including leading and trailing characters. */ static hc_auth_type_t hc_auth_to_type(const char *auth, size_t *authlen) { int i; size_t len; if (auth[0] != ':') return (HC_AUTH_MAX); for (i = 0; i < HC_AUTH_MAX; i++) { len = strlen(hc_auth_table[i]); if (strncmp(auth + 1, hc_auth_table[i], len) == 0 && auth[len + 1] == '=') { if (authlen) *authlen = len + 2; break; } } return (i); } /*ARGSUSED*/ ulong_t topo_fmri_strhash_internal(topo_hdl_t *thp, const char *fmri, boolean_t noauth) { const char *auth, *next; const char *enclosure; ulong_t h; hc_auth_type_t type; if (strncmp(fmri, "hc://", 5) != 0) return (topo_fmri_strhash_one(fmri, strlen(fmri))); enclosure = strstr(fmri, SES_ENCLOSURE); h = 0; auth = next = fmri + 5; while (*next != '/') { auth = next; if ((next = topo_fmri_next_auth(auth)) == NULL) { next = auth; break; } if ((type = hc_auth_to_type(auth, NULL)) == HC_AUTH_MAX) continue; if (!noauth || type == HC_AUTH_CHASSIS) h += topo_fmri_strhash_one(auth, next - auth); } if (enclosure) { next = enclosure + sizeof (SES_ENCLOSURE); while (isdigit(*next)) next++; } h += topo_fmri_strhash_one(next, strlen(next)); return (h); } /*ARGSUSED*/ ulong_t topo_fmri_strhash(topo_hdl_t *thp, const char *fmri) { return (topo_fmri_strhash_internal(thp, fmri, B_FALSE)); } /*ARGSUSED*/ ulong_t topo_fmri_strhash_noauth(topo_hdl_t *thp, const char *fmri) { return (topo_fmri_strhash_internal(thp, fmri, B_TRUE)); } static void topo_fmri_strcmp_parse_auth(const char *auth, const char *authtype[], size_t authlen[]) { int i; const char *next; hc_auth_type_t type; size_t len; for (i = 0; i < HC_AUTH_MAX; i++) authlen[i] = 0; while (*auth != '/' && (next = topo_fmri_next_auth(auth)) != NULL) { if ((type = hc_auth_to_type(auth, &len)) == HC_AUTH_MAX) { auth = next; continue; } authtype[type] = auth + len; authlen[type] = next - (auth + len); auth = next; } } /*ARGSUSED*/ static boolean_t topo_fmri_strcmp_internal(topo_hdl_t *thp, const char *a, const char *b, boolean_t noauth) { const char *fmria, *fmrib; const char *autha[HC_AUTH_MAX], *authb[HC_AUTH_MAX]; size_t authlena[HC_AUTH_MAX], authlenb[HC_AUTH_MAX]; int i; /* * For non-hc FMRIs, we don't do anything. */ if (strncmp(a, "hc://", 5) != 0 || strncmp(b, "hc://", 5) != 0) return (strcmp(a, b) == 0); /* * Get the portion of the FMRI independent of the authority * information. */ fmria = strchr(a + 5, '/'); fmrib = strchr(b + 5, '/'); if (fmria == NULL || fmrib == NULL) return (strcmp(a, b)); fmria++; fmrib++; /* * Comparing fmri authority information is a bit of a pain, because * there may be a different number of members, and they can (but * shouldn't be) in a different order. We need to create a copy of the * authority and parse it into pieces. Because this function is * intended to be fast (and not necessarily extensible), we hard-code * the list of possible authority members in an enum and parse it into * an array. */ topo_fmri_strcmp_parse_auth(a + 5, autha, authlena); topo_fmri_strcmp_parse_auth(b + 5, authb, authlenb); for (i = 0; i < HC_AUTH_MAX; i++) { if (noauth && i != HC_AUTH_CHASSIS) continue; if (authlena[i] == 0 && authlenb[i] == 0) continue; if (authlena[i] != authlenb[i]) return (B_FALSE); if (strncmp(autha[i], authb[i], authlena[i]) != 0) return (B_FALSE); } /* * If this is rooted at a ses-enclosure node, skip past the instance * number, as it has no meaning. */ if (strncmp(fmria, SES_ENCLOSURE, sizeof (SES_ENCLOSURE) - 1) == 0 && strncmp(fmrib, SES_ENCLOSURE, sizeof (SES_ENCLOSURE) - 1) == 0) { fmria += sizeof (SES_ENCLOSURE); fmrib += sizeof (SES_ENCLOSURE); while (isdigit(*fmria)) fmria++; while (isdigit(*fmrib)) fmrib++; } return (strcmp(fmria, fmrib) == 0); } /*ARGSUSED*/ boolean_t topo_fmri_strcmp(topo_hdl_t *thp, const char *a, const char *b) { return (topo_fmri_strcmp_internal(thp, a, b, B_FALSE)); } /*ARGSUSED*/ boolean_t topo_fmri_strcmp_noauth(topo_hdl_t *thp, const char *a, const char *b) { return (topo_fmri_strcmp_internal(thp, a, b, B_TRUE)); } int topo_fmri_facility(topo_hdl_t *thp, nvlist_t *rsrc, const char *fac_type, uint32_t fac_subtype, topo_walk_cb_t cb, void *cb_args, int *err) { int rv; nvlist_t *in = NULL, *out; tnode_t *rnode; char *scheme; if (nvlist_lookup_string(rsrc, FM_FMRI_SCHEME, &scheme) != 0) return (set_error(thp, ETOPO_FMRI_MALFORM, err, TOPO_METH_PROP_GET, in)); if ((rnode = topo_hdl_root(thp, scheme)) == NULL) return (set_error(thp, ETOPO_METHOD_NOTSUP, err, TOPO_METH_PROP_GET, in)); if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0) return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_PROP_GET, in)); rv = nvlist_add_nvlist(in, TOPO_PROP_RESOURCE, rsrc); rv |= nvlist_add_string(in, FM_FMRI_FACILITY_TYPE, fac_type); rv |= nvlist_add_uint32(in, "type", fac_subtype); #ifdef _LP64 rv |= nvlist_add_uint64(in, "callback", (uint64_t)cb); rv |= nvlist_add_uint64(in, "callback-args", (uint64_t)cb_args); #else rv |= nvlist_add_uint32(in, "callback", (uint32_t)cb); rv |= nvlist_add_uint32(in, "callback-args", (uint32_t)cb_args); #endif if (rv != 0) return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_PROP_GET, in)); rv = topo_method_invoke(rnode, TOPO_METH_FACILITY, TOPO_METH_FACILITY_VERSION, in, &out, err); nvlist_free(in); if (rv != 0) return (-1); /* *err is set for us */ return (0); }