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
print_header(FILE * fp)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
begin_element(FILE * fp,const char * ename,...)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
begin_end_element(FILE * fp,const char * ename,...)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
end_element(FILE * fp,const char * ename)115 end_element(FILE *fp, const char *ename)
116 {
117 	(void) fprintf(fp, "</%s>\n", ename);
118 }
119 
120 static void
txml_print_prop(topo_hdl_t * thp,FILE * fp,tnode_t * node,const char * pgname,topo_propval_t * pv)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 			topo_hdl_strfreev(thp, val, nelem);
322 
323 			end_element(fp, Propval);
324 			break;
325 		}
326 		case TOPO_TYPE_FMRI_ARRAY: {
327 			nvlist_t **val;
328 			char *fmristr = NULL;
329 			int ret;
330 
331 			if (topo_prop_get_fmri_array(node, pgname,
332 			    pv->tp_name, &val, &nelem, &err) != 0)
333 				return;
334 
335 			begin_element(fp, Propval, Name, pv->tp_name, Type,
336 			    FMRI_Arr, NULL);
337 
338 			for (uint_t i = 0; i < nelem; i++) {
339 				if ((ret = topo_fmri_nvl2str(thp, val[i],
340 				    &fmristr, &err)) != 0)
341 					break;
342 				begin_end_element(fp, Propitem, Value, fmristr,
343 				    NULL);
344 				topo_hdl_strfree(thp, fmristr);
345 			}
346 			for (uint_t i = 0; i < nelem; i++) {
347 				nvlist_free(val[i]);
348 			}
349 			topo_hdl_free(thp, val, nelem * sizeof (nvlist_t *));
350 			end_element(fp, Propval);
351 			break;
352 		}
353 		default:
354 			return;
355 	}
356 }
357 
358 static void
txml_print_pgroup(topo_hdl_t * thp,FILE * fp,tnode_t * node,topo_pgroup_t * pg)359 txml_print_pgroup(topo_hdl_t *thp, FILE *fp, tnode_t *node, topo_pgroup_t *pg)
360 {
361 	topo_ipgroup_info_t *pip = pg->tpg_info;
362 	topo_proplist_t *plp;
363 	const char *namestab, *datastab;
364 	char version[INT32BUFSZ];
365 
366 	namestab = topo_stability2name(pip->tpi_namestab);
367 	datastab = topo_stability2name(pip->tpi_datastab);
368 	(void) snprintf(version, INT32BUFSZ, "%d", pip->tpi_version);
369 	begin_element(fp, Propgrp, Name, pip->tpi_name, Namestab,
370 	    namestab, Datastab, datastab, Version, version, NULL);
371 	for (plp = topo_list_next(&pg->tpg_pvals); plp != NULL;
372 	    plp = topo_list_next(plp)) {
373 		txml_print_prop(thp, fp, node, pip->tpi_name, plp->tp_pval);
374 	}
375 	end_element(fp, Propgrp);
376 }
377 
378 static void
txml_print_dependents(topo_hdl_t * thp,FILE * fp,tnode_t * node)379 txml_print_dependents(topo_hdl_t *thp, FILE *fp, tnode_t *node)
380 {
381 	if (topo_list_next(&node->tn_children) == NULL)
382 		return;
383 
384 	if (txml_print_range(thp, fp, node, 1) == 1)
385 		end_element(fp, Dependents);
386 }
387 
388 static void
txml_print_node(topo_hdl_t * thp,FILE * fp,tnode_t * node)389 txml_print_node(topo_hdl_t *thp, FILE *fp, tnode_t *node)
390 {
391 	char inst[INT32BUFSZ];
392 	topo_pgroup_t *pg;
393 
394 	(void) snprintf(inst, INT32BUFSZ, "%d", node->tn_instance);
395 	/*
396 	 * The "static" attribute for the "node" element controls whether the
397 	 * node gets enumerated, if it doesn't already exist.  Setting it to
398 	 * true causes the node to not be created.  The primary use-case for
399 	 * setting it to true is when want to use XML to override a property
400 	 * value on a topo node that was already created by an enumerator
401 	 * module.  In this case we're trying to serialize the whole topology
402 	 * in a fashion such that we could reconstitute it from the generated
403 	 * XML. In which case, we relly need it to create all the nodes becuase
404 	 * no enumerator modules will be running.  Hence, we set static to
405 	 * false.
406 	 */
407 	begin_element(fp, Node, Instance, inst, Static, False, NULL);
408 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
409 	    pg = topo_list_next(pg)) {
410 		txml_print_pgroup(thp, fp, node, pg);
411 	}
412 	txml_print_dependents(thp, fp, node);
413 	end_element(fp, Node);
414 
415 }
416 
417 static int
txml_print_range(topo_hdl_t * thp,FILE * fp,tnode_t * node,int dependent)418 txml_print_range(topo_hdl_t *thp, FILE *fp, tnode_t *node, int dependent)
419 {
420 	int i, create = 0, ret = 0;
421 	topo_nodehash_t *nhp;
422 	char min[INT32BUFSZ], max[INT32BUFSZ];
423 
424 	for (nhp = topo_list_next(&node->tn_children); nhp != NULL;
425 	    nhp = topo_list_next(nhp)) {
426 		(void) snprintf(min, INT32BUFSZ, "%d", nhp->th_range.tr_min);
427 		(void) snprintf(max, INT32BUFSZ, "%d", nhp->th_range.tr_max);
428 
429 		/*
430 		 * Some enumerators create empty ranges: make sure there
431 		 * are real nodes before creating this range
432 		 */
433 		for (i = 0; i < nhp->th_arrlen; ++i) {
434 			if (nhp->th_nodearr[i] != NULL)
435 				++create;
436 		}
437 		if (!create)
438 			continue;
439 
440 		if (dependent) {
441 			begin_element(fp, Dependents, Grouping, Children, NULL);
442 			dependent = 0;
443 			ret = 1;
444 		}
445 		begin_element(fp, Range, Name, nhp->th_name, Min, min, Max,
446 		    max, NULL);
447 		for (i = 0; i < nhp->th_arrlen; ++i) {
448 			if (nhp->th_nodearr[i] != NULL)
449 				txml_print_node(thp, fp, nhp->th_nodearr[i]);
450 		}
451 		end_element(fp, Range);
452 	}
453 
454 	return (ret);
455 }
456 
457 static void
txml_print_topology(topo_hdl_t * thp,FILE * fp,char * scheme,tnode_t * node)458 txml_print_topology(topo_hdl_t *thp, FILE *fp, char *scheme, tnode_t *node)
459 {
460 	char *name;
461 
462 	if (thp->th_product != NULL)
463 		name = thp->th_product;
464 	else
465 		name = thp->th_platform;
466 
467 	begin_element(fp, Topology, Name, name, Scheme, scheme,
468 	    NULL);
469 	(void) txml_print_range(thp, fp, node, 0);
470 	end_element(fp, Topology);
471 
472 }
473 
474 int
topo_xml_print(topo_hdl_t * thp,FILE * fp,const char * scheme,int * err)475 topo_xml_print(topo_hdl_t *thp,  FILE *fp, const char *scheme, int *err)
476 {
477 	ttree_t *tp;
478 
479 	print_header(fp);
480 	for (tp = topo_list_next(&thp->th_trees); tp != NULL;
481 	    tp = topo_list_next(tp)) {
482 		if (strcmp(scheme, tp->tt_scheme) == 0) {
483 			txml_print_topology(thp, fp, tp->tt_scheme,
484 			    tp->tt_root);
485 			return (0);
486 		}
487 	}
488 
489 	*err = EINVAL;
490 	return (-1);
491 }
492