xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/topo_2xml.c (revision ebee07ff4f102cbd3179db7c5070283da35a79f3)
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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 /*
27  * Copyright (c) 2019, Joyent, Inc. All rights reserved.
28  */
29 
30 #include <stdio.h>
31 #include <strings.h>
32 #include <time.h>
33 #include <sys/types.h>
34 #include <sys/fm/protocol.h>
35 #include <sys/utsname.h>
36 
37 #include <topo_parse.h>
38 #include <topo_prop.h>
39 #include <topo_tree.h>
40 
41 /*
42  * In the XML representation of the topo snapshot, 32-bit integer values are
43  * represented as base-10 values.
44  *
45  * 10 bytes for base-10 value + 1 for sign + nul
46  */
47 #define	INT32BUFSZ	12
48 /*
49  * Buffer that is large enough to hold the string representation of any signed
50  * or unsigned 64-bit integer.
51  *
52  * 2 bytes for "0x" + 16 bytes for the base-16 value + nul
53  * or
54  * 19 bytes for base-10 value + 1 for sign + nul
55  */
56 #define	INT64BUFSZ	21
57 #define	XML_VERSION	"1.0"
58 
59 static int txml_print_range(topo_hdl_t *, FILE *, tnode_t *, int);
60 
61 void
62 print_header(FILE *fp)
63 {
64 	char buf[32];
65 	time_t tod = time(NULL);
66 	struct utsname uts;
67 
68 	(void) fprintf(fp, "<?xml version=\"%s\"?>\n", XML_VERSION);
69 	(void) fprintf(fp, "<!DOCTYPE topology SYSTEM \"%s\">\n",
70 	    TOPO_DTD_PATH);
71 
72 	(void) uname(&uts);
73 	(void) strftime(buf, sizeof (buf), "%b %d %T", localtime(&tod));
74 	(void) fprintf(fp, "<!--\n");
75 	(void) fprintf(fp, " This topology map file was generated on "
76 	    "%-15s for %s\n", buf, uts.nodename);
77 	(void) fprintf(fp, "<-->\n\n");
78 }
79 
80 void
81 begin_element(FILE *fp, const char *ename, ...)
82 {
83 	char *name, *value;
84 	va_list ap;
85 
86 	(void) fprintf(fp, "<%s ", ename);
87 	va_start(ap, ename);
88 	name = va_arg(ap, char *);
89 	while (name != NULL) {
90 		value = va_arg(ap, char *);
91 		(void) fprintf(fp, "%s='%s' ", name, value);
92 		name = va_arg(ap, char *);
93 	}
94 	(void) fprintf(fp, ">\n");
95 }
96 
97 void
98 begin_end_element(FILE *fp, const char *ename, ...)
99 {
100 	char *name, *value;
101 	va_list ap;
102 
103 	(void) fprintf(fp, "<%s ", ename);
104 	va_start(ap, ename);
105 	name = va_arg(ap, char *);
106 	while (name != NULL) {
107 		value = va_arg(ap, char *);
108 		(void) fprintf(fp, "%s='%s' ", name, value);
109 		name = va_arg(ap, char *);
110 	}
111 	(void) fprintf(fp, "/>\n");
112 }
113 
114 void
115 end_element(FILE *fp, const char *ename)
116 {
117 	(void) fprintf(fp, "</%s>\n", ename);
118 }
119 
120 static void
121 txml_print_prop(topo_hdl_t *thp, FILE *fp, tnode_t *node, const char *pgname,
122     topo_propval_t *pv)
123 {
124 	int err;
125 	uint_t nelem;
126 	char vbuf[INT64BUFSZ];
127 
128 	switch (pv->tp_type) {
129 		case TOPO_TYPE_INT32: {
130 			int32_t val;
131 
132 			if (topo_prop_get_int32(node, pgname, pv->tp_name, &val,
133 			    &err) != 0)
134 				return;
135 
136 			(void) snprintf(vbuf, INT64BUFSZ, "%d", val);
137 			begin_end_element(fp, Propval, Name, pv->tp_name, Type,
138 			    Int32, Value, vbuf, NULL);
139 			break;
140 		}
141 		case TOPO_TYPE_UINT32: {
142 			uint32_t val;
143 
144 			if (topo_prop_get_uint32(node, pgname, pv->tp_name,
145 			    &val, &err) != 0)
146 				return;
147 
148 			(void) snprintf(vbuf, INT64BUFSZ, "0x%x", val);
149 			begin_end_element(fp, Propval, Name, pv->tp_name, Type,
150 			    UInt32, Value, vbuf, NULL);
151 			break;
152 		}
153 		case TOPO_TYPE_INT64: {
154 			int64_t val;
155 
156 			if (topo_prop_get_int64(node, pgname, pv->tp_name, &val,
157 			    &err) != 0)
158 				return;
159 
160 			(void) snprintf(vbuf, INT64BUFSZ, "%" PRId64, val);
161 			begin_end_element(fp, Propval, Name, pv->tp_name, Type,
162 			    Int64, Value, vbuf, NULL);
163 			break;
164 		}
165 		case TOPO_TYPE_UINT64: {
166 			uint64_t val;
167 
168 			if (topo_prop_get_uint64(node, pgname, pv->tp_name,
169 			    &val, &err) != 0)
170 				return;
171 
172 			(void) snprintf(vbuf, INT64BUFSZ, "0x%" PRIx64, val);
173 			begin_end_element(fp, Propval, Name, pv->tp_name, Type,
174 			    UInt64, Value, vbuf, NULL);
175 			break;
176 		}
177 		case TOPO_TYPE_DOUBLE: {
178 			double val;
179 			char *dblstr = NULL;
180 
181 			if (topo_prop_get_double(node, pgname, pv->tp_name,
182 			    &val, &err) != 0)
183 				return;
184 
185 			/*
186 			 * The %a format specifier allows floating point values
187 			 * to be serialized without losing precision.
188 			 */
189 			if (asprintf(&dblstr, "%a", val) < 0)
190 				return;
191 			begin_end_element(fp, Propval, Name, pv->tp_name, Type,
192 			    Double, Value, dblstr, NULL);
193 			free(dblstr);
194 			break;
195 		}
196 		case TOPO_TYPE_STRING: {
197 			char *strbuf = NULL;
198 
199 			if (topo_prop_get_string(node, pgname, pv->tp_name,
200 			    &strbuf, &err) != 0)
201 				return;
202 
203 			begin_end_element(fp, Propval, Name, pv->tp_name, Type,
204 			    String, Value, strbuf, NULL);
205 			topo_hdl_strfree(thp, strbuf);
206 			break;
207 		}
208 		case TOPO_TYPE_FMRI: {
209 			nvlist_t *val = NULL;
210 			char *fmristr = NULL;
211 
212 			if (topo_prop_get_fmri(node, pgname, pv->tp_name, &val,
213 			    &err) != 0 ||
214 			    topo_fmri_nvl2str(thp, val, &fmristr, &err) != 0) {
215 				nvlist_free(val);
216 				return;
217 			}
218 			nvlist_free(val);
219 			begin_end_element(fp, Propval, Name, pv->tp_name, Type,
220 			    FMRI, Value, fmristr, NULL);
221 			topo_hdl_strfree(thp, fmristr);
222 			break;
223 		}
224 		case TOPO_TYPE_INT32_ARRAY: {
225 			int32_t *val;
226 
227 			if (topo_prop_get_int32_array(node, pgname,
228 			    pv->tp_name, &val, &nelem, &err) != 0)
229 				return;
230 
231 			begin_element(fp, Propval, Name, pv->tp_name, Type,
232 			    Int32_Arr, NULL);
233 
234 			for (uint_t i = 0; i < nelem; i++) {
235 				(void) snprintf(vbuf, INT64BUFSZ, "%d", val[i]);
236 				begin_end_element(fp, Propitem, Value, vbuf,
237 				    NULL);
238 			}
239 
240 			topo_hdl_free(thp, val, nelem * sizeof (int32_t));
241 			end_element(fp, Propval);
242 			break;
243 		}
244 		case TOPO_TYPE_UINT32_ARRAY: {
245 			uint32_t *val;
246 
247 			if (topo_prop_get_uint32_array(node, pgname,
248 			    pv->tp_name, &val, &nelem, &err) != 0)
249 				return;
250 
251 			begin_element(fp, Propval, Name, pv->tp_name, Type,
252 			    UInt32_Arr, NULL);
253 
254 			for (uint_t i = 0; i < nelem; i++) {
255 				(void) snprintf(vbuf, INT64BUFSZ, "0x%x",
256 				    val[i]);
257 				begin_end_element(fp, Propitem, Value, vbuf,
258 				    NULL);
259 			}
260 
261 			topo_hdl_free(thp, val, nelem * sizeof (uint32_t));
262 			end_element(fp, Propval);
263 			break;
264 		}
265 		case TOPO_TYPE_INT64_ARRAY: {
266 			int64_t *val;
267 
268 			if (topo_prop_get_int64_array(node, pgname,
269 			    pv->tp_name, &val, &nelem, &err) != 0)
270 				return;
271 
272 			begin_element(fp, Propval, Name, pv->tp_name, Type,
273 			    Int64_Arr, NULL);
274 
275 			for (uint_t i = 0; i < nelem; i++) {
276 				(void) snprintf(vbuf, INT64BUFSZ, "%" PRId64,
277 				    val[i]);
278 				begin_end_element(fp, Propitem, Value, vbuf,
279 				    NULL);
280 			}
281 
282 			topo_hdl_free(thp, val, nelem * sizeof (int64_t));
283 			end_element(fp, Propval);
284 			break;
285 		}
286 		case TOPO_TYPE_UINT64_ARRAY: {
287 			uint64_t *val;
288 
289 			if (topo_prop_get_uint64_array(node, pgname,
290 			    pv->tp_name, &val, &nelem, &err) != 0)
291 				return;
292 
293 			begin_element(fp, Propval, Name, pv->tp_name, Type,
294 			    UInt64_Arr, NULL);
295 
296 			for (uint_t i = 0; i < nelem; i++) {
297 				(void) snprintf(vbuf, INT64BUFSZ, "0x%" PRIx64,
298 				    val[i]);
299 				begin_end_element(fp, Propitem, Value, vbuf,
300 				    NULL);
301 			}
302 
303 			topo_hdl_free(thp, val, nelem * sizeof (uint64_t));
304 			end_element(fp, Propval);
305 			break;
306 		}
307 		case TOPO_TYPE_STRING_ARRAY: {
308 			char **val;
309 
310 			if (topo_prop_get_string_array(node, pgname,
311 			    pv->tp_name, &val, &nelem, &err) != 0)
312 				return;
313 
314 			begin_element(fp, Propval, Name, pv->tp_name, Type,
315 			    String_Arr, NULL);
316 
317 			for (uint_t i = 0; i < nelem; i++) {
318 				begin_end_element(fp, Propitem, Value, val[i],
319 				    NULL);
320 			}
321 			for (uint_t i = 0; i < nelem; i++) {
322 				topo_hdl_strfree(thp, val[i]);
323 			}
324 			topo_hdl_free(thp, val, nelem * sizeof (char *));
325 
326 			end_element(fp, Propval);
327 			break;
328 		}
329 		case TOPO_TYPE_FMRI_ARRAY: {
330 			nvlist_t **val;
331 			char *fmristr = NULL;
332 			int ret;
333 
334 			if (topo_prop_get_fmri_array(node, pgname,
335 			    pv->tp_name, &val, &nelem, &err) != 0)
336 				return;
337 
338 			begin_element(fp, Propval, Name, pv->tp_name, Type,
339 			    FMRI_Arr, NULL);
340 
341 			for (uint_t i = 0; i < nelem; i++) {
342 				if ((ret = topo_fmri_nvl2str(thp, val[i],
343 				    &fmristr, &err)) != 0)
344 					break;
345 				begin_end_element(fp, Propitem, Value, fmristr,
346 				    NULL);
347 				topo_hdl_strfree(thp, fmristr);
348 			}
349 			for (uint_t i = 0; i < nelem; i++) {
350 				nvlist_free(val[i]);
351 			}
352 			topo_hdl_free(thp, val, nelem * sizeof (nvlist_t *));
353 			end_element(fp, Propval);
354 			break;
355 		}
356 		default:
357 			return;
358 	}
359 }
360 
361 static void
362 txml_print_pgroup(topo_hdl_t *thp, FILE *fp, tnode_t *node, topo_pgroup_t *pg)
363 {
364 	topo_ipgroup_info_t *pip = pg->tpg_info;
365 	topo_proplist_t *plp;
366 	const char *namestab, *datastab;
367 	char version[INT32BUFSZ];
368 
369 	namestab = topo_stability2name(pip->tpi_namestab);
370 	datastab = topo_stability2name(pip->tpi_datastab);
371 	(void) snprintf(version, INT32BUFSZ, "%d", pip->tpi_version);
372 	begin_element(fp, Propgrp, Name, pip->tpi_name, Namestab,
373 	    namestab, Datastab, datastab, Version, version, NULL);
374 	for (plp = topo_list_next(&pg->tpg_pvals); plp != NULL;
375 	    plp = topo_list_next(plp)) {
376 		txml_print_prop(thp, fp, node, pip->tpi_name, plp->tp_pval);
377 	}
378 	end_element(fp, Propgrp);
379 }
380 
381 static void
382 txml_print_dependents(topo_hdl_t *thp, FILE *fp, tnode_t *node)
383 {
384 	if (topo_list_next(&node->tn_children) == NULL)
385 		return;
386 
387 	if (txml_print_range(thp, fp, node, 1) == 1)
388 		end_element(fp, Dependents);
389 }
390 
391 static void
392 txml_print_node(topo_hdl_t *thp, FILE *fp, tnode_t *node)
393 {
394 	char inst[INT32BUFSZ];
395 	topo_pgroup_t *pg;
396 
397 	(void) snprintf(inst, INT32BUFSZ, "%d", node->tn_instance);
398 	/*
399 	 * The "static" attribute for the "node" element controls whether the
400 	 * node gets enumerated, if it doesn't already exist.  Setting it to
401 	 * true causes the node to not be created.  The primary use-case for
402 	 * setting it to true is when want to use XML to override a property
403 	 * value on a topo node that was already created by an enumerator
404 	 * module.  In this case we're trying to serialize the whole topology
405 	 * in a fashion such that we could reconstitute it from the generated
406 	 * XML. In which case, we relly need it to create all the nodes becuase
407 	 * no enumerator modules will be running.  Hence, we set static to
408 	 * false.
409 	 */
410 	begin_element(fp, Node, Instance, inst, Static, False, NULL);
411 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
412 	    pg = topo_list_next(pg)) {
413 		txml_print_pgroup(thp, fp, node, pg);
414 	}
415 	txml_print_dependents(thp, fp, node);
416 	end_element(fp, Node);
417 
418 }
419 
420 static int
421 txml_print_range(topo_hdl_t *thp, FILE *fp, tnode_t *node, int dependent)
422 {
423 	int i, create = 0, ret = 0;
424 	topo_nodehash_t *nhp;
425 	char min[INT32BUFSZ], max[INT32BUFSZ];
426 
427 	for (nhp = topo_list_next(&node->tn_children); nhp != NULL;
428 	    nhp = topo_list_next(nhp)) {
429 		(void) snprintf(min, INT32BUFSZ, "%d", nhp->th_range.tr_min);
430 		(void) snprintf(max, INT32BUFSZ, "%d", nhp->th_range.tr_max);
431 
432 		/*
433 		 * Some enumerators create empty ranges: make sure there
434 		 * are real nodes before creating this range
435 		 */
436 		for (i = 0; i < nhp->th_arrlen; ++i) {
437 			if (nhp->th_nodearr[i] != NULL)
438 				++create;
439 		}
440 		if (!create)
441 			continue;
442 
443 		if (dependent) {
444 			begin_element(fp, Dependents, Grouping, Children, NULL);
445 			dependent = 0;
446 			ret = 1;
447 		}
448 		begin_element(fp, Range, Name, nhp->th_name, Min, min, Max,
449 		    max, NULL);
450 		for (i = 0; i < nhp->th_arrlen; ++i) {
451 			if (nhp->th_nodearr[i] != NULL)
452 				txml_print_node(thp, fp, nhp->th_nodearr[i]);
453 		}
454 		end_element(fp, Range);
455 	}
456 
457 	return (ret);
458 }
459 
460 static void
461 txml_print_topology(topo_hdl_t *thp, FILE *fp, char *scheme, tnode_t *node)
462 {
463 	char *name;
464 
465 	if (thp->th_product != NULL)
466 		name = thp->th_product;
467 	else
468 		name = thp->th_platform;
469 
470 	begin_element(fp, Topology, Name, name, Scheme, scheme,
471 	    NULL);
472 	(void) txml_print_range(thp, fp, node, 0);
473 	end_element(fp, Topology);
474 
475 }
476 
477 int
478 topo_xml_print(topo_hdl_t *thp,  FILE *fp, const char *scheme, int *err)
479 {
480 	ttree_t *tp;
481 
482 	print_header(fp);
483 	for (tp = topo_list_next(&thp->th_trees); tp != NULL;
484 	    tp = topo_list_next(tp)) {
485 		if (strcmp(scheme, tp->tt_scheme) == 0) {
486 			txml_print_topology(thp, fp, tp->tt_scheme,
487 			    tp->tt_root);
488 			return (0);
489 		}
490 	}
491 
492 	*err = EINVAL;
493 	return (-1);
494 }
495