xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/topo_xml.c (revision 672986541be54a7a471bb088e60780c37e371d7e)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <libxml/parser.h>
30 #include <libxml/xinclude.h>
31 #include <sys/fm/protocol.h>
32 #include <assert.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <ctype.h>
36 #include <errno.h>
37 #include <limits.h>
38 #include <fm/libtopo.h>
39 #include <unistd.h>
40 #include <sys/stat.h>
41 #include <fcntl.h>
42 #include <topo_mod.h>
43 #include <topo_subr.h>
44 #include <topo_alloc.h>
45 #include <topo_parse.h>
46 #include <topo_error.h>
47 
48 static tf_rdata_t *topo_xml_walk(topo_mod_t *,
49     tf_info_t *, xmlNodePtr, tnode_t *);
50 
51 int
52 xmlattr_to_stab(topo_mod_t *mp, xmlNodePtr n, const char *stabname,
53     topo_stability_t *rs)
54 {
55 	xmlChar *str;
56 	int rv = 0;
57 
58 	if (n == NULL) {
59 		/* If there is no Stability defined, we default to private */
60 		*rs = TOPO_STABILITY_PRIVATE;
61 		return (0);
62 	}
63 	if ((str = xmlGetProp(n, (xmlChar *)stabname)) == NULL) {
64 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
65 		    "attribute to stability:\n");
66 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
67 	}
68 
69 	if (xmlStrcmp(str, (xmlChar *)Internal) == 0) {
70 		*rs = TOPO_STABILITY_INTERNAL;
71 	} else if (xmlStrcmp(str, (xmlChar *)Private) == 0) {
72 		*rs = TOPO_STABILITY_PRIVATE;
73 	} else if (xmlStrcmp(str, (xmlChar *)Obsolete) == 0) {
74 		*rs = TOPO_STABILITY_OBSOLETE;
75 	} else if (xmlStrcmp(str, (xmlChar *)External) == 0) {
76 		*rs = TOPO_STABILITY_EXTERNAL;
77 	} else if (xmlStrcmp(str, (xmlChar *)Unstable) == 0) {
78 		*rs = TOPO_STABILITY_UNSTABLE;
79 	} else if (xmlStrcmp(str, (xmlChar *)Evolving) == 0) {
80 		*rs = TOPO_STABILITY_EVOLVING;
81 	} else if (xmlStrcmp(str, (xmlChar *)Stable) == 0) {
82 		*rs = TOPO_STABILITY_STABLE;
83 	} else if (xmlStrcmp(str, (xmlChar *)Standard) == 0) {
84 		*rs = TOPO_STABILITY_STANDARD;
85 	} else {
86 		xmlFree(str);
87 		return (topo_mod_seterrno(mp, ETOPO_PRSR_BADSTAB));
88 	}
89 	xmlFree(str);
90 	return (rv);
91 }
92 
93 int
94 xmlattr_to_int(topo_mod_t *mp,
95     xmlNodePtr n, const char *propname, uint64_t *value)
96 {
97 	xmlChar *str;
98 	xmlChar *estr;
99 
100 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "attribute to int\n");
101 	if ((str = xmlGetProp(n, (xmlChar *)propname)) == NULL)
102 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
103 	*value = strtoull((char *)str, (char **)&estr, 10);
104 	if (estr == str) {
105 		/* no conversion was done */
106 		xmlFree(str);
107 		return (topo_mod_seterrno(mp, ETOPO_PRSR_BADNUM));
108 	}
109 	xmlFree(str);
110 	return (0);
111 }
112 
113 static int
114 xmlattr_to_fmri(topo_mod_t *mp,
115     xmlNodePtr xn, const char *propname, nvlist_t **rnvl)
116 {
117 	xmlChar *str;
118 
119 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "attribute to int\n");
120 	if ((str = xmlGetProp(xn, (xmlChar *)propname)) == NULL)
121 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
122 	if (topo_mod_str2nvl(mp, (const char *)str, rnvl) < 0)
123 		return (-1);
124 	xmlFree(str);
125 	return (0);
126 }
127 
128 static topo_type_t
129 xmlattr_to_type(topo_mod_t *mp, xmlNodePtr xn)
130 {
131 	topo_type_t rv;
132 	xmlChar *str;
133 	if ((str = xmlGetProp(xn, (xmlChar *)Type)) == NULL) {
134 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "Property missing type");
135 		(void) topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
136 		return (TOPO_TYPE_INVALID);
137 	}
138 	if (xmlStrcmp(str, (xmlChar *)Int32) == 0) {
139 		rv = TOPO_TYPE_INT32;
140 	} else if (xmlStrcmp(str, (xmlChar *)UInt32) == 0) {
141 		rv = TOPO_TYPE_UINT32;
142 	} else if (xmlStrcmp(str, (xmlChar *)Int64) == 0) {
143 		rv = TOPO_TYPE_INT64;
144 	} else if (xmlStrcmp(str, (xmlChar *)UInt64) == 0) {
145 		rv = TOPO_TYPE_UINT64;
146 	} else if (xmlStrcmp(str, (xmlChar *)FMRI) == 0) {
147 		rv = TOPO_TYPE_FMRI;
148 	} else if (xmlStrcmp(str, (xmlChar *)String) == 0) {
149 		rv = TOPO_TYPE_STRING;
150 	} else {
151 		xmlFree(str);
152 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
153 		    "Unrecognized type attribute.\n");
154 		(void) topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE);
155 		return (TOPO_TYPE_INVALID);
156 	}
157 	xmlFree(str);
158 	return (rv);
159 }
160 
161 static int
162 xmlprop_xlate(topo_mod_t *mp, xmlNodePtr xn, nvlist_t *nvl)
163 {
164 	topo_type_t ptype;
165 	xmlChar *str;
166 	nvlist_t *fmri;
167 	uint64_t ui;
168 	int e;
169 
170 	if ((str = xmlGetProp(xn, (xmlChar *)Immutable)) != NULL) {
171 		if (xmlStrcmp(str, (xmlChar *)False) == 0)
172 			(void) nvlist_add_boolean_value(nvl, INV_IMMUTE,
173 			    B_FALSE);
174 		else
175 			(void) nvlist_add_boolean_value(nvl, INV_IMMUTE,
176 			    B_TRUE);
177 		xmlFree(str);
178 	} else {
179 		(void) nvlist_add_boolean_value(nvl, INV_IMMUTE, B_TRUE);
180 	}
181 
182 	if ((ptype = xmlattr_to_type(mp, xn)) == TOPO_TYPE_INVALID)
183 		return (-1);
184 	e = nvlist_add_int32(nvl, INV_PVALTYPE, ptype);
185 	if (e != 0)
186 		return (-1);
187 	switch (ptype) {
188 	case TOPO_TYPE_INT32:
189 		if (xmlattr_to_int(mp, xn, Value, &ui) < 0)
190 			return (-1);
191 		e = nvlist_add_int32(nvl, INV_PVAL, (int32_t)ui);
192 		break;
193 	case TOPO_TYPE_UINT32:
194 		if (xmlattr_to_int(mp, xn, Value, &ui) < 0)
195 			return (-1);
196 		e = nvlist_add_uint32(nvl, INV_PVAL, (uint32_t)ui);
197 		break;
198 	case TOPO_TYPE_INT64:
199 		if (xmlattr_to_int(mp, xn, Value, &ui) < 0)
200 			return (-1);
201 		e = nvlist_add_int64(nvl, INV_PVAL, (int64_t)ui);
202 		break;
203 	case TOPO_TYPE_UINT64:
204 		if (xmlattr_to_int(mp, xn, Value, &ui) < 0)
205 			return (-1);
206 		e = nvlist_add_uint64(nvl, INV_PVAL, ui);
207 		break;
208 	case TOPO_TYPE_FMRI:
209 		if (xmlattr_to_fmri(mp, xn, Value, &fmri) < 0)
210 			return (-1);
211 		e = nvlist_add_nvlist(nvl, INV_PVAL, fmri);
212 		nvlist_free(fmri);
213 		break;
214 	case TOPO_TYPE_STRING:
215 		if ((str = xmlGetProp(xn, (xmlChar *)Value)) == NULL)
216 			return (-1);
217 		e = nvlist_add_string(nvl, INV_PVAL, (char *)str);
218 		xmlFree(str);
219 		break;
220 	default:
221 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
222 		    "Unrecognized type attribute.\n");
223 		return (topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE));
224 	}
225 	if (e != 0) {
226 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
227 		    "Nvlist construction failed.\n");
228 		return (topo_mod_seterrno(mp, ETOPO_NOMEM));
229 	}
230 	return (0);
231 }
232 
233 static int
234 dependent_create(topo_mod_t *mp,
235     tf_info_t *xinfo, tf_pad_t *pad, xmlNodePtr dxn, tnode_t *ptn)
236 {
237 	tf_rdata_t *rp, *pp, *np;
238 	xmlChar *grptype;
239 	int sibs = 0;
240 
241 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "dependent create\n");
242 	if ((grptype = xmlGetProp(dxn, (xmlChar *)Grouping)) == NULL) {
243 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
244 		    "Dependents missing grouping attribute");
245 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
246 	}
247 
248 	pp = NULL;
249 	if (xmlStrcmp(grptype, (xmlChar *)Siblings) == 0) {
250 		rp = pad->tpad_sibs;
251 		sibs++;
252 	} else if (xmlStrcmp(grptype, (xmlChar *)Children) == 0) {
253 		rp = pad->tpad_child;
254 	} else {
255 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
256 		    "Dependents have bogus grouping attribute");
257 		xmlFree(grptype);
258 		return (topo_mod_seterrno(mp, ETOPO_PRSR_BADGRP));
259 	}
260 	xmlFree(grptype);
261 	/* Add processed dependents to the tail of the list */
262 	while (rp != NULL) {
263 		pp = rp;
264 		rp = rp->rd_next;
265 	}
266 	if ((np = topo_xml_walk(mp, xinfo, dxn, ptn)) == NULL) {
267 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
268 		    "error within dependent .xml topology: "
269 		    "%s\n", topo_strerror(topo_mod_errno(mp)));
270 		return (-1);
271 	}
272 	if (pp != NULL)
273 		pp->rd_next = np;
274 	else if (sibs == 1)
275 		pad->tpad_sibs = np;
276 	else
277 		pad->tpad_child = np;
278 	return (0);
279 }
280 
281 static int
282 dependents_create(topo_mod_t *mp,
283     tf_info_t *xinfo, tf_pad_t *pad, xmlNodePtr pxn, tnode_t *ptn)
284 {
285 	xmlNodePtr cn;
286 
287 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "dependents create\n");
288 	for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
289 		if (xmlStrcmp(cn->name, (xmlChar *)Dependents) == 0) {
290 			if (dependent_create(mp, xinfo, pad, cn, ptn) < 0)
291 				return (-1);
292 		}
293 	}
294 	return (0);
295 }
296 
297 static int
298 prop_create(topo_mod_t *mp,
299     nvlist_t *pfmri, tnode_t *ptn, const char *gnm, const char *pnm,
300     topo_type_t ptype, int flag)
301 {
302 	nvlist_t *fmri;
303 	uint32_t ui32;
304 	uint64_t ui64;
305 	int32_t i32;
306 	int64_t i64;
307 	char *str;
308 	int err, e;
309 
310 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "prop create\n");
311 	switch (ptype) {
312 	case TOPO_TYPE_INT32:
313 		e = nvlist_lookup_int32(pfmri, INV_PVAL, &i32);
314 		break;
315 	case TOPO_TYPE_UINT32:
316 		e = nvlist_lookup_uint32(pfmri, INV_PVAL, &ui32);
317 		break;
318 	case TOPO_TYPE_INT64:
319 		e = nvlist_lookup_int64(pfmri, INV_PVAL, &i64);
320 		break;
321 	case TOPO_TYPE_UINT64:
322 		e = nvlist_lookup_uint64(pfmri, INV_PVAL, &ui64);
323 		break;
324 	case TOPO_TYPE_FMRI:
325 		e = nvlist_lookup_nvlist(pfmri, INV_PVAL, &fmri);
326 		break;
327 	case TOPO_TYPE_STRING:
328 		e = nvlist_lookup_string(pfmri, INV_PVAL, &str);
329 		break;
330 	default:
331 		e = ETOPO_PRSR_BADTYPE;
332 	}
333 	if (e != 0) {
334 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
335 		    "prop value lookup failed.\n");
336 		return (topo_mod_seterrno(mp, e));
337 	}
338 	switch (ptype) {
339 	case TOPO_TYPE_INT32:
340 		e = topo_prop_set_int32(ptn, gnm, pnm, flag, i32, &err);
341 		break;
342 	case TOPO_TYPE_UINT32:
343 		e = topo_prop_set_uint32(ptn, gnm, pnm, flag, ui32, &err);
344 		break;
345 	case TOPO_TYPE_INT64:
346 		e = topo_prop_set_int64(ptn, gnm, pnm, flag, i64, &err);
347 		break;
348 	case TOPO_TYPE_UINT64:
349 		e = topo_prop_set_uint64(ptn, gnm, pnm, flag, ui64, &err);
350 		break;
351 	case TOPO_TYPE_FMRI:
352 		e = topo_prop_set_fmri(ptn, gnm, pnm, flag, fmri, &err);
353 		break;
354 	case TOPO_TYPE_STRING:
355 		e = topo_prop_set_string(ptn, gnm, pnm, flag, str, &err);
356 		break;
357 	}
358 	if (e != 0 && err != ETOPO_PROP_DEFD) {
359 
360 		/*
361 		 * Some properties may have already been set
362 		 * in topo_node_bind() or topo_prop_inherit if we are
363 		 * enumerating from a static .xml file
364 		 */
365 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "prop set "
366 		    "failed %s/%s:%s\n", gnm, pnm, topo_strerror(err));
367 		return (topo_mod_seterrno(mp, err));
368 	}
369 	return (0);
370 }
371 
372 static int
373 props_create(topo_mod_t *mp,
374     tnode_t *ptn, const char *gnm, nvlist_t **props, int nprops)
375 {
376 	topo_type_t ptype;
377 	boolean_t pim;
378 	char *pnm;
379 	int32_t i32;
380 	int flag;
381 	int pn;
382 	int e;
383 
384 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "props create\n");
385 	for (pn = 0; pn < nprops; pn++) {
386 		e = nvlist_lookup_string(props[pn], INV_PNAME, &pnm);
387 		if (e != 0) {
388 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
389 			    "props create lookup (%s) failure: %s",
390 			    INV_PNAME, topo_strerror(e));
391 			return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
392 		}
393 		e = nvlist_lookup_boolean_value(props[pn], INV_IMMUTE, &pim);
394 		if (e != 0) {
395 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
396 			    "props create lookup (%s) failure: %s",
397 			    INV_IMMUTE, topo_strerror(e));
398 			return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
399 		}
400 		flag = (pim == B_TRUE) ?
401 		    TOPO_PROP_IMMUTABLE : TOPO_PROP_MUTABLE;
402 
403 		e = nvlist_lookup_int32(props[pn], INV_PVALTYPE, &i32);
404 		if (e != 0) {
405 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
406 			    "props create lookup (%s) failure: %s",
407 			    INV_PVALTYPE, topo_strerror(e));
408 			return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
409 		}
410 		ptype = (topo_type_t)i32;
411 		if (prop_create(mp, props[pn], ptn, gnm, pnm, ptype, flag) < 0)
412 			return (-1);
413 	}
414 	return (0);
415 }
416 
417 static int
418 pgroups_create(topo_mod_t *mp, tf_pad_t *pad, tnode_t *ptn)
419 {
420 	topo_pgroup_info_t pgi;
421 	nvlist_t **props;
422 	char *gnm;
423 	char *nmstab, *dstab;
424 	uint32_t rnprops, nprops;
425 	uint32_t gv;
426 	int pg;
427 	int e;
428 
429 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroups create\n");
430 	for (pg = 0; pg < pad->tpad_pgcnt; pg++) {
431 		e = nvlist_lookup_string(pad->tpad_pgs[pg],
432 		    INV_PGRP_NAME, &gnm);
433 		if (e != 0) {
434 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
435 			    "pad lookup (%s) failed.\n",
436 			    INV_PGRP_NAME);
437 			return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
438 		}
439 		e = nvlist_lookup_string(pad->tpad_pgs[pg],
440 		    INV_PGRP_NMSTAB, &nmstab);
441 		if (e != 0) {
442 			if (e != ENOENT) {
443 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
444 				    "pad lookup (%s) "
445 				    "failed.\n", INV_PGRP_NMSTAB);
446 				return (topo_mod_seterrno(mp,
447 				    ETOPO_PRSR_NVPROP));
448 			} else {
449 				nmstab = TOPO_STABSTR_PRIVATE;
450 			}
451 		}
452 		e = nvlist_lookup_string(pad->tpad_pgs[pg],
453 		    INV_PGRP_DSTAB, &dstab);
454 		if (e != 0) {
455 			if (e != ENOENT) {
456 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
457 				    "pad lookup (%s) failed.\n",
458 				    INV_PGRP_DSTAB);
459 				return (topo_mod_seterrno(mp,
460 				    ETOPO_PRSR_NVPROP));
461 			} else {
462 				dstab = TOPO_STABSTR_PRIVATE;
463 			}
464 		}
465 		e = nvlist_lookup_uint32(pad->tpad_pgs[pg],
466 		    INV_PGRP_VER, &gv);
467 		if (e != 0) {
468 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
469 			    "pad lookup (%s) failed.\n",
470 			    INV_PGRP_VER);
471 			return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
472 		}
473 		pgi.tpi_name = gnm;
474 		pgi.tpi_namestab = topo_name2stability(nmstab);
475 		pgi.tpi_datastab = topo_name2stability(dstab);
476 		pgi.tpi_version = gv;
477 		if (topo_pgroup_create(ptn, &pgi, &e) != 0) {
478 			if (e != ETOPO_PROP_DEFD) {
479 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
480 				    "pgroups create failure: %s\n",
481 				    topo_strerror(e));
482 				return (-1);
483 			}
484 		}
485 		e = nvlist_lookup_uint32(pad->tpad_pgs[pg],
486 		    INV_PGRP_NPROP, &rnprops);
487 		e |= nvlist_lookup_nvlist_array(pad->tpad_pgs[pg],
488 		    INV_PGRP_ALLPROPS, &props, &nprops);
489 		if (rnprops != nprops) {
490 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
491 			    "recorded number of props %d does not "
492 			    "match number of props recorded %d.\n",
493 			    rnprops, nprops);
494 		}
495 		if (props_create(mp, ptn, gnm, props, nprops) < 0)
496 			return (-1);
497 	}
498 	return (0);
499 }
500 
501 static nvlist_t *
502 pval_record(topo_mod_t *mp, xmlNodePtr xn)
503 {
504 	nvlist_t *pnvl = NULL;
505 	xmlChar *pname;
506 
507 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pval record\n");
508 	if ((pname = xmlGetProp(xn, (xmlChar *)Name)) == NULL) {
509 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
510 		    "propval lacks a name\n");
511 		(void) topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
512 		return (NULL);
513 	}
514 	if (topo_mod_nvalloc(mp, &pnvl, NV_UNIQUE_NAME) < 0) {
515 		xmlFree(pname);
516 		return (NULL);
517 	}
518 	if (nvlist_add_string(pnvl, INV_PNAME, (char *)pname) < 0) {
519 		xmlFree(pname);
520 		nvlist_free(pnvl);
521 		return (NULL);
522 	}
523 	xmlFree(pname);
524 	/* FMXXX stability of the property name */
525 
526 	if (xmlprop_xlate(mp, xn, pnvl) < 0) {
527 		nvlist_free(pnvl);
528 		return (NULL);
529 	}
530 	return (pnvl);
531 }
532 
533 static int
534 pgroup_record(topo_mod_t *mp, xmlNodePtr pxn, tf_pad_t *rpad, int pi)
535 {
536 	topo_stability_t nmstab, dstab;
537 	uint64_t ver;
538 	xmlNodePtr cn;
539 	xmlChar *name;
540 	nvlist_t **apl = NULL;
541 	nvlist_t *pgnvl = NULL;
542 	int pcnt = 0;
543 	int ai = 0;
544 	int e;
545 
546 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroup record\n");
547 	if ((name = xmlGetProp(pxn, (xmlChar *)Name)) == NULL) {
548 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
549 		    "propgroup lacks a name\n");
550 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
551 	}
552 	if (xmlattr_to_int(mp, pxn, Version, &ver) < 0) {
553 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
554 		    "propgroup lacks a version\n");
555 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
556 	}
557 	if (xmlattr_to_stab(mp, pxn, Namestab, &nmstab) < 0) {
558 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
559 		    "propgroup lacks name-stability\n");
560 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
561 	}
562 	if (xmlattr_to_stab(mp, pxn, Datastab, &dstab) < 0) {
563 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
564 		    "propgroup lacks data-stability\n");
565 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
566 	}
567 
568 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroup %s\n", (char *)name);
569 	for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
570 		if (xmlStrcmp(cn->name, (xmlChar *)Propval) == 0)
571 			pcnt++;
572 	}
573 
574 	if (topo_mod_nvalloc(mp, &pgnvl, NV_UNIQUE_NAME) < 0) {
575 		xmlFree(name);
576 		return (-1);
577 	}
578 
579 	e = nvlist_add_string(pgnvl, INV_PGRP_NAME, (char *)name);
580 	e |= nvlist_add_uint32(pgnvl, INV_PGRP_NMSTAB, nmstab);
581 	e |= nvlist_add_uint32(pgnvl, INV_PGRP_DSTAB, dstab);
582 	e |= nvlist_add_uint32(pgnvl, INV_PGRP_VER, ver);
583 	e |= nvlist_add_uint32(pgnvl, INV_PGRP_NPROP, pcnt);
584 	if (e != 0 ||
585 	    (apl = topo_mod_zalloc(mp, pcnt * sizeof (nvlist_t *))) == NULL) {
586 		xmlFree(name);
587 		nvlist_free(pgnvl);
588 		return (-1);
589 	}
590 	for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
591 		if (xmlStrcmp(cn->name, (xmlChar *)Propval) == 0) {
592 			if (ai < pcnt) {
593 				if ((apl[ai] = pval_record(mp, cn)) == NULL)
594 					break;
595 			}
596 			ai++;
597 		}
598 	}
599 	xmlFree(name);
600 	e |= (ai != pcnt);
601 	e |= nvlist_add_nvlist_array(pgnvl, INV_PGRP_ALLPROPS, apl, pcnt);
602 	for (ai = 0; ai < pcnt; ai++)
603 		if (apl[ai] != NULL)
604 			nvlist_free(apl[ai]);
605 	topo_mod_free(mp, apl, pcnt * sizeof (nvlist_t *));
606 	if (e != 0) {
607 		nvlist_free(pgnvl);
608 		return (-1);
609 	}
610 	rpad->tpad_pgs[pi] = pgnvl;
611 	return (0);
612 }
613 
614 static int
615 pgroups_record(topo_mod_t *mp, xmlNodePtr pxn, tf_pad_t *rpad)
616 {
617 	xmlNodePtr cn;
618 	int pi = 0;
619 
620 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroups record\n");
621 	for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
622 		if (xmlStrcmp(cn->name, (xmlChar *)Propgrp) == 0) {
623 			if (pgroup_record(mp, cn, rpad, pi++) < 0)
624 				return (-1);
625 		}
626 	}
627 	return (0);
628 }
629 
630 /*
631  * Process the property group and dependents xmlNode children of
632  * parent xmlNode pxn.
633  */
634 static int
635 pad_process(topo_mod_t *mp,
636     tf_info_t *xinfo, xmlNodePtr pxn, tnode_t *ptn, tf_pad_t **rpad)
637 {
638 	xmlNodePtr cn;
639 	tf_pad_t *new = *rpad;
640 	int pgcnt = 0;
641 	int dcnt = 0;
642 
643 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
644 	    "pad process beneath %s\n", topo_node_name(ptn));
645 	if (new == NULL) {
646 		for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
647 			if (xmlStrcmp(cn->name, (xmlChar *)Dependents) == 0)
648 				dcnt++;
649 			else if (xmlStrcmp(cn->name, (xmlChar *)Propgrp) == 0)
650 				pgcnt++;
651 		}
652 		if ((new = tf_pad_new(mp, pgcnt, dcnt)) == NULL)
653 			return (-1);
654 		if (dcnt == 0 && pgcnt == 0) {
655 			*rpad = new;
656 			return (0);
657 		}
658 
659 		if (pgcnt > 0) {
660 			new->tpad_pgs =
661 			    topo_mod_zalloc(mp, pgcnt * sizeof (nvlist_t *));
662 			if (new->tpad_pgs == NULL) {
663 				tf_pad_free(mp, new);
664 				return (NULL);
665 			}
666 			if (pgroups_record(mp, pxn, new) < 0) {
667 				tf_pad_free(mp, new);
668 				return (NULL);
669 			}
670 		}
671 		*rpad = new;
672 	}
673 
674 	if (new->tpad_dcnt > 0)
675 		if (dependents_create(mp, xinfo, new, pxn, ptn) < 0)
676 			return (-1);
677 
678 	if (new->tpad_pgcnt > 0)
679 		if (pgroups_create(mp, new, ptn) < 0)
680 			return (-1);
681 	return (0);
682 }
683 
684 static int
685 node_process(topo_mod_t *mp, xmlNodePtr nn, tf_rdata_t *rd)
686 {
687 	xmlChar *str;
688 	topo_instance_t inst;
689 	tf_idata_t *newi;
690 	tnode_t *ntn;
691 	uint64_t ui;
692 	int rv = -1;
693 	int s = 0;
694 
695 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
696 	    "node process %s\n", rd->rd_name);
697 
698 	if (xmlattr_to_int(mp, nn, Instance, &ui) < 0)
699 		goto nodedone;
700 	inst = (topo_instance_t)ui;
701 
702 	if ((str = xmlGetProp(nn, (xmlChar *)Static)) != NULL) {
703 		if (xmlStrcmp(str, (xmlChar *)True) == 0)
704 			s = 1;
705 		xmlFree(str);
706 	}
707 
708 	if (s == 0) {
709 		if (topo_mod_enumerate(rd->rd_mod, rd->rd_pn,
710 		    rd->rd_finfo->tf_scheme, rd->rd_name, inst, inst,
711 		    s == 1 ? &s : NULL) < 0)
712 			goto nodedone;
713 	}
714 	ntn = topo_node_lookup(rd->rd_pn, rd->rd_name, inst);
715 	if (ntn == NULL) {
716 
717 		/*
718 		 * If this is a static node declaration, we can
719 		 * ignore the lookup failure and continue
720 		 * processing.  Otherwise, something
721 		 * went wrong during enumeration
722 		 */
723 		if (s == 1)
724 			rv = 0;
725 		goto nodedone;
726 	}
727 
728 	if ((newi = tf_idata_new(mp, inst, ntn)) == NULL) {
729 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
730 		    "tf_idata_new failed.\n");
731 		goto nodedone;
732 	}
733 	if (tf_idata_insert(&rd->rd_instances, newi) < 0) {
734 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
735 		    "tf_idata_insert failed.\n");
736 		goto nodedone;
737 	}
738 	if (pad_process(mp, rd->rd_finfo, nn, ntn, &newi->ti_pad) < 0)
739 		goto nodedone;
740 	rv = 0;
741 nodedone:
742 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "done with node %s.\n",
743 	    rd->rd_name);
744 	return (rv);
745 }
746 
747 static tf_edata_t *
748 enum_attributes_process(topo_mod_t *mp, xmlNodePtr en)
749 {
750 	tf_edata_t *einfo;
751 	uint64_t ui;
752 
753 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enum attributes process\n");
754 	if ((einfo = topo_mod_zalloc(mp, sizeof (tf_edata_t))) == NULL) {
755 		(void) topo_mod_seterrno(mp, ETOPO_NOMEM);
756 		return (NULL);
757 	}
758 	einfo->te_name = (char *)xmlGetProp(en, (xmlChar *)Name);
759 	if (einfo->te_name == NULL) {
760 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
761 		    "Enumerator name attribute missing.\n");
762 		(void) topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
763 		goto enodedone;
764 	}
765 
766 	/*
767 	 * Check for recursive enumeration
768 	 */
769 	if (strcmp(einfo->te_name, mp->tm_name) == 0) {
770 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
771 		    "Recursive enumeration detected for %s\n",
772 		    einfo->te_name);
773 		(void) topo_mod_seterrno(mp, ETOPO_ENUM_RECURS);
774 		goto enodedone;
775 	}
776 	if (xmlattr_to_int(mp, en, Version, &ui) < 0)
777 		goto enodedone;
778 	einfo->te_vers = (int)ui;
779 
780 	return (einfo);
781 
782 enodedone:
783 	if (einfo->te_name != NULL)
784 		xmlFree(einfo->te_name);
785 	return (NULL);
786 }
787 
788 static int
789 enum_run(topo_mod_t *mp, tf_rdata_t *rd)
790 {
791 	topo_hdl_t *thp = mp->tm_hdl;
792 	int e = -1;
793 
794 	/*
795 	 * Check if the enumerator module is already loaded.
796 	 * Module loading is single-threaded at this point so there's
797 	 * no need to worry about the module going away or bumping the
798 	 * ref count.
799 	 */
800 	if ((rd->rd_mod = topo_mod_lookup(thp, rd->rd_einfo->te_name,
801 	    0)) == NULL) {
802 		if ((rd->rd_mod = topo_mod_load(mp, rd->rd_einfo->te_name,
803 		    rd->rd_einfo->te_vers)) == NULL) {
804 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
805 			    "mod_load of %s failed: %s.\n",
806 			    rd->rd_einfo->te_name,
807 			    topo_strerror(topo_mod_errno(mp)));
808 			(void) topo_hdl_seterrno(thp, topo_mod_errno(mp));
809 			return (e);
810 		}
811 	}
812 	/*
813 	 * We're live, so let's enumerate.
814 	 */
815 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enumerate request. (%s)\n",
816 	    rd->rd_einfo->te_name);
817 	e = topo_mod_enumerate(rd->rd_mod, rd->rd_pn, rd->rd_einfo->te_name,
818 	    rd->rd_name, rd->rd_min, rd->rd_max, NULL);
819 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "back from enumeration. %d\n",
820 	    e);
821 	if (e != 0) {
822 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
823 		    "Enumeration failed (%s)\n",
824 		    topo_strerror(topo_mod_errno(mp)));
825 		(void) topo_hdl_seterrno(thp, EMOD_PARTIAL_ENUM);
826 		return (topo_mod_seterrno(mp, EMOD_PARTIAL_ENUM));
827 	}
828 	return (e);
829 }
830 
831 int
832 topo_xml_range_process(topo_mod_t *mp, xmlNodePtr rn, tf_rdata_t *rd)
833 {
834 	/*
835 	 * The range may have several children xmlNodes, that may
836 	 * represent the enumeration method, property groups,
837 	 * dependents or nodes.
838 	 */
839 	xmlNodePtr cn;
840 	tnode_t *ct;
841 	int e, ccnt = 0;
842 
843 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "process %s range beneath %s\n",
844 	    rd->rd_name, topo_node_name(rd->rd_pn));
845 	e = topo_node_range_create(mp,
846 	    rd->rd_pn, rd->rd_name, rd->rd_min, rd->rd_max);
847 	if (e != 0 && topo_mod_errno(mp) != ETOPO_NODE_DUP) {
848 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
849 		    "Range create failed due to %s.\n",
850 		    topo_strerror(topo_mod_errno(mp)));
851 		return (-1);
852 	}
853 	for (cn = rn->xmlChildrenNode; cn != NULL; cn = cn->next)
854 		if (xmlStrcmp(cn->name, (xmlChar *)Enum_meth) == 0)
855 			break;
856 
857 	if (cn != NULL) {
858 		if ((rd->rd_einfo = enum_attributes_process(mp, cn)) == NULL)
859 			return (-1);
860 		if (enum_run(mp, rd) < 0) {
861 			/*
862 			 * Note the failure but continue on
863 			 */
864 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
865 			    "Enumeration failed.\n");
866 		}
867 	}
868 
869 	/* Now look for nodes, i.e., hard instances */
870 	for (cn = rn->xmlChildrenNode; cn != NULL; cn = cn->next) {
871 		if (xmlStrcmp(cn->name, (xmlChar *)Node) == 0)
872 			if (node_process(mp, cn, rd) < 0) {
873 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
874 				    "node processing failed: %s.\n",
875 				    topo_strerror(topo_mod_errno(mp)));
876 				return (topo_mod_seterrno(mp,
877 				    EMOD_PARTIAL_ENUM));
878 			}
879 	}
880 
881 	/* Property groups and Dependencies */
882 	ct = topo_child_first(rd->rd_pn);
883 	while (ct != NULL) {
884 		/* Only care about instances within the range */
885 		if (strcmp(topo_node_name(ct), rd->rd_name) != 0) {
886 			ct = topo_child_next(rd->rd_pn, ct);
887 			continue;
888 		}
889 		if (pad_process(mp, rd->rd_finfo, rn, ct, &rd->rd_pad) < 0)
890 			return (-1);
891 		ct = topo_child_next(rd->rd_pn, ct);
892 		ccnt++;
893 	}
894 
895 	if (ccnt == 0) {
896 		topo_node_range_destroy(rd->rd_pn, rd->rd_name);
897 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "no nodes processed for "
898 		    "range %s\n", rd->rd_name);
899 		return (-1);
900 	}
901 
902 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "end range process %s\n",
903 	    rd->rd_name);
904 	return (0);
905 }
906 
907 static tf_rdata_t *
908 topo_xml_walk(topo_mod_t *mp,
909     tf_info_t *xinfo, xmlNodePtr croot, tnode_t *troot)
910 {
911 	xmlNodePtr curr;
912 	tf_rdata_t *rr, *pr, *rdp;
913 
914 	/*
915 	 * What we're interested in are children xmlNodes of croot tagged
916 	 * as 'ranges', these define topology nodes may exist, and need
917 	 * to be verified.
918 	 */
919 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_walk\n");
920 	rr = pr = NULL;
921 	for (curr = croot->xmlChildrenNode; curr != NULL; curr = curr->next) {
922 		if (curr->name == NULL) {
923 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
924 			    "Ignoring nameless xmlnode\n");
925 			continue;
926 		}
927 		if (xmlStrcmp(curr->name, (xmlChar *)Range) != 0) {
928 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
929 			    "Ignoring non-range %s.\n", curr->name);
930 			continue;
931 		}
932 		if ((rdp = tf_rdata_new(mp, xinfo, curr, troot)) == NULL) {
933 			/*
934 			 * Range processing error, continue walk
935 			 */
936 			continue;
937 		}
938 		if (pr == NULL) {
939 			rr = pr = rdp;
940 		} else {
941 			pr->rd_next = rdp;
942 			pr = rdp;
943 		}
944 		rr->rd_cnt++;
945 	}
946 	return (rr);
947 }
948 
949 /*
950  *  Convert parsed xml topology description into topology nodes
951  */
952 int
953 topo_xml_enum(topo_mod_t *tmp, tf_info_t *xinfo, tnode_t *troot)
954 {
955 	xmlNodePtr xroot;
956 
957 	if ((xroot = xmlDocGetRootElement(xinfo->tf_xdoc)) == NULL) {
958 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
959 		    "Couldn't get root xmlNode.\n");
960 		return (-1);
961 	}
962 	if ((xinfo->tf_rd = topo_xml_walk(tmp, xinfo, xroot, troot)) == NULL) {
963 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
964 		    "error within .xml topology: %s\n",
965 		    topo_strerror(topo_mod_errno(tmp)));
966 		return (-1);
967 	}
968 	return (0);
969 }
970 
971 /*
972  * Load an XML tree from filename and read it into a DOM parse tree.
973  */
974 static tf_info_t *
975 txml_file_parse(topo_mod_t *tmp,
976     int fd, const char *filenm, const char *escheme)
977 {
978 	xmlValidCtxtPtr vcp;
979 	xmlNodePtr cursor;
980 	xmlDocPtr document;
981 	xmlDtdPtr dtd = NULL;
982 	xmlChar *scheme = NULL;
983 	char *dtdpath = NULL;
984 	int readflags = 0;
985 	tf_info_t *r;
986 	int e, validate = 0;
987 
988 	/*
989 	 * Since topologies can XInclude other topologies, and libxml2
990 	 * doesn't do DTD-based validation with XInclude, by default
991 	 * we don't validate topology files.  One can force
992 	 * validation, though, by creating a TOPOXML_VALIDATE
993 	 * environment variable and creating a TOPO_DTD environment
994 	 * variable with the path to the DTD against which to validate.
995 	 */
996 	if (getenv("TOPOXML_VALIDATE") != NULL) {
997 		dtdpath = getenv("TOPO_DTD");
998 		if (dtdpath != NULL)
999 			xmlLoadExtDtdDefaultValue = 0;
1000 		validate = 1;
1001 	}
1002 
1003 	/*
1004 	 * Splat warnings and errors related to parsing the topology
1005 	 * file if the TOPOXML_PERROR environment variable exists.
1006 	 */
1007 	if (getenv("TOPOXML_PERROR") == NULL)
1008 		readflags = XML_PARSE_NOERROR | XML_PARSE_NOWARNING;
1009 
1010 	if ((document = xmlReadFd(fd, filenm, NULL, readflags)) == NULL) {
1011 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1012 		    "couldn't parse document.\n");
1013 		return (NULL);
1014 	}
1015 
1016 	/*
1017 	 * Verify that this is a document type we understand.
1018 	 */
1019 	if ((dtd = xmlGetIntSubset(document)) == NULL) {
1020 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1021 		    "document has no DTD.\n");
1022 		xmlFreeDoc(document);
1023 		return (NULL);
1024 	}
1025 
1026 	if (strcmp((const char *)dtd->SystemID, TOPO_DTD_PATH) != 0) {
1027 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1028 		    "document DTD unknown; bad topology file\n");
1029 		xmlFreeDoc(document);
1030 		return (NULL);
1031 	}
1032 
1033 	if ((cursor = xmlDocGetRootElement(document)) == NULL) {
1034 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, "document is empty.\n");
1035 		xmlFreeDoc(document);
1036 		return (NULL);
1037 	}
1038 
1039 	/*
1040 	 * Make sure we're looking at a topology description in the
1041 	 * expected scheme.
1042 	 */
1043 	if (xmlStrcmp(cursor->name, (xmlChar *)Topology) != 0) {
1044 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1045 		    "document is not a topology description.\n");
1046 		xmlFreeDoc(document);
1047 		return (NULL);
1048 	}
1049 	if ((scheme = xmlGetProp(cursor, (xmlChar *)Scheme)) == NULL) {
1050 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1051 		    "topology lacks a scheme.\n");
1052 		(void) topo_mod_seterrno(tmp, ETOPO_PRSR_NOATTR);
1053 		xmlFreeDoc(document);
1054 		return (NULL);
1055 	}
1056 	if (xmlStrcmp(scheme, (xmlChar *)escheme) != 0) {
1057 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1058 		    "topology in unrecognized scheme, %s, expecting %s\n",
1059 		    scheme, escheme);
1060 		(void) topo_mod_seterrno(tmp, ETOPO_PRSR_BADSCH);
1061 		xmlFree(scheme);
1062 		xmlFreeDoc(document);
1063 		return (NULL);
1064 	}
1065 
1066 	if (dtdpath != NULL) {
1067 		dtd = xmlParseDTD(NULL, (xmlChar *)dtdpath);
1068 		if (dtd == NULL) {
1069 			topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1070 			    "Could not parse DTD \"%s\".\n",
1071 			    dtdpath);
1072 			xmlFree(scheme);
1073 			xmlFreeDoc(document);
1074 			return (NULL);
1075 		}
1076 
1077 		if (document->extSubset != NULL)
1078 			xmlFreeDtd(document->extSubset);
1079 
1080 		document->extSubset = dtd;
1081 	}
1082 
1083 	if (xmlXIncludeProcessFlags(document, XML_PARSE_XINCLUDE) == -1) {;
1084 		xmlFree(scheme);
1085 		xmlFreeDoc(document);
1086 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1087 		    "couldn't handle XInclude statements in document\n");
1088 		return (NULL);
1089 	}
1090 
1091 	if (validate) {
1092 		if ((vcp = xmlNewValidCtxt()) == NULL) {
1093 			xmlFree(scheme);
1094 			xmlFreeDoc(document);
1095 			return (NULL);
1096 		}
1097 		vcp->warning = xmlParserValidityWarning;
1098 		vcp->error = xmlParserValidityError;
1099 
1100 		e = xmlValidateDocument(vcp, document);
1101 
1102 		xmlFreeValidCtxt(vcp);
1103 
1104 		if (e == 0)
1105 			topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1106 			    "Document is not valid.\n");
1107 	}
1108 
1109 	if ((r = tf_info_new(tmp, document, scheme)) == NULL) {
1110 		xmlFree(scheme);
1111 		xmlFreeDoc(document);
1112 		return (NULL);
1113 	}
1114 
1115 	xmlFree(scheme);
1116 	scheme = NULL;
1117 	return (r);
1118 }
1119 
1120 static int
1121 txml_file_open(topo_mod_t *mp, const char *filename)
1122 {
1123 	int rfd;
1124 	if ((rfd = open(filename, O_RDONLY)) < 0)
1125 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOENT));
1126 	return (rfd);
1127 }
1128 
1129 tf_info_t *
1130 topo_xml_read(topo_mod_t *tmp, const char *path, const char *escheme)
1131 {
1132 	int fd;
1133 	tf_info_t *tip;
1134 
1135 	if ((fd = txml_file_open(tmp, path)) < 0) {
1136 		return (NULL);
1137 	}
1138 	tip = txml_file_parse(tmp, fd, path, escheme);
1139 	(void) close(fd);
1140 	return (tip);
1141 }
1142