/* * 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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #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 int decorate_nodes(topo_mod_t *mp, tf_info_t *xinfo, xmlNodePtr pxn, tnode_t *ptn, char *name, tf_pad_t **rpad); 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; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "attribute to int\n"); if ((str = xmlGetProp(n, (xmlChar *)propname)) == NULL) return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR)); *value = strtoull((char *)str, (char **)&estr, 10); if (estr == str) { /* no conversion was done */ 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, "attribute to int\n"); 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 { xmlFree(str); topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "Unrecognized type attribute value.\n"); (void) topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE); 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; uint64_t ui; nvlist_t *fmri; xmlChar *str; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "xlate_common\n"); 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_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; default: topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "Unrecognized type attribute.\n"); return (topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE)); } 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; uint32_t ui32; uint64_t ui64; int32_t i32; int64_t i64; char *str; int err, e; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "prop_create(gnm = %s, " "pnm = %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_FMRI: e = nvlist_lookup_nvlist(pfmri, INV_PVAL, &fmri); break; case TOPO_TYPE_STRING: e = nvlist_lookup_string(pfmri, INV_PVAL, &str); break; default: e = ETOPO_PRSR_BADTYPE; } 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_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; } 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(gnm = %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\n"); 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 on node %s=%d (%s)\n", meth->meth_name, meth->prop_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; topo_type_t prop_type; struct propmeth_data meth; int ret = 0; topo_type_t ptype; tnode_t *tmp; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pmeth_record\n"); /* * 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; } 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; } } } else { if (register_method(mp, tn, &meth) != 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); if (arg_nvl) 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++) if (apl[ai] != NULL) 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 "propset" XML node * key: string to search the propset for * * returns: 1, if the propset contains key * 0, otherwise */ static int propset_contains(topo_mod_t *mp, char *key, char *set) { char *prod; int rv = 0; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "propset_contains(key = %s, " "set = %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_info_t *xinfo, xmlNodePtr pxn, tnode_t *ptn, tf_pad_t **rpad, const char *rname) { xmlNodePtr cn, gcn, psn; xmlNodePtr def_propset = NULL; tf_pad_t *new = *rpad; int pgcnt = 0; int dcnt = 0; int joined_propset = 0; xmlChar *set; char *key; topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pad_process beneath %s\n", topo_node_name(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 * three types of elements: * 1) dependents elements * 2) unconstrained pgroup elements * 3) pgroup elements constrained by propset elements */ if (xmlStrcmp(cn->name, (xmlChar *)Dependents) == 0) dcnt++; else if (xmlStrcmp(cn->name, (xmlChar *)Propgrp) == 0) pgcnt++; else if (xmlStrcmp(cn->name, (xmlChar *)Propset) == 0) { set = xmlGetProp(cn, (xmlChar *)Set); if (mp->tm_hdl->th_product) key = mp->tm_hdl->th_product; else key = mp->tm_hdl->th_platform; /* * If it's the default propset then we'll store * a pointer to it so that if none of the other * propsets apply to our product we can fall * back to this one. */ if (strcmp((char *)set, "default") == 0) def_propset = cn; else if (propset_contains(mp, key, (char *)set)) { psn = cn; joined_propset = 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 propset that contains our product AND * a default propset exists, then we'll process it. */ if (!joined_propset && def_propset) { topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "Falling back to default propset\n"); joined_propset = 1; psn = def_propset; 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, joined_propset=%d\n", dcnt, pgcnt, joined_propset); /* * 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 (dcnt == 0 && pgcnt == 0) { *rpad = new; return (0); } 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 (joined_propset) { /* * If the property groups are contained within a * propset then they will be one level lower in * the XML tree. */ if (pgroups_record(mp, psn, ptn, rname, new, (const char *)pxn->name) < 0) { tf_pad_free(mp, new); return (-1); } } else { if (pgroups_record(mp, pxn, ptn, rname, new, (const char *)pxn->name) < 0) { tf_pad_free(mp, new); return (-1); } } } *rpad = new; } if (new->tpad_dcnt > 0) if (dependents_create(mp, xinfo, new, pxn, ptn) < 0) return (-1); if (new->tpad_pgcnt > 0) if (pgroups_create(mp, new, ptn) < 0) return (-1); 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->rd_finfo, nn, ntn, &newi->ti_pad, rd->rd_name) < 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); 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); } int decorate_nodes(topo_mod_t *mp, tf_info_t *xinfo, xmlNodePtr pxn, tnode_t *ptn, char *name, 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), name) != 0) { ctn = topo_child_next(ptn, ctn); continue; } if (pad_process(mp, xinfo, pxn, ctn, rpad, name) < 0) return (-1); if (decorate_nodes(mp, xinfo, pxn, ctn, name, 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) != ETOPO_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->rd_finfo, rn, rd->rd_pn, rd->rd_name, &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->rd_finfo, rn, ct, &rd->rd_pad, rd->rd_name) < 0) return (-1); ct = topo_child_next(rd->rd_pn, ct); ccnt++; } if (ccnt == 0) { topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "no nodes " "processed for range %s\n", rd->rd_name); topo_node_range_destroy(rd->rd_pn, rd->rd_name); return (-1); } } 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; tf_rdata_t *rr, *pr, *rdp; /* * What we're interested in are children xmlNodes of croot tagged * as 'ranges'. These define what topology nodes may exist, and need * to be verified. */ topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_walk\n"); rr = pr = NULL; 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) { topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_walk: Ignoring non-range %s.\n", curr->name); 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"); if (dtdpath != NULL) xmlLoadExtDtdDefaultValue = 0; 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); }