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 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 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 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 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 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 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 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