xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/topo_xml.c (revision e54334fcbcd9d9be8f3ad6fbf06b2af4890bfeca)
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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2020 Joyent, Inc.
25  * Copyright 2026 Oxide Computer Company
26  */
27 
28 #include <libxml/parser.h>
29 #include <libxml/xinclude.h>
30 #include <sys/fm/protocol.h>
31 #include <assert.h>
32 #include <string.h>
33 #include <strings.h>
34 #include <ctype.h>
35 #include <errno.h>
36 #include <limits.h>
37 #include <fm/libtopo.h>
38 #include <unistd.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41 #include <topo_file.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 *, tf_info_t *, xmlNodePtr,
49     tnode_t *);
50 static tf_edata_t *enum_attributes_process(topo_mod_t *, xmlNodePtr);
51 static int enum_run(topo_mod_t *, tf_rdata_t *);
52 static int fac_enum_run(topo_mod_t *, tnode_t *, const char *);
53 static int fac_process(topo_mod_t *, xmlNodePtr, tf_rdata_t *, tnode_t *);
54 static int fac_enum_process(topo_mod_t *, xmlNodePtr, tnode_t *);
55 static int decorate_nodes(topo_mod_t *, tf_rdata_t *, xmlNodePtr, tnode_t *,
56     tf_pad_t **);
57 
58 
59 int
xmlattr_to_stab(topo_mod_t * mp,xmlNodePtr n,const char * stabname,topo_stability_t * rs)60 xmlattr_to_stab(topo_mod_t *mp, xmlNodePtr n, const char *stabname,
61     topo_stability_t *rs)
62 {
63 	xmlChar *str;
64 	int rv = 0;
65 
66 	if (n == NULL) {
67 		/* If there is no Stability defined, we default to private */
68 		*rs = TOPO_STABILITY_PRIVATE;
69 		return (0);
70 	}
71 	if ((str = xmlGetProp(n, (xmlChar *)stabname)) == NULL) {
72 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
73 		    "attribute to stability:\n");
74 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
75 	}
76 
77 	if (xmlStrcmp(str, (xmlChar *)Internal) == 0) {
78 		*rs = TOPO_STABILITY_INTERNAL;
79 	} else if (xmlStrcmp(str, (xmlChar *)Private) == 0) {
80 		*rs = TOPO_STABILITY_PRIVATE;
81 	} else if (xmlStrcmp(str, (xmlChar *)Obsolete) == 0) {
82 		*rs = TOPO_STABILITY_OBSOLETE;
83 	} else if (xmlStrcmp(str, (xmlChar *)External) == 0) {
84 		*rs = TOPO_STABILITY_EXTERNAL;
85 	} else if (xmlStrcmp(str, (xmlChar *)Unstable) == 0) {
86 		*rs = TOPO_STABILITY_UNSTABLE;
87 	} else if (xmlStrcmp(str, (xmlChar *)Evolving) == 0) {
88 		*rs = TOPO_STABILITY_EVOLVING;
89 	} else if (xmlStrcmp(str, (xmlChar *)Stable) == 0) {
90 		*rs = TOPO_STABILITY_STABLE;
91 	} else if (xmlStrcmp(str, (xmlChar *)Standard) == 0) {
92 		*rs = TOPO_STABILITY_STANDARD;
93 	} else {
94 		xmlFree(str);
95 		return (topo_mod_seterrno(mp, ETOPO_PRSR_BADSTAB));
96 	}
97 	xmlFree(str);
98 	return (rv);
99 }
100 
101 int
xmlattr_to_int(topo_mod_t * mp,xmlNodePtr n,const char * propname,uint64_t * value)102 xmlattr_to_int(topo_mod_t *mp,
103     xmlNodePtr n, const char *propname, uint64_t *value)
104 {
105 	xmlChar *str;
106 	xmlChar *estr;
107 
108 	if ((str = xmlGetProp(n, (xmlChar *)propname)) == NULL) {
109 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
110 		    "%s: failed to lookup %s attribute", __func__, propname);
111 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
112 	}
113 	errno = 0;
114 	*value = strtoull((char *)str, (char **)&estr, 0);
115 	if (errno != 0 || *estr != '\0') {
116 		/* no conversion was done */
117 		xmlFree(str);
118 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
119 		    "%s: failed to convert %s attribute", __func__, propname);
120 		return (topo_mod_seterrno(mp, ETOPO_PRSR_BADNUM));
121 	}
122 	xmlFree(str);
123 	return (0);
124 }
125 
126 int
xmlattr_to_double(topo_mod_t * mp,xmlNodePtr n,const char * propname,double * value)127 xmlattr_to_double(topo_mod_t *mp,
128     xmlNodePtr n, const char *propname, double *value)
129 {
130 	xmlChar *str;
131 	xmlChar *estr;
132 
133 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
134 	    "xmlattr_to_double(propname=%s)\n", propname);
135 	if ((str = xmlGetProp(n, (xmlChar *)propname)) == NULL)
136 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
137 
138 	errno = 0;
139 	*value = strtold((char *)str, (char **)&estr);
140 	if (errno != 0 || *estr != '\0') {
141 		/* full or partial conversion failure */
142 		xmlFree(str);
143 		return (topo_mod_seterrno(mp, ETOPO_PRSR_BADNUM));
144 	}
145 	xmlFree(str);
146 	return (0);
147 }
148 
149 static int
xmlattr_to_fmri(topo_mod_t * mp,xmlNodePtr xn,const char * propname,nvlist_t ** rnvl)150 xmlattr_to_fmri(topo_mod_t *mp,
151     xmlNodePtr xn, const char *propname, nvlist_t **rnvl)
152 {
153 	xmlChar *str;
154 
155 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "xmlattr_to_fmri(propname=%s)\n",
156 	    propname);
157 	if ((str = xmlGetProp(xn, (xmlChar *)propname)) == NULL)
158 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
159 	if (topo_mod_str2nvl(mp, (const char *)str, rnvl) < 0) {
160 		xmlFree(str);
161 		return (-1);
162 	}
163 	xmlFree(str);
164 	return (0);
165 }
166 
167 static topo_type_t
xmlattr_to_type(topo_mod_t * mp,xmlNodePtr xn,xmlChar * attr)168 xmlattr_to_type(topo_mod_t *mp, xmlNodePtr xn, xmlChar *attr)
169 {
170 	topo_type_t rv;
171 	xmlChar *str;
172 	if ((str = xmlGetProp(xn, (xmlChar *)attr)) == NULL) {
173 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "%s attribute missing",
174 		    attr);
175 		(void) topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
176 		return (TOPO_TYPE_INVALID);
177 	}
178 	if (xmlStrcmp(str, (xmlChar *)Int32) == 0) {
179 		rv = TOPO_TYPE_INT32;
180 	} else if (xmlStrcmp(str, (xmlChar *)UInt32) == 0) {
181 		rv = TOPO_TYPE_UINT32;
182 	} else if (xmlStrcmp(str, (xmlChar *)Int64) == 0) {
183 		rv = TOPO_TYPE_INT64;
184 	} else if (xmlStrcmp(str, (xmlChar *)UInt64) == 0) {
185 		rv = TOPO_TYPE_UINT64;
186 	} else if (xmlStrcmp(str, (xmlChar *)FMRI) == 0) {
187 		rv = TOPO_TYPE_FMRI;
188 	} else if (xmlStrcmp(str, (xmlChar *)String) == 0) {
189 		rv = TOPO_TYPE_STRING;
190 	} else if (xmlStrcmp(str, (xmlChar *)Double) == 0) {
191 		rv = TOPO_TYPE_DOUBLE;
192 	} else if (xmlStrcmp(str, (xmlChar *)Int32_Arr) == 0) {
193 		rv = TOPO_TYPE_INT32_ARRAY;
194 	} else if (xmlStrcmp(str, (xmlChar *)UInt32_Arr) == 0) {
195 		rv = TOPO_TYPE_UINT32_ARRAY;
196 	} else if (xmlStrcmp(str, (xmlChar *)Int64_Arr) == 0) {
197 		rv = TOPO_TYPE_INT64_ARRAY;
198 	} else if (xmlStrcmp(str, (xmlChar *)UInt64_Arr) == 0) {
199 		rv = TOPO_TYPE_UINT64_ARRAY;
200 	} else if (xmlStrcmp(str, (xmlChar *)String_Arr) == 0) {
201 		rv = TOPO_TYPE_STRING_ARRAY;
202 	} else if (xmlStrcmp(str, (xmlChar *)FMRI_Arr) == 0) {
203 		rv = TOPO_TYPE_FMRI_ARRAY;
204 	} else {
205 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
206 		    "Unrecognized type attribute value '%s'.\n", str);
207 		(void) topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE);
208 		xmlFree(str);
209 		return (TOPO_TYPE_INVALID);
210 	}
211 	xmlFree(str);
212 	return (rv);
213 }
214 
215 static int
xlate_common(topo_mod_t * mp,xmlNodePtr xn,topo_type_t ptype,nvlist_t * nvl,const char * name)216 xlate_common(topo_mod_t *mp, xmlNodePtr xn, topo_type_t ptype, nvlist_t *nvl,
217     const char *name)
218 {
219 	int rv = 0;
220 	uint64_t ui;
221 	double dbl;
222 	uint_t i = 0, nelems = 0;
223 	nvlist_t *fmri;
224 	xmlChar *str;
225 	char **strarrbuf;
226 	void *arrbuf;
227 	nvlist_t **nvlarrbuf;
228 	xmlNodePtr cn;
229 
230 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "xlate_common(name=%s)\n", name);
231 	switch (ptype) {
232 	case TOPO_TYPE_INT32:
233 		if (xmlattr_to_int(mp, xn, Value, &ui) < 0)
234 			return (-1);
235 		rv = nvlist_add_int32(nvl, name, (int32_t)ui);
236 		break;
237 	case TOPO_TYPE_UINT32:
238 		if (xmlattr_to_int(mp, xn, Value, &ui) < 0)
239 			return (-1);
240 		rv = nvlist_add_uint32(nvl, name, (uint32_t)ui);
241 		break;
242 	case TOPO_TYPE_INT64:
243 		if (xmlattr_to_int(mp, xn, Value, &ui) < 0)
244 			return (-1);
245 		rv = nvlist_add_int64(nvl, name, (int64_t)ui);
246 		break;
247 	case TOPO_TYPE_UINT64:
248 		if (xmlattr_to_int(mp, xn, Value, &ui) < 0)
249 			return (-1);
250 		rv = nvlist_add_uint64(nvl, name, ui);
251 		break;
252 	case TOPO_TYPE_DOUBLE:
253 		if (xmlattr_to_double(mp, xn, Value, &dbl) < 0)
254 			return (-1);
255 		rv = nvlist_add_double(nvl, name, dbl);
256 		break;
257 	case TOPO_TYPE_FMRI:
258 		if (xmlattr_to_fmri(mp, xn, Value, &fmri) < 0)
259 			return (-1);
260 		rv = nvlist_add_nvlist(nvl, name, fmri);
261 		nvlist_free(fmri);
262 		break;
263 	case TOPO_TYPE_STRING:
264 		if ((str = xmlGetProp(xn, (xmlChar *)Value)) == NULL)
265 			return (-1);
266 		rv = nvlist_add_string(nvl, name, (char *)str);
267 		xmlFree(str);
268 		break;
269 	case TOPO_TYPE_INT32_ARRAY:
270 	case TOPO_TYPE_UINT32_ARRAY:
271 	case TOPO_TYPE_INT64_ARRAY:
272 	case TOPO_TYPE_UINT64_ARRAY:
273 	case TOPO_TYPE_STRING_ARRAY:
274 	case TOPO_TYPE_FMRI_ARRAY:
275 		for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next)
276 			if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) ||
277 			    (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0))
278 				nelems++;
279 
280 		if (nelems < 1) {
281 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "No <propitem> "
282 			    "or <argitem> elements found for array val");
283 			return (-1);
284 		}
285 		break;
286 	default:
287 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
288 		    "Unrecognized type attribute (ptype = %d)\n", ptype);
289 		return (topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE));
290 	}
291 
292 	switch (ptype) {
293 	case TOPO_TYPE_INT32_ARRAY:
294 		if ((arrbuf = topo_mod_alloc(mp, (nelems * sizeof (int32_t))))
295 		    == NULL)
296 			return (topo_mod_seterrno(mp, ETOPO_NOMEM));
297 		for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
298 			if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) ||
299 			    (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0)) {
300 
301 				if (xmlattr_to_int(mp, cn, Value, &ui) < 0)
302 					return (-1);
303 				((int32_t *)arrbuf)[i++] = (int32_t)ui;
304 			}
305 		}
306 
307 		rv = nvlist_add_int32_array(nvl, name, (int32_t *)arrbuf,
308 		    nelems);
309 		topo_mod_free(mp, arrbuf, (nelems * sizeof (int32_t)));
310 		break;
311 	case TOPO_TYPE_UINT32_ARRAY:
312 		if ((arrbuf = topo_mod_alloc(mp, (nelems * sizeof (uint32_t))))
313 		    == NULL)
314 			return (topo_mod_seterrno(mp, ETOPO_NOMEM));
315 		for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
316 			if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) ||
317 			    (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0)) {
318 
319 				if (xmlattr_to_int(mp, cn, Value, &ui) < 0)
320 					return (-1);
321 				((uint32_t *)arrbuf)[i++] = (uint32_t)ui;
322 			}
323 		}
324 
325 		rv = nvlist_add_uint32_array(nvl, name, (uint32_t *)arrbuf,
326 		    nelems);
327 		topo_mod_free(mp, arrbuf, (nelems * sizeof (uint32_t)));
328 		break;
329 	case TOPO_TYPE_INT64_ARRAY:
330 		if ((arrbuf = topo_mod_alloc(mp, (nelems * sizeof (int64_t))))
331 		    == NULL)
332 			return (topo_mod_seterrno(mp, ETOPO_NOMEM));
333 		for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
334 			if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) ||
335 			    (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0)) {
336 
337 				if (xmlattr_to_int(mp, cn, Value, &ui) < 0)
338 					return (-1);
339 				((int64_t *)arrbuf)[i++] = (int64_t)ui;
340 			}
341 		}
342 
343 		rv = nvlist_add_int64_array(nvl, name, (int64_t *)arrbuf,
344 		    nelems);
345 		topo_mod_free(mp, arrbuf, (nelems * sizeof (int64_t)));
346 		break;
347 	case TOPO_TYPE_UINT64_ARRAY:
348 		if ((arrbuf = topo_mod_alloc(mp, (nelems * sizeof (uint64_t))))
349 		    == NULL)
350 			return (topo_mod_seterrno(mp, ETOPO_NOMEM));
351 		for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
352 			if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) ||
353 			    (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0)) {
354 
355 				if (xmlattr_to_int(mp, cn, Value, &ui) < 0)
356 					return (-1);
357 				((uint64_t *)arrbuf)[i++] = ui;
358 			}
359 		}
360 
361 		rv = nvlist_add_uint64_array(nvl, name, arrbuf,
362 		    nelems);
363 		topo_mod_free(mp, arrbuf, (nelems * sizeof (uint64_t)));
364 		break;
365 	case TOPO_TYPE_STRING_ARRAY:
366 		if ((strarrbuf = topo_mod_alloc(mp, (nelems * sizeof (char *))))
367 		    == NULL)
368 			return (topo_mod_seterrno(mp, ETOPO_NOMEM));
369 		for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
370 			if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) ||
371 			    (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0)) {
372 
373 				if ((str = xmlGetProp(cn, (xmlChar *)Value))
374 				    == NULL)
375 					return (-1);
376 
377 				strarrbuf[i++] =
378 				    topo_mod_strdup(mp, (const char *)str);
379 				xmlFree(str);
380 			}
381 		}
382 
383 		rv = nvlist_add_string_array(nvl, name, strarrbuf, nelems);
384 		topo_mod_strfreev(mp, strarrbuf, nelems);
385 		break;
386 	case TOPO_TYPE_FMRI_ARRAY:
387 		if ((nvlarrbuf = topo_mod_alloc(mp, (nelems *
388 		    sizeof (nvlist_t *)))) == NULL)
389 			return (topo_mod_seterrno(mp, ETOPO_NOMEM));
390 		for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
391 			if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) ||
392 			    (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0)) {
393 
394 				if ((str = xmlGetProp(cn, (xmlChar *)Value))
395 				    == NULL)
396 					return (-1);
397 
398 				if (topo_mod_str2nvl(mp, (const char *)str,
399 				    &(nvlarrbuf[i++])) < 0) {
400 					xmlFree(str);
401 					return (-1);
402 				}
403 				xmlFree(str);
404 			}
405 		}
406 
407 		rv = nvlist_add_nvlist_array(nvl, name, nvlarrbuf,
408 		    nelems);
409 		topo_mod_free(mp, nvlarrbuf, (nelems * sizeof (nvlist_t *)));
410 		break;
411 	default:
412 		/*
413 		 * Invalid types were handled in the switch statement above
414 		 * this. Items in this case don't need additional processing.
415 		 */
416 		break;
417 	}
418 
419 	if (rv != 0) {
420 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
421 		    "Nvlist construction failed.\n");
422 		return (topo_mod_seterrno(mp, ETOPO_NOMEM));
423 	} else
424 		return (0);
425 }
426 
427 static int
xmlprop_xlate(topo_mod_t * mp,xmlNodePtr xn,nvlist_t * nvl)428 xmlprop_xlate(topo_mod_t *mp, xmlNodePtr xn, nvlist_t *nvl)
429 {
430 	topo_type_t ptype;
431 	xmlChar *str;
432 
433 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "xmlprop_xlate\n");
434 	if ((str = xmlGetProp(xn, (xmlChar *)Immutable)) != NULL) {
435 		if (xmlStrcmp(str, (xmlChar *)False) == 0)
436 			(void) nvlist_add_boolean_value(nvl, INV_IMMUTE,
437 			    B_FALSE);
438 		else
439 			(void) nvlist_add_boolean_value(nvl, INV_IMMUTE,
440 			    B_TRUE);
441 		xmlFree(str);
442 	} else {
443 		(void) nvlist_add_boolean_value(nvl, INV_IMMUTE, B_TRUE);
444 	}
445 
446 	if ((ptype = xmlattr_to_type(mp, xn, (xmlChar *)Type))
447 	    == TOPO_TYPE_INVALID)
448 		return (-1);
449 
450 	if (nvlist_add_int32(nvl, INV_PVALTYPE, ptype) != 0)
451 		return (-1);
452 
453 	return (xlate_common(mp, xn, ptype, nvl, INV_PVAL));
454 }
455 
456 static int
dependent_create(topo_mod_t * mp,tf_info_t * xinfo,tf_pad_t * pad,xmlNodePtr dxn,tnode_t * ptn)457 dependent_create(topo_mod_t *mp,
458     tf_info_t *xinfo, tf_pad_t *pad, xmlNodePtr dxn, tnode_t *ptn)
459 {
460 	tf_rdata_t *rp, *pp, *np;
461 	xmlChar *grptype;
462 	int sibs = 0;
463 
464 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "dependent_create\n");
465 	if ((grptype = xmlGetProp(dxn, (xmlChar *)Grouping)) == NULL) {
466 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
467 		    "Dependents missing grouping attribute");
468 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
469 	}
470 
471 	pp = NULL;
472 	if (xmlStrcmp(grptype, (xmlChar *)Siblings) == 0) {
473 		rp = pad->tpad_sibs;
474 		sibs++;
475 	} else if (xmlStrcmp(grptype, (xmlChar *)Children) == 0) {
476 		rp = pad->tpad_child;
477 	} else {
478 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
479 		    "Dependents have bogus grouping attribute");
480 		xmlFree(grptype);
481 		return (topo_mod_seterrno(mp, ETOPO_PRSR_BADGRP));
482 	}
483 	xmlFree(grptype);
484 	/* Add processed dependents to the tail of the list */
485 	while (rp != NULL) {
486 		pp = rp;
487 		rp = rp->rd_next;
488 	}
489 	if ((np = topo_xml_walk(mp, xinfo, dxn, ptn)) == NULL) {
490 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
491 		    "error within dependent .xml topology: "
492 		    "%s\n", topo_strerror(topo_mod_errno(mp)));
493 		return (-1);
494 	}
495 	if (pp != NULL)
496 		pp->rd_next = np;
497 	else if (sibs == 1)
498 		pad->tpad_sibs = np;
499 	else
500 		pad->tpad_child = np;
501 	return (0);
502 }
503 
504 static int
dependents_create(topo_mod_t * mp,tf_info_t * xinfo,tf_pad_t * pad,xmlNodePtr pxn,tnode_t * ptn)505 dependents_create(topo_mod_t *mp,
506     tf_info_t *xinfo, tf_pad_t *pad, xmlNodePtr pxn, tnode_t *ptn)
507 {
508 	xmlNodePtr cn;
509 
510 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "dependents_create\n");
511 	for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
512 		if (xmlStrcmp(cn->name, (xmlChar *)Dependents) == 0) {
513 			if (dependent_create(mp, xinfo, pad, cn, ptn) < 0)
514 				return (-1);
515 		}
516 	}
517 	return (0);
518 }
519 
520 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)521 prop_create(topo_mod_t *mp,
522     nvlist_t *pfmri, tnode_t *ptn, const char *gnm, const char *pnm,
523     topo_type_t ptype, int flag)
524 {
525 	nvlist_t *fmri, **fmriarr;
526 	uint32_t ui32, *ui32arr;
527 	uint64_t ui64, *ui64arr;
528 	int32_t i32, *i32arr;
529 	int64_t i64, *i64arr;
530 	double dbl;
531 	uint_t nelem;
532 	char *str, **strarr;
533 	int err, e;
534 
535 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "prop_create(pgrp = %s, "
536 	    "prop = %s)\n", gnm, pnm);
537 	switch (ptype) {
538 	case TOPO_TYPE_INT32:
539 		e = nvlist_lookup_int32(pfmri, INV_PVAL, &i32);
540 		break;
541 	case TOPO_TYPE_UINT32:
542 		e = nvlist_lookup_uint32(pfmri, INV_PVAL, &ui32);
543 		break;
544 	case TOPO_TYPE_INT64:
545 		e = nvlist_lookup_int64(pfmri, INV_PVAL, &i64);
546 		break;
547 	case TOPO_TYPE_UINT64:
548 		e = nvlist_lookup_uint64(pfmri, INV_PVAL, &ui64);
549 		break;
550 	case TOPO_TYPE_DOUBLE:
551 		e = nvlist_lookup_double(pfmri, INV_PVAL, &dbl);
552 		break;
553 	case TOPO_TYPE_FMRI:
554 		e = nvlist_lookup_nvlist(pfmri, INV_PVAL, &fmri);
555 		break;
556 	case TOPO_TYPE_STRING:
557 		e = nvlist_lookup_string(pfmri, INV_PVAL, &str);
558 		break;
559 	case TOPO_TYPE_INT32_ARRAY:
560 		e = nvlist_lookup_int32_array(pfmri, INV_PVAL, &i32arr, &nelem);
561 		break;
562 	case TOPO_TYPE_UINT32_ARRAY:
563 		e = nvlist_lookup_uint32_array(pfmri, INV_PVAL, &ui32arr,
564 		    &nelem);
565 		break;
566 	case TOPO_TYPE_INT64_ARRAY:
567 		e = nvlist_lookup_int64_array(pfmri, INV_PVAL, &i64arr,
568 		    &nelem);
569 		break;
570 	case TOPO_TYPE_UINT64_ARRAY:
571 		e = nvlist_lookup_uint64_array(pfmri, INV_PVAL, &ui64arr,
572 		    &nelem);
573 		break;
574 	case TOPO_TYPE_STRING_ARRAY:
575 		e = nvlist_lookup_string_array(pfmri, INV_PVAL, &strarr,
576 		    &nelem);
577 		break;
578 	case TOPO_TYPE_FMRI_ARRAY:
579 		e = nvlist_lookup_nvlist_array(pfmri, INV_PVAL, &fmriarr,
580 		    &nelem);
581 		break;
582 	default:
583 		e = ETOPO_PRSR_BADTYPE;
584 		break;
585 	}
586 	if (e != 0) {
587 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
588 		    "prop_create: prop value lookup failed.\n");
589 		return (topo_mod_seterrno(mp, e));
590 	}
591 	switch (ptype) {
592 	case TOPO_TYPE_INT32:
593 		e = topo_prop_set_int32(ptn, gnm, pnm, flag, i32, &err);
594 		break;
595 	case TOPO_TYPE_UINT32:
596 		e = topo_prop_set_uint32(ptn, gnm, pnm, flag, ui32, &err);
597 		break;
598 	case TOPO_TYPE_INT64:
599 		e = topo_prop_set_int64(ptn, gnm, pnm, flag, i64, &err);
600 		break;
601 	case TOPO_TYPE_UINT64:
602 		e = topo_prop_set_uint64(ptn, gnm, pnm, flag, ui64, &err);
603 		break;
604 	case TOPO_TYPE_DOUBLE:
605 		e = topo_prop_set_double(ptn, gnm, pnm, flag, dbl, &err);
606 		break;
607 	case TOPO_TYPE_FMRI:
608 		e = topo_prop_set_fmri(ptn, gnm, pnm, flag, fmri, &err);
609 		break;
610 	case TOPO_TYPE_STRING:
611 		e = topo_prop_set_string(ptn, gnm, pnm, flag, str, &err);
612 		break;
613 	case TOPO_TYPE_INT32_ARRAY:
614 		e = topo_prop_set_int32_array(ptn, gnm, pnm, flag, i32arr,
615 		    nelem, &err);
616 		break;
617 	case TOPO_TYPE_UINT32_ARRAY:
618 		e = topo_prop_set_uint32_array(ptn, gnm, pnm, flag, ui32arr,
619 		    nelem, &err);
620 		break;
621 	case TOPO_TYPE_INT64_ARRAY:
622 		e = topo_prop_set_int64_array(ptn, gnm, pnm, flag, i64arr,
623 		    nelem, &err);
624 		break;
625 	case TOPO_TYPE_UINT64_ARRAY:
626 		e = topo_prop_set_uint64_array(ptn, gnm, pnm, flag, ui64arr,
627 		    nelem, &err);
628 		break;
629 	case TOPO_TYPE_STRING_ARRAY:
630 		e = topo_prop_set_string_array(ptn, gnm, pnm, flag,
631 		    (const char **)strarr, nelem, &err);
632 		break;
633 	case TOPO_TYPE_FMRI_ARRAY:
634 		e = topo_prop_set_fmri_array(ptn, gnm, pnm, flag,
635 		    (const nvlist_t **)fmriarr, nelem, &err);
636 		break;
637 	default:
638 		e = ETOPO_PRSR_BADTYPE;
639 		break;
640 	}
641 	if (e != 0 && err != ETOPO_PROP_DEFD) {
642 
643 		/*
644 		 * Some properties may have already been set
645 		 * in topo_node_bind() or topo_prop_inherit if we are
646 		 * enumerating from a static .xml file
647 		 */
648 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "prop set "
649 		    "failed %s/%s:%s\n", gnm, pnm, topo_strerror(err));
650 		return (topo_mod_seterrno(mp, err));
651 	}
652 	return (0);
653 }
654 
655 static int
props_create(topo_mod_t * mp,tnode_t * ptn,const char * gnm,nvlist_t ** props,int nprops)656 props_create(topo_mod_t *mp,
657     tnode_t *ptn, const char *gnm, nvlist_t **props, int nprops)
658 {
659 	topo_type_t ptype;
660 	boolean_t pim;
661 	char *pnm;
662 	int32_t i32;
663 	int flag;
664 	int pn;
665 	int e;
666 
667 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "props_create(pgrp = %s)\n",
668 	    gnm);
669 	for (pn = 0; pn < nprops; pn++) {
670 		e = nvlist_lookup_string(props[pn], INV_PNAME, &pnm);
671 		if (e != 0) {
672 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
673 			    "props create lookup (%s) failure: %s",
674 			    INV_PNAME, strerror(e));
675 			return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
676 		}
677 		e = nvlist_lookup_boolean_value(props[pn], INV_IMMUTE, &pim);
678 		if (e != 0) {
679 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
680 			    "props create lookup (%s) failure: %s",
681 			    INV_IMMUTE, strerror(e));
682 			return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
683 		}
684 		flag = (pim == B_TRUE) ?
685 		    TOPO_PROP_IMMUTABLE : TOPO_PROP_MUTABLE;
686 
687 		e = nvlist_lookup_int32(props[pn], INV_PVALTYPE, &i32);
688 		if (e != 0) {
689 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
690 			    "props create lookup (%s) failure: %s",
691 			    INV_PVALTYPE, strerror(e));
692 			return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
693 		}
694 		ptype = (topo_type_t)i32;
695 		if (prop_create(mp, props[pn], ptn, gnm, pnm, ptype, flag) < 0)
696 			return (-1);
697 	}
698 	return (0);
699 }
700 
701 static int
pgroups_create(topo_mod_t * mp,tf_pad_t * pad,tnode_t * ptn)702 pgroups_create(topo_mod_t *mp, tf_pad_t *pad, tnode_t *ptn)
703 {
704 	topo_pgroup_info_t pgi;
705 	nvlist_t **props;
706 	char *gnm;
707 	char *nmstab, *dstab;
708 	uint32_t rnprops, nprops;
709 	uint32_t gv;
710 	int pg;
711 	int e;
712 
713 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroups_create: %s=%d\n",
714 	    topo_node_name(ptn), topo_node_instance(ptn));
715 	for (pg = 0; pg < pad->tpad_pgcnt; pg++) {
716 		e = nvlist_lookup_string(pad->tpad_pgs[pg],
717 		    INV_PGRP_NAME, &gnm);
718 		if (e != 0) {
719 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
720 			    "pad lookup (%s) failed (%s).\n",
721 			    INV_PGRP_NAME, strerror(errno));
722 			return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
723 		}
724 		e = nvlist_lookup_string(pad->tpad_pgs[pg],
725 		    INV_PGRP_NMSTAB, &nmstab);
726 		if (e != 0) {
727 			if (e != ENOENT) {
728 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
729 				    "pad lookup (%s) "
730 				    "failed.\n", INV_PGRP_NMSTAB);
731 				return (topo_mod_seterrno(mp,
732 				    ETOPO_PRSR_NVPROP));
733 			} else {
734 				nmstab = TOPO_STABSTR_PRIVATE;
735 			}
736 		}
737 		e = nvlist_lookup_string(pad->tpad_pgs[pg],
738 		    INV_PGRP_DSTAB, &dstab);
739 		if (e != 0) {
740 			if (e != ENOENT) {
741 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
742 				    "pad lookup (%s) failed.\n",
743 				    INV_PGRP_DSTAB);
744 				return (topo_mod_seterrno(mp,
745 				    ETOPO_PRSR_NVPROP));
746 			} else {
747 				dstab = TOPO_STABSTR_PRIVATE;
748 			}
749 		}
750 		e = nvlist_lookup_uint32(pad->tpad_pgs[pg],
751 		    INV_PGRP_VER, &gv);
752 		if (e != 0) {
753 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
754 			    "pad lookup (%s) failed.\n",
755 			    INV_PGRP_VER);
756 			return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
757 		}
758 		pgi.tpi_name = gnm;
759 		pgi.tpi_namestab = topo_name2stability(nmstab);
760 		pgi.tpi_datastab = topo_name2stability(dstab);
761 		pgi.tpi_version = gv;
762 		if (topo_pgroup_create(ptn, &pgi, &e) != 0) {
763 			if (e != ETOPO_PROP_DEFD) {
764 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
765 				    "pgroups create failure: %s\n",
766 				    topo_strerror(e));
767 				return (-1);
768 			}
769 		}
770 		e = nvlist_lookup_uint32(pad->tpad_pgs[pg],
771 		    INV_PGRP_NPROP, &rnprops);
772 		/*
773 		 * The number of properties could be zero if the property
774 		 * group only contains propmethod declarations
775 		 */
776 		if (rnprops > 0) {
777 			e |= nvlist_lookup_nvlist_array(pad->tpad_pgs[pg],
778 			    INV_PGRP_ALLPROPS, &props, &nprops);
779 			if (rnprops != nprops) {
780 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
781 				    "recorded number of props %d does not "
782 				    "match number of props recorded %d.\n",
783 				    rnprops, nprops);
784 			}
785 			if (props_create(mp, ptn, gnm, props, nprops) < 0)
786 				return (-1);
787 		}
788 	}
789 	return (0);
790 }
791 
792 static nvlist_t *
pval_record(topo_mod_t * mp,xmlNodePtr xn)793 pval_record(topo_mod_t *mp, xmlNodePtr xn)
794 {
795 	nvlist_t *pnvl = NULL;
796 	xmlChar *pname;
797 
798 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pval_record\n");
799 	if ((pname = xmlGetProp(xn, (xmlChar *)Name)) == NULL) {
800 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
801 		    "propval lacks a name\n");
802 		(void) topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
803 		return (NULL);
804 	}
805 	if (topo_mod_nvalloc(mp, &pnvl, NV_UNIQUE_NAME) < 0) {
806 		xmlFree(pname);
807 		return (NULL);
808 	}
809 	if (nvlist_add_string(pnvl, INV_PNAME, (char *)pname) < 0) {
810 		xmlFree(pname);
811 		nvlist_free(pnvl);
812 		return (NULL);
813 	}
814 	xmlFree(pname);
815 	/* FMXXX stability of the property name */
816 
817 	if (xmlprop_xlate(mp, xn, pnvl) < 0) {
818 		nvlist_free(pnvl);
819 		return (NULL);
820 	}
821 	return (pnvl);
822 }
823 
824 
825 struct propmeth_data {
826 	const char *pg_name;
827 	const char *prop_name;
828 	topo_type_t prop_type;
829 	const char *meth_name;
830 	topo_version_t meth_ver;
831 	nvlist_t *arg_nvl;
832 };
833 
834 static int
register_method(topo_mod_t * mp,tnode_t * ptn,struct propmeth_data * meth)835 register_method(topo_mod_t *mp, tnode_t *ptn, struct propmeth_data *meth)
836 {
837 	int err;
838 
839 	if (topo_prop_method_version_register(ptn, meth->pg_name,
840 	    meth->prop_name, meth->prop_type, meth->meth_name, meth->meth_ver,
841 	    meth->arg_nvl, &err) != 0) {
842 
843 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "failed to register "
844 		    "propmethod %s for property \"%s\" in propgrp %s on node "
845 		    "%s=%d (%s)\n",
846 		    meth->meth_name, meth->prop_name, meth->pg_name,
847 		    topo_node_name(ptn), topo_node_instance(ptn),
848 		    topo_strerror(err));
849 		return (-1);
850 	}
851 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
852 	    "registered method %s on %s=%d\n",
853 	    meth->meth_name, topo_node_name(ptn), topo_node_instance(ptn));
854 
855 	return (0);
856 }
857 
858 static int
pmeth_record(topo_mod_t * mp,const char * pg_name,xmlNodePtr xn,tnode_t * tn,const char * rname,const char * ppgrp_name)859 pmeth_record(topo_mod_t *mp, const char *pg_name, xmlNodePtr xn, tnode_t *tn,
860     const char *rname, const char *ppgrp_name)
861 {
862 	nvlist_t *arg_nvl = NULL;
863 	xmlNodePtr cn;
864 	xmlChar *meth_name = NULL, *prop_name = NULL;
865 	xmlChar *arg_name = NULL;
866 	uint64_t meth_ver, is_mutable = 0, is_nonvolatile = 0;
867 	topo_type_t prop_type;
868 	struct propmeth_data meth;
869 	int ret = 0, err;
870 	topo_type_t ptype;
871 	tnode_t *tmp;
872 
873 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pmeth_record: %s=%d "
874 	    "(pgrp=%s)\n", topo_node_name(tn), topo_node_instance(tn), pg_name);
875 
876 	/*
877 	 * Get propmethod attribute values
878 	 */
879 	if ((meth_name = xmlGetProp(xn, (xmlChar *)Name)) == NULL) {
880 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
881 		    "propmethod element lacks a name attribute\n");
882 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
883 	}
884 	if (xmlattr_to_int(mp, xn, Version, &meth_ver) < 0) {
885 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
886 		    "propmethod element lacks version attribute\n");
887 		ret = topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
888 		goto pmr_done;
889 	}
890 	/*
891 	 * The "mutable" and "nonvoltile" attributes are optional.  If not
892 	 * specified we default to false (0)
893 	 */
894 	(void) xmlattr_to_int(mp, xn, Mutable, &is_mutable);
895 	(void) xmlattr_to_int(mp, xn, Nonvolatile, &is_nonvolatile);
896 
897 	if ((prop_name = xmlGetProp(xn, (xmlChar *)Propname)) == NULL) {
898 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
899 		    "propmethod element lacks propname attribute\n");
900 		ret = topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
901 		goto pmr_done;
902 	}
903 	if ((prop_type = xmlattr_to_type(mp, xn, (xmlChar *)Proptype))
904 	    == TOPO_TYPE_INVALID) {
905 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
906 		    "error decoding proptype attribute\n");
907 		ret = topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
908 		goto pmr_done;
909 	}
910 
911 	/*
912 	 * Allocate method argument nvlist
913 	 */
914 	if (topo_mod_nvalloc(mp, &arg_nvl, NV_UNIQUE_NAME) < 0) {
915 		ret = topo_mod_seterrno(mp, ETOPO_NOMEM);
916 		goto pmr_done;
917 	}
918 
919 	/*
920 	 * Iterate through the argval nodes and build the argval nvlist
921 	 */
922 	for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
923 		if (xmlStrcmp(cn->name, (xmlChar *)Argval) == 0) {
924 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
925 			    "found argval element\n");
926 			if ((arg_name = xmlGetProp(cn, (xmlChar *)Name))
927 			    == NULL) {
928 				topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
929 				    "argval element lacks a name attribute\n");
930 				ret = topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
931 				goto pmr_done;
932 			}
933 			if ((ptype = xmlattr_to_type(mp, cn, (xmlChar *)Type))
934 			    == TOPO_TYPE_INVALID) {
935 				ret = topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE);
936 				xmlFree(arg_name);
937 				break;
938 			}
939 			if (xlate_common(mp, cn, ptype, arg_nvl,
940 			    (const char *)arg_name) != 0) {
941 				ret = topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE);
942 				xmlFree(arg_name);
943 				break;
944 			}
945 		}
946 		if (arg_name) {
947 			xmlFree(arg_name);
948 			arg_name = NULL;
949 		}
950 	}
951 
952 	if (ret != 0)
953 		goto pmr_done;
954 
955 	/*
956 	 * Register the prop method for all of the nodes in our range
957 	 */
958 	meth.pg_name = (const char *)pg_name;
959 	meth.prop_name = (const char *)prop_name;
960 	meth.prop_type = prop_type;
961 	meth.meth_name = (const char *)meth_name;
962 	meth.meth_ver = meth_ver;
963 	meth.arg_nvl = arg_nvl;
964 
965 	/*
966 	 * If the propgroup element is under a range element, we'll apply
967 	 * the method to all of the topo nodes at this level with the same
968 	 * range name.
969 	 *
970 	 * Otherwise, if the propgroup element is under a node element
971 	 * then we'll simply register the method for this node.
972 	 */
973 	if (strcmp(ppgrp_name, Range) == 0) {
974 		for (tmp = tn; tmp != NULL; tmp = topo_child_next(NULL, tmp)) {
975 			if (strcmp(rname, topo_node_name(tmp)) == 0) {
976 				if (register_method(mp, tmp, &meth) != 0) {
977 					ret = topo_mod_seterrno(mp,
978 					    ETOPO_PRSR_REGMETH);
979 					goto pmr_done;
980 				}
981 				if (is_mutable) {
982 					if (topo_prop_setmutable(tmp,
983 					    meth.pg_name, meth.prop_name, &err)
984 					    != 0) {
985 						ret = topo_mod_seterrno(mp,
986 						    ETOPO_PRSR_REGMETH);
987 						goto pmr_done;
988 					}
989 				}
990 				if (is_nonvolatile) {
991 					if (topo_prop_setnonvolatile(tmp,
992 					    meth.pg_name, meth.prop_name, &err)
993 					    != 0) {
994 						ret = topo_mod_seterrno(mp,
995 						    ETOPO_PRSR_REGMETH);
996 						goto pmr_done;
997 					}
998 				}
999 			}
1000 		}
1001 	} else {
1002 		if (register_method(mp, tn, &meth) != 0) {
1003 			ret = topo_mod_seterrno(mp, ETOPO_PRSR_REGMETH);
1004 			goto pmr_done;
1005 		}
1006 		if (is_mutable) {
1007 			if (topo_prop_setmutable(tn, meth.pg_name,
1008 			    meth.prop_name, &err) != 0) {
1009 				ret = topo_mod_seterrno(mp,
1010 				    ETOPO_PRSR_REGMETH);
1011 				goto pmr_done;
1012 			}
1013 		}
1014 		if (is_nonvolatile) {
1015 			if (topo_prop_setnonvolatile(tn, meth.pg_name,
1016 			    meth.prop_name, &err) != 0) {
1017 				ret = topo_mod_seterrno(mp,
1018 				    ETOPO_PRSR_REGMETH);
1019 				goto pmr_done;
1020 			}
1021 		}
1022 	}
1023 
1024 pmr_done:
1025 	if (meth_name)
1026 		xmlFree(meth_name);
1027 	if (prop_name)
1028 		xmlFree(prop_name);
1029 	nvlist_free(arg_nvl);
1030 	return (ret);
1031 }
1032 
1033 
1034 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)1035 pgroup_record(topo_mod_t *mp, xmlNodePtr pxn, tnode_t *tn, const char *rname,
1036     tf_pad_t *rpad, int pi, const char *ppgrp_name)
1037 {
1038 	topo_stability_t nmstab, dstab;
1039 	uint64_t ver;
1040 	xmlNodePtr cn;
1041 	xmlChar *name;
1042 	nvlist_t **apl = NULL;
1043 	nvlist_t *pgnvl = NULL;
1044 	int pcnt = 0;
1045 	int ai = 0;
1046 	int e;
1047 
1048 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroup_record\n");
1049 	if ((name = xmlGetProp(pxn, (xmlChar *)Name)) == NULL) {
1050 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1051 		    "propgroup lacks a name\n");
1052 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
1053 	}
1054 	if (xmlattr_to_int(mp, pxn, Version, &ver) < 0) {
1055 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1056 		    "propgroup lacks a version\n");
1057 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
1058 	}
1059 	if (xmlattr_to_stab(mp, pxn, Namestab, &nmstab) < 0) {
1060 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1061 		    "propgroup lacks name-stability\n");
1062 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
1063 	}
1064 	if (xmlattr_to_stab(mp, pxn, Datastab, &dstab) < 0) {
1065 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1066 		    "propgroup lacks data-stability\n");
1067 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
1068 	}
1069 
1070 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroup %s\n", (char *)name);
1071 	for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1072 		if (xmlStrcmp(cn->name, (xmlChar *)Propval) == 0)
1073 			pcnt++;
1074 	}
1075 
1076 	if (topo_mod_nvalloc(mp, &pgnvl, NV_UNIQUE_NAME) < 0) {
1077 		xmlFree(name);
1078 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1079 		    "failed to allocate propgroup nvlist\n");
1080 		return (topo_mod_seterrno(mp, ETOPO_NOMEM));
1081 	}
1082 
1083 	e = nvlist_add_string(pgnvl, INV_PGRP_NAME, (char *)name);
1084 	e |= nvlist_add_uint32(pgnvl, INV_PGRP_NMSTAB, nmstab);
1085 	e |= nvlist_add_uint32(pgnvl, INV_PGRP_DSTAB, dstab);
1086 	e |= nvlist_add_uint32(pgnvl, INV_PGRP_VER, ver);
1087 	e |= nvlist_add_uint32(pgnvl, INV_PGRP_NPROP, pcnt);
1088 	if (pcnt > 0)
1089 		if (e != 0 ||
1090 		    (apl = topo_mod_zalloc(mp, pcnt * sizeof (nvlist_t *)))
1091 		    == NULL) {
1092 			xmlFree(name);
1093 			nvlist_free(pgnvl);
1094 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1095 			    "failed to allocate nvlist array for properties"
1096 			    "(e=%d)\n", e);
1097 			return (topo_mod_seterrno(mp, ETOPO_NOMEM));
1098 		}
1099 	for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1100 		if (xmlStrcmp(cn->name, (xmlChar *)Propval) == 0) {
1101 			if (ai < pcnt) {
1102 				if ((apl[ai] = pval_record(mp, cn)) == NULL)
1103 					break;
1104 			}
1105 			ai++;
1106 		} else if (xmlStrcmp(cn->name, (xmlChar *)Prop_meth) == 0) {
1107 			if (pmeth_record(mp, (const char *)name, cn, tn, rname,
1108 			    ppgrp_name) < 0)
1109 				break;
1110 		}
1111 	}
1112 	xmlFree(name);
1113 	if (pcnt > 0) {
1114 		e |= (ai != pcnt);
1115 		e |= nvlist_add_nvlist_array(pgnvl, INV_PGRP_ALLPROPS, apl,
1116 		    pcnt);
1117 		for (ai = 0; ai < pcnt; ai++)
1118 			nvlist_free(apl[ai]);
1119 		topo_mod_free(mp, apl, pcnt * sizeof (nvlist_t *));
1120 		if (e != 0) {
1121 			nvlist_free(pgnvl);
1122 			return (-1);
1123 		}
1124 	}
1125 	rpad->tpad_pgs[pi] = pgnvl;
1126 	return (0);
1127 }
1128 
1129 static int
pgroups_record(topo_mod_t * mp,xmlNodePtr pxn,tnode_t * tn,const char * rname,tf_pad_t * rpad,const char * ppgrp)1130 pgroups_record(topo_mod_t *mp, xmlNodePtr pxn, tnode_t *tn, const char *rname,
1131     tf_pad_t *rpad, const char *ppgrp)
1132 {
1133 	xmlNodePtr cn;
1134 	int pi = 0;
1135 
1136 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroups_record: pxn->name=%s\n",
1137 	    pxn->name);
1138 	for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1139 		if (xmlStrcmp(cn->name, (xmlChar *)Propgrp) == 0) {
1140 			if (pgroup_record(mp, cn, tn, rname, rpad, pi++, ppgrp)
1141 			    < 0)
1142 				return (-1);
1143 		}
1144 	}
1145 	return (0);
1146 }
1147 
1148 /*
1149  * psn:	pointer to a "set" XML node
1150  * key: string to search the set for
1151  *
1152  * returns: 1, if the set contains key
1153  *          0, otherwise
1154  */
1155 static int
set_contains(topo_mod_t * mp,char * key,char * set)1156 set_contains(topo_mod_t *mp, char *key, char *set)
1157 {
1158 	char *prod;
1159 	int rv = 0;
1160 
1161 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "set_contains(key = %s, "
1162 	    "setlist = %s)\n", key, set);
1163 
1164 	prod = strtok((char *)set, "|");
1165 	if (prod && (strcmp(key, prod) == 0))
1166 		return (1);
1167 
1168 	while ((prod = strtok(NULL, "|")))
1169 		if (strcmp(key, prod) == 0)
1170 			return (1);
1171 
1172 	return (rv);
1173 }
1174 
1175 
1176 /*
1177  * Process the property group and dependents xmlNode children of
1178  * parent xmlNode pxn.
1179  */
1180 static int
pad_process(topo_mod_t * mp,tf_rdata_t * rd,xmlNodePtr pxn,tnode_t * ptn,tf_pad_t ** rpad)1181 pad_process(topo_mod_t *mp, tf_rdata_t *rd, xmlNodePtr pxn, tnode_t *ptn,
1182     tf_pad_t **rpad)
1183 {
1184 	xmlNodePtr cn, gcn, psn = NULL, target;
1185 	/* Explicitly initialize ecn to help -Wmaybe-unit */
1186 	xmlNodePtr def_set = NULL, ecn = NULL;
1187 	tnode_t *ct;
1188 	tf_pad_t *new = *rpad;
1189 	tf_rdata_t tmp_rd;
1190 	int pgcnt = 0;
1191 	int dcnt = 0;
1192 	int ecnt = 0;
1193 	int joined_set = 0, inst;
1194 	xmlChar *set;
1195 	char *key;
1196 
1197 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1198 	    "pad_process beneath %s=%d\n", topo_node_name(ptn),
1199 	    topo_node_instance(ptn));
1200 	if (new == NULL) {
1201 		for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1202 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1203 			    "cn->name is %s \n", (char *)cn->name);
1204 			/*
1205 			 * We're iterating through the XML children looking for
1206 			 * four types of elements:
1207 			 *   1) dependents elements
1208 			 *   2) unconstrained pgroup elements
1209 			 *   3) pgroup elements constrained by set elements
1210 			 *   4) enum-method elements for the case that we want
1211 			 *	to post-process a statically defined node
1212 			 */
1213 			if (xmlStrcmp(cn->name, (xmlChar *)Dependents) == 0)
1214 				dcnt++;
1215 			else if (xmlStrcmp(cn->name, (xmlChar *)Propgrp) == 0)
1216 				pgcnt++;
1217 			else if (xmlStrcmp(cn->name, (xmlChar *)Enum_meth)
1218 			    == 0) {
1219 				ecn = cn;
1220 				ecnt++;
1221 			} else if (xmlStrcmp(cn->name, (xmlChar *)Set) == 0) {
1222 				if (joined_set)
1223 					continue;
1224 				set = xmlGetProp(cn, (xmlChar *)Setlist);
1225 
1226 				key = mp->tm_hdl->th_product;
1227 
1228 				/*
1229 				 * If it's the default set then we'll store
1230 				 * a pointer to it so that if none of the other
1231 				 * sets apply to our product we can fall
1232 				 * back to this one.
1233 				 */
1234 				if (strcmp((char *)set, "default") == 0)
1235 					def_set = cn;
1236 				else if (set_contains(mp, key, (char *)set)) {
1237 					psn = cn;
1238 					joined_set = 1;
1239 					for (gcn = cn->xmlChildrenNode;
1240 					    gcn != NULL; gcn = gcn->next) {
1241 						if (xmlStrcmp(gcn->name,
1242 						    (xmlChar *)Propgrp) == 0)
1243 							pgcnt++;
1244 					}
1245 				}
1246 				xmlFree(set);
1247 			}
1248 		}
1249 		/*
1250 		 * If we haven't found a set that contains our product AND
1251 		 * a default set exists, then we'll process it.
1252 		 */
1253 		if (!joined_set && def_set) {
1254 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1255 			    "Falling back to default set\n");
1256 			joined_set = 1;
1257 			psn = def_set;
1258 			for (gcn = psn->xmlChildrenNode; gcn != NULL;
1259 			    gcn = gcn->next) {
1260 				if (xmlStrcmp(gcn->name, (xmlChar *)Propgrp)
1261 				    == 0)
1262 					pgcnt++;
1263 			}
1264 		}
1265 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1266 		    "pad_process: dcnt=%d, pgcnt=%d, ecnt=%d, joined_set=%d\n",
1267 		    dcnt, pgcnt, ecnt, joined_set);
1268 		/*
1269 		 * Here we allocate an element in an intermediate data structure
1270 		 * which keeps track property groups and dependents of the range
1271 		 * currently being processed.
1272 		 *
1273 		 * This structure is referenced in pgroups_record() to create
1274 		 * the actual property groups in the topo tree
1275 		 */
1276 		if ((new = tf_pad_new(mp, pgcnt, dcnt)) == NULL)
1277 			return (-1);
1278 
1279 		if (pgcnt > 0) {
1280 			new->tpad_pgs =
1281 			    topo_mod_zalloc(mp, pgcnt * sizeof (nvlist_t *));
1282 			if (new->tpad_pgs == NULL) {
1283 				tf_pad_free(mp, new);
1284 				return (-1);
1285 			}
1286 		}
1287 		/*
1288 		 * If the property groups are contained within a set
1289 		 * then they will be one level lower in the XML tree.
1290 		 */
1291 		if (joined_set)
1292 			target = psn;
1293 		else
1294 			target = pxn;
1295 
1296 		/*
1297 		 * If there is no "node" element under the "range"
1298 		 * element, then we need to attach the facility node to
1299 		 * each node in this range.
1300 		 *
1301 		 * Otherwise we only attach it to the current node
1302 		 */
1303 		if (xmlStrcmp(target->name, (xmlChar *)Range) == 0 ||
1304 		    xmlStrcmp(target->name, (xmlChar *)Set) == 0) {
1305 			for (ct = topo_child_first(rd->rd_pn);
1306 			    ct != NULL;
1307 			    ct = topo_child_next(rd->rd_pn, ct)) {
1308 
1309 				if (strcmp(topo_node_name(ct),
1310 				    rd->rd_name) != 0)
1311 					continue;
1312 
1313 				inst = topo_node_instance(ct);
1314 				if (inst < rd->rd_min || inst > rd->rd_max)
1315 					continue;
1316 
1317 				if (fac_enum_process(mp, target, ct) < 0)
1318 					return (-1);
1319 
1320 				if (fac_process(mp, target, rd, ct) < 0)
1321 					return (-1);
1322 			}
1323 		} else {
1324 			if (fac_enum_process(mp, target, ptn) < 0)
1325 				return (-1);
1326 			if (fac_process(mp, target, rd, ptn) < 0)
1327 				return (-1);
1328 		}
1329 		if (pgcnt > 0 && pgroups_record(mp, target, ptn, rd->rd_name,
1330 		    new, (const char *)pxn->name) < 0) {
1331 			tf_pad_free(mp, new);
1332 			return (-1);
1333 		}
1334 		*rpad = new;
1335 	}
1336 
1337 	/*
1338 	 * We need to process the property groups before enumerating any
1339 	 * dependents as that enuemration can itself have dependencies on
1340 	 * properties set on the parent node.
1341 	 */
1342 	if (new->tpad_pgcnt > 0)
1343 		if (pgroups_create(mp, new, ptn) < 0)
1344 			return (-1);
1345 
1346 	/*
1347 	 * If an enum-method element was found, AND we're a child of a
1348 	 * node element, then we invoke the enumerator so that it can do
1349 	 * post-processing of the node.
1350 	 */
1351 	if (ecnt && (strcmp((const char *)pxn->name, Node) == 0)) {
1352 		if ((tmp_rd.rd_einfo = enum_attributes_process(mp, ecn))
1353 		    == NULL)
1354 			return (-1);
1355 		tmp_rd.rd_mod = mp;
1356 		tmp_rd.rd_name = rd->rd_name;
1357 		tmp_rd.rd_min = rd->rd_min;
1358 		tmp_rd.rd_max = rd->rd_max;
1359 		tmp_rd.rd_pn = ptn;
1360 		if (enum_run(mp, &tmp_rd) < 0) {
1361 			/*
1362 			 * Note the failure but continue on
1363 			 */
1364 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1365 			    "pad_process: enumeration failed.\n");
1366 		}
1367 		tf_edata_free(mp, tmp_rd.rd_einfo);
1368 	}
1369 
1370 	if (new->tpad_dcnt > 0)
1371 		if (dependents_create(mp, rd->rd_finfo, new, pxn, ptn) < 0)
1372 			return (-1);
1373 
1374 	return (0);
1375 }
1376 
1377 
1378 static int
fac_enum_process(topo_mod_t * mp,xmlNodePtr pn,tnode_t * ptn)1379 fac_enum_process(topo_mod_t *mp, xmlNodePtr pn, tnode_t *ptn)
1380 {
1381 	xmlNodePtr cn;
1382 	xmlChar *fprov = NULL;
1383 	int rv = 0;
1384 
1385 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1386 	    "fac_enum_process() called for %s=%d\n", topo_node_name(ptn),
1387 	    topo_node_instance(ptn));
1388 
1389 	for (cn = pn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1390 
1391 		if (xmlStrcmp(cn->name, (xmlChar *)"fac-enum") != 0)
1392 			continue;
1393 
1394 		if ((fprov = xmlGetProp(cn, (xmlChar *)Provider)) == NULL)
1395 			goto fenumdone;
1396 		/*
1397 		 * Invoke enum entry point in facility provider which will
1398 		 * cause the facility enumeration node method to be
1399 		 * registered.
1400 		 */
1401 		if (fac_enum_run(mp, ptn, (const char *)fprov) != 0) {
1402 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1403 			    "fac_enum_process: enum entry point failed!\n");
1404 			goto fenumdone;
1405 		}
1406 		xmlFree(fprov);
1407 	}
1408 	return (0);
1409 fenumdone:
1410 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "fac-enum processing failed\n");
1411 
1412 	if (fprov != NULL)
1413 		xmlFree(fprov);
1414 
1415 	return (rv);
1416 }
1417 
1418 
1419 static int
fac_process(topo_mod_t * mp,xmlNodePtr pn,tf_rdata_t * rd,tnode_t * ptn)1420 fac_process(topo_mod_t *mp, xmlNodePtr pn, tf_rdata_t *rd, tnode_t *ptn)
1421 {
1422 	xmlNodePtr cn;
1423 	xmlChar *fname = NULL, *ftype = NULL, *provider = NULL;
1424 	tnode_t *ntn = NULL;
1425 	tf_idata_t *newi;
1426 	int err;
1427 	topo_pgroup_info_t pgi;
1428 
1429 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1430 	    "fac_process() called for %s=%d\n", topo_node_name(ptn),
1431 	    topo_node_instance(ptn));
1432 
1433 	for (cn = pn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1434 
1435 		if (xmlStrcmp(cn->name, (xmlChar *)Facility) != 0)
1436 			continue;
1437 
1438 		if ((fname = xmlGetProp(cn, (xmlChar *)Name)) == NULL)
1439 			goto facdone;
1440 
1441 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1442 		    "processing facility node '%s'\n", fname);
1443 
1444 		if ((ftype = xmlGetProp(cn, (xmlChar *)Type)) == NULL)
1445 			goto facdone;
1446 
1447 		if ((provider = xmlGetProp(cn, (xmlChar *)Provider)) == NULL)
1448 			goto facdone;
1449 
1450 		if (xmlStrcmp(ftype, (xmlChar *)Sensor) != 0 &&
1451 		    xmlStrcmp(ftype, (xmlChar *)Indicator) != 0)
1452 			goto facdone;
1453 
1454 		if ((ntn = topo_node_facbind(mp, ptn, (char *)fname,
1455 		    (char *)ftype)) == NULL)
1456 			goto facdone;
1457 
1458 		pgi.tpi_name = TOPO_PGROUP_FACILITY;
1459 		pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
1460 		pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
1461 		pgi.tpi_version = 1;
1462 		if (topo_pgroup_create(ntn, &pgi, &err) != 0) {
1463 			if (err != ETOPO_PROP_DEFD) {
1464 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1465 				    "pgroups create failure: %s\n",
1466 				    topo_strerror(err));
1467 				return (-1);
1468 			}
1469 		}
1470 		/*
1471 		 * Invoke enum entry point in the facility provider module,
1472 		 * which will cause the provider methods to be registered on
1473 		 * this node
1474 		 */
1475 		if (fac_enum_run(mp, ntn, (const char *)provider) != 0) {
1476 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "fac_process: "
1477 			    "enum entry point failed for provider %s!\n",
1478 			    provider);
1479 			goto facdone;
1480 		}
1481 
1482 		if ((newi = tf_idata_new(mp, 0, ntn)) == NULL)
1483 			goto facdone;
1484 
1485 		if (tf_idata_insert(&rd->rd_instances, newi) < 0)
1486 			goto facdone;
1487 
1488 		if (pad_process(mp, rd, cn, ntn, &newi->ti_pad) < 0)
1489 			goto facdone;
1490 
1491 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "done with "
1492 		    "facility %s=%s.\n", ftype, fname);
1493 
1494 		xmlFree(ftype);
1495 		xmlFree(fname);
1496 		xmlFree(provider);
1497 	}
1498 
1499 	return (0);
1500 
1501 facdone:
1502 	topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "facility processing failed\n");
1503 
1504 	if (ftype != NULL)
1505 		xmlFree(ftype);
1506 	if (fname != NULL)
1507 		xmlFree(fname);
1508 	if (provider != NULL)
1509 		xmlFree(provider);
1510 	if (ntn != NULL)
1511 		topo_node_unbind(ntn);
1512 
1513 	return (0);
1514 }
1515 
1516 static int
node_process(topo_mod_t * mp,xmlNodePtr nn,tf_rdata_t * rd)1517 node_process(topo_mod_t *mp, xmlNodePtr nn, tf_rdata_t *rd)
1518 {
1519 	xmlChar *str;
1520 	topo_instance_t inst;
1521 	tf_idata_t *newi;
1522 	tnode_t *ntn;
1523 	uint64_t ui;
1524 	int rv = -1;
1525 	int s = 0;
1526 
1527 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1528 	    "node_process %s\n", rd->rd_name);
1529 
1530 	if (xmlattr_to_int(mp, nn, Instance, &ui) < 0)
1531 		goto nodedone;
1532 	inst = (topo_instance_t)ui;
1533 
1534 	if ((str = xmlGetProp(nn, (xmlChar *)Static)) != NULL) {
1535 		if (xmlStrcmp(str, (xmlChar *)True) == 0)
1536 			s = 1;
1537 		xmlFree(str);
1538 	}
1539 
1540 	if (s == 0) {
1541 		if (topo_mod_enumerate(rd->rd_mod, rd->rd_pn,
1542 		    rd->rd_finfo->tf_scheme, rd->rd_name, inst, inst,
1543 		    s == 1 ? &s : NULL) < 0)
1544 			goto nodedone;
1545 	}
1546 	ntn = topo_node_lookup(rd->rd_pn, rd->rd_name, inst);
1547 
1548 	if (ntn == NULL) {
1549 
1550 		/*
1551 		 * If this is a static node declaration, we can
1552 		 * ignore the lookup failure and continue
1553 		 * processing.  Otherwise, something
1554 		 * went wrong during enumeration
1555 		 */
1556 		if (s == 1)
1557 			rv = 0;
1558 		goto nodedone;
1559 	}
1560 	if ((newi = tf_idata_new(mp, inst, ntn)) == NULL) {
1561 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1562 		    "node_process: tf_idata_new failed.\n");
1563 		goto nodedone;
1564 	}
1565 	if (tf_idata_insert(&rd->rd_instances, newi) < 0) {
1566 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1567 		    "node_process: tf_idata_insert failed.\n");
1568 		goto nodedone;
1569 	}
1570 	if (pad_process(mp, rd, nn, ntn, &newi->ti_pad) < 0)
1571 		goto nodedone;
1572 	if (fac_process(mp, nn, rd, ntn) < 0)
1573 		goto nodedone;
1574 	rv = 0;
1575 nodedone:
1576 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "done with node %s.\n",
1577 	    rd->rd_name);
1578 	return (rv);
1579 }
1580 
1581 static tf_edata_t *
enum_attributes_process(topo_mod_t * mp,xmlNodePtr en)1582 enum_attributes_process(topo_mod_t *mp, xmlNodePtr en)
1583 {
1584 	tf_edata_t *einfo;
1585 	uint64_t ui;
1586 
1587 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enum_attributes_process\n");
1588 	if ((einfo = topo_mod_zalloc(mp, sizeof (tf_edata_t))) == NULL) {
1589 		(void) topo_mod_seterrno(mp, ETOPO_NOMEM);
1590 		return (NULL);
1591 	}
1592 	einfo->te_name = (char *)xmlGetProp(en, (xmlChar *)Name);
1593 	if (einfo->te_name == NULL) {
1594 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1595 		    "Enumerator name attribute missing.\n");
1596 		(void) topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
1597 		goto enodedone;
1598 	}
1599 
1600 	/*
1601 	 * Check for recursive enumeration
1602 	 */
1603 	if (strcmp(einfo->te_name, mp->tm_name) == 0) {
1604 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1605 		    "Recursive enumeration detected for %s\n",
1606 		    einfo->te_name);
1607 		(void) topo_mod_seterrno(mp, ETOPO_ENUM_RECURS);
1608 		goto enodedone;
1609 	}
1610 	if (xmlattr_to_int(mp, en, Version, &ui) < 0)
1611 		goto enodedone;
1612 	einfo->te_vers = (int)ui;
1613 
1614 	return (einfo);
1615 
1616 enodedone:
1617 	if (einfo->te_name != NULL)
1618 		xmlFree(einfo->te_name);
1619 	topo_mod_free(mp, einfo, sizeof (tf_edata_t));
1620 	return (NULL);
1621 }
1622 
1623 static int
enum_run(topo_mod_t * mp,tf_rdata_t * rd)1624 enum_run(topo_mod_t *mp, tf_rdata_t *rd)
1625 {
1626 	topo_hdl_t *thp = mp->tm_hdl;
1627 	int e = -1;
1628 
1629 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enum_run\n");
1630 	/*
1631 	 * Check if the enumerator module is already loaded.
1632 	 * Module loading is single-threaded at this point so there's
1633 	 * no need to worry about the module going away or bumping the
1634 	 * ref count.
1635 	 */
1636 	if ((rd->rd_mod = topo_mod_lookup(thp, rd->rd_einfo->te_name,
1637 	    0)) == NULL) {
1638 		if ((rd->rd_mod = topo_mod_load(mp, rd->rd_einfo->te_name,
1639 		    rd->rd_einfo->te_vers)) == NULL) {
1640 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1641 			    "enum_run: mod_load of %s failed: %s.\n",
1642 			    rd->rd_einfo->te_name,
1643 			    topo_strerror(topo_mod_errno(mp)));
1644 			(void) topo_hdl_seterrno(thp, topo_mod_errno(mp));
1645 			return (e);
1646 		}
1647 	}
1648 	/*
1649 	 * We're live, so let's enumerate.
1650 	 */
1651 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enumerate request. (%s)\n",
1652 	    rd->rd_einfo->te_name);
1653 	e = topo_mod_enumerate(rd->rd_mod, rd->rd_pn, rd->rd_einfo->te_name,
1654 	    rd->rd_name, rd->rd_min, rd->rd_max, NULL);
1655 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "back from enumeration. %d\n",
1656 	    e);
1657 	if (e != 0) {
1658 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1659 		    "Enumeration failed (%s)\n",
1660 		    topo_strerror(topo_mod_errno(rd->rd_mod)));
1661 		(void) topo_hdl_seterrno(thp, EMOD_PARTIAL_ENUM);
1662 		return (topo_mod_seterrno(mp, EMOD_PARTIAL_ENUM));
1663 	}
1664 	return (e);
1665 }
1666 
1667 static int
fac_enum_run(topo_mod_t * mp,tnode_t * node,const char * name)1668 fac_enum_run(topo_mod_t *mp, tnode_t *node, const char *name)
1669 {
1670 	topo_hdl_t *thp = mp->tm_hdl;
1671 	topo_mod_t *fmod;
1672 	int e = -1;
1673 
1674 	topo_dprintf(thp, TOPO_DBG_XML, "fac_enum_run\n");
1675 	/*
1676 	 * Check if the enumerator module is already loaded.
1677 	 * Module loading is single-threaded at this point so there's
1678 	 * no need to worry about the module going away or bumping the
1679 	 * ref count.
1680 	 */
1681 	if ((fmod = topo_mod_lookup(thp, name, 0)) == NULL) {
1682 		if ((fmod = topo_mod_load(mp, name, TOPO_VERSION)) == NULL) {
1683 			topo_dprintf(thp, TOPO_DBG_ERR,
1684 			    "fac_enum_run: mod_load of %s failed: %s.\n",
1685 			    name, topo_strerror(topo_mod_errno(mp)));
1686 			(void) topo_hdl_seterrno(thp, topo_mod_errno(mp));
1687 			return (e);
1688 		}
1689 	}
1690 	/*
1691 	 * We're live, so let's enumerate.
1692 	 */
1693 	topo_dprintf(thp, TOPO_DBG_XML, "fac enumerate request. (%s)\n", name);
1694 	e = topo_mod_enumerate(fmod, node, name, name, 0, 0, NULL);
1695 	topo_dprintf(thp, TOPO_DBG_XML, "back from enumeration. %d\n", e);
1696 	if (e != 0) {
1697 		topo_dprintf(thp, TOPO_DBG_ERR,
1698 		    "Facility provider enumeration failed (%s)\n",
1699 		    topo_strerror(topo_mod_errno(fmod)));
1700 		(void) topo_hdl_seterrno(thp, EMOD_PARTIAL_ENUM);
1701 		return (topo_mod_seterrno(mp, EMOD_PARTIAL_ENUM));
1702 	}
1703 	return (e);
1704 }
1705 
1706 int
decorate_nodes(topo_mod_t * mp,tf_rdata_t * rd,xmlNodePtr pxn,tnode_t * ptn,tf_pad_t ** rpad)1707 decorate_nodes(topo_mod_t *mp, tf_rdata_t *rd, xmlNodePtr pxn, tnode_t *ptn,
1708     tf_pad_t **rpad)
1709 {
1710 	tnode_t *ctn;
1711 
1712 	ctn = topo_child_first(ptn);
1713 	while (ctn != NULL) {
1714 		/* Only care about instances within the range */
1715 		if (strcmp(topo_node_name(ctn), rd->rd_name) != 0) {
1716 			ctn = topo_child_next(ptn, ctn);
1717 			continue;
1718 		}
1719 		if (pad_process(mp, rd, pxn, ctn, rpad) < 0)
1720 			return (-1);
1721 		if (decorate_nodes(mp, rd, pxn, ctn, rpad) < 0)
1722 			return (-1);
1723 		ctn = topo_child_next(ptn, ctn);
1724 	}
1725 	return (0);
1726 }
1727 
1728 int
topo_xml_range_process(topo_mod_t * mp,xmlNodePtr rn,tf_rdata_t * rd)1729 topo_xml_range_process(topo_mod_t *mp, xmlNodePtr rn, tf_rdata_t *rd)
1730 {
1731 	/*
1732 	 * The range may have several children xmlNodes, that may
1733 	 * represent the enumeration method, property groups,
1734 	 * dependents, nodes or services.
1735 	 */
1736 	xmlNodePtr cn, enum_node = NULL, pmap_node = NULL;
1737 	xmlChar *pmap_name;
1738 	tnode_t *ct;
1739 	int e, ccnt = 0;
1740 
1741 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_range_process\n"
1742 	    "process %s range beneath %s\n", rd->rd_name,
1743 	    topo_node_name(rd->rd_pn));
1744 
1745 	e = topo_node_range_create(mp,
1746 	    rd->rd_pn, rd->rd_name, rd->rd_min, rd->rd_max);
1747 	if (e != 0 && topo_mod_errno(mp) != EMOD_NODE_DUP) {
1748 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1749 		    "Range create failed due to %s.\n",
1750 		    topo_strerror(topo_mod_errno(mp)));
1751 		return (-1);
1752 	}
1753 
1754 	/*
1755 	 * Before we process any of the other child xmlNodes, we iterate through
1756 	 * the children and looking for either enum-method or propmap elements.
1757 	 */
1758 	for (cn = rn->xmlChildrenNode; cn != NULL; cn = cn->next)
1759 		if (xmlStrcmp(cn->name, (xmlChar *)Enum_meth) == 0)
1760 			enum_node = cn;
1761 		else if (xmlStrcmp(cn->name, (xmlChar *)Propmap) == 0)
1762 			pmap_node = cn;
1763 
1764 	/*
1765 	 * If we found an enum-method element, process it first
1766 	 */
1767 	if (enum_node != NULL) {
1768 		if ((rd->rd_einfo = enum_attributes_process(mp, enum_node))
1769 		    == NULL)
1770 			return (-1);
1771 		if (enum_run(mp, rd) < 0) {
1772 			/*
1773 			 * Note the failure but continue on
1774 			 */
1775 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1776 			    "Enumeration failed.\n");
1777 		}
1778 	}
1779 
1780 	/*
1781 	 * Next, check if a propmap element was found and if so, load it in
1782 	 * and parse it.
1783 	 */
1784 	if (pmap_node != NULL) {
1785 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "found a propmap "
1786 		    "element\n");
1787 		if ((pmap_name = xmlGetProp(pmap_node, (xmlChar *)Name))
1788 		    == NULL) {
1789 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1790 			    "propmap element missing name attribute.\n");
1791 		} else {
1792 			if (topo_file_load(mp, rd->rd_pn,
1793 			    (const char *)pmap_name,
1794 			    rd->rd_finfo->tf_scheme, 1) < 0) {
1795 
1796 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1797 				    "topo_xml_range_process: topo_file_load"
1798 				    "failed: %s.\n",
1799 				    topo_strerror(topo_mod_errno(mp)));
1800 			}
1801 			xmlFree(pmap_name);
1802 		}
1803 	}
1804 
1805 	/* Now look for nodes, i.e., hard instances */
1806 	for (cn = rn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1807 		if (xmlStrcmp(cn->name, (xmlChar *)Node) == 0) {
1808 			if (node_process(mp, cn, rd) < 0) {
1809 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1810 				    "node processing failed: %s.\n",
1811 				    topo_strerror(topo_mod_errno(mp)));
1812 				return (topo_mod_seterrno(mp,
1813 				    EMOD_PARTIAL_ENUM));
1814 			}
1815 			ccnt++;
1816 		}
1817 	}
1818 
1819 	/*
1820 	 * Finally, process the property groups and dependents
1821 	 *
1822 	 * If the TF_PROPMAP flag is set for the XML file we're currently
1823 	 * processing, then this XML file was loaded via propmap.  In that case
1824 	 * we call a special routine to recursively apply the propgroup settings
1825 	 * to all of nodes in this range
1826 	 */
1827 	if (rd->rd_finfo->tf_flags & TF_PROPMAP)
1828 		(void) decorate_nodes(mp, rd, rn, rd->rd_pn, &rd->rd_pad);
1829 	else {
1830 		ct = topo_child_first(rd->rd_pn);
1831 		while (ct != NULL) {
1832 			/* Only care about instances within the range */
1833 			if (strcmp(topo_node_name(ct), rd->rd_name) != 0) {
1834 				ct = topo_child_next(rd->rd_pn, ct);
1835 				continue;
1836 			}
1837 			if (pad_process(mp, rd, rn, ct, &rd->rd_pad)
1838 			    < 0)
1839 				return (-1);
1840 
1841 			if (fac_process(mp, rn, rd, ct) < 0)
1842 				return (-1);
1843 
1844 			ct = topo_child_next(rd->rd_pn, ct);
1845 			ccnt++;
1846 		}
1847 	}
1848 
1849 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_range_process: end "
1850 	    "range process %s\n", rd->rd_name);
1851 
1852 	return (0);
1853 }
1854 
1855 static tf_rdata_t *
topo_xml_walk(topo_mod_t * mp,tf_info_t * xinfo,xmlNodePtr croot,tnode_t * troot)1856 topo_xml_walk(topo_mod_t *mp,
1857     tf_info_t *xinfo, xmlNodePtr croot, tnode_t *troot)
1858 {
1859 	xmlNodePtr curr, def_set = NULL;
1860 	tf_rdata_t *rr, *pr, *rdp;
1861 	xmlChar *set;
1862 	char *key;
1863 	int joined_set = 0;
1864 
1865 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_walk\n");
1866 	rr = pr = NULL;
1867 	/*
1868 	 * First iterate through all the XML nodes at this level to look for
1869 	 * set nodes.
1870 	 */
1871 	for (curr = croot->xmlChildrenNode; curr != NULL; curr = curr->next) {
1872 		if (curr->name == NULL) {
1873 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1874 			    "topo_xml_walk: Ignoring nameless xmlnode\n");
1875 			continue;
1876 		}
1877 		if (xmlStrcmp(curr->name, (xmlChar *)Set) == 0) {
1878 			if (joined_set)
1879 				continue;
1880 
1881 			set = xmlGetProp(curr, (xmlChar *)Setlist);
1882 
1883 			key = mp->tm_hdl->th_product;
1884 
1885 			/*
1886 			 * If it's the default set then we'll store
1887 			 * a pointer to it so that if none of the other
1888 			 * sets apply to our product we can fall
1889 			 * back to this one.
1890 			 */
1891 			if (strcmp((char *)set, "default") == 0)
1892 				def_set = curr;
1893 			else if (set_contains(mp, key, (char *)set)) {
1894 				joined_set = 1;
1895 				if ((rdp = topo_xml_walk(mp, xinfo, curr,
1896 				    troot)) == NULL) {
1897 					topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1898 					    "topo_xml_walk: failed1\n");
1899 				} else {
1900 					if (pr == NULL) {
1901 						rr = pr = rdp;
1902 					} else {
1903 						pr->rd_next = rdp;
1904 						pr = rdp;
1905 					}
1906 					rr->rd_cnt++;
1907 				}
1908 			}
1909 			xmlFree(set);
1910 		}
1911 	}
1912 	/*
1913 	 * If we haven't found a set that contains our product AND a default set
1914 	 * exists, then we'll process it.
1915 	 */
1916 	if (!joined_set && def_set) {
1917 		if ((rdp = topo_xml_walk(mp, xinfo, def_set, troot)) == NULL) {
1918 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1919 			    "topo_xml_walk: failed2\n");
1920 		}
1921 		if (pr == NULL) {
1922 			rr = pr = rdp;
1923 		} else {
1924 			pr->rd_next = rdp;
1925 			pr = rdp;
1926 		}
1927 		rr->rd_cnt++;
1928 	}
1929 	/*
1930 	 * Now we're interested in children xmlNodes of croot tagged
1931 	 * as 'ranges'.  These define what topology nodes may exist, and need
1932 	 * to be verified.
1933 	 */
1934 	for (curr = croot->xmlChildrenNode; curr != NULL; curr = curr->next) {
1935 		if (curr->name == NULL) {
1936 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1937 			    "topo_xml_walk: Ignoring nameless xmlnode\n");
1938 			continue;
1939 		}
1940 		if (xmlStrcmp(curr->name, (xmlChar *)Range) != 0)
1941 			continue;
1942 		if ((rdp = tf_rdata_new(mp, xinfo, curr, troot)) == NULL) {
1943 			/*
1944 			 * Range processing error, continue walk
1945 			 */
1946 			continue;
1947 		}
1948 		if (pr == NULL) {
1949 			rr = pr = rdp;
1950 		} else {
1951 			pr->rd_next = rdp;
1952 			pr = rdp;
1953 		}
1954 		rr->rd_cnt++;
1955 	}
1956 
1957 	return (rr);
1958 }
1959 
1960 /*
1961  *  Convert parsed xml topology description into topology nodes
1962  */
1963 int
topo_xml_enum(topo_mod_t * tmp,tf_info_t * xinfo,tnode_t * troot)1964 topo_xml_enum(topo_mod_t *tmp, tf_info_t *xinfo, tnode_t *troot)
1965 {
1966 	xmlNodePtr xroot;
1967 
1968 	topo_dprintf(tmp->tm_hdl, TOPO_DBG_XML, "topo_xml_enum\n");
1969 
1970 	if ((xroot = xmlDocGetRootElement(xinfo->tf_xdoc)) == NULL) {
1971 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1972 		    "Couldn't get root xmlNode.\n");
1973 		return (-1);
1974 	}
1975 	if ((xinfo->tf_rd = topo_xml_walk(tmp, xinfo, xroot, troot)) == NULL) {
1976 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1977 		    "error within .xml topology: %s\n",
1978 		    topo_strerror(topo_mod_errno(tmp)));
1979 		return (-1);
1980 	}
1981 	return (0);
1982 }
1983 
1984 /*
1985  * Load an XML tree from filename and read it into a DOM parse tree.
1986  */
1987 static tf_info_t *
txml_file_parse(topo_mod_t * tmp,int fd,const char * filenm,const char * escheme)1988 txml_file_parse(topo_mod_t *tmp,
1989     int fd, const char *filenm, const char *escheme)
1990 {
1991 	xmlValidCtxtPtr vcp;
1992 	xmlNodePtr cursor;
1993 	xmlDocPtr document;
1994 	xmlDtdPtr dtd = NULL;
1995 	xmlChar *scheme = NULL;
1996 	char *dtdpath = NULL;
1997 	int readflags = 0;
1998 	tf_info_t *r;
1999 	int e, validate = 0;
2000 
2001 	topo_dprintf(tmp->tm_hdl, TOPO_DBG_XML,
2002 	    "txml_file_parse(filenm=%s, escheme=%s)\n", filenm, escheme);
2003 
2004 	/*
2005 	 * Since topologies can XInclude other topologies, and libxml2
2006 	 * doesn't do DTD-based validation with XInclude, by default
2007 	 * we don't validate topology files.  One can force
2008 	 * validation, though, by creating a TOPOXML_VALIDATE
2009 	 * environment variable and creating a TOPO_DTD environment
2010 	 * variable with the path to the DTD against which to validate.
2011 	 */
2012 	if (getenv("TOPOXML_VALIDATE") != NULL) {
2013 		dtdpath = getenv("TOPO_DTD");
2014 		validate = 1;
2015 	}
2016 
2017 	/*
2018 	 * Splat warnings and errors related to parsing the topology
2019 	 * file if the TOPOXML_PERROR environment variable exists.
2020 	 */
2021 	if (getenv("TOPOXML_PERROR") == NULL)
2022 		readflags = XML_PARSE_NOERROR | XML_PARSE_NOWARNING;
2023 
2024 	if ((document = xmlReadFd(fd, filenm, NULL, readflags)) == NULL) {
2025 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2026 		    "txml_file_parse: couldn't parse document.\n");
2027 		return (NULL);
2028 	}
2029 
2030 	/*
2031 	 * Verify that this is a document type we understand.
2032 	 */
2033 	if ((dtd = xmlGetIntSubset(document)) == NULL) {
2034 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2035 		    "document has no DTD.\n");
2036 		xmlFreeDoc(document);
2037 		return (NULL);
2038 	}
2039 
2040 	if (strcmp((const char *)dtd->SystemID, TOPO_DTD_PATH) != 0) {
2041 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2042 		    "document DTD unknown; bad topology file\n");
2043 		xmlFreeDoc(document);
2044 		return (NULL);
2045 	}
2046 
2047 	if ((cursor = xmlDocGetRootElement(document)) == NULL) {
2048 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, "document is empty.\n");
2049 		xmlFreeDoc(document);
2050 		return (NULL);
2051 	}
2052 
2053 	/*
2054 	 * Make sure we're looking at a topology description in the
2055 	 * expected scheme.
2056 	 */
2057 	if (xmlStrcmp(cursor->name, (xmlChar *)Topology) != 0) {
2058 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2059 		    "document is not a topology description.\n");
2060 		xmlFreeDoc(document);
2061 		return (NULL);
2062 	}
2063 	if ((scheme = xmlGetProp(cursor, (xmlChar *)Scheme)) == NULL) {
2064 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2065 		    "topology lacks a scheme.\n");
2066 		(void) topo_mod_seterrno(tmp, ETOPO_PRSR_NOATTR);
2067 		xmlFreeDoc(document);
2068 		return (NULL);
2069 	}
2070 	if (xmlStrcmp(scheme, (xmlChar *)escheme) != 0) {
2071 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2072 		    "topology in unrecognized scheme, %s, expecting %s\n",
2073 		    scheme, escheme);
2074 		(void) topo_mod_seterrno(tmp, ETOPO_PRSR_BADSCH);
2075 		xmlFree(scheme);
2076 		xmlFreeDoc(document);
2077 		return (NULL);
2078 	}
2079 
2080 	if (dtdpath != NULL) {
2081 		dtd = xmlParseDTD(NULL, (xmlChar *)dtdpath);
2082 		if (dtd == NULL) {
2083 			topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2084 			    "Could not parse DTD \"%s\".\n",
2085 			    dtdpath);
2086 			xmlFree(scheme);
2087 			xmlFreeDoc(document);
2088 			return (NULL);
2089 		}
2090 
2091 		if (document->extSubset != NULL)
2092 			xmlFreeDtd(document->extSubset);
2093 
2094 		document->extSubset = dtd;
2095 	}
2096 
2097 	if (xmlXIncludeProcessFlags(document, XML_PARSE_XINCLUDE) == -1) {
2098 		xmlFree(scheme);
2099 		xmlFreeDoc(document);
2100 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2101 		    "couldn't handle XInclude statements in document\n");
2102 		return (NULL);
2103 	}
2104 
2105 	if (validate) {
2106 		if ((vcp = xmlNewValidCtxt()) == NULL) {
2107 			xmlFree(scheme);
2108 			xmlFreeDoc(document);
2109 			return (NULL);
2110 		}
2111 		vcp->warning = xmlParserValidityWarning;
2112 		vcp->error = xmlParserValidityError;
2113 
2114 		e = xmlValidateDocument(vcp, document);
2115 
2116 		xmlFreeValidCtxt(vcp);
2117 
2118 		if (e == 0)
2119 			topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2120 			    "Document is not valid.\n");
2121 	}
2122 
2123 	if ((r = tf_info_new(tmp, document, scheme)) == NULL) {
2124 		xmlFree(scheme);
2125 		xmlFreeDoc(document);
2126 		return (NULL);
2127 	}
2128 
2129 	xmlFree(scheme);
2130 	scheme = NULL;
2131 	return (r);
2132 }
2133 
2134 tf_info_t *
topo_xml_read(topo_mod_t * tmp,const char * path,const char * escheme)2135 topo_xml_read(topo_mod_t *tmp, const char *path, const char *escheme)
2136 {
2137 	int fd;
2138 	tf_info_t *tip;
2139 
2140 	if ((fd = open(path, O_RDONLY)) < 0) {
2141 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2142 		    "failed to open %s for reading\n", path);
2143 		return (NULL);
2144 	}
2145 	tip = txml_file_parse(tmp, fd, path, escheme);
2146 	(void) close(fd);
2147 	return (tip);
2148 }
2149