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 const char *name = thp->th_product;
461
462 begin_element(fp, Topology, Name, name, Scheme, scheme,
463 NULL);
464 (void) txml_print_range(thp, fp, node, 0);
465 end_element(fp, Topology);
466 }
467
468 int
topo_xml_print(topo_hdl_t * thp,FILE * fp,const char * scheme,int * err)469 topo_xml_print(topo_hdl_t *thp, FILE *fp, const char *scheme, int *err)
470 {
471 ttree_t *tp;
472
473 print_header(fp);
474 for (tp = topo_list_next(&thp->th_trees); tp != NULL;
475 tp = topo_list_next(tp)) {
476 if (strcmp(scheme, tp->tt_scheme) == 0) {
477 txml_print_topology(thp, fp, tp->tt_scheme,
478 tp->tt_root);
479 return (0);
480 }
481 }
482
483 *err = EINVAL;
484 return (-1);
485 }
486