/* * 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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2020 Joyent, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static tf_rdata_t *topo_xml_walk(topo_mod_t *, tf_info_t *, xmlNodePtr, tnode_t *); static tf_edata_t *enum_attributes_process(topo_mod_t *, xmlNodePtr); static int enum_run(topo_mod_t *, tf_rdata_t *); static int fac_enum_run(topo_mod_t *, tnode_t *, const char *); static int fac_process(topo_mod_t *, xmlNodePtr, tf_rdata_t *, tnode_t *); static int fac_enum_process(topo_mod_t *, xmlNodePtr, tnode_t *); static int decorate_nodes(topo_mod_t *, tf_rdata_t *, xmlNodePtr, tnode_t *, tf_pad_t **); int xmlattr_to_stab(topo_mod_t *mp, xmlNodePtr n, const char *stabname, topo_stability_t *rs) { xmlChar *str; int rv = 0; if (n == NULL) { /* If there is no Stability defined, we default to private */ *rs = TOPO_STABILITY_PRIVATE; return (0); } if ((str = xmlGetProp(n, (xmlChar *)stabname)) == NULL) { topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "attribute to stability:\n"); return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR)); } if (xmlStrcmp(str, (xmlChar *)Internal) == 0) { *rs = TOPO_STABILITY_INTERNAL; } else if (xmlStrcmp(str, (xmlChar *)Private) == 0) { *rs = TOPO_STABILITY_PRIVATE; } else if (xmlStrcmp(str, (xmlChar *)Obsolete) == 0) { *rs = TOPO_STABILITY_OBSOLETE; } else if (xmlStrcmp(str, (xmlChar *)External) == 0) { *rs = TOPO_STABILITY_EXTERNAL; } else if (xmlStrcmp(str, (xmlChar *)Unstable) == 0) { *rs = TOPO_STABILITY_UNSTABLE; } else if (xmlStrcmp(str, (xmlChar *)Evolving) == 0) { *rs = TOPO_STABILITY_EVOLVING; } else if (xmlStrcmp(str, (xmlChar *)Stable) == 0) { *rs = TOPO_STABILITY_STABLE; } else if (xmlStrcmp(str, (xmlChar *)Standard) == 0) { *rs = TOPO_STABILITY_STANDARD; } else { xmlFree(str); return (topo_mod_seterrno(mp, ETOPO_PRSR_BADSTAB)); } xmlFree(str); return (rv); } int xmlattr_to_int(topo_mod_t *mp, xmlNodePtr n, const char *propname, uint64_t *value) { xmlChar *str; xmlChar *estr; if ((str = xmlGetProp(n, (xmlChar *)propname)) == NULL) { topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "%s: failed to lookup %s attribute", __func__, propname); return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR)); } errno = 0; *value = strtoull((char *)str, (char **)&estr, 0); if (errno != 0 || *estr != '\0') { /* no conversion was done */ xmlFree(str); topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "%s: failed to convert %s attribute", __func__, propname); return (topo_mod_seterrno(mp, ETOPO_PRSR_BADNUM)); } xmlFree(str); return (0); } int xmlattr_to_double(topo_mod_t *mp, xmlNodePtr n, const char *propname, double *value) { xmlChar *str; xmlChar *estr; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "xmlattr_to_double(propname=%s)\n", propname); if ((str = xmlGetProp(n, (xmlChar *)propname)) == NULL) return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR)); errno = 0; *value = strtold((char *)str, (char **)&estr); if (errno != 0 || *estr != '\0') { /* full or partial conversion failure */ xmlFree(str); return (topo_mod_seterrno(mp, ETOPO_PRSR_BADNUM)); } xmlFree(str); return (0); } static int xmlattr_to_fmri(topo_mod_t *mp, xmlNodePtr xn, const char *propname, nvlist_t **rnvl) { xmlChar *str; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "xmlattr_to_fmri(propname=%s)\n", propname); if ((str = xmlGetProp(xn, (xmlChar *)propname)) == NULL) return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR)); if (topo_mod_str2nvl(mp, (const char *)str, rnvl) < 0) { xmlFree(str); return (-1); } xmlFree(str); return (0); } static topo_type_t xmlattr_to_type(topo_mod_t *mp, xmlNodePtr xn, xmlChar *attr) { topo_type_t rv; xmlChar *str; if ((str = xmlGetProp(xn, (xmlChar *)attr)) == NULL) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "%s attribute missing", attr); (void) topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR); return (TOPO_TYPE_INVALID); } if (xmlStrcmp(str, (xmlChar *)Int32) == 0) { rv = TOPO_TYPE_INT32; } else if (xmlStrcmp(str, (xmlChar *)UInt32) == 0) { rv = TOPO_TYPE_UINT32; } else if (xmlStrcmp(str, (xmlChar *)Int64) == 0) { rv = TOPO_TYPE_INT64; } else if (xmlStrcmp(str, (xmlChar *)UInt64) == 0) { rv = TOPO_TYPE_UINT64; } else if (xmlStrcmp(str, (xmlChar *)FMRI) == 0) { rv = TOPO_TYPE_FMRI; } else if (xmlStrcmp(str, (xmlChar *)String) == 0) { rv = TOPO_TYPE_STRING; } else if (xmlStrcmp(str, (xmlChar *)Double) == 0) { rv = TOPO_TYPE_DOUBLE; } else if (xmlStrcmp(str, (xmlChar *)Int32_Arr) == 0) { rv = TOPO_TYPE_INT32_ARRAY; } else if (xmlStrcmp(str, (xmlChar *)UInt32_Arr) == 0) { rv = TOPO_TYPE_UINT32_ARRAY; } else if (xmlStrcmp(str, (xmlChar *)Int64_Arr) == 0) { rv = TOPO_TYPE_INT64_ARRAY; } else if (xmlStrcmp(str, (xmlChar *)UInt64_Arr) == 0) { rv = TOPO_TYPE_UINT64_ARRAY; } else if (xmlStrcmp(str, (xmlChar *)String_Arr) == 0) { rv = TOPO_TYPE_STRING_ARRAY; } else if (xmlStrcmp(str, (xmlChar *)FMRI_Arr) == 0) { rv = TOPO_TYPE_FMRI_ARRAY; } else { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "Unrecognized type attribute value '%s'.\n", str); (void) topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE); xmlFree(str); return (TOPO_TYPE_INVALID); } xmlFree(str); return (rv); } static int xlate_common(topo_mod_t *mp, xmlNodePtr xn, topo_type_t ptype, nvlist_t *nvl, const char *name) { int rv = 0; uint64_t ui; double dbl; uint_t i = 0, nelems = 0; nvlist_t *fmri; xmlChar *str; char **strarrbuf; void *arrbuf; nvlist_t **nvlarrbuf; xmlNodePtr cn; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "xlate_common(name=%s)\n", name); switch (ptype) { case TOPO_TYPE_INT32: if (xmlattr_to_int(mp, xn, Value, &ui) < 0) return (-1); rv = nvlist_add_int32(nvl, name, (int32_t)ui); break; case TOPO_TYPE_UINT32: if (xmlattr_to_int(mp, xn, Value, &ui) < 0) return (-1); rv = nvlist_add_uint32(nvl, name, (uint32_t)ui); break; case TOPO_TYPE_INT64: if (xmlattr_to_int(mp, xn, Value, &ui) < 0) return (-1); rv = nvlist_add_int64(nvl, name, (int64_t)ui); break; case TOPO_TYPE_UINT64: if (xmlattr_to_int(mp, xn, Value, &ui) < 0) return (-1); rv = nvlist_add_uint64(nvl, name, ui); break; case TOPO_TYPE_DOUBLE: if (xmlattr_to_double(mp, xn, Value, &dbl) < 0) return (-1); rv = nvlist_add_double(nvl, name, dbl); break; case TOPO_TYPE_FMRI: if (xmlattr_to_fmri(mp, xn, Value, &fmri) < 0) return (-1); rv = nvlist_add_nvlist(nvl, name, fmri); nvlist_free(fmri); break; case TOPO_TYPE_STRING: if ((str = xmlGetProp(xn, (xmlChar *)Value)) == NULL) return (-1); rv = nvlist_add_string(nvl, name, (char *)str); xmlFree(str); break; case TOPO_TYPE_INT32_ARRAY: case TOPO_TYPE_UINT32_ARRAY: case TOPO_TYPE_INT64_ARRAY: case TOPO_TYPE_UINT64_ARRAY: case TOPO_TYPE_STRING_ARRAY: case TOPO_TYPE_FMRI_ARRAY: for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) || (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0)) nelems++; if (nelems < 1) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "No " "or elements found for array val"); return (-1); } break; default: topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "Unrecognized type attribute (ptype = %d)\n", ptype); return (topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE)); } switch (ptype) { case TOPO_TYPE_INT32_ARRAY: if ((arrbuf = topo_mod_alloc(mp, (nelems * sizeof (int32_t)))) == NULL) return (topo_mod_seterrno(mp, ETOPO_NOMEM)); for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) { if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) || (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0)) { if (xmlattr_to_int(mp, cn, Value, &ui) < 0) return (-1); ((int32_t *)arrbuf)[i++] = (int32_t)ui; } } rv = nvlist_add_int32_array(nvl, name, (int32_t *)arrbuf, nelems); topo_mod_free(mp, arrbuf, (nelems * sizeof (int32_t))); break; case TOPO_TYPE_UINT32_ARRAY: if ((arrbuf = topo_mod_alloc(mp, (nelems * sizeof (uint32_t)))) == NULL) return (topo_mod_seterrno(mp, ETOPO_NOMEM)); for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) { if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) || (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0)) { if (xmlattr_to_int(mp, cn, Value, &ui) < 0) return (-1); ((uint32_t *)arrbuf)[i++] = (uint32_t)ui; } } rv = nvlist_add_uint32_array(nvl, name, (uint32_t *)arrbuf, nelems); topo_mod_free(mp, arrbuf, (nelems * sizeof (uint32_t))); break; case TOPO_TYPE_INT64_ARRAY: if ((arrbuf = topo_mod_alloc(mp, (nelems * sizeof (int64_t)))) == NULL) return (topo_mod_seterrno(mp, ETOPO_NOMEM)); for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) { if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) || (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0)) { if (xmlattr_to_int(mp, cn, Value, &ui) < 0) return (-1); ((int64_t *)arrbuf)[i++] = (int64_t)ui; } } rv = nvlist_add_int64_array(nvl, name, (int64_t *)arrbuf, nelems); topo_mod_free(mp, arrbuf, (nelems * sizeof (int64_t))); break; case TOPO_TYPE_UINT64_ARRAY: if ((arrbuf = topo_mod_alloc(mp, (nelems * sizeof (uint64_t)))) == NULL) return (topo_mod_seterrno(mp, ETOPO_NOMEM)); for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) { if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) || (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0)) { if (xmlattr_to_int(mp, cn, Value, &ui) < 0) return (-1); ((uint64_t *)arrbuf)[i++] = ui; } } rv = nvlist_add_uint64_array(nvl, name, arrbuf, nelems); topo_mod_free(mp, arrbuf, (nelems * sizeof (uint64_t))); break; case TOPO_TYPE_STRING_ARRAY: if ((strarrbuf = topo_mod_alloc(mp, (nelems * sizeof (char *)))) == NULL) return (topo_mod_seterrno(mp, ETOPO_NOMEM)); for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) { if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) || (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0)) { if ((str = xmlGetProp(cn, (xmlChar *)Value)) == NULL) return (-1); strarrbuf[i++] = topo_mod_strdup(mp, (const char *)str); xmlFree(str); } } rv = nvlist_add_string_array(nvl, name, strarrbuf, nelems); topo_mod_strfreev(mp, strarrbuf, nelems); break; case TOPO_TYPE_FMRI_ARRAY: if ((nvlarrbuf = topo_mod_alloc(mp, (nelems * sizeof (nvlist_t *)))) == NULL) return (topo_mod_seterrno(mp, ETOPO_NOMEM)); for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) { if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) || (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0)) { if ((str = xmlGetProp(cn, (xmlChar *)Value)) == NULL) return (-1); if (topo_mod_str2nvl(mp, (const char *)str, &(nvlarrbuf[i++])) < 0) { xmlFree(str); return (-1); } xmlFree(str); } } rv = nvlist_add_nvlist_array(nvl, name, nvlarrbuf, nelems); topo_mod_free(mp, nvlarrbuf, (nelems * sizeof (nvlist_t *))); break; default: /* * Invalid types were handled in the switch statement above * this. Items in this case don't need additional processing. */ break; } if (rv != 0) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "Nvlist construction failed.\n"); return (topo_mod_seterrno(mp, ETOPO_NOMEM)); } else return (0); } static int xmlprop_xlate(topo_mod_t *mp, xmlNodePtr xn, nvlist_t *nvl) { topo_type_t ptype; xmlChar *str; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "xmlprop_xlate\n"); if ((str = xmlGetProp(xn, (xmlChar *)Immutable)) != NULL) { if (xmlStrcmp(str, (xmlChar *)False) == 0) (void) nvlist_add_boolean_value(nvl, INV_IMMUTE, B_FALSE); else (void) nvlist_add_boolean_value(nvl, INV_IMMUTE, B_TRUE); xmlFree(str); } else { (void) nvlist_add_boolean_value(nvl, INV_IMMUTE, B_TRUE); } if ((ptype = xmlattr_to_type(mp, xn, (xmlChar *)Type)) == TOPO_TYPE_INVALID) return (-1); if (nvlist_add_int32(nvl, INV_PVALTYPE, ptype) != 0) return (-1); return (xlate_common(mp, xn, ptype, nvl, INV_PVAL)); } static int dependent_create(topo_mod_t *mp, tf_info_t *xinfo, tf_pad_t *pad, xmlNodePtr dxn, tnode_t *ptn) { tf_rdata_t *rp, *pp, *np; xmlChar *grptype; int sibs = 0; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "dependent_create\n"); if ((grptype = xmlGetProp(dxn, (xmlChar *)Grouping)) == NULL) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "Dependents missing grouping attribute"); return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR)); } pp = NULL; if (xmlStrcmp(grptype, (xmlChar *)Siblings) == 0) { rp = pad->tpad_sibs; sibs++; } else if (xmlStrcmp(grptype, (xmlChar *)Children) == 0) { rp = pad->tpad_child; } else { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "Dependents have bogus grouping attribute"); xmlFree(grptype); return (topo_mod_seterrno(mp, ETOPO_PRSR_BADGRP)); } xmlFree(grptype); /* Add processed dependents to the tail of the list */ while (rp != NULL) { pp = rp; rp = rp->rd_next; } if ((np = topo_xml_walk(mp, xinfo, dxn, ptn)) == NULL) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "error within dependent .xml topology: " "%s\n", topo_strerror(topo_mod_errno(mp))); return (-1); } if (pp != NULL) pp->rd_next = np; else if (sibs == 1) pad->tpad_sibs = np; else pad->tpad_child = np; return (0); } static int dependents_create(topo_mod_t *mp, tf_info_t *xinfo, tf_pad_t *pad, xmlNodePtr pxn, tnode_t *ptn) { xmlNodePtr cn; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "dependents_create\n"); for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) { if (xmlStrcmp(cn->name, (xmlChar *)Dependents) == 0) { if (dependent_create(mp, xinfo, pad, cn, ptn) < 0) return (-1); } } return (0); } static int prop_create(topo_mod_t *mp, nvlist_t *pfmri, tnode_t *ptn, const char *gnm, const char *pnm, topo_type_t ptype, int flag) { nvlist_t *fmri, **fmriarr; uint32_t ui32, *ui32arr; uint64_t ui64, *ui64arr; int32_t i32, *i32arr; int64_t i64, *i64arr; double dbl; uint_t nelem; char *str, **strarr; int err, e; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "prop_create(pgrp = %s, " "prop = %s)\n", gnm, pnm); switch (ptype) { case TOPO_TYPE_INT32: e = nvlist_lookup_int32(pfmri, INV_PVAL, &i32); break; case TOPO_TYPE_UINT32: e = nvlist_lookup_uint32(pfmri, INV_PVAL, &ui32); break; case TOPO_TYPE_INT64: e = nvlist_lookup_int64(pfmri, INV_PVAL, &i64); break; case TOPO_TYPE_UINT64: e = nvlist_lookup_uint64(pfmri, INV_PVAL, &ui64); break; case TOPO_TYPE_DOUBLE: e = nvlist_lookup_double(pfmri, INV_PVAL, &dbl); break; case TOPO_TYPE_FMRI: e = nvlist_lookup_nvlist(pfmri, INV_PVAL, &fmri); break; case TOPO_TYPE_STRING: e = nvlist_lookup_string(pfmri, INV_PVAL, &str); break; case TOPO_TYPE_INT32_ARRAY: e = nvlist_lookup_int32_array(pfmri, INV_PVAL, &i32arr, &nelem); break; case TOPO_TYPE_UINT32_ARRAY: e = nvlist_lookup_uint32_array(pfmri, INV_PVAL, &ui32arr, &nelem); break; case TOPO_TYPE_INT64_ARRAY: e = nvlist_lookup_int64_array(pfmri, INV_PVAL, &i64arr, &nelem); break; case TOPO_TYPE_UINT64_ARRAY: e = nvlist_lookup_uint64_array(pfmri, INV_PVAL, &ui64arr, &nelem); break; case TOPO_TYPE_STRING_ARRAY: e = nvlist_lookup_string_array(pfmri, INV_PVAL, &strarr, &nelem); break; case TOPO_TYPE_FMRI_ARRAY: e = nvlist_lookup_nvlist_array(pfmri, INV_PVAL, &fmriarr, &nelem); break; default: e = ETOPO_PRSR_BADTYPE; break; } if (e != 0) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "prop_create: prop value lookup failed.\n"); return (topo_mod_seterrno(mp, e)); } switch (ptype) { case TOPO_TYPE_INT32: e = topo_prop_set_int32(ptn, gnm, pnm, flag, i32, &err); break; case TOPO_TYPE_UINT32: e = topo_prop_set_uint32(ptn, gnm, pnm, flag, ui32, &err); break; case TOPO_TYPE_INT64: e = topo_prop_set_int64(ptn, gnm, pnm, flag, i64, &err); break; case TOPO_TYPE_UINT64: e = topo_prop_set_uint64(ptn, gnm, pnm, flag, ui64, &err); break; case TOPO_TYPE_DOUBLE: e = topo_prop_set_double(ptn, gnm, pnm, flag, dbl, &err); break; case TOPO_TYPE_FMRI: e = topo_prop_set_fmri(ptn, gnm, pnm, flag, fmri, &err); break; case TOPO_TYPE_STRING: e = topo_prop_set_string(ptn, gnm, pnm, flag, str, &err); break; case TOPO_TYPE_INT32_ARRAY: e = topo_prop_set_int32_array(ptn, gnm, pnm, flag, i32arr, nelem, &err); break; case TOPO_TYPE_UINT32_ARRAY: e = topo_prop_set_uint32_array(ptn, gnm, pnm, flag, ui32arr, nelem, &err); break; case TOPO_TYPE_INT64_ARRAY: e = topo_prop_set_int64_array(ptn, gnm, pnm, flag, i64arr, nelem, &err); break; case TOPO_TYPE_UINT64_ARRAY: e = topo_prop_set_uint64_array(ptn, gnm, pnm, flag, ui64arr, nelem, &err); break; case TOPO_TYPE_STRING_ARRAY: e = topo_prop_set_string_array(ptn, gnm, pnm, flag, (const char **)strarr, nelem, &err); break; case TOPO_TYPE_FMRI_ARRAY: e = topo_prop_set_fmri_array(ptn, gnm, pnm, flag, (const nvlist_t **)fmriarr, nelem, &err); break; default: e = ETOPO_PRSR_BADTYPE; break; } if (e != 0 && err != ETOPO_PROP_DEFD) { /* * Some properties may have already been set * in topo_node_bind() or topo_prop_inherit if we are * enumerating from a static .xml file */ topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "prop set " "failed %s/%s:%s\n", gnm, pnm, topo_strerror(err)); return (topo_mod_seterrno(mp, err)); } return (0); } static int props_create(topo_mod_t *mp, tnode_t *ptn, const char *gnm, nvlist_t **props, int nprops) { topo_type_t ptype; boolean_t pim; char *pnm; int32_t i32; int flag; int pn; int e; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "props_create(pgrp = %s)\n", gnm); for (pn = 0; pn < nprops; pn++) { e = nvlist_lookup_string(props[pn], INV_PNAME, &pnm); if (e != 0) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "props create lookup (%s) failure: %s", INV_PNAME, strerror(e)); return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP)); } e = nvlist_lookup_boolean_value(props[pn], INV_IMMUTE, &pim); if (e != 0) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "props create lookup (%s) failure: %s", INV_IMMUTE, strerror(e)); return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP)); } flag = (pim == B_TRUE) ? TOPO_PROP_IMMUTABLE : TOPO_PROP_MUTABLE; e = nvlist_lookup_int32(props[pn], INV_PVALTYPE, &i32); if (e != 0) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "props create lookup (%s) failure: %s", INV_PVALTYPE, strerror(e)); return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP)); } ptype = (topo_type_t)i32; if (prop_create(mp, props[pn], ptn, gnm, pnm, ptype, flag) < 0) return (-1); } return (0); } static int pgroups_create(topo_mod_t *mp, tf_pad_t *pad, tnode_t *ptn) { topo_pgroup_info_t pgi; nvlist_t **props; char *gnm; char *nmstab, *dstab; uint32_t rnprops, nprops; uint32_t gv; int pg; int e; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroups_create: %s=%d\n", topo_node_name(ptn), topo_node_instance(ptn)); for (pg = 0; pg < pad->tpad_pgcnt; pg++) { e = nvlist_lookup_string(pad->tpad_pgs[pg], INV_PGRP_NAME, &gnm); if (e != 0) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "pad lookup (%s) failed (%s).\n", INV_PGRP_NAME, strerror(errno)); return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP)); } e = nvlist_lookup_string(pad->tpad_pgs[pg], INV_PGRP_NMSTAB, &nmstab); if (e != 0) { if (e != ENOENT) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "pad lookup (%s) " "failed.\n", INV_PGRP_NMSTAB); return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP)); } else { nmstab = TOPO_STABSTR_PRIVATE; } } e = nvlist_lookup_string(pad->tpad_pgs[pg], INV_PGRP_DSTAB, &dstab); if (e != 0) { if (e != ENOENT) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "pad lookup (%s) failed.\n", INV_PGRP_DSTAB); return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP)); } else { dstab = TOPO_STABSTR_PRIVATE; } } e = nvlist_lookup_uint32(pad->tpad_pgs[pg], INV_PGRP_VER, &gv); if (e != 0) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "pad lookup (%s) failed.\n", INV_PGRP_VER); return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP)); } pgi.tpi_name = gnm; pgi.tpi_namestab = topo_name2stability(nmstab); pgi.tpi_datastab = topo_name2stability(dstab); pgi.tpi_version = gv; if (topo_pgroup_create(ptn, &pgi, &e) != 0) { if (e != ETOPO_PROP_DEFD) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "pgroups create failure: %s\n", topo_strerror(e)); return (-1); } } e = nvlist_lookup_uint32(pad->tpad_pgs[pg], INV_PGRP_NPROP, &rnprops); /* * The number of properties could be zero if the property * group only contains propmethod declarations */ if (rnprops > 0) { e |= nvlist_lookup_nvlist_array(pad->tpad_pgs[pg], INV_PGRP_ALLPROPS, &props, &nprops); if (rnprops != nprops) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "recorded number of props %d does not " "match number of props recorded %d.\n", rnprops, nprops); } if (props_create(mp, ptn, gnm, props, nprops) < 0) return (-1); } } return (0); } static nvlist_t * pval_record(topo_mod_t *mp, xmlNodePtr xn) { nvlist_t *pnvl = NULL; xmlChar *pname; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pval_record\n"); if ((pname = xmlGetProp(xn, (xmlChar *)Name)) == NULL) { topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "propval lacks a name\n"); (void) topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR); return (NULL); } if (topo_mod_nvalloc(mp, &pnvl, NV_UNIQUE_NAME) < 0) { xmlFree(pname); return (NULL); } if (nvlist_add_string(pnvl, INV_PNAME, (char *)pname) < 0) { xmlFree(pname); nvlist_free(pnvl); return (NULL); } xmlFree(pname); /* FMXXX stability of the property name */ if (xmlprop_xlate(mp, xn, pnvl) < 0) { nvlist_free(pnvl); return (NULL); } return (pnvl); } struct propmeth_data { const char *pg_name; const char *prop_name; topo_type_t prop_type; const char *meth_name; topo_version_t meth_ver; nvlist_t *arg_nvl; }; static int register_method(topo_mod_t *mp, tnode_t *ptn, struct propmeth_data *meth) { int err; if (topo_prop_method_version_register(ptn, meth->pg_name, meth->prop_name, meth->prop_type, meth->meth_name, meth->meth_ver, meth->arg_nvl, &err) != 0) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "failed to register " "propmethod %s for property \"%s\" in propgrp %s on node " "%s=%d (%s)\n", meth->meth_name, meth->prop_name, meth->pg_name, topo_node_name(ptn), topo_node_instance(ptn), topo_strerror(err)); return (-1); } topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "registered method %s on %s=%d\n", meth->meth_name, topo_node_name(ptn), topo_node_instance(ptn)); return (0); } static int pmeth_record(topo_mod_t *mp, const char *pg_name, xmlNodePtr xn, tnode_t *tn, const char *rname, const char *ppgrp_name) { nvlist_t *arg_nvl = NULL; xmlNodePtr cn; xmlChar *meth_name = NULL, *prop_name = NULL; xmlChar *arg_name = NULL; uint64_t meth_ver, is_mutable = 0, is_nonvolatile = 0; topo_type_t prop_type; struct propmeth_data meth; int ret = 0, err; topo_type_t ptype; tnode_t *tmp; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pmeth_record: %s=%d " "(pgrp=%s)\n", topo_node_name(tn), topo_node_instance(tn), pg_name); /* * Get propmethod attribute values */ if ((meth_name = xmlGetProp(xn, (xmlChar *)Name)) == NULL) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "propmethod element lacks a name attribute\n"); return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR)); } if (xmlattr_to_int(mp, xn, Version, &meth_ver) < 0) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "propmethod element lacks version attribute\n"); ret = topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR); goto pmr_done; } /* * The "mutable" and "nonvoltile" attributes are optional. If not * specified we default to false (0) */ (void) xmlattr_to_int(mp, xn, Mutable, &is_mutable); (void) xmlattr_to_int(mp, xn, Nonvolatile, &is_nonvolatile); if ((prop_name = xmlGetProp(xn, (xmlChar *)Propname)) == NULL) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "propmethod element lacks propname attribute\n"); ret = topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR); goto pmr_done; } if ((prop_type = xmlattr_to_type(mp, xn, (xmlChar *)Proptype)) == TOPO_TYPE_INVALID) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "error decoding proptype attribute\n"); ret = topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR); goto pmr_done; } /* * Allocate method argument nvlist */ if (topo_mod_nvalloc(mp, &arg_nvl, NV_UNIQUE_NAME) < 0) { ret = topo_mod_seterrno(mp, ETOPO_NOMEM); goto pmr_done; } /* * Iterate through the argval nodes and build the argval nvlist */ for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) { if (xmlStrcmp(cn->name, (xmlChar *)Argval) == 0) { topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "found argval element\n"); if ((arg_name = xmlGetProp(cn, (xmlChar *)Name)) == NULL) { topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "argval element lacks a name attribute\n"); ret = topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR); goto pmr_done; } if ((ptype = xmlattr_to_type(mp, cn, (xmlChar *)Type)) == TOPO_TYPE_INVALID) { ret = topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE); xmlFree(arg_name); break; } if (xlate_common(mp, cn, ptype, arg_nvl, (const char *)arg_name) != 0) { ret = topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE); xmlFree(arg_name); break; } } if (arg_name) { xmlFree(arg_name); arg_name = NULL; } } if (ret != 0) goto pmr_done; /* * Register the prop method for all of the nodes in our range */ meth.pg_name = (const char *)pg_name; meth.prop_name = (const char *)prop_name; meth.prop_type = prop_type; meth.meth_name = (const char *)meth_name; meth.meth_ver = meth_ver; meth.arg_nvl = arg_nvl; /* * If the propgroup element is under a range element, we'll apply * the method to all of the topo nodes at this level with the same * range name. * * Otherwise, if the propgroup element is under a node element * then we'll simply register the method for this node. */ if (strcmp(ppgrp_name, Range) == 0) { for (tmp = tn; tmp != NULL; tmp = topo_child_next(NULL, tmp)) { if (strcmp(rname, topo_node_name(tmp)) == 0) { if (register_method(mp, tmp, &meth) != 0) { ret = topo_mod_seterrno(mp, ETOPO_PRSR_REGMETH); goto pmr_done; } if (is_mutable) { if (topo_prop_setmutable(tmp, meth.pg_name, meth.prop_name, &err) != 0) { ret = topo_mod_seterrno(mp, ETOPO_PRSR_REGMETH); goto pmr_done; } } if (is_nonvolatile) { if (topo_prop_setnonvolatile(tmp, meth.pg_name, meth.prop_name, &err) != 0) { ret = topo_mod_seterrno(mp, ETOPO_PRSR_REGMETH); goto pmr_done; } } } } } else { if (register_method(mp, tn, &meth) != 0) { ret = topo_mod_seterrno(mp, ETOPO_PRSR_REGMETH); goto pmr_done; } if (is_mutable) { if (topo_prop_setmutable(tn, meth.pg_name, meth.prop_name, &err) != 0) { ret = topo_mod_seterrno(mp, ETOPO_PRSR_REGMETH); goto pmr_done; } } if (is_nonvolatile) { if (topo_prop_setnonvolatile(tn, meth.pg_name, meth.prop_name, &err) != 0) { ret = topo_mod_seterrno(mp, ETOPO_PRSR_REGMETH); goto pmr_done; } } } pmr_done: if (meth_name) xmlFree(meth_name); if (prop_name) xmlFree(prop_name); nvlist_free(arg_nvl); return (ret); } static int pgroup_record(topo_mod_t *mp, xmlNodePtr pxn, tnode_t *tn, const char *rname, tf_pad_t *rpad, int pi, const char *ppgrp_name) { topo_stability_t nmstab, dstab; uint64_t ver; xmlNodePtr cn; xmlChar *name; nvlist_t **apl = NULL; nvlist_t *pgnvl = NULL; int pcnt = 0; int ai = 0; int e; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroup_record\n"); if ((name = xmlGetProp(pxn, (xmlChar *)Name)) == NULL) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "propgroup lacks a name\n"); return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR)); } if (xmlattr_to_int(mp, pxn, Version, &ver) < 0) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "propgroup lacks a version\n"); return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR)); } if (xmlattr_to_stab(mp, pxn, Namestab, &nmstab) < 0) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "propgroup lacks name-stability\n"); return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR)); } if (xmlattr_to_stab(mp, pxn, Datastab, &dstab) < 0) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "propgroup lacks data-stability\n"); return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR)); } topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroup %s\n", (char *)name); for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) { if (xmlStrcmp(cn->name, (xmlChar *)Propval) == 0) pcnt++; } if (topo_mod_nvalloc(mp, &pgnvl, NV_UNIQUE_NAME) < 0) { xmlFree(name); topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "failed to allocate propgroup nvlist\n"); return (topo_mod_seterrno(mp, ETOPO_NOMEM)); } e = nvlist_add_string(pgnvl, INV_PGRP_NAME, (char *)name); e |= nvlist_add_uint32(pgnvl, INV_PGRP_NMSTAB, nmstab); e |= nvlist_add_uint32(pgnvl, INV_PGRP_DSTAB, dstab); e |= nvlist_add_uint32(pgnvl, INV_PGRP_VER, ver); e |= nvlist_add_uint32(pgnvl, INV_PGRP_NPROP, pcnt); if (pcnt > 0) if (e != 0 || (apl = topo_mod_zalloc(mp, pcnt * sizeof (nvlist_t *))) == NULL) { xmlFree(name); nvlist_free(pgnvl); topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "failed to allocate nvlist array for properties" "(e=%d)\n", e); return (topo_mod_seterrno(mp, ETOPO_NOMEM)); } for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) { if (xmlStrcmp(cn->name, (xmlChar *)Propval) == 0) { if (ai < pcnt) { if ((apl[ai] = pval_record(mp, cn)) == NULL) break; } ai++; } else if (xmlStrcmp(cn->name, (xmlChar *)Prop_meth) == 0) { if (pmeth_record(mp, (const char *)name, cn, tn, rname, ppgrp_name) < 0) break; } } xmlFree(name); if (pcnt > 0) { e |= (ai != pcnt); e |= nvlist_add_nvlist_array(pgnvl, INV_PGRP_ALLPROPS, apl, pcnt); for (ai = 0; ai < pcnt; ai++) nvlist_free(apl[ai]); topo_mod_free(mp, apl, pcnt * sizeof (nvlist_t *)); if (e != 0) { nvlist_free(pgnvl); return (-1); } } rpad->tpad_pgs[pi] = pgnvl; return (0); } static int pgroups_record(topo_mod_t *mp, xmlNodePtr pxn, tnode_t *tn, const char *rname, tf_pad_t *rpad, const char *ppgrp) { xmlNodePtr cn; int pi = 0; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroups_record: pxn->name=%s\n", pxn->name); for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) { if (xmlStrcmp(cn->name, (xmlChar *)Propgrp) == 0) { if (pgroup_record(mp, cn, tn, rname, rpad, pi++, ppgrp) < 0) return (-1); } } return (0); } /* * psn: pointer to a "set" XML node * key: string to search the set for * * returns: 1, if the set contains key * 0, otherwise */ static int set_contains(topo_mod_t *mp, char *key, char *set) { char *prod; int rv = 0; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "set_contains(key = %s, " "setlist = %s)\n", key, set); prod = strtok((char *)set, "|"); if (prod && (strcmp(key, prod) == 0)) return (1); while ((prod = strtok(NULL, "|"))) if (strcmp(key, prod) == 0) return (1); return (rv); } /* * Process the property group and dependents xmlNode children of * parent xmlNode pxn. */ static int pad_process(topo_mod_t *mp, tf_rdata_t *rd, xmlNodePtr pxn, tnode_t *ptn, tf_pad_t **rpad) { xmlNodePtr cn, gcn, psn, target; /* Explicitly initialize ecn to help -Wmaybe-unit */ xmlNodePtr def_set = NULL, ecn = NULL; tnode_t *ct; tf_pad_t *new = *rpad; tf_rdata_t tmp_rd; int pgcnt = 0; int dcnt = 0; int ecnt = 0; int joined_set = 0, inst; xmlChar *set; char *key; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pad_process beneath %s=%d\n", topo_node_name(ptn), topo_node_instance(ptn)); if (new == NULL) { for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) { topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "cn->name is %s \n", (char *)cn->name); /* * We're iterating through the XML children looking for * four types of elements: * 1) dependents elements * 2) unconstrained pgroup elements * 3) pgroup elements constrained by set elements * 4) enum-method elements for the case that we want * to post-process a statically defined node */ if (xmlStrcmp(cn->name, (xmlChar *)Dependents) == 0) dcnt++; else if (xmlStrcmp(cn->name, (xmlChar *)Propgrp) == 0) pgcnt++; else if (xmlStrcmp(cn->name, (xmlChar *)Enum_meth) == 0) { ecn = cn; ecnt++; } else if (xmlStrcmp(cn->name, (xmlChar *)Set) == 0) { if (joined_set) continue; set = xmlGetProp(cn, (xmlChar *)Setlist); key = mp->tm_hdl->th_product; /* * If it's the default set then we'll store * a pointer to it so that if none of the other * sets apply to our product we can fall * back to this one. */ if (strcmp((char *)set, "default") == 0) def_set = cn; else if (set_contains(mp, key, (char *)set)) { psn = cn; joined_set = 1; for (gcn = cn->xmlChildrenNode; gcn != NULL; gcn = gcn->next) { if (xmlStrcmp(gcn->name, (xmlChar *)Propgrp) == 0) pgcnt++; } } xmlFree(set); } } /* * If we haven't found a set that contains our product AND * a default set exists, then we'll process it. */ if (!joined_set && def_set) { topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "Falling back to default set\n"); joined_set = 1; psn = def_set; for (gcn = psn->xmlChildrenNode; gcn != NULL; gcn = gcn->next) { if (xmlStrcmp(gcn->name, (xmlChar *)Propgrp) == 0) pgcnt++; } } topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pad_process: dcnt=%d, pgcnt=%d, ecnt=%d, joined_set=%d\n", dcnt, pgcnt, ecnt, joined_set); /* * Here we allocate an element in an intermediate data structure * which keeps track property groups and dependents of the range * currently being processed. * * This structure is referenced in pgroups_record() to create * the actual property groups in the topo tree */ if ((new = tf_pad_new(mp, pgcnt, dcnt)) == NULL) return (-1); if (pgcnt > 0) { new->tpad_pgs = topo_mod_zalloc(mp, pgcnt * sizeof (nvlist_t *)); if (new->tpad_pgs == NULL) { tf_pad_free(mp, new); return (-1); } } /* * If the property groups are contained within a set * then they will be one level lower in the XML tree. */ if (joined_set) target = psn; else target = pxn; /* * If there is no "node" element under the "range" * element, then we need to attach the facility node to * each node in this range. * * Otherwise we only attach it to the current node */ if (xmlStrcmp(target->name, (xmlChar *)Range) == 0 || xmlStrcmp(target->name, (xmlChar *)Set) == 0) { for (ct = topo_child_first(rd->rd_pn); ct != NULL; ct = topo_child_next(rd->rd_pn, ct)) { if (strcmp(topo_node_name(ct), rd->rd_name) != 0) continue; inst = topo_node_instance(ct); if (inst < rd->rd_min || inst > rd->rd_max) continue; if (fac_enum_process(mp, target, ct) < 0) return (-1); if (fac_process(mp, target, rd, ct) < 0) return (-1); } } else { if (fac_enum_process(mp, target, ptn) < 0) return (-1); if (fac_process(mp, target, rd, ptn) < 0) return (-1); } if (pgcnt > 0 && pgroups_record(mp, target, ptn, rd->rd_name, new, (const char *)pxn->name) < 0) { tf_pad_free(mp, new); return (-1); } *rpad = new; } /* * We need to process the property groups before enumerating any * dependents as that enuemration can itself have dependencies on * properties set on the parent node. */ if (new->tpad_pgcnt > 0) if (pgroups_create(mp, new, ptn) < 0) return (-1); /* * If an enum-method element was found, AND we're a child of a * node element, then we invoke the enumerator so that it can do * post-processing of the node. */ if (ecnt && (strcmp((const char *)pxn->name, Node) == 0)) { if ((tmp_rd.rd_einfo = enum_attributes_process(mp, ecn)) == NULL) return (-1); tmp_rd.rd_mod = mp; tmp_rd.rd_name = rd->rd_name; tmp_rd.rd_min = rd->rd_min; tmp_rd.rd_max = rd->rd_max; tmp_rd.rd_pn = ptn; if (enum_run(mp, &tmp_rd) < 0) { /* * Note the failure but continue on */ topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "pad_process: enumeration failed.\n"); } tf_edata_free(mp, tmp_rd.rd_einfo); } if (new->tpad_dcnt > 0) if (dependents_create(mp, rd->rd_finfo, new, pxn, ptn) < 0) return (-1); return (0); } static int fac_enum_process(topo_mod_t *mp, xmlNodePtr pn, tnode_t *ptn) { xmlNodePtr cn; xmlChar *fprov = NULL; int rv = 0; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "fac_enum_process() called for %s=%d\n", topo_node_name(ptn), topo_node_instance(ptn)); for (cn = pn->xmlChildrenNode; cn != NULL; cn = cn->next) { if (xmlStrcmp(cn->name, (xmlChar *)"fac-enum") != 0) continue; if ((fprov = xmlGetProp(cn, (xmlChar *)Provider)) == NULL) goto fenumdone; /* * Invoke enum entry point in facility provider which will * cause the facility enumeration node method to be * registered. */ if (fac_enum_run(mp, ptn, (const char *)fprov) != 0) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "fac_enum_process: enum entry point failed!\n"); goto fenumdone; } xmlFree(fprov); } return (0); fenumdone: topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "fac-enum processing failed\n"); if (fprov != NULL) xmlFree(fprov); return (rv); } static int fac_process(topo_mod_t *mp, xmlNodePtr pn, tf_rdata_t *rd, tnode_t *ptn) { xmlNodePtr cn; xmlChar *fname = NULL, *ftype = NULL, *provider = NULL; tnode_t *ntn = NULL; tf_idata_t *newi; int err; topo_pgroup_info_t pgi; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "fac_process() called for %s=%d\n", topo_node_name(ptn), topo_node_instance(ptn)); for (cn = pn->xmlChildrenNode; cn != NULL; cn = cn->next) { if (xmlStrcmp(cn->name, (xmlChar *)Facility) != 0) continue; if ((fname = xmlGetProp(cn, (xmlChar *)Name)) == NULL) goto facdone; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "processing facility node '%s'\n", fname); if ((ftype = xmlGetProp(cn, (xmlChar *)Type)) == NULL) goto facdone; if ((provider = xmlGetProp(cn, (xmlChar *)Provider)) == NULL) goto facdone; if (xmlStrcmp(ftype, (xmlChar *)Sensor) != 0 && xmlStrcmp(ftype, (xmlChar *)Indicator) != 0) goto facdone; if ((ntn = topo_node_facbind(mp, ptn, (char *)fname, (char *)ftype)) == NULL) goto facdone; pgi.tpi_name = TOPO_PGROUP_FACILITY; pgi.tpi_namestab = TOPO_STABILITY_PRIVATE; pgi.tpi_datastab = TOPO_STABILITY_PRIVATE; pgi.tpi_version = 1; if (topo_pgroup_create(ntn, &pgi, &err) != 0) { if (err != ETOPO_PROP_DEFD) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "pgroups create failure: %s\n", topo_strerror(err)); return (-1); } } /* * Invoke enum entry point in the facility provider module, * which will cause the provider methods to be registered on * this node */ if (fac_enum_run(mp, ntn, (const char *)provider) != 0) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "fac_process: " "enum entry point failed for provider %s!\n", provider); goto facdone; } if ((newi = tf_idata_new(mp, 0, ntn)) == NULL) goto facdone; if (tf_idata_insert(&rd->rd_instances, newi) < 0) goto facdone; if (pad_process(mp, rd, cn, ntn, &newi->ti_pad) < 0) goto facdone; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "done with " "facility %s=%s.\n", ftype, fname); xmlFree(ftype); xmlFree(fname); xmlFree(provider); } return (0); facdone: topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "facility processing failed\n"); if (ftype != NULL) xmlFree(ftype); if (fname != NULL) xmlFree(fname); if (provider != NULL) xmlFree(provider); if (ntn != NULL) topo_node_unbind(ntn); return (0); } static int node_process(topo_mod_t *mp, xmlNodePtr nn, tf_rdata_t *rd) { xmlChar *str; topo_instance_t inst; tf_idata_t *newi; tnode_t *ntn; uint64_t ui; int rv = -1; int s = 0; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "node_process %s\n", rd->rd_name); if (xmlattr_to_int(mp, nn, Instance, &ui) < 0) goto nodedone; inst = (topo_instance_t)ui; if ((str = xmlGetProp(nn, (xmlChar *)Static)) != NULL) { if (xmlStrcmp(str, (xmlChar *)True) == 0) s = 1; xmlFree(str); } if (s == 0) { if (topo_mod_enumerate(rd->rd_mod, rd->rd_pn, rd->rd_finfo->tf_scheme, rd->rd_name, inst, inst, s == 1 ? &s : NULL) < 0) goto nodedone; } ntn = topo_node_lookup(rd->rd_pn, rd->rd_name, inst); if (ntn == NULL) { /* * If this is a static node declaration, we can * ignore the lookup failure and continue * processing. Otherwise, something * went wrong during enumeration */ if (s == 1) rv = 0; goto nodedone; } if ((newi = tf_idata_new(mp, inst, ntn)) == NULL) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "node_process: tf_idata_new failed.\n"); goto nodedone; } if (tf_idata_insert(&rd->rd_instances, newi) < 0) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "node_process: tf_idata_insert failed.\n"); goto nodedone; } if (pad_process(mp, rd, nn, ntn, &newi->ti_pad) < 0) goto nodedone; if (fac_process(mp, nn, rd, ntn) < 0) goto nodedone; rv = 0; nodedone: topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "done with node %s.\n", rd->rd_name); return (rv); } static tf_edata_t * enum_attributes_process(topo_mod_t *mp, xmlNodePtr en) { tf_edata_t *einfo; uint64_t ui; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enum_attributes_process\n"); if ((einfo = topo_mod_zalloc(mp, sizeof (tf_edata_t))) == NULL) { (void) topo_mod_seterrno(mp, ETOPO_NOMEM); return (NULL); } einfo->te_name = (char *)xmlGetProp(en, (xmlChar *)Name); if (einfo->te_name == NULL) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "Enumerator name attribute missing.\n"); (void) topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR); goto enodedone; } /* * Check for recursive enumeration */ if (strcmp(einfo->te_name, mp->tm_name) == 0) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "Recursive enumeration detected for %s\n", einfo->te_name); (void) topo_mod_seterrno(mp, ETOPO_ENUM_RECURS); goto enodedone; } if (xmlattr_to_int(mp, en, Version, &ui) < 0) goto enodedone; einfo->te_vers = (int)ui; return (einfo); enodedone: if (einfo->te_name != NULL) xmlFree(einfo->te_name); topo_mod_free(mp, einfo, sizeof (tf_edata_t)); return (NULL); } static int enum_run(topo_mod_t *mp, tf_rdata_t *rd) { topo_hdl_t *thp = mp->tm_hdl; int e = -1; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enum_run\n"); /* * Check if the enumerator module is already loaded. * Module loading is single-threaded at this point so there's * no need to worry about the module going away or bumping the * ref count. */ if ((rd->rd_mod = topo_mod_lookup(thp, rd->rd_einfo->te_name, 0)) == NULL) { if ((rd->rd_mod = topo_mod_load(mp, rd->rd_einfo->te_name, rd->rd_einfo->te_vers)) == NULL) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "enum_run: mod_load of %s failed: %s.\n", rd->rd_einfo->te_name, topo_strerror(topo_mod_errno(mp))); (void) topo_hdl_seterrno(thp, topo_mod_errno(mp)); return (e); } } /* * We're live, so let's enumerate. */ topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enumerate request. (%s)\n", rd->rd_einfo->te_name); e = topo_mod_enumerate(rd->rd_mod, rd->rd_pn, rd->rd_einfo->te_name, rd->rd_name, rd->rd_min, rd->rd_max, NULL); topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "back from enumeration. %d\n", e); if (e != 0) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "Enumeration failed (%s)\n", topo_strerror(topo_mod_errno(mp))); (void) topo_hdl_seterrno(thp, EMOD_PARTIAL_ENUM); return (topo_mod_seterrno(mp, EMOD_PARTIAL_ENUM)); } return (e); } static int fac_enum_run(topo_mod_t *mp, tnode_t *node, const char *name) { topo_hdl_t *thp = mp->tm_hdl; topo_mod_t *fmod; int e = -1; topo_dprintf(thp, TOPO_DBG_XML, "fac_enum_run\n"); /* * Check if the enumerator module is already loaded. * Module loading is single-threaded at this point so there's * no need to worry about the module going away or bumping the * ref count. */ if ((fmod = topo_mod_lookup(thp, name, 0)) == NULL) { if ((fmod = topo_mod_load(mp, name, TOPO_VERSION)) == NULL) { topo_dprintf(thp, TOPO_DBG_ERR, "fac_enum_run: mod_load of %s failed: %s.\n", name, topo_strerror(topo_mod_errno(mp))); (void) topo_hdl_seterrno(thp, topo_mod_errno(mp)); return (e); } } /* * We're live, so let's enumerate. */ topo_dprintf(thp, TOPO_DBG_XML, "fac enumerate request. (%s)\n", name); e = topo_mod_enumerate(fmod, node, name, name, 0, 0, NULL); topo_dprintf(thp, TOPO_DBG_XML, "back from enumeration. %d\n", e); if (e != 0) { topo_dprintf(thp, TOPO_DBG_ERR, "Facility provider enumeration failed (%s)\n", topo_strerror(topo_mod_errno(mp))); (void) topo_hdl_seterrno(thp, EMOD_PARTIAL_ENUM); return (topo_mod_seterrno(mp, EMOD_PARTIAL_ENUM)); } return (e); } int decorate_nodes(topo_mod_t *mp, tf_rdata_t *rd, xmlNodePtr pxn, tnode_t *ptn, tf_pad_t **rpad) { tnode_t *ctn; ctn = topo_child_first(ptn); while (ctn != NULL) { /* Only care about instances within the range */ if (strcmp(topo_node_name(ctn), rd->rd_name) != 0) { ctn = topo_child_next(ptn, ctn); continue; } if (pad_process(mp, rd, pxn, ctn, rpad) < 0) return (-1); if (decorate_nodes(mp, rd, pxn, ctn, rpad) < 0) return (-1); ctn = topo_child_next(ptn, ctn); } return (0); } int topo_xml_range_process(topo_mod_t *mp, xmlNodePtr rn, tf_rdata_t *rd) { /* * The range may have several children xmlNodes, that may * represent the enumeration method, property groups, * dependents, nodes or services. */ xmlNodePtr cn, enum_node = NULL, pmap_node = NULL; xmlChar *pmap_name; tnode_t *ct; int e, ccnt = 0; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_range_process\n" "process %s range beneath %s\n", rd->rd_name, topo_node_name(rd->rd_pn)); e = topo_node_range_create(mp, rd->rd_pn, rd->rd_name, rd->rd_min, rd->rd_max); if (e != 0 && topo_mod_errno(mp) != EMOD_NODE_DUP) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "Range create failed due to %s.\n", topo_strerror(topo_mod_errno(mp))); return (-1); } /* * Before we process any of the other child xmlNodes, we iterate through * the children and looking for either enum-method or propmap elements. */ for (cn = rn->xmlChildrenNode; cn != NULL; cn = cn->next) if (xmlStrcmp(cn->name, (xmlChar *)Enum_meth) == 0) enum_node = cn; else if (xmlStrcmp(cn->name, (xmlChar *)Propmap) == 0) pmap_node = cn; /* * If we found an enum-method element, process it first */ if (enum_node != NULL) { if ((rd->rd_einfo = enum_attributes_process(mp, enum_node)) == NULL) return (-1); if (enum_run(mp, rd) < 0) { /* * Note the failure but continue on */ topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "Enumeration failed.\n"); } } /* * Next, check if a propmap element was found and if so, load it in * and parse it. */ if (pmap_node != NULL) { topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "found a propmap " "element\n"); if ((pmap_name = xmlGetProp(pmap_node, (xmlChar *)Name)) == NULL) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "propmap element missing name attribute.\n"); } else { if (topo_file_load(mp, rd->rd_pn, (const char *)pmap_name, rd->rd_finfo->tf_scheme, 1) < 0) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "topo_xml_range_process: topo_file_load" "failed: %s.\n", topo_strerror(topo_mod_errno(mp))); } xmlFree(pmap_name); } } /* Now look for nodes, i.e., hard instances */ for (cn = rn->xmlChildrenNode; cn != NULL; cn = cn->next) { if (xmlStrcmp(cn->name, (xmlChar *)Node) == 0) { if (node_process(mp, cn, rd) < 0) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "node processing failed: %s.\n", topo_strerror(topo_mod_errno(mp))); return (topo_mod_seterrno(mp, EMOD_PARTIAL_ENUM)); } ccnt++; } } /* * Finally, process the property groups and dependents * * If the TF_PROPMAP flag is set for the XML file we're currently * processing, then this XML file was loaded via propmap. In that case * we call a special routine to recursively apply the propgroup settings * to all of nodes in this range */ if (rd->rd_finfo->tf_flags & TF_PROPMAP) (void) decorate_nodes(mp, rd, rn, rd->rd_pn, &rd->rd_pad); else { ct = topo_child_first(rd->rd_pn); while (ct != NULL) { /* Only care about instances within the range */ if (strcmp(topo_node_name(ct), rd->rd_name) != 0) { ct = topo_child_next(rd->rd_pn, ct); continue; } if (pad_process(mp, rd, rn, ct, &rd->rd_pad) < 0) return (-1); if (fac_process(mp, rn, rd, ct) < 0) return (-1); ct = topo_child_next(rd->rd_pn, ct); ccnt++; } } topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_range_process: end " "range process %s\n", rd->rd_name); return (0); } static tf_rdata_t * topo_xml_walk(topo_mod_t *mp, tf_info_t *xinfo, xmlNodePtr croot, tnode_t *troot) { xmlNodePtr curr, def_set = NULL; tf_rdata_t *rr, *pr, *rdp; xmlChar *set; char *key; int joined_set = 0; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_walk\n"); rr = pr = NULL; /* * First iterate through all the XML nodes at this level to look for * set nodes. */ for (curr = croot->xmlChildrenNode; curr != NULL; curr = curr->next) { if (curr->name == NULL) { topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_walk: Ignoring nameless xmlnode\n"); continue; } if (xmlStrcmp(curr->name, (xmlChar *)Set) == 0) { if (joined_set) continue; set = xmlGetProp(curr, (xmlChar *)Setlist); key = mp->tm_hdl->th_product; /* * If it's the default set then we'll store * a pointer to it so that if none of the other * sets apply to our product we can fall * back to this one. */ if (strcmp((char *)set, "default") == 0) def_set = curr; else if (set_contains(mp, key, (char *)set)) { joined_set = 1; if ((rdp = topo_xml_walk(mp, xinfo, curr, troot)) == NULL) { topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_walk: failed1\n"); } else { if (pr == NULL) { rr = pr = rdp; } else { pr->rd_next = rdp; pr = rdp; } rr->rd_cnt++; } } xmlFree(set); } } /* * If we haven't found a set that contains our product AND a default set * exists, then we'll process it. */ if (!joined_set && def_set) { if ((rdp = topo_xml_walk(mp, xinfo, def_set, troot)) == NULL) { topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_walk: failed2\n"); } if (pr == NULL) { rr = pr = rdp; } else { pr->rd_next = rdp; pr = rdp; } rr->rd_cnt++; } /* * Now we're interested in children xmlNodes of croot tagged * as 'ranges'. These define what topology nodes may exist, and need * to be verified. */ for (curr = croot->xmlChildrenNode; curr != NULL; curr = curr->next) { if (curr->name == NULL) { topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_walk: Ignoring nameless xmlnode\n"); continue; } if (xmlStrcmp(curr->name, (xmlChar *)Range) != 0) continue; if ((rdp = tf_rdata_new(mp, xinfo, curr, troot)) == NULL) { /* * Range processing error, continue walk */ continue; } if (pr == NULL) { rr = pr = rdp; } else { pr->rd_next = rdp; pr = rdp; } rr->rd_cnt++; } return (rr); } /* * Convert parsed xml topology description into topology nodes */ int topo_xml_enum(topo_mod_t *tmp, tf_info_t *xinfo, tnode_t *troot) { xmlNodePtr xroot; topo_dprintf(tmp->tm_hdl, TOPO_DBG_XML, "topo_xml_enum\n"); if ((xroot = xmlDocGetRootElement(xinfo->tf_xdoc)) == NULL) { topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, "Couldn't get root xmlNode.\n"); return (-1); } if ((xinfo->tf_rd = topo_xml_walk(tmp, xinfo, xroot, troot)) == NULL) { topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, "error within .xml topology: %s\n", topo_strerror(topo_mod_errno(tmp))); return (-1); } return (0); } /* * Load an XML tree from filename and read it into a DOM parse tree. */ static tf_info_t * txml_file_parse(topo_mod_t *tmp, int fd, const char *filenm, const char *escheme) { xmlValidCtxtPtr vcp; xmlNodePtr cursor; xmlDocPtr document; xmlDtdPtr dtd = NULL; xmlChar *scheme = NULL; char *dtdpath = NULL; int readflags = 0; tf_info_t *r; int e, validate = 0; topo_dprintf(tmp->tm_hdl, TOPO_DBG_XML, "txml_file_parse(filenm=%s, escheme=%s)\n", filenm, escheme); /* * Since topologies can XInclude other topologies, and libxml2 * doesn't do DTD-based validation with XInclude, by default * we don't validate topology files. One can force * validation, though, by creating a TOPOXML_VALIDATE * environment variable and creating a TOPO_DTD environment * variable with the path to the DTD against which to validate. */ if (getenv("TOPOXML_VALIDATE") != NULL) { dtdpath = getenv("TOPO_DTD"); validate = 1; } /* * Splat warnings and errors related to parsing the topology * file if the TOPOXML_PERROR environment variable exists. */ if (getenv("TOPOXML_PERROR") == NULL) readflags = XML_PARSE_NOERROR | XML_PARSE_NOWARNING; if ((document = xmlReadFd(fd, filenm, NULL, readflags)) == NULL) { topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, "txml_file_parse: couldn't parse document.\n"); return (NULL); } /* * Verify that this is a document type we understand. */ if ((dtd = xmlGetIntSubset(document)) == NULL) { topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, "document has no DTD.\n"); xmlFreeDoc(document); return (NULL); } if (strcmp((const char *)dtd->SystemID, TOPO_DTD_PATH) != 0) { topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, "document DTD unknown; bad topology file\n"); xmlFreeDoc(document); return (NULL); } if ((cursor = xmlDocGetRootElement(document)) == NULL) { topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, "document is empty.\n"); xmlFreeDoc(document); return (NULL); } /* * Make sure we're looking at a topology description in the * expected scheme. */ if (xmlStrcmp(cursor->name, (xmlChar *)Topology) != 0) { topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, "document is not a topology description.\n"); xmlFreeDoc(document); return (NULL); } if ((scheme = xmlGetProp(cursor, (xmlChar *)Scheme)) == NULL) { topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, "topology lacks a scheme.\n"); (void) topo_mod_seterrno(tmp, ETOPO_PRSR_NOATTR); xmlFreeDoc(document); return (NULL); } if (xmlStrcmp(scheme, (xmlChar *)escheme) != 0) { topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, "topology in unrecognized scheme, %s, expecting %s\n", scheme, escheme); (void) topo_mod_seterrno(tmp, ETOPO_PRSR_BADSCH); xmlFree(scheme); xmlFreeDoc(document); return (NULL); } if (dtdpath != NULL) { dtd = xmlParseDTD(NULL, (xmlChar *)dtdpath); if (dtd == NULL) { topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, "Could not parse DTD \"%s\".\n", dtdpath); xmlFree(scheme); xmlFreeDoc(document); return (NULL); } if (document->extSubset != NULL) xmlFreeDtd(document->extSubset); document->extSubset = dtd; } if (xmlXIncludeProcessFlags(document, XML_PARSE_XINCLUDE) == -1) { xmlFree(scheme); xmlFreeDoc(document); topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, "couldn't handle XInclude statements in document\n"); return (NULL); } if (validate) { if ((vcp = xmlNewValidCtxt()) == NULL) { xmlFree(scheme); xmlFreeDoc(document); return (NULL); } vcp->warning = xmlParserValidityWarning; vcp->error = xmlParserValidityError; e = xmlValidateDocument(vcp, document); xmlFreeValidCtxt(vcp); if (e == 0) topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, "Document is not valid.\n"); } if ((r = tf_info_new(tmp, document, scheme)) == NULL) { xmlFree(scheme); xmlFreeDoc(document); return (NULL); } xmlFree(scheme); scheme = NULL; return (r); } tf_info_t * topo_xml_read(topo_mod_t *tmp, const char *path, const char *escheme) { int fd; tf_info_t *tip; if ((fd = open(path, O_RDONLY)) < 0) { topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, "failed to open %s for reading\n", path); return (NULL); } tip = txml_file_parse(tmp, fd, path, escheme); (void) close(fd); return (tip); }