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