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