1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2020 Joyent, Inc.
14  */
15 
16 /*
17  * This file implements the following two routines for serializing and
18  * deserializing digraphs to/from XML, respectively:
19  *
20  * topo_digraph_serialize()
21  * topo_digraph_deserialize()
22  *
23  * Refer to the following file for the XML schema being used:
24  * usr/src/lib/fm/topo/maps/common/digraph-topology.dtd.1
25  */
26 #include <time.h>
27 #include <sys/utsname.h>
28 #include <libxml/parser.h>
29 #include <libtopo.h>
30 
31 #include <topo_digraph.h>
32 #include <topo_digraph_xml.h>
33 
34 #define	__STDC_FORMAT_MACROS
35 #include <inttypes.h>
36 
37 extern int xmlattr_to_int(topo_mod_t *, xmlNodePtr, const char *, uint64_t *);
38 static int serialize_nvpair(topo_hdl_t *thp, FILE *, uint_t, const char *,
39     nvpair_t *);
40 
41 static void
tdg_xml_nvstring(FILE * fp,uint_t pad,const char * name,const char * value)42 tdg_xml_nvstring(FILE *fp, uint_t pad, const char *name, const char *value)
43 {
44 	(void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%s' />\n", pad, "",
45 	    TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_STRING,
46 	    TDG_XML_VALUE, value);
47 }
48 
49 static void
tdg_xml_nvlist(FILE * fp,uint_t pad,const char * name)50 tdg_xml_nvlist(FILE *fp, uint_t pad, const char *name)
51 {
52 	(void) fprintf(fp, "%*s<%s %s='%s' %s='%s'>\n", pad, "",
53 	    TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_NVLIST);
54 }
55 
56 static void
tdg_xml_nvuint8(FILE * fp,uint_t pad,const char * name,const uint8_t value)57 tdg_xml_nvuint8(FILE *fp, uint_t pad, const char *name, const uint8_t value)
58 {
59 	(void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%u' />\n", pad, "",
60 	    TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_UINT8,
61 	    TDG_XML_VALUE, value);
62 }
63 
64 static void
tdg_xml_nvint8(FILE * fp,uint_t pad,const char * name,const uint8_t value)65 tdg_xml_nvint8(FILE *fp, uint_t pad, const char *name, const uint8_t value)
66 {
67 	(void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%d' />\n", pad, "",
68 	    TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_INT8,
69 	    TDG_XML_VALUE, value);
70 }
71 
72 static void
tdg_xml_nvuint16(FILE * fp,uint_t pad,const char * name,const uint8_t value)73 tdg_xml_nvuint16(FILE *fp, uint_t pad, const char *name, const uint8_t value)
74 {
75 	(void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%u' />\n", pad, "",
76 	    TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_UINT16,
77 	    TDG_XML_VALUE, value);
78 }
79 
80 static void
tdg_xml_nvint16(FILE * fp,uint_t pad,const char * name,const uint8_t value)81 tdg_xml_nvint16(FILE *fp, uint_t pad, const char *name, const uint8_t value)
82 {
83 	(void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%d' />\n", pad, "",
84 	    TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_INT16,
85 	    TDG_XML_VALUE, value);
86 }
87 
88 static void
tdg_xml_nvuint32(FILE * fp,uint_t pad,const char * name,const uint32_t value)89 tdg_xml_nvuint32(FILE *fp, uint_t pad, const char *name, const uint32_t value)
90 {
91 	(void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%u' />\n", pad, "",
92 	    TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_UINT32,
93 	    TDG_XML_VALUE, value);
94 }
95 
96 static void
tdg_xml_nvint32(FILE * fp,uint_t pad,const char * name,const int32_t value)97 tdg_xml_nvint32(FILE *fp, uint_t pad, const char *name, const int32_t value)
98 {
99 	(void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%d' />\n", pad, "",
100 	    TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_UINT32,
101 	    TDG_XML_VALUE, value);
102 }
103 
104 static void
tdg_xml_nvuint64(FILE * fp,uint_t pad,const char * name,const uint64_t value)105 tdg_xml_nvuint64(FILE *fp, uint_t pad, const char *name, const uint64_t value)
106 {
107 	(void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='0x%" PRIx64 "' />\n",
108 	    pad, "", TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE,
109 	    TDG_XML_UINT64, TDG_XML_VALUE, value);
110 }
111 
112 static void
tdg_xml_nvint64(FILE * fp,uint_t pad,const char * name,const int64_t value)113 tdg_xml_nvint64(FILE *fp, uint_t pad, const char *name, const int64_t value)
114 {
115 	(void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%" PRIi64 "' />\n", pad,
116 	    "", TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE,
117 	    TDG_XML_UINT64, TDG_XML_VALUE, value);
118 }
119 
120 static void
tdg_xml_nvdbl(FILE * fp,uint_t pad,const char * name,const double value)121 tdg_xml_nvdbl(FILE *fp, uint_t pad, const char *name, const double value)
122 {
123 	(void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%lf' />\n", pad, ""
124 	    TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_UINT64,
125 	    TDG_XML_VALUE, value);
126 }
127 
128 static void
tdg_xml_nvarray(FILE * fp,uint_t pad,const char * name,const char * type)129 tdg_xml_nvarray(FILE *fp, uint_t pad, const char *name, const char *type)
130 {
131 	(void) fprintf(fp, "%*s<%s %s='%s' %s='%s'>\n", pad, "",
132 	    TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, type);
133 }
134 
135 static void
tdg_xml_nvint32arr(FILE * fp,uint_t pad,const char * name,int32_t * val,uint_t nelems)136 tdg_xml_nvint32arr(FILE *fp, uint_t pad, const char *name, int32_t *val,
137     uint_t nelems)
138 {
139 	(void) fprintf(fp, "%*s<%s %s='%s' %s='%s'>\n", pad, "",
140 	    TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE,
141 	    TDG_XML_INT32_ARR);
142 
143 	for (uint_t i = 0; i < nelems; i++) {
144 		(void) fprintf(fp, "%*s<%s %s='%d' />\n", (pad + 2), "",
145 		    TDG_XML_NVPAIR, TDG_XML_VALUE, val[i]);
146 	}
147 	(void) fprintf(fp, "%*s</%s>\n", pad, "", TDG_XML_NVPAIR);
148 }
149 
150 static void
tdg_xml_nvuint32arr(FILE * fp,uint_t pad,const char * name,uint32_t * val,uint_t nelems)151 tdg_xml_nvuint32arr(FILE *fp, uint_t pad, const char *name, uint32_t *val,
152     uint_t nelems)
153 {
154 	(void) fprintf(fp, "%*s<%s %s='%s' %s='%s'>\n", pad, "",
155 	    TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE,
156 	    TDG_XML_UINT32_ARR);
157 
158 	for (uint_t i = 0; i < nelems; i++) {
159 		(void) fprintf(fp, "%*s<%s %s='%d' />\n", (pad + 2), "",
160 		    TDG_XML_NVPAIR, TDG_XML_VALUE, val[i]);
161 	}
162 	(void) fprintf(fp, "%*s</%s>\n", pad, "", TDG_XML_NVPAIR);
163 }
164 
165 static void
tdg_xml_nvint64arr(FILE * fp,uint_t pad,const char * name,int64_t * val,uint_t nelems)166 tdg_xml_nvint64arr(FILE *fp, uint_t pad, const char *name, int64_t *val,
167     uint_t nelems)
168 {
169 	(void) fprintf(fp, "%*s<%s %s='%s' %s='%s'>\n", pad, "",
170 	    TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE,
171 	    TDG_XML_INT64_ARR);
172 
173 	for (uint_t i = 0; i < nelems; i++) {
174 		(void) fprintf(fp, "%*s<%s %s='%" PRIi64 "' />\n", (pad + 2),
175 		    "", TDG_XML_NVPAIR, TDG_XML_VALUE, val[i]);
176 	}
177 	(void) fprintf(fp, "%*s</%s>\n", pad, "", TDG_XML_NVPAIR);
178 }
179 
180 static void
tdg_xml_nvuint64arr(FILE * fp,uint_t pad,const char * name,uint64_t * val,uint_t nelems)181 tdg_xml_nvuint64arr(FILE *fp, uint_t pad, const char *name, uint64_t *val,
182     uint_t nelems)
183 {
184 	(void) fprintf(fp, "%*s<%s %s='%s' %s='%s'>\n", pad, "",
185 	    TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE,
186 	    TDG_XML_UINT64_ARR);
187 
188 	for (uint_t i = 0; i < nelems; i++) {
189 		(void) fprintf(fp, "%*s<%s %s='0x%" PRIx64 "' />\n", (pad + 2),
190 		    "", TDG_XML_NVPAIR, TDG_XML_VALUE, val[i]);
191 	}
192 	(void) fprintf(fp, "%*s</%s>\n", pad, "", TDG_XML_NVPAIR);
193 }
194 
195 static int
serialize_nvpair_nvlist(topo_hdl_t * thp,FILE * fp,uint_t pad,const char * name,nvlist_t * nvl)196 serialize_nvpair_nvlist(topo_hdl_t *thp, FILE *fp, uint_t pad,
197     const char *name, nvlist_t *nvl)
198 {
199 	nvpair_t *elem = NULL;
200 
201 	tdg_xml_nvlist(fp, pad, name);
202 
203 	(void) fprintf(fp, "%*s<%s>\n", pad, "", TDG_XML_NVLIST);
204 
205 	while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
206 		char *nvname = nvpair_name(elem);
207 
208 		if (serialize_nvpair(thp, fp, (pad + 2), nvname, elem) != 0) {
209 			/* errno set */
210 			return (-1);
211 		}
212 	}
213 
214 	(void) fprintf(fp, "%*s</%s>\n", pad, "", TDG_XML_NVLIST);
215 	(void) fprintf(fp, "%*s</%s> <!-- %s -->\n", pad, "", TDG_XML_NVPAIR,
216 	    name);
217 
218 	return (0);
219 }
220 
221 static int
serialize_nvpair(topo_hdl_t * thp,FILE * fp,uint_t pad,const char * pname,nvpair_t * nvp)222 serialize_nvpair(topo_hdl_t *thp, FILE *fp, uint_t pad, const char *pname,
223     nvpair_t *nvp)
224 {
225 	data_type_t type = nvpair_type(nvp);
226 
227 	switch (type) {
228 		case DATA_TYPE_INT8: {
229 			int8_t val;
230 
231 			if (nvpair_value_int8(nvp, &val) != 0)
232 				return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
233 
234 			tdg_xml_nvint8(fp, pad, pname, val);
235 			break;
236 		}
237 		case DATA_TYPE_UINT8: {
238 			uint8_t val;
239 
240 			if (nvpair_value_uint8(nvp, &val) != 0)
241 				return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
242 
243 			tdg_xml_nvuint8(fp, pad, pname, val);
244 			break;
245 		}
246 		case DATA_TYPE_INT16: {
247 			int16_t val;
248 
249 			if (nvpair_value_int16(nvp, &val) != 0)
250 				return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
251 
252 			tdg_xml_nvint16(fp, pad, pname, val);
253 			break;
254 		}
255 		case DATA_TYPE_UINT16: {
256 			uint16_t val;
257 
258 			if (nvpair_value_uint16(nvp, &val) != 0)
259 				return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
260 
261 			tdg_xml_nvuint16(fp, pad, pname, val);
262 			break;
263 		}
264 		case DATA_TYPE_INT32: {
265 			int32_t val;
266 
267 			if (nvpair_value_int32(nvp, &val) != 0)
268 				return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
269 
270 			tdg_xml_nvint32(fp, pad, pname, val);
271 			break;
272 		}
273 		case DATA_TYPE_UINT32: {
274 			uint32_t val;
275 
276 			if (nvpair_value_uint32(nvp, &val) != 0)
277 				return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
278 
279 			tdg_xml_nvuint32(fp, pad, pname, val);
280 			break;
281 		}
282 		case DATA_TYPE_INT64: {
283 			int64_t val;
284 
285 			if (nvpair_value_int64(nvp, &val) != 0)
286 				return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
287 
288 			tdg_xml_nvint64(fp, pad, pname, val);
289 			break;
290 		}
291 		case DATA_TYPE_UINT64: {
292 			uint64_t val;
293 
294 			if (nvpair_value_uint64(nvp, &val) != 0)
295 				return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
296 
297 			tdg_xml_nvuint64(fp, pad, pname, val);
298 			break;
299 		}
300 		case DATA_TYPE_DOUBLE: {
301 			double val;
302 
303 			if (nvpair_value_double(nvp, &val) != 0)
304 				return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
305 
306 			tdg_xml_nvdbl(fp, pad, pname, val);
307 			break;
308 		}
309 		case DATA_TYPE_STRING: {
310 			char *val;
311 
312 			if (nvpair_value_string(nvp, &val) != 0)
313 				return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
314 
315 			tdg_xml_nvstring(fp, pad, pname, val);
316 			break;
317 		}
318 		case DATA_TYPE_NVLIST: {
319 			nvlist_t *nvl;
320 
321 			if (nvpair_value_nvlist(nvp, &nvl) != 0)
322 				return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
323 
324 			if (serialize_nvpair_nvlist(thp, fp, pad + 2, pname,
325 			    nvl) != 0) {
326 				return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
327 			}
328 			break;
329 		}
330 		case DATA_TYPE_INT32_ARRAY: {
331 			uint_t nelems;
332 			int32_t *val;
333 
334 			if (nvpair_value_int32_array(nvp, &val, &nelems) != 0)
335 				return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
336 
337 			tdg_xml_nvint32arr(fp, pad + 2, pname, val, nelems);
338 
339 			break;
340 		}
341 		case DATA_TYPE_UINT32_ARRAY: {
342 			uint_t nelems;
343 			uint32_t *val;
344 
345 			if (nvpair_value_uint32_array(nvp, &val, &nelems) != 0)
346 				return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
347 
348 			tdg_xml_nvuint32arr(fp, pad + 2, pname,  val, nelems);
349 
350 			break;
351 		}
352 		case DATA_TYPE_INT64_ARRAY: {
353 			uint_t nelems;
354 			int64_t *val;
355 
356 			if (nvpair_value_int64_array(nvp, &val, &nelems) != 0)
357 				return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
358 
359 			tdg_xml_nvint64arr(fp, pad + 2, pname,  val, nelems);
360 
361 			break;
362 		}
363 		case DATA_TYPE_UINT64_ARRAY: {
364 			uint_t nelems;
365 			uint64_t *val;
366 
367 			if (nvpair_value_uint64_array(nvp, &val, &nelems) != 0)
368 				return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
369 
370 			tdg_xml_nvuint64arr(fp, pad + 2, pname,  val, nelems);
371 
372 			break;
373 		}
374 		case DATA_TYPE_STRING_ARRAY: {
375 			uint_t nelems;
376 			char **val;
377 
378 			if (nvpair_value_string_array(nvp, &val, &nelems) != 0)
379 				return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
380 
381 			tdg_xml_nvarray(fp, pad, pname, TDG_XML_STRING_ARR);
382 			for (uint_t i = 0; i < nelems; i++) {
383 				(void) fprintf(fp, "%*s<%s %s='%s' />\n",
384 				    (pad + 2), "", TDG_XML_NVPAIR,
385 				    TDG_XML_VALUE, val[i]);
386 			}
387 			(void) fprintf(fp, "%*s</%s>\n", (pad + 2), "",
388 			    TDG_XML_NVPAIR);
389 
390 			break;
391 		}
392 		case DATA_TYPE_NVLIST_ARRAY: {
393 			uint_t nelems;
394 			nvlist_t **val;
395 
396 			if (nvpair_value_nvlist_array(nvp, &val, &nelems) != 0)
397 				return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
398 
399 			tdg_xml_nvarray(fp, pad, pname, TDG_XML_NVLIST_ARR);
400 			for (uint_t i = 0; i < nelems; i++) {
401 				nvpair_t *elem = NULL;
402 
403 				(void) fprintf(fp, "%*s<%s>\n", (pad + 2), "",
404 				    TDG_XML_NVLIST);
405 
406 				while ((elem = nvlist_next_nvpair(val[i],
407 				    elem)) != NULL) {
408 					char *nvname = nvpair_name(elem);
409 
410 					if (serialize_nvpair(thp, fp,
411 					    (pad + 4), nvname, elem) != 0) {
412 						/* errno set */
413 						return (-1);
414 					}
415 				}
416 
417 				(void) fprintf(fp, "%*s</%s>\n", (pad + 2), "",
418 				    TDG_XML_NVLIST);
419 			}
420 			(void) fprintf(fp, "%*s</%s>\n", pad, "",
421 			    TDG_XML_NVPAIR);
422 
423 			break;
424 		}
425 		default:
426 			topo_dprintf(thp, TOPO_DBG_XML, "Invalid nvpair data "
427 			    "type: %d\n", type);
428 			(void) topo_hdl_seterrno(thp, ETOPO_MOD_XENUM);
429 			return (-1);
430 	}
431 	return (0);
432 }
433 
434 static int
serialize_edge(topo_hdl_t * thp,topo_edge_t * edge,boolean_t last_edge,void * arg)435 serialize_edge(topo_hdl_t *thp, topo_edge_t *edge, boolean_t last_edge,
436     void *arg)
437 {
438 	nvlist_t *fmri = NULL;
439 	char *fmristr;
440 	int err;
441 	tnode_t *tn;
442 	FILE *fp = (FILE *)arg;
443 
444 	tn = topo_vertex_node(edge->tve_vertex);
445 	if (topo_node_resource(tn, &fmri, &err) != 0 ||
446 	    topo_fmri_nvl2str(thp, fmri, &fmristr, &err) != 0) {
447 		/* errno set */
448 		nvlist_free(fmri);
449 		return (TOPO_WALK_ERR);
450 	}
451 	nvlist_free(fmri);
452 
453 	(void) fprintf(fp, "%*s<%s %s='%s' />\n", 4, "", TDG_XML_EDGE,
454 	    TDG_XML_FMRI, fmristr);
455 	topo_hdl_strfree(thp, fmristr);
456 
457 	return (TOPO_WALK_NEXT);
458 }
459 
460 /*
461  * Some node property values aren't available unless we go through the libtopo
462  * API's topo_prop_get_* routines. We do that here to make sure the nodes have
463  * all of their properties populated, then we vector off to type-specific
464  * XML serialization functions.
465  */
466 static int
serialize_property(topo_hdl_t * thp,FILE * fp,uint_t pad,tnode_t * tn,topo_propval_t * pv,const char * pgname)467 serialize_property(topo_hdl_t *thp, FILE *fp, uint_t pad, tnode_t *tn,
468     topo_propval_t *pv, const char *pgname)
469 {
470 	topo_type_t type = pv->tp_type;
471 	const char *pname = pv->tp_name;
472 	int err;
473 	char *name = TDG_XML_PROP_VALUE;
474 
475 	switch (type) {
476 		case TOPO_TYPE_INT32: {
477 			int32_t val;
478 
479 			if (topo_prop_get_int32(tn, pgname, pname, &val,
480 			    &err) != 0)
481 				return (-1);
482 
483 			tdg_xml_nvint32(fp, pad, name, val);
484 			break;
485 		}
486 		case TOPO_TYPE_UINT32: {
487 			uint32_t val;
488 
489 			if (topo_prop_get_uint32(tn, pgname, pname, &val,
490 			    &err) != 0)
491 				return (-1);
492 
493 			tdg_xml_nvuint32(fp, pad, name, val);
494 			break;
495 		}
496 		case TOPO_TYPE_INT64: {
497 			int64_t val;
498 
499 			if (topo_prop_get_int64(tn, pgname, pname, &val,
500 			    &err) != 0)
501 				return (-1);
502 
503 			tdg_xml_nvint64(fp, pad, name, val);
504 			break;
505 		}
506 		case TOPO_TYPE_UINT64: {
507 			uint64_t val;
508 
509 			if (topo_prop_get_uint64(tn, pgname, pname, &val,
510 			    &err) != 0)
511 				return (-1);
512 
513 			tdg_xml_nvuint64(fp, pad, name, val);
514 			break;
515 		}
516 		case TOPO_TYPE_STRING: {
517 			char *val;
518 
519 			if (topo_prop_get_string(tn, pgname, pname, &val,
520 			    &err) != 0)
521 				return (-1);
522 
523 			tdg_xml_nvstring(fp, pad, name, val);
524 
525 			topo_hdl_strfree(thp, val);
526 			break;
527 		}
528 		case TOPO_TYPE_FMRI: {
529 			nvlist_t *nvl;
530 
531 			if (topo_prop_get_fmri(tn, pgname, pname, &nvl,
532 			    &err) != 0)
533 				return (-1);
534 
535 			if (serialize_nvpair_nvlist(thp, fp, pad + 2, name,
536 			    nvl) != 0) {
537 				nvlist_free(nvl);
538 				return (-1);
539 			}
540 
541 			nvlist_free(nvl);
542 			break;
543 		}
544 		case TOPO_TYPE_INT32_ARRAY: {
545 			uint_t nelems;
546 			int32_t *val;
547 
548 			if (topo_prop_get_int32_array(tn, pgname, pname, &val,
549 			    &nelems, &err) != 0)
550 				return (-1);
551 
552 			tdg_xml_nvint32arr(fp, pad, pname, val, nelems);
553 			topo_hdl_free(thp, val, (sizeof (int32_t) * nelems));
554 			break;
555 		}
556 		case TOPO_TYPE_UINT32_ARRAY: {
557 			uint_t nelems;
558 			uint32_t *val;
559 
560 			if (topo_prop_get_uint32_array(tn, pgname, pname, &val,
561 			    &nelems, &err) != 0)
562 				return (-1);
563 
564 			tdg_xml_nvuint32arr(fp, pad, pname,  val, nelems);
565 			topo_hdl_free(thp, val, (sizeof (uint32_t) * nelems));
566 			break;
567 		}
568 		case TOPO_TYPE_INT64_ARRAY: {
569 			uint_t nelems;
570 			int64_t *val;
571 
572 			if (topo_prop_get_int64_array(tn, pgname, pname, &val,
573 			    &nelems, &err) != 0)
574 				return (-1);
575 
576 			tdg_xml_nvint64arr(fp, pad, pname,  val, nelems);
577 			topo_hdl_free(thp, val, (sizeof (int64_t) * nelems));
578 			break;
579 		}
580 		case TOPO_TYPE_UINT64_ARRAY: {
581 			uint_t nelems;
582 			uint64_t *val;
583 
584 			if (topo_prop_get_uint64_array(tn, pgname, pname, &val,
585 			    &nelems, &err) != 0)
586 				return (-1);
587 
588 			tdg_xml_nvuint64arr(fp, pad, pname,  val, nelems);
589 			topo_hdl_free(thp, val, (sizeof (uint64_t) * nelems));
590 			break;
591 		}
592 		default:
593 			topo_dprintf(thp, TOPO_DBG_XML, "Invalid nvpair data "
594 			    "type: %d\n", type);
595 			(void) topo_hdl_seterrno(thp, ETOPO_MOD_XENUM);
596 			return (-1);
597 	}
598 	return (0);
599 }
600 
601 static int
serialize_pgroups(topo_hdl_t * thp,FILE * fp,tnode_t * tn)602 serialize_pgroups(topo_hdl_t *thp, FILE *fp, tnode_t *tn)
603 {
604 	topo_pgroup_t *pg;
605 	uint_t npgs = 0;
606 
607 	for (pg = topo_list_next(&tn->tn_pgroups); pg != NULL;
608 	    pg = topo_list_next(pg)) {
609 
610 		npgs++;
611 	}
612 
613 	tdg_xml_nvarray(fp, 2, TDG_XML_PGROUPS, TDG_XML_NVLIST_ARR);
614 
615 	for (pg = topo_list_next(&tn->tn_pgroups); pg != NULL;
616 	    pg = topo_list_next(pg)) {
617 
618 		topo_proplist_t *pvl;
619 		uint_t nprops = 0;
620 
621 		(void) fprintf(fp, "%*s<%s>\n", 4, "", TDG_XML_NVLIST);
622 		tdg_xml_nvstring(fp, 6, TOPO_PROP_GROUP_NAME,
623 		    pg->tpg_info->tpi_name);
624 
625 		for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL;
626 		    pvl = topo_list_next(pvl))
627 			nprops++;
628 
629 		tdg_xml_nvarray(fp, 6, TDG_XML_PVALS, TDG_XML_NVLIST_ARR);
630 
631 		for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL;
632 		    pvl = topo_list_next(pvl)) {
633 
634 			topo_propval_t *pv = pvl->tp_pval;
635 
636 			(void) fprintf(fp, "%*s<%s>\n", 8, "", TDG_XML_NVLIST);
637 			tdg_xml_nvstring(fp, 10, TDG_XML_PROP_NAME,
638 			    pv->tp_name);
639 			tdg_xml_nvuint32(fp, 10, TDG_XML_PROP_TYPE,
640 			    pv->tp_type);
641 
642 			if (serialize_property(thp, fp, 10, tn, pv,
643 			    pg->tpg_info->tpi_name) != 0) {
644 				/* errno set */
645 				return (-1);
646 			}
647 			(void) fprintf(fp, "%*s</%s>\n", 8, "",
648 			    TDG_XML_NVLIST);
649 		}
650 
651 		(void) fprintf(fp, "%*s</%s> <!-- %s -->\n", 6, "",
652 		    TDG_XML_NVPAIR, TDG_XML_PVALS);
653 		(void) fprintf(fp, "%*s</%s>\n", 4, "", TDG_XML_NVLIST);
654 	}
655 	(void) fprintf(fp, "%*s</%s> <!-- %s -->\n", 2, "", TDG_XML_NVPAIR,
656 	    TDG_XML_PGROUPS);
657 
658 	return (0);
659 }
660 
661 static int
serialize_vertex(topo_hdl_t * thp,topo_vertex_t * vtx,boolean_t last_vtx,void * arg)662 serialize_vertex(topo_hdl_t *thp, topo_vertex_t *vtx, boolean_t last_vtx,
663     void *arg)
664 {
665 	nvlist_t *fmri = NULL;
666 	char *fmristr;
667 	tnode_t *tn;
668 	int err;
669 	FILE *fp = (FILE *)arg;
670 
671 	tn = topo_vertex_node(vtx);
672 	if (topo_node_resource(tn, &fmri, &err) != 0 ||
673 	    topo_fmri_nvl2str(thp, fmri, &fmristr, &err) != 0) {
674 		/* errno set */
675 		nvlist_free(fmri);
676 		return (TOPO_WALK_ERR);
677 	}
678 	nvlist_free(fmri);
679 
680 	(void) fprintf(fp, "<%s %s='%s' %s='0x%" PRIx64 "' %s='%s'>\n",
681 	    TDG_XML_VERTEX, TDG_XML_NAME, topo_node_name(tn),
682 	    TDG_XML_INSTANCE, topo_node_instance(tn),
683 	    TDG_XML_FMRI, fmristr);
684 
685 	topo_hdl_strfree(thp, fmristr);
686 
687 	if (serialize_pgroups(thp, fp, tn) != 0) {
688 		/* errno set */
689 		return (TOPO_WALK_ERR);
690 	}
691 
692 	if (vtx->tvt_noutgoing != 0) {
693 		(void) fprintf(fp, "  <%s>\n", TDG_XML_OUTEDGES);
694 
695 		if (topo_edge_iter(thp, vtx, serialize_edge, fp) != 0) {
696 			topo_dprintf(thp, TOPO_DBG_XML, "failed to iterate "
697 			    "edges on %s=%" PRIx64 "\n", topo_node_name(tn),
698 			    topo_node_instance(tn));
699 			/* errno set */
700 			return (TOPO_WALK_ERR);
701 		}
702 		(void) fprintf(fp, "  </%s>\n", TDG_XML_OUTEDGES);
703 	}
704 	(void) fprintf(fp, "</%s>\n\n", TDG_XML_VERTEX);
705 
706 	return (TOPO_WALK_NEXT);
707 }
708 
709 /*
710  * This function takes a topo_digraph_t and serializes it to XML.
711  *
712  * The schema is described in detail in:
713  * usr/src/lib/fm/topo/maps/common/digraph-topology.dtd.1
714  *
715  * On success, this function writes the XML to the specified file and
716  * returns 0.
717  *
718  * On failure, this function returns -1.
719  */
720 int
topo_digraph_serialize(topo_hdl_t * thp,topo_digraph_t * tdg,FILE * fp)721 topo_digraph_serialize(topo_hdl_t *thp, topo_digraph_t *tdg, FILE *fp)
722 {
723 	struct utsname uts = { 0 };
724 	time_t utc_time;
725 	char tstamp[64];
726 	int ret;
727 
728 	if ((ret = uname(&uts)) < 0) {
729 		topo_dprintf(thp, TOPO_DBG_XML, "uname failed (ret = %d)\n",
730 		    ret);
731 		return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
732 	}
733 
734 	if (time(&utc_time) < 0) {
735 		topo_dprintf(thp, TOPO_DBG_XML, "uname failed (%s)\n",
736 		    strerror(errno));
737 		return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
738 	}
739 
740 	/*
741 	 * strftime returns 0 if the size of the result is larger than the
742 	 * buffer size passed in to it.  We've sized tstamp to be pretty
743 	 * large, so this really shouldn't happen.
744 	 */
745 	if (strftime(tstamp, sizeof (tstamp), "%Y-%m-%dT%H:%M:%SZ",
746 	    gmtime(&utc_time)) == 0) {
747 		topo_dprintf(thp, TOPO_DBG_XML, "strftime failed\n");
748 		return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
749 	}
750 
751 	(void) fprintf(fp, "<?xml version=\"1.0\"?>\n");
752 	(void) fprintf(fp, "<!DOCTYPE topology SYSTEM \"%s\">\n", TDG_DTD);
753 	(void) fprintf(fp, "<%s %s='%s' %s='%s' %s='%s' %s='%s' %s='%s'>\n",
754 	    TDG_XML_TOPO_DIGRAPH, TDG_XML_SCHEME, tdg->tdg_scheme,
755 	    TDG_XML_NODENAME, uts.nodename, TDG_XML_OSVERSION, uts.version,
756 	    TDG_XML_PRODUCT, thp->th_product, TDG_XML_TSTAMP, tstamp);
757 	(void) fprintf(fp, "<%s>\n", TDG_XML_VERTICES);
758 
759 	if (topo_vertex_iter(thp, tdg, serialize_vertex, fp) != 0) {
760 		topo_dprintf(thp, TOPO_DBG_XML, "\nfailed to iterate "
761 		    "vertices\n");
762 		/* errno set */
763 		return (-1);
764 	}
765 
766 	(void) fprintf(fp, "</%s>\n", TDG_XML_VERTICES);
767 	(void) fprintf(fp, "</%s>\n", TDG_XML_TOPO_DIGRAPH);
768 
769 	if (ferror(fp) != 0) {
770 		topo_dprintf(thp, TOPO_DBG_XML, "An unknown error ocurrred "
771 		    "while writing out the serialize topology.");
772 		return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
773 	}
774 	return (0);
775 }
776 
777 static xmlNodePtr
get_child_by_name(xmlNodePtr xn,xmlChar * name)778 get_child_by_name(xmlNodePtr xn, xmlChar *name)
779 {
780 	for (xmlNodePtr cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next)
781 		if (xmlStrcmp(cn->name, name) == 0)
782 			return (cn);
783 
784 	return (NULL);
785 }
786 
787 static void
dump_xml_node(topo_hdl_t * thp,xmlNodePtr xn)788 dump_xml_node(topo_hdl_t *thp, xmlNodePtr xn)
789 {
790 	topo_dprintf(thp, TOPO_DBG_XML, "node: %s", (char *)xn->name);
791 	for (xmlAttrPtr attr = xn->properties; attr != NULL; attr = attr->next)
792 		topo_dprintf(thp, TOPO_DBG_XML, "attribute: %s",
793 		    (char *)attr->name);
794 
795 	for (xmlNodePtr cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next)
796 		topo_dprintf(thp, TOPO_DBG_XML, "\tchild node: %s",
797 		    (char *)cn->name);
798 }
799 
800 struct edge_cb_arg {
801 	const char	*from_fmri;
802 	const char	*to_fmri;
803 	topo_vertex_t	*from_vtx;
804 	topo_vertex_t	*to_vtx;
805 };
806 
807 static int
edge_cb(topo_hdl_t * thp,topo_vertex_t * vtx,boolean_t last_vtx,void * arg)808 edge_cb(topo_hdl_t *thp, topo_vertex_t *vtx, boolean_t last_vtx, void *arg)
809 {
810 	struct edge_cb_arg *cbarg = arg;
811 	tnode_t *tn;
812 	nvlist_t *fmri = NULL;
813 	char *fmristr = NULL;
814 	int err;
815 
816 	tn = topo_vertex_node(vtx);
817 	if (topo_node_resource(tn, &fmri, &err) != 0 ||
818 	    topo_fmri_nvl2str(thp, fmri, &fmristr, &err) != 0) {
819 		topo_dprintf(thp, TOPO_DBG_XML, "failed to convert FMRI for "
820 		    "%s=%" PRIx64 " to a string\n", topo_node_name(tn),
821 		    topo_node_instance(tn));
822 		if (thp->th_debug & TOPO_DBG_XML)
823 			nvlist_print(stdout, fmri);
824 		nvlist_free(fmri);
825 		return (TOPO_WALK_ERR);
826 	}
827 	nvlist_free(fmri);
828 
829 	if (strcmp(fmristr, cbarg->from_fmri) == 0)
830 		cbarg->from_vtx = vtx;
831 	else if (strcmp(fmristr, cbarg->to_fmri) == 0)
832 		cbarg->to_vtx = vtx;
833 
834 	topo_hdl_strfree(thp, fmristr);
835 	if (cbarg->from_vtx != NULL && cbarg->to_vtx != NULL)
836 		return (TOPO_WALK_TERMINATE);
837 	else
838 		return (TOPO_WALK_NEXT);
839 }
840 
841 static int
deserialize_edges(topo_hdl_t * thp,topo_mod_t * mod,topo_digraph_t * tdg,xmlChar * from_fmri,xmlNodePtr xn)842 deserialize_edges(topo_hdl_t *thp, topo_mod_t *mod, topo_digraph_t *tdg,
843     xmlChar *from_fmri, xmlNodePtr xn)
844 {
845 	for (xmlNodePtr cn = xn->xmlChildrenNode; cn != NULL;
846 	    cn = cn->next) {
847 		xmlChar *fmri;
848 		struct edge_cb_arg cbarg = { 0 };
849 
850 		if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_EDGE) != 0)
851 			continue;
852 
853 		if ((fmri = xmlGetProp(cn, (xmlChar *)TDG_XML_FMRI)) == NULL) {
854 			topo_dprintf(thp, TOPO_DBG_XML,
855 			    "error parsing %s element", (char *)cn->name);
856 			dump_xml_node(thp, cn);
857 			return (-1);
858 		}
859 		cbarg.from_fmri = (char *)from_fmri;
860 		cbarg.to_fmri = (char *)fmri;
861 
862 		if (topo_vertex_iter(mod->tm_hdl, tdg, edge_cb, &cbarg) != 0) {
863 			xmlFree(fmri);
864 			return (-1);
865 		}
866 		xmlFree(fmri);
867 
868 		if (cbarg.from_vtx == NULL || cbarg.to_vtx == NULL) {
869 			return (-1);
870 		}
871 		if (topo_edge_new(mod, cbarg.from_vtx, cbarg.to_vtx) != 0) {
872 			return (-1);
873 		}
874 	}
875 
876 	return (0);
877 }
878 
879 static int
add_edges(topo_hdl_t * thp,topo_mod_t * mod,topo_digraph_t * tdg,xmlNodePtr xn)880 add_edges(topo_hdl_t *thp, topo_mod_t *mod, topo_digraph_t *tdg,
881     xmlNodePtr xn)
882 {
883 	int ret = -1;
884 	nvlist_t *props = NULL;
885 	xmlChar *name = NULL, *fmri = NULL;
886 	xmlNodePtr cn;
887 	uint64_t inst;
888 
889 	if ((name = xmlGetProp(xn, (xmlChar *)TDG_XML_NAME)) == NULL ||
890 	    (fmri = xmlGetProp(xn, (xmlChar *)TDG_XML_FMRI)) == NULL ||
891 	    xmlattr_to_int(mod, xn, TDG_XML_INSTANCE, &inst) != 0) {
892 		goto fail;
893 	}
894 
895 	if ((cn = get_child_by_name(xn, (xmlChar *)TDG_XML_OUTEDGES)) !=
896 	    NULL) {
897 		if (deserialize_edges(thp, mod, tdg, fmri, cn) != 0)
898 			goto fail;
899 	}
900 	ret = 0;
901 
902 fail:
903 	if (ret != 0) {
904 		topo_dprintf(thp, TOPO_DBG_XML, "%s: error parsing %s element",
905 		    __func__, TDG_XML_VERTEX);
906 		dump_xml_node(thp, xn);
907 	}
908 	nvlist_free(props);
909 	if (name != NULL)
910 		xmlFree(name);
911 	if (fmri != NULL)
912 		xmlFree(fmri);
913 
914 	return (ret);
915 }
916 
917 static topo_pgroup_info_t pginfo = {
918 	NULL,
919 	TOPO_STABILITY_PRIVATE,
920 	TOPO_STABILITY_PRIVATE,
921 	1
922 };
923 
924 static int
add_props(topo_hdl_t * thp,topo_vertex_t * vtx,nvlist_t * pgroups)925 add_props(topo_hdl_t *thp, topo_vertex_t *vtx, nvlist_t *pgroups)
926 {
927 	tnode_t *tn;
928 	nvlist_t **pgs;
929 	uint_t npgs = 0;
930 
931 	tn = topo_vertex_node(vtx);
932 	if (nvlist_lookup_nvlist_array(pgroups, TDG_XML_PGROUPS, &pgs,
933 	    &npgs) != 0) {
934 		goto fail;
935 	}
936 
937 	for (uint_t i = 0; i < npgs; i++) {
938 		char *pgname;
939 		nvlist_t **props;
940 		uint_t nprops;
941 		int err;
942 
943 		if (nvlist_lookup_string(pgs[i], TDG_XML_PGROUP_NAME,
944 		    &pgname) != 0 ||
945 		    nvlist_lookup_nvlist_array(pgs[i], TDG_XML_PVALS, &props,
946 		    &nprops) != 0) {
947 			goto fail;
948 		}
949 		pginfo.tpi_name = pgname;
950 
951 		if (topo_pgroup_create(tn, &pginfo, &err) != 0) {
952 			topo_dprintf(thp, TOPO_DBG_XML, "failed to create "
953 			    "pgroup: %s", pgname);
954 			goto fail;
955 		}
956 		for (uint_t j = 0; j < nprops; j++) {
957 			if (topo_prop_setprop(tn, pgname, props[j],
958 			    TOPO_PROP_IMMUTABLE, props[j], &err) != 0) {
959 				topo_dprintf(thp, TOPO_DBG_XML, "failed to "
960 				    "set properties in pgroup: %s", pgname);
961 				goto fail;
962 			}
963 		}
964 	}
965 	return (0);
966 fail:
967 	topo_dprintf(thp, TOPO_DBG_XML, "%s: error decoding properties for "
968 	    "%s=%" PRIx64, __func__, topo_node_name(tn),
969 	    topo_node_instance(tn));
970 	if (thp->th_debug & TOPO_DBG_XML)
971 		nvlist_print(stdout, pgroups);
972 
973 	return (-1);
974 }
975 
976 static void
free_nvlist_array(topo_hdl_t * thp,nvlist_t ** nvlarr,uint_t nelems)977 free_nvlist_array(topo_hdl_t *thp, nvlist_t **nvlarr, uint_t nelems)
978 {
979 	for (uint_t i = 0; i < nelems; i++) {
980 		if (nvlarr[i] != NULL)
981 			nvlist_free(nvlarr[i]);
982 	}
983 	topo_hdl_free(thp, nvlarr, nelems * sizeof (nvlist_t *));
984 }
985 
986 static boolean_t
is_overflow(topo_hdl_t * thp,uint64_t val,uint_t nbits)987 is_overflow(topo_hdl_t *thp, uint64_t val, uint_t nbits)
988 {
989 	if ((val >> nbits) != 0) {
990 		topo_dprintf(thp, TOPO_DBG_XML, "value exceeds %u bits", nbits);
991 		return (B_TRUE);
992 	}
993 	return (B_FALSE);
994 }
995 
996 /*
997  * Recursive function for parsing nvpair XML elements, which can contain
998  * nested nvlist and nvpair elements.
999  */
1000 static int
deserialize_nvpair(topo_hdl_t * thp,topo_mod_t * mod,nvlist_t * nvl,xmlNodePtr xn)1001 deserialize_nvpair(topo_hdl_t *thp, topo_mod_t *mod, nvlist_t *nvl,
1002     xmlNodePtr xn)
1003 {
1004 	int ret = -1;
1005 	xmlChar *name = NULL, *type = NULL, *sval = NULL;
1006 	uint64_t val;
1007 
1008 	if ((name = xmlGetProp(xn, (xmlChar *)TDG_XML_NAME)) == NULL ||
1009 	    (type = xmlGetProp(xn, (xmlChar *)TDG_XML_TYPE)) == NULL) {
1010 		goto fail;
1011 	}
1012 
1013 	if (xmlStrcmp(type, (xmlChar *)TDG_XML_NVLIST) == 0) {
1014 		nvlist_t *cnvl = NULL;
1015 
1016 		if (topo_hdl_nvalloc(thp, &cnvl, NV_UNIQUE_NAME) != 0) {
1017 			goto fail;
1018 		}
1019 
1020 		for (xmlNodePtr cn = xn->xmlChildrenNode;
1021 		    cn != NULL; cn = cn->next) {
1022 
1023 			if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVLIST) != 0)
1024 				continue;
1025 
1026 			for (xmlNodePtr gcn = cn->xmlChildrenNode;
1027 			    gcn != NULL; gcn = gcn->next) {
1028 
1029 				if (xmlStrcmp(gcn->name,
1030 				    (xmlChar *)TDG_XML_NVPAIR) != 0)
1031 					continue;
1032 				if (deserialize_nvpair(thp, mod, cnvl, gcn) !=
1033 				    0) {
1034 					nvlist_free(cnvl);
1035 					goto fail;
1036 				}
1037 			}
1038 			if (nvlist_add_nvlist(nvl, (char *)name, cnvl) != 0) {
1039 				nvlist_free(cnvl);
1040 				goto fail;
1041 			}
1042 			nvlist_free(cnvl);
1043 			break;
1044 		}
1045 	} else if (xmlStrcmp(type, (xmlChar *)TDG_XML_INT8) == 0) {
1046 		if (xmlattr_to_int(mod, xn, TDG_XML_VALUE, &val) != 0 ||
1047 		    is_overflow(thp, val, 8) ||
1048 		    nvlist_add_int8(nvl, (char *)name, (int8_t)val) != 0) {
1049 			goto fail;
1050 		}
1051 	} else if (xmlStrcmp(type, (xmlChar *)TDG_XML_INT16) == 0) {
1052 		if (xmlattr_to_int(mod, xn, TDG_XML_VALUE, &val) != 0 ||
1053 		    is_overflow(thp, val, 16) ||
1054 		    nvlist_add_int16(nvl, (char *)name, (int16_t)val) != 0) {
1055 			goto fail;
1056 		}
1057 	} else if (xmlStrcmp(type, (xmlChar *)TDG_XML_INT32) == 0) {
1058 		if (xmlattr_to_int(mod, xn, TDG_XML_VALUE, &val) != 0 ||
1059 		    is_overflow(thp, val, 32) ||
1060 		    nvlist_add_int32(nvl, (char *)name, (int32_t)val) != 0) {
1061 			goto fail;
1062 		}
1063 	} else if (xmlStrcmp(type, (xmlChar *)TDG_XML_INT64) == 0) {
1064 		if (xmlattr_to_int(mod, xn, TDG_XML_VALUE, &val) != 0 ||
1065 		    nvlist_add_int64(nvl, (char *)name, (int64_t)val) != 0) {
1066 			goto fail;
1067 		}
1068 	} else if (xmlStrcmp(type, (xmlChar *)TDG_XML_UINT8) == 0) {
1069 		if (xmlattr_to_int(mod, xn, TDG_XML_VALUE, &val) != 0 ||
1070 		    is_overflow(thp, val, 8) ||
1071 		    nvlist_add_uint8(nvl, (char *)name, (uint8_t)val) != 0) {
1072 			goto fail;
1073 		}
1074 	} else if (xmlStrcmp(type, (xmlChar *)TDG_XML_UINT16) == 0) {
1075 		if (xmlattr_to_int(mod, xn, TDG_XML_VALUE, &val) != 0 ||
1076 		    is_overflow(thp, val, 16) ||
1077 		    nvlist_add_uint16(nvl, (char *)name, (uint16_t)val) != 0) {
1078 			goto fail;
1079 		}
1080 	} else if (xmlStrcmp(type, (xmlChar *)TDG_XML_UINT32) == 0) {
1081 		if (xmlattr_to_int(mod, xn, TDG_XML_VALUE, &val) != 0 ||
1082 		    is_overflow(thp, val, 32) ||
1083 		    nvlist_add_uint32(nvl, (char *)name, (uint32_t)val) != 0) {
1084 			goto fail;
1085 		}
1086 	} else if (xmlStrcmp(type, (xmlChar *)TDG_XML_UINT64) == 0) {
1087 		if (xmlattr_to_int(mod, xn, TDG_XML_VALUE, &val) != 0 ||
1088 		    nvlist_add_uint64(nvl, (char *)name, (uint64_t)val) != 0) {
1089 			goto fail;
1090 		}
1091 	} else if (xmlStrcmp(type, (xmlChar *)TDG_XML_STRING) == 0) {
1092 		if ((sval = xmlGetProp(xn, (xmlChar *)TDG_XML_VALUE)) == NULL ||
1093 		    nvlist_add_string(nvl, (char *)name, (char *)sval) != 0) {
1094 			goto fail;
1095 		}
1096 	} else if (xmlStrcmp(type, (xmlChar *)TDG_XML_NVLIST_ARR) == 0) {
1097 		uint64_t nelem = 0;
1098 		nvlist_t **nvlarr = NULL;
1099 		uint_t i = 0;
1100 		xmlNodePtr cn = xn->xmlChildrenNode;
1101 
1102 		/*
1103 		 * Count the number of child nvlist elements
1104 		 */
1105 		while (cn != NULL) {
1106 			if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVLIST) ==
1107 			    0) {
1108 				nelem++;
1109 			}
1110 			cn = cn->next;
1111 		}
1112 
1113 		if ((nvlarr = topo_hdl_zalloc(thp,
1114 		    (nelem * sizeof (nvlist_t *)))) == NULL) {
1115 			goto fail;
1116 		}
1117 
1118 		for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1119 			if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVLIST) !=
1120 			    0)
1121 				continue;
1122 
1123 			if (topo_hdl_nvalloc(thp, &nvlarr[i],
1124 			    NV_UNIQUE_NAME) != 0) {
1125 				free_nvlist_array(thp, nvlarr, nelem);
1126 				goto fail;
1127 			}
1128 
1129 			for (xmlNodePtr gcn = cn->xmlChildrenNode;
1130 			    gcn != NULL; gcn = gcn->next) {
1131 				if (xmlStrcmp(gcn->name,
1132 				    (xmlChar *)TDG_XML_NVPAIR) != 0)
1133 					continue;
1134 				if (deserialize_nvpair(thp, mod, nvlarr[i],
1135 				    gcn) != 0) {
1136 					free_nvlist_array(thp, nvlarr, nelem);
1137 					goto fail;
1138 				}
1139 			}
1140 			i++;
1141 		}
1142 		if (nvlist_add_nvlist_array(nvl, (char *)name, nvlarr,
1143 		    nelem) != 0) {
1144 			free_nvlist_array(thp, nvlarr, nelem);
1145 			goto fail;
1146 		}
1147 		free_nvlist_array(thp, nvlarr, nelem);
1148 	} else if (xmlStrcmp(type, (xmlChar *)TDG_XML_UINT32_ARR) == 0) {
1149 		uint64_t nelem = 0;
1150 		uint32_t *arr = NULL;
1151 		uint_t i = 0;
1152 		xmlNodePtr cn = xn->xmlChildrenNode;
1153 
1154 		/*
1155 		 * Count the number of child nvpair elements
1156 		 */
1157 		while (cn != NULL) {
1158 			if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) ==
1159 			    0) {
1160 				nelem++;
1161 			}
1162 			cn = cn->next;
1163 		}
1164 
1165 		if ((arr = topo_hdl_zalloc(thp,
1166 		    (nelem * sizeof (uint32_t)))) == NULL) {
1167 			goto fail;
1168 		}
1169 
1170 		for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1171 			if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) != 0)
1172 				continue;
1173 
1174 			if (xmlattr_to_int(mod, cn, TDG_XML_VALUE, &val) != 0) {
1175 				topo_hdl_free(thp, arr,
1176 				    (nelem * sizeof (uint32_t)));
1177 				goto fail;
1178 			}
1179 
1180 			arr[i] = val;
1181 			i++;
1182 		}
1183 		if (nvlist_add_uint32_array(nvl, (char *)name, arr,
1184 		    nelem) != 0) {
1185 			topo_hdl_free(thp, arr, (nelem * sizeof (uint32_t)));
1186 			goto fail;
1187 		}
1188 		topo_hdl_free(thp, arr, (nelem * sizeof (uint32_t)));
1189 	} else if (xmlStrcmp(type, (xmlChar *)TDG_XML_INT32_ARR) == 0) {
1190 		uint64_t nelem = 0;
1191 		int32_t *arr = NULL;
1192 		uint_t i = 0;
1193 		xmlNodePtr cn = xn->xmlChildrenNode;
1194 
1195 		/*
1196 		 * Count the number of child nvpair elements
1197 		 */
1198 		while (cn != NULL) {
1199 			if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) ==
1200 			    0) {
1201 				nelem++;
1202 			}
1203 			cn = cn->next;
1204 		}
1205 
1206 		if ((arr = topo_hdl_zalloc(thp,
1207 		    (nelem * sizeof (int32_t)))) == NULL) {
1208 			goto fail;
1209 		}
1210 
1211 		for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1212 			if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) != 0)
1213 				continue;
1214 
1215 			if (xmlattr_to_int(mod, cn, TDG_XML_VALUE, &val) != 0) {
1216 				topo_hdl_free(thp, arr,
1217 				    (nelem * sizeof (int32_t)));
1218 				goto fail;
1219 			}
1220 
1221 			arr[i] = val;
1222 			i++;
1223 		}
1224 		if (nvlist_add_int32_array(nvl, (char *)name, arr,
1225 		    nelem) != 0) {
1226 			topo_hdl_free(thp, arr, (nelem * sizeof (int32_t)));
1227 			goto fail;
1228 		}
1229 		topo_hdl_free(thp, arr, (nelem * sizeof (int32_t)));
1230 	} else if (xmlStrcmp(type, (xmlChar *)TDG_XML_UINT64_ARR) == 0) {
1231 		uint64_t nelem = 0, *arr = NULL;
1232 		uint_t i = 0;
1233 		xmlNodePtr cn = xn->xmlChildrenNode;
1234 
1235 		/*
1236 		 * Count the number of child nvpair elements
1237 		 */
1238 		while (cn != NULL) {
1239 			if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) ==
1240 			    0) {
1241 				nelem++;
1242 			}
1243 			cn = cn->next;
1244 		}
1245 
1246 		if ((arr = topo_hdl_zalloc(thp,
1247 		    (nelem * sizeof (uint64_t)))) == NULL) {
1248 			goto fail;
1249 		}
1250 
1251 		for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1252 			if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) != 0)
1253 				continue;
1254 
1255 			if (xmlattr_to_int(mod, cn, TDG_XML_VALUE, &val) != 0) {
1256 				topo_hdl_free(thp, arr,
1257 				    (nelem * sizeof (uint64_t)));
1258 				goto fail;
1259 			}
1260 
1261 			arr[i] = val;
1262 			i++;
1263 		}
1264 		if (nvlist_add_uint64_array(nvl, (char *)name, arr,
1265 		    nelem) != 0) {
1266 			topo_hdl_free(thp, arr, (nelem * sizeof (uint64_t)));
1267 			goto fail;
1268 		}
1269 		topo_hdl_free(thp, arr, (nelem * sizeof (uint64_t)));
1270 	} else if (xmlStrcmp(type, (xmlChar *)TDG_XML_INT64_ARR) == 0) {
1271 		uint64_t nelem = 0;
1272 		int64_t *arr = NULL;
1273 		uint_t i = 0;
1274 		xmlNodePtr cn = xn->xmlChildrenNode;
1275 
1276 		/*
1277 		 * Count the number of child nvpair elements
1278 		 */
1279 		while (cn != NULL) {
1280 			if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) ==
1281 			    0) {
1282 				nelem++;
1283 			}
1284 			cn = cn->next;
1285 		}
1286 
1287 		if ((arr = topo_hdl_zalloc(thp,
1288 		    (nelem * sizeof (int64_t)))) == NULL) {
1289 			goto fail;
1290 		}
1291 
1292 		for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1293 			if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) != 0)
1294 				continue;
1295 
1296 			if (xmlattr_to_int(mod, cn, TDG_XML_VALUE, &val) != 0) {
1297 				topo_hdl_free(thp, arr,
1298 				    (nelem * sizeof (int64_t)));
1299 				goto fail;
1300 			}
1301 
1302 			arr[i] = val;
1303 			i++;
1304 		}
1305 		if (nvlist_add_int64_array(nvl, (char *)name, arr,
1306 		    nelem) != 0) {
1307 			topo_hdl_free(thp, arr, (nelem * sizeof (int64_t)));
1308 			goto fail;
1309 		}
1310 		topo_hdl_free(thp, arr, (nelem * sizeof (int64_t)));
1311 	}
1312 	ret = 0;
1313 fail:
1314 	if (ret != 0) {
1315 		topo_dprintf(thp, TOPO_DBG_XML, "%s: error parsing %s "
1316 		    "element: name: %s, type: %s, nvl: %p", __func__, xn->name,
1317 		    (name != NULL) ? (char *)name : "MISSING!",
1318 		    (type != NULL) ? (char *)type : "MISSING!", nvl);
1319 		dump_xml_node(thp, xn);
1320 	}
1321 	if (name != NULL)
1322 		xmlFree(name);
1323 	if (type != NULL)
1324 		xmlFree(type);
1325 	if (sval != NULL)
1326 		xmlFree(sval);
1327 
1328 	return (ret);
1329 }
1330 
1331 static int
deserialize_vertex(topo_hdl_t * thp,topo_mod_t * mod,topo_digraph_t * tdg,xmlNodePtr xn)1332 deserialize_vertex(topo_hdl_t *thp, topo_mod_t *mod, topo_digraph_t *tdg,
1333     xmlNodePtr xn)
1334 {
1335 	int ret = -1;
1336 	topo_vertex_t *vtx = NULL;
1337 	nvlist_t *props = NULL;
1338 	xmlChar *name = NULL, *fmri = NULL;
1339 	uint64_t inst;
1340 
1341 	if ((name = xmlGetProp(xn, (xmlChar *)TDG_XML_NAME)) == NULL ||
1342 	    (fmri = xmlGetProp(xn, (xmlChar *)TDG_XML_FMRI)) == NULL ||
1343 	    xmlattr_to_int(mod, xn, TDG_XML_INSTANCE, &inst) != 0) {
1344 		goto fail;
1345 	}
1346 
1347 	if ((vtx = topo_vertex_new(mod, (char *)name, inst)) == NULL) {
1348 		goto fail;
1349 	}
1350 
1351 	for (xmlNodePtr cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1352 		if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) == 0) {
1353 			if (topo_hdl_nvalloc(thp, &props, NV_UNIQUE_NAME) != 0)
1354 				goto fail;
1355 			if (deserialize_nvpair(thp, mod, props, cn) != 0 ||
1356 			    add_props(thp, vtx, props) != 0)
1357 				goto fail;
1358 		}
1359 	}
1360 	ret = 0;
1361 
1362 fail:
1363 	if (ret != 0) {
1364 		topo_dprintf(thp, TOPO_DBG_XML, "%s: error parsing %s element",
1365 		    __func__, TDG_XML_VERTEX);
1366 		dump_xml_node(thp, xn);
1367 	}
1368 	nvlist_free(props);
1369 	if (name != NULL)
1370 		xmlFree(name);
1371 	if (fmri != NULL)
1372 		xmlFree(fmri);
1373 
1374 	return (ret);
1375 }
1376 
1377 /*
1378  * This function takes a buffer containing XML data describing a directed graph
1379  * topology.  This data is parsed to the original directed graph is rehydrated.
1380  *
1381  * On success, a pointer to a topo_digraph_t representing the graph is
1382  * returned.  The caller is responsible for destroying the graph via a call to
1383  * topo_digraph_destroy()
1384  *
1385  * On failure, NULL is returned.
1386  */
1387 topo_digraph_t *
topo_digraph_deserialize(topo_hdl_t * thp,const char * xml,size_t sz)1388 topo_digraph_deserialize(topo_hdl_t *thp, const char *xml, size_t sz)
1389 {
1390 	xmlDocPtr doc;
1391 	xmlDtdPtr dtd = NULL;
1392 	xmlNodePtr root, vertices;
1393 	xmlChar *scheme = NULL;
1394 	topo_mod_t *mod;
1395 	topo_digraph_t *tdg, *ret = NULL;
1396 
1397 	if ((doc = xmlReadMemory(xml, sz, "", NULL, 0)) == NULL) {
1398 		topo_dprintf(thp, TOPO_DBG_XML, "Failed to parse XML");
1399 		goto fail;
1400 	}
1401 
1402 	/*
1403 	 * As a sanity check, extract the DTD from the XML and verify it
1404 	 * matches the DTD for a digraph topology.
1405 	 */
1406 	if ((dtd = xmlGetIntSubset(doc)) == NULL) {
1407 		topo_dprintf(thp, TOPO_DBG_XML,  "document has no DTD.\n");
1408 		goto fail;
1409 	}
1410 
1411 	if (strcmp((const char *)dtd->SystemID, TDG_DTD) != 0) {
1412 		topo_dprintf(thp, TOPO_DBG_XML, "unexpected DTD: %s",
1413 		    dtd->SystemID);
1414 		goto fail;
1415 	}
1416 
1417 	/*
1418 	 * Verify the root element is what we're expecting and then grab the
1419 	 * FMRI scheme from its attributes.
1420 	 */
1421 	if ((root = xmlDocGetRootElement(doc)) == NULL) {
1422 		topo_dprintf(thp, TOPO_DBG_XML, "document is empty.\n");
1423 		goto fail;
1424 	}
1425 
1426 	if (xmlStrcmp(root->name, (xmlChar *)TDG_XML_TOPO_DIGRAPH) != 0 ||
1427 	    (scheme = xmlGetProp(root, (xmlChar *)TDG_XML_SCHEME)) ==
1428 	    NULL) {
1429 		topo_dprintf(thp, TOPO_DBG_XML,
1430 		    "failed to parse %s element", TDG_XML_TOPO_DIGRAPH);
1431 		goto fail;
1432 	}
1433 
1434 	/*
1435 	 * Load the topo module associated with this FMRI scheme.
1436 	 */
1437 	if ((mod = topo_mod_lookup(thp, (const char *)scheme, 1)) == NULL) {
1438 		topo_dprintf(thp, TOPO_DBG_XML, "failed to load %s module",
1439 		    scheme);
1440 		goto fail;
1441 	}
1442 	/*
1443 	 * If we have a builtin module for this scheme, then there will
1444 	 * already be an empty digraph attached to the handle.  Otherwise,
1445 	 * create a new empty digraph and attach it to the handle.
1446 	 */
1447 	tdg = topo_digraph_get(mod->tm_hdl, mod->tm_info->tmi_scheme);
1448 	if (tdg == NULL) {
1449 		if ((tdg = topo_digraph_new(thp, mod, (const char *)scheme)) ==
1450 		    NULL) {
1451 			topo_dprintf(thp, TOPO_DBG_XML, "failed to create new "
1452 			    "digraph");
1453 			goto fail;
1454 		} else {
1455 			topo_list_append(&thp->th_digraphs, tdg);
1456 		}
1457 	}
1458 
1459 	/*
1460 	 * Iterate through the vertex XML elements to reconstruct the graph
1461 	 */
1462 	vertices = get_child_by_name(root, (xmlChar *)TDG_XML_VERTICES);
1463 	if (vertices == NULL ||
1464 	    xmlStrcmp(vertices->name, (xmlChar *)TDG_XML_VERTICES) != 0) {
1465 		topo_dprintf(thp, TOPO_DBG_XML, "failed to parse %s element",
1466 		    TDG_XML_VERTICES);
1467 		dump_xml_node(thp, root);
1468 		goto fail;
1469 	}
1470 
1471 	for (xmlNodePtr xn = vertices->xmlChildrenNode; xn != NULL;
1472 	    xn = xn->next) {
1473 		if (xmlStrcmp(xn->name, (xmlChar *)TDG_XML_VERTEX) != 0)
1474 			continue;
1475 		if (deserialize_vertex(thp, mod, tdg, xn) != 0)
1476 			goto fail;
1477 	}
1478 
1479 	/*
1480 	 * Now that all of the vertices have been created, go back through
1481 	 * the vertex XML elements and add the edges.
1482 	 */
1483 	for (xmlNodePtr xn = vertices->xmlChildrenNode; xn != NULL;
1484 	    xn = xn->next) {
1485 		if (xmlStrcmp(xn->name, (xmlChar *)TDG_XML_VERTEX) != 0)
1486 			continue;
1487 		if (add_edges(thp, mod, tdg, xn) != 0)
1488 			goto fail;
1489 	}
1490 
1491 	ret = tdg;
1492 
1493 fail:
1494 	if (scheme != NULL)
1495 		xmlFree(scheme);
1496 
1497 	if (doc != NULL)
1498 		xmlFreeDoc(doc);
1499 
1500 	(void) topo_hdl_seterrno(thp, ETOPO_MOD_XENUM);
1501 	return (ret);
1502 }
1503