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