1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2020 Joyent, Inc. 14 */ 15 16 /* 17 * This file implements the following two routines for serializing and 18 * deserializing digraphs to/from XML, respectively: 19 * 20 * topo_digraph_serialize() 21 * topo_digraph_deserialize() 22 * 23 * Refer to the following file for the XML schema being used: 24 * usr/src/lib/fm/topo/maps/common/digraph-topology.dtd.1 25 */ 26 #include <time.h> 27 #include <sys/utsname.h> 28 #include <libxml/parser.h> 29 #include <libtopo.h> 30 31 #include <topo_digraph.h> 32 #include <topo_digraph_xml.h> 33 34 #define __STDC_FORMAT_MACROS 35 #include <inttypes.h> 36 37 extern int xmlattr_to_int(topo_mod_t *, xmlNodePtr, const char *, uint64_t *); 38 static int serialize_nvpair(topo_hdl_t *thp, FILE *, uint_t, const char *, 39 nvpair_t *); 40 41 static void 42 tdg_xml_nvstring(FILE *fp, uint_t pad, const char *name, const char *value) 43 { 44 (void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%s' />\n", pad, "", 45 TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_STRING, 46 TDG_XML_VALUE, value); 47 } 48 49 static void 50 tdg_xml_nvlist(FILE *fp, uint_t pad, const char *name) 51 { 52 (void) fprintf(fp, "%*s<%s %s='%s' %s='%s'>\n", pad, "", 53 TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_NVLIST); 54 } 55 56 static void 57 tdg_xml_nvuint8(FILE *fp, uint_t pad, const char *name, const uint8_t value) 58 { 59 (void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%u' />\n", pad, "", 60 TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_UINT8, 61 TDG_XML_VALUE, value); 62 } 63 64 static void 65 tdg_xml_nvint8(FILE *fp, uint_t pad, const char *name, const uint8_t value) 66 { 67 (void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%d' />\n", pad, "", 68 TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_INT8, 69 TDG_XML_VALUE, value); 70 } 71 72 static void 73 tdg_xml_nvuint16(FILE *fp, uint_t pad, const char *name, const uint8_t value) 74 { 75 (void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%u' />\n", pad, "", 76 TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_UINT16, 77 TDG_XML_VALUE, value); 78 } 79 80 static void 81 tdg_xml_nvint16(FILE *fp, uint_t pad, const char *name, const uint8_t value) 82 { 83 (void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%d' />\n", pad, "", 84 TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_INT16, 85 TDG_XML_VALUE, value); 86 } 87 88 static void 89 tdg_xml_nvuint32(FILE *fp, uint_t pad, const char *name, const uint32_t value) 90 { 91 (void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%u' />\n", pad, "", 92 TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_UINT32, 93 TDG_XML_VALUE, value); 94 } 95 96 static void 97 tdg_xml_nvint32(FILE *fp, uint_t pad, const char *name, const int32_t value) 98 { 99 (void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%d' />\n", pad, "", 100 TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_UINT32, 101 TDG_XML_VALUE, value); 102 } 103 104 static void 105 tdg_xml_nvuint64(FILE *fp, uint_t pad, const char *name, const uint64_t value) 106 { 107 (void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='0x%" PRIx64 "' />\n", 108 pad, "", TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, 109 TDG_XML_UINT64, TDG_XML_VALUE, value); 110 } 111 112 static void 113 tdg_xml_nvint64(FILE *fp, uint_t pad, const char *name, const int64_t value) 114 { 115 (void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%" PRIi64 "' />\n", pad, 116 "", TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, 117 TDG_XML_UINT64, TDG_XML_VALUE, value); 118 } 119 120 static void 121 tdg_xml_nvdbl(FILE *fp, uint_t pad, const char *name, const double value) 122 { 123 (void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%lf' />\n", pad, "" 124 TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_UINT64, 125 TDG_XML_VALUE, value); 126 } 127 128 static void 129 tdg_xml_nvarray(FILE *fp, uint_t pad, const char *name, const char *type) 130 { 131 (void) fprintf(fp, "%*s<%s %s='%s' %s='%s'>\n", pad, "", 132 TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, type); 133 } 134 135 static void 136 tdg_xml_nvint32arr(FILE *fp, uint_t pad, const char *name, int32_t *val, 137 uint_t nelems) 138 { 139 (void) fprintf(fp, "%*s<%s %s='%s' %s='%s'>\n", pad, "", 140 TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, 141 TDG_XML_INT32_ARR); 142 143 for (uint_t i = 0; i < nelems; i++) { 144 (void) fprintf(fp, "%*s<%s %s='%d' />\n", (pad + 2), "", 145 TDG_XML_NVPAIR, TDG_XML_VALUE, val[i]); 146 } 147 (void) fprintf(fp, "%*s</%s>\n", pad, "", TDG_XML_NVPAIR); 148 } 149 150 static void 151 tdg_xml_nvuint32arr(FILE *fp, uint_t pad, const char *name, uint32_t *val, 152 uint_t nelems) 153 { 154 (void) fprintf(fp, "%*s<%s %s='%s' %s='%s'>\n", pad, "", 155 TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, 156 TDG_XML_UINT32_ARR); 157 158 for (uint_t i = 0; i < nelems; i++) { 159 (void) fprintf(fp, "%*s<%s %s='%d' />\n", (pad + 2), "", 160 TDG_XML_NVPAIR, TDG_XML_VALUE, val[i]); 161 } 162 (void) fprintf(fp, "%*s</%s>\n", pad, "", TDG_XML_NVPAIR); 163 } 164 165 static void 166 tdg_xml_nvint64arr(FILE *fp, uint_t pad, const char *name, int64_t *val, 167 uint_t nelems) 168 { 169 (void) fprintf(fp, "%*s<%s %s='%s' %s='%s'>\n", pad, "", 170 TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, 171 TDG_XML_INT64_ARR); 172 173 for (uint_t i = 0; i < nelems; i++) { 174 (void) fprintf(fp, "%*s<%s %s='%" PRIi64 "' />\n", (pad + 2), 175 "", TDG_XML_NVPAIR, TDG_XML_VALUE, val[i]); 176 } 177 (void) fprintf(fp, "%*s</%s>\n", pad, "", TDG_XML_NVPAIR); 178 } 179 180 static void 181 tdg_xml_nvuint64arr(FILE *fp, uint_t pad, const char *name, uint64_t *val, 182 uint_t nelems) 183 { 184 (void) fprintf(fp, "%*s<%s %s='%s' %s='%s'>\n", pad, "", 185 TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, 186 TDG_XML_UINT64_ARR); 187 188 for (uint_t i = 0; i < nelems; i++) { 189 (void) fprintf(fp, "%*s<%s %s='0x%" PRIx64 "' />\n", (pad + 2), 190 "", TDG_XML_NVPAIR, TDG_XML_VALUE, val[i]); 191 } 192 (void) fprintf(fp, "%*s</%s>\n", pad, "", TDG_XML_NVPAIR); 193 } 194 195 static int 196 serialize_nvpair_nvlist(topo_hdl_t *thp, FILE *fp, uint_t pad, 197 const char *name, nvlist_t *nvl) 198 { 199 nvpair_t *elem = NULL; 200 201 tdg_xml_nvlist(fp, pad, name); 202 203 (void) fprintf(fp, "%*s<%s>\n", pad, "", TDG_XML_NVLIST); 204 205 while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) { 206 char *nvname = nvpair_name(elem); 207 208 if (serialize_nvpair(thp, fp, (pad + 2), nvname, elem) != 0) { 209 /* errno set */ 210 return (-1); 211 } 212 } 213 214 (void) fprintf(fp, "%*s</%s>\n", pad, "", TDG_XML_NVLIST); 215 (void) fprintf(fp, "%*s</%s> <!-- %s -->\n", pad, "", TDG_XML_NVPAIR, 216 name); 217 218 return (0); 219 } 220 221 static int 222 serialize_nvpair(topo_hdl_t *thp, FILE *fp, uint_t pad, const char *pname, 223 nvpair_t *nvp) 224 { 225 data_type_t type = nvpair_type(nvp); 226 227 switch (type) { 228 case DATA_TYPE_INT8: { 229 int8_t val; 230 231 if (nvpair_value_int8(nvp, &val) != 0) 232 return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN)); 233 234 tdg_xml_nvint8(fp, pad, pname, val); 235 break; 236 } 237 case DATA_TYPE_UINT8: { 238 uint8_t val; 239 240 if (nvpair_value_uint8(nvp, &val) != 0) 241 return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN)); 242 243 tdg_xml_nvuint8(fp, pad, pname, val); 244 break; 245 } 246 case DATA_TYPE_INT16: { 247 int16_t val; 248 249 if (nvpair_value_int16(nvp, &val) != 0) 250 return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN)); 251 252 tdg_xml_nvint16(fp, pad, pname, val); 253 break; 254 } 255 case DATA_TYPE_UINT16: { 256 uint16_t val; 257 258 if (nvpair_value_uint16(nvp, &val) != 0) 259 return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN)); 260 261 tdg_xml_nvuint16(fp, pad, pname, val); 262 break; 263 } 264 case DATA_TYPE_INT32: { 265 int32_t val; 266 267 if (nvpair_value_int32(nvp, &val) != 0) 268 return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN)); 269 270 tdg_xml_nvint32(fp, pad, pname, val); 271 break; 272 } 273 case DATA_TYPE_UINT32: { 274 uint32_t val; 275 276 if (nvpair_value_uint32(nvp, &val) != 0) 277 return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN)); 278 279 tdg_xml_nvuint32(fp, pad, pname, val); 280 break; 281 } 282 case DATA_TYPE_INT64: { 283 int64_t val; 284 285 if (nvpair_value_int64(nvp, &val) != 0) 286 return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN)); 287 288 tdg_xml_nvint64(fp, pad, pname, val); 289 break; 290 } 291 case DATA_TYPE_UINT64: { 292 uint64_t val; 293 294 if (nvpair_value_uint64(nvp, &val) != 0) 295 return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN)); 296 297 tdg_xml_nvuint64(fp, pad, pname, val); 298 break; 299 } 300 case DATA_TYPE_DOUBLE: { 301 double val; 302 303 if (nvpair_value_double(nvp, &val) != 0) 304 return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN)); 305 306 tdg_xml_nvdbl(fp, pad, pname, val); 307 break; 308 } 309 case DATA_TYPE_STRING: { 310 char *val; 311 312 if (nvpair_value_string(nvp, &val) != 0) 313 return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN)); 314 315 tdg_xml_nvstring(fp, pad, pname, val); 316 break; 317 } 318 case DATA_TYPE_NVLIST: { 319 nvlist_t *nvl; 320 321 if (nvpair_value_nvlist(nvp, &nvl) != 0) 322 return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN)); 323 324 if (serialize_nvpair_nvlist(thp, fp, pad + 2, pname, 325 nvl) != 0) { 326 return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN)); 327 } 328 break; 329 } 330 case DATA_TYPE_INT32_ARRAY: { 331 uint_t nelems; 332 int32_t *val; 333 334 if (nvpair_value_int32_array(nvp, &val, &nelems) != 0) 335 return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN)); 336 337 tdg_xml_nvint32arr(fp, pad + 2, pname, val, nelems); 338 339 break; 340 } 341 case DATA_TYPE_UINT32_ARRAY: { 342 uint_t nelems; 343 uint32_t *val; 344 345 if (nvpair_value_uint32_array(nvp, &val, &nelems) != 0) 346 return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN)); 347 348 tdg_xml_nvuint32arr(fp, pad + 2, pname, val, nelems); 349 350 break; 351 } 352 case DATA_TYPE_INT64_ARRAY: { 353 uint_t nelems; 354 int64_t *val; 355 356 if (nvpair_value_int64_array(nvp, &val, &nelems) != 0) 357 return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN)); 358 359 tdg_xml_nvint64arr(fp, pad + 2, pname, val, nelems); 360 361 break; 362 } 363 case DATA_TYPE_UINT64_ARRAY: { 364 uint_t nelems; 365 uint64_t *val; 366 367 if (nvpair_value_uint64_array(nvp, &val, &nelems) != 0) 368 return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN)); 369 370 tdg_xml_nvuint64arr(fp, pad + 2, pname, val, nelems); 371 372 break; 373 } 374 case DATA_TYPE_STRING_ARRAY: { 375 uint_t nelems; 376 char **val; 377 378 if (nvpair_value_string_array(nvp, &val, &nelems) != 0) 379 return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN)); 380 381 tdg_xml_nvarray(fp, pad, pname, TDG_XML_STRING_ARR); 382 for (uint_t i = 0; i < nelems; i++) { 383 (void) fprintf(fp, "%*s<%s %s='%s' />\n", 384 (pad + 2), "", TDG_XML_NVPAIR, 385 TDG_XML_VALUE, val[i]); 386 } 387 (void) fprintf(fp, "%*s</%s>\n", (pad + 2), "", 388 TDG_XML_NVPAIR); 389 390 break; 391 } 392 case DATA_TYPE_NVLIST_ARRAY: { 393 uint_t nelems; 394 nvlist_t **val; 395 396 if (nvpair_value_nvlist_array(nvp, &val, &nelems) != 0) 397 return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN)); 398 399 tdg_xml_nvarray(fp, pad, pname, TDG_XML_NVLIST_ARR); 400 for (uint_t i = 0; i < nelems; i++) { 401 nvpair_t *elem = NULL; 402 403 (void) fprintf(fp, "%*s<%s>\n", (pad + 2), "", 404 TDG_XML_NVLIST); 405 406 while ((elem = nvlist_next_nvpair(val[i], 407 elem)) != NULL) { 408 char *nvname = nvpair_name(elem); 409 410 if (serialize_nvpair(thp, fp, 411 (pad + 4), nvname, elem) != 0) { 412 /* errno set */ 413 return (-1); 414 } 415 } 416 417 (void) fprintf(fp, "%*s</%s>\n", (pad + 2), "", 418 TDG_XML_NVLIST); 419 } 420 (void) fprintf(fp, "%*s</%s>\n", pad, "", 421 TDG_XML_NVPAIR); 422 423 break; 424 } 425 default: 426 topo_dprintf(thp, TOPO_DBG_XML, "Invalid nvpair data " 427 "type: %d\n", type); 428 (void) topo_hdl_seterrno(thp, ETOPO_MOD_XENUM); 429 return (-1); 430 } 431 return (0); 432 } 433 434 static int 435 serialize_edge(topo_hdl_t *thp, topo_edge_t *edge, boolean_t last_edge, 436 void *arg) 437 { 438 nvlist_t *fmri = NULL; 439 char *fmristr; 440 int err; 441 tnode_t *tn; 442 FILE *fp = (FILE *)arg; 443 444 tn = topo_vertex_node(edge->tve_vertex); 445 if (topo_node_resource(tn, &fmri, &err) != 0 || 446 topo_fmri_nvl2str(thp, fmri, &fmristr, &err) != 0) { 447 /* errno set */ 448 nvlist_free(fmri); 449 return (TOPO_WALK_ERR); 450 } 451 nvlist_free(fmri); 452 453 (void) fprintf(fp, "%*s<%s %s='%s' />\n", 4, "", TDG_XML_EDGE, 454 TDG_XML_FMRI, fmristr); 455 topo_hdl_strfree(thp, fmristr); 456 457 return (TOPO_WALK_NEXT); 458 } 459 460 /* 461 * Some node property values aren't available unless we go through the libtopo 462 * API's topo_prop_get_* routines. We do that here to make sure the nodes have 463 * all of their properties populated, then we vector off to type-specific 464 * XML serialization functions. 465 */ 466 static int 467 serialize_property(topo_hdl_t *thp, FILE *fp, uint_t pad, tnode_t *tn, 468 topo_propval_t *pv, const char *pgname) 469 { 470 topo_type_t type = pv->tp_type; 471 const char *pname = pv->tp_name; 472 int err; 473 char *name = TDG_XML_PROP_VALUE; 474 475 switch (type) { 476 case TOPO_TYPE_INT32: { 477 int32_t val; 478 479 if (topo_prop_get_int32(tn, pgname, pname, &val, 480 &err) != 0) 481 return (-1); 482 483 tdg_xml_nvint32(fp, pad, name, val); 484 break; 485 } 486 case TOPO_TYPE_UINT32: { 487 uint32_t val; 488 489 if (topo_prop_get_uint32(tn, pgname, pname, &val, 490 &err) != 0) 491 return (-1); 492 493 tdg_xml_nvuint32(fp, pad, name, val); 494 break; 495 } 496 case TOPO_TYPE_INT64: { 497 int64_t val; 498 499 if (topo_prop_get_int64(tn, pgname, pname, &val, 500 &err) != 0) 501 return (-1); 502 503 tdg_xml_nvint64(fp, pad, name, val); 504 break; 505 } 506 case TOPO_TYPE_UINT64: { 507 uint64_t val; 508 509 if (topo_prop_get_uint64(tn, pgname, pname, &val, 510 &err) != 0) 511 return (-1); 512 513 tdg_xml_nvuint64(fp, pad, name, val); 514 break; 515 } 516 case TOPO_TYPE_STRING: { 517 char *val; 518 519 if (topo_prop_get_string(tn, pgname, pname, &val, 520 &err) != 0) 521 return (-1); 522 523 tdg_xml_nvstring(fp, pad, name, val); 524 525 topo_hdl_strfree(thp, val); 526 break; 527 } 528 case TOPO_TYPE_FMRI: { 529 nvlist_t *nvl; 530 531 if (topo_prop_get_fmri(tn, pgname, pname, &nvl, 532 &err) != 0) 533 return (-1); 534 535 if (serialize_nvpair_nvlist(thp, fp, pad + 2, name, 536 nvl) != 0) { 537 nvlist_free(nvl); 538 return (-1); 539 } 540 541 nvlist_free(nvl); 542 break; 543 } 544 case TOPO_TYPE_INT32_ARRAY: { 545 uint_t nelems; 546 int32_t *val; 547 548 if (topo_prop_get_int32_array(tn, pgname, pname, &val, 549 &nelems, &err) != 0) 550 return (-1); 551 552 tdg_xml_nvint32arr(fp, pad, pname, val, nelems); 553 topo_hdl_free(thp, val, (sizeof (int32_t) * nelems)); 554 break; 555 } 556 case TOPO_TYPE_UINT32_ARRAY: { 557 uint_t nelems; 558 uint32_t *val; 559 560 if (topo_prop_get_uint32_array(tn, pgname, pname, &val, 561 &nelems, &err) != 0) 562 return (-1); 563 564 tdg_xml_nvuint32arr(fp, pad, pname, val, nelems); 565 topo_hdl_free(thp, val, (sizeof (uint32_t) * nelems)); 566 break; 567 } 568 case TOPO_TYPE_INT64_ARRAY: { 569 uint_t nelems; 570 int64_t *val; 571 572 if (topo_prop_get_int64_array(tn, pgname, pname, &val, 573 &nelems, &err) != 0) 574 return (-1); 575 576 tdg_xml_nvint64arr(fp, pad, pname, val, nelems); 577 topo_hdl_free(thp, val, (sizeof (int64_t) * nelems)); 578 break; 579 } 580 case TOPO_TYPE_UINT64_ARRAY: { 581 uint_t nelems; 582 uint64_t *val; 583 584 if (topo_prop_get_uint64_array(tn, pgname, pname, &val, 585 &nelems, &err) != 0) 586 return (-1); 587 588 tdg_xml_nvuint64arr(fp, pad, pname, val, nelems); 589 topo_hdl_free(thp, val, (sizeof (uint64_t) * nelems)); 590 break; 591 } 592 default: 593 topo_dprintf(thp, TOPO_DBG_XML, "Invalid nvpair data " 594 "type: %d\n", type); 595 (void) topo_hdl_seterrno(thp, ETOPO_MOD_XENUM); 596 return (-1); 597 } 598 return (0); 599 } 600 601 static int 602 serialize_pgroups(topo_hdl_t *thp, FILE *fp, tnode_t *tn) 603 { 604 topo_pgroup_t *pg; 605 uint_t npgs = 0; 606 607 for (pg = topo_list_next(&tn->tn_pgroups); pg != NULL; 608 pg = topo_list_next(pg)) { 609 610 npgs++; 611 } 612 613 tdg_xml_nvarray(fp, 2, TDG_XML_PGROUPS, TDG_XML_NVLIST_ARR); 614 615 for (pg = topo_list_next(&tn->tn_pgroups); pg != NULL; 616 pg = topo_list_next(pg)) { 617 618 topo_proplist_t *pvl; 619 uint_t nprops = 0; 620 621 (void) fprintf(fp, "%*s<%s>\n", 4, "", TDG_XML_NVLIST); 622 tdg_xml_nvstring(fp, 6, TOPO_PROP_GROUP_NAME, 623 pg->tpg_info->tpi_name); 624 625 for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL; 626 pvl = topo_list_next(pvl)) 627 nprops++; 628 629 tdg_xml_nvarray(fp, 6, TDG_XML_PVALS, TDG_XML_NVLIST_ARR); 630 631 for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL; 632 pvl = topo_list_next(pvl)) { 633 634 topo_propval_t *pv = pvl->tp_pval; 635 636 (void) fprintf(fp, "%*s<%s>\n", 8, "", TDG_XML_NVLIST); 637 tdg_xml_nvstring(fp, 10, TDG_XML_PROP_NAME, 638 pv->tp_name); 639 tdg_xml_nvuint32(fp, 10, TDG_XML_PROP_TYPE, 640 pv->tp_type); 641 642 if (serialize_property(thp, fp, 10, tn, pv, 643 pg->tpg_info->tpi_name) != 0) { 644 /* errno set */ 645 return (-1); 646 } 647 (void) fprintf(fp, "%*s</%s>\n", 8, "", 648 TDG_XML_NVLIST); 649 } 650 651 (void) fprintf(fp, "%*s</%s> <!-- %s -->\n", 6, "", 652 TDG_XML_NVPAIR, TDG_XML_PVALS); 653 (void) fprintf(fp, "%*s</%s>\n", 4, "", TDG_XML_NVLIST); 654 } 655 (void) fprintf(fp, "%*s</%s> <!-- %s -->\n", 2, "", TDG_XML_NVPAIR, 656 TDG_XML_PGROUPS); 657 658 return (0); 659 } 660 661 static int 662 serialize_vertex(topo_hdl_t *thp, topo_vertex_t *vtx, boolean_t last_vtx, 663 void *arg) 664 { 665 nvlist_t *fmri = NULL; 666 char *fmristr; 667 tnode_t *tn; 668 int err; 669 FILE *fp = (FILE *)arg; 670 671 tn = topo_vertex_node(vtx); 672 if (topo_node_resource(tn, &fmri, &err) != 0 || 673 topo_fmri_nvl2str(thp, fmri, &fmristr, &err) != 0) { 674 /* errno set */ 675 nvlist_free(fmri); 676 return (TOPO_WALK_ERR); 677 } 678 nvlist_free(fmri); 679 680 (void) fprintf(fp, "<%s %s='%s' %s='0x%" PRIx64 "' %s='%s'>\n", 681 TDG_XML_VERTEX, TDG_XML_NAME, topo_node_name(tn), 682 TDG_XML_INSTANCE, topo_node_instance(tn), 683 TDG_XML_FMRI, fmristr); 684 685 topo_hdl_strfree(thp, fmristr); 686 687 if (serialize_pgroups(thp, fp, tn) != 0) { 688 /* errno set */ 689 return (TOPO_WALK_ERR); 690 } 691 692 if (vtx->tvt_noutgoing != 0) { 693 (void) fprintf(fp, " <%s>\n", TDG_XML_OUTEDGES); 694 695 if (topo_edge_iter(thp, vtx, serialize_edge, fp) != 0) { 696 topo_dprintf(thp, TOPO_DBG_XML, "failed to iterate " 697 "edges on %s=%" PRIx64 "\n", topo_node_name(tn), 698 topo_node_instance(tn)); 699 /* errno set */ 700 return (TOPO_WALK_ERR); 701 } 702 (void) fprintf(fp, " </%s>\n", TDG_XML_OUTEDGES); 703 } 704 (void) fprintf(fp, "</%s>\n\n", TDG_XML_VERTEX); 705 706 return (TOPO_WALK_NEXT); 707 } 708 709 /* 710 * This function takes a topo_digraph_t and serializes it to XML. 711 * 712 * The schema is described in detail in: 713 * usr/src/lib/fm/topo/maps/common/digraph-topology.dtd.1 714 * 715 * On success, this function writes the XML to the specified file and 716 * returns 0. 717 * 718 * On failure, this function returns -1. 719 */ 720 int 721 topo_digraph_serialize(topo_hdl_t *thp, topo_digraph_t *tdg, FILE *fp) 722 { 723 struct utsname uts = { 0 }; 724 time_t utc_time; 725 char tstamp[64]; 726 int ret; 727 728 if ((ret = uname(&uts)) < 0) { 729 topo_dprintf(thp, TOPO_DBG_XML, "uname failed (ret = %d)\n", 730 ret); 731 return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN)); 732 } 733 734 if (time(&utc_time) < 0) { 735 topo_dprintf(thp, TOPO_DBG_XML, "uname failed (%s)\n", 736 strerror(errno)); 737 return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN)); 738 } 739 740 /* 741 * strftime returns 0 if the size of the result is larger than the 742 * buffer size passed in to it. We've sized tstamp to be pretty 743 * large, so this really shouldn't happen. 744 */ 745 if (strftime(tstamp, sizeof (tstamp), "%Y-%m-%dT%H:%M:%SZ", 746 gmtime(&utc_time)) == 0) { 747 topo_dprintf(thp, TOPO_DBG_XML, "strftime failed\n"); 748 return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN)); 749 } 750 751 (void) fprintf(fp, "<?xml version=\"1.0\"?>\n"); 752 (void) fprintf(fp, "<!DOCTYPE topology SYSTEM \"%s\">\n", TDG_DTD); 753 (void) fprintf(fp, "<%s %s='%s' %s='%s' %s='%s' %s='%s' %s='%s'>\n", 754 TDG_XML_TOPO_DIGRAPH, TDG_XML_SCHEME, tdg->tdg_scheme, 755 TDG_XML_NODENAME, uts.nodename, TDG_XML_OSVERSION, uts.version, 756 TDG_XML_PRODUCT, thp->th_product, TDG_XML_TSTAMP, tstamp); 757 (void) fprintf(fp, "<%s>\n", TDG_XML_VERTICES); 758 759 if (topo_vertex_iter(thp, tdg, serialize_vertex, fp) != 0) { 760 topo_dprintf(thp, TOPO_DBG_XML, "\nfailed to iterate " 761 "vertices\n"); 762 /* errno set */ 763 return (-1); 764 } 765 766 (void) fprintf(fp, "</%s>\n", TDG_XML_VERTICES); 767 (void) fprintf(fp, "</%s>\n", TDG_XML_TOPO_DIGRAPH); 768 769 if (ferror(fp) != 0) { 770 topo_dprintf(thp, TOPO_DBG_XML, "An unknown error ocurrred " 771 "while writing out the serialize topology."); 772 return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN)); 773 } 774 return (0); 775 } 776 777 static xmlNodePtr 778 get_child_by_name(xmlNodePtr xn, xmlChar *name) 779 { 780 for (xmlNodePtr cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) 781 if (xmlStrcmp(cn->name, name) == 0) 782 return (cn); 783 784 return (NULL); 785 } 786 787 static void 788 dump_xml_node(topo_hdl_t *thp, xmlNodePtr xn) 789 { 790 topo_dprintf(thp, TOPO_DBG_XML, "node: %s", (char *)xn->name); 791 for (xmlAttrPtr attr = xn->properties; attr != NULL; attr = attr->next) 792 topo_dprintf(thp, TOPO_DBG_XML, "attribute: %s", 793 (char *)attr->name); 794 795 for (xmlNodePtr cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) 796 topo_dprintf(thp, TOPO_DBG_XML, "\tchild node: %s", 797 (char *)cn->name); 798 } 799 800 struct edge_cb_arg { 801 const char *from_fmri; 802 const char *to_fmri; 803 topo_vertex_t *from_vtx; 804 topo_vertex_t *to_vtx; 805 }; 806 807 static int 808 edge_cb(topo_hdl_t *thp, topo_vertex_t *vtx, boolean_t last_vtx, void *arg) 809 { 810 struct edge_cb_arg *cbarg = arg; 811 tnode_t *tn; 812 nvlist_t *fmri = NULL; 813 char *fmristr = NULL; 814 int err; 815 816 tn = topo_vertex_node(vtx); 817 if (topo_node_resource(tn, &fmri, &err) != 0 || 818 topo_fmri_nvl2str(thp, fmri, &fmristr, &err) != 0) { 819 topo_dprintf(thp, TOPO_DBG_XML, "failed to convert FMRI for " 820 "%s=%" PRIx64 " to a string\n", topo_node_name(tn), 821 topo_node_instance(tn)); 822 if (thp->th_debug & TOPO_DBG_XML) 823 nvlist_print(stdout, fmri); 824 nvlist_free(fmri); 825 return (TOPO_WALK_ERR); 826 } 827 nvlist_free(fmri); 828 829 if (strcmp(fmristr, cbarg->from_fmri) == 0) 830 cbarg->from_vtx = vtx; 831 else if (strcmp(fmristr, cbarg->to_fmri) == 0) 832 cbarg->to_vtx = vtx; 833 834 topo_hdl_strfree(thp, fmristr); 835 if (cbarg->from_vtx != NULL && cbarg->to_vtx != NULL) 836 return (TOPO_WALK_TERMINATE); 837 else 838 return (TOPO_WALK_NEXT); 839 } 840 841 static int 842 deserialize_edges(topo_hdl_t *thp, topo_mod_t *mod, topo_digraph_t *tdg, 843 xmlChar *from_fmri, xmlNodePtr xn) 844 { 845 for (xmlNodePtr cn = xn->xmlChildrenNode; cn != NULL; 846 cn = cn->next) { 847 xmlChar *fmri; 848 struct edge_cb_arg cbarg = { 0 }; 849 850 if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_EDGE) != 0) 851 continue; 852 853 if ((fmri = xmlGetProp(cn, (xmlChar *)TDG_XML_FMRI)) == NULL) { 854 topo_dprintf(thp, TOPO_DBG_XML, 855 "error parsing %s element", (char *)cn->name); 856 dump_xml_node(thp, cn); 857 return (-1); 858 } 859 cbarg.from_fmri = (char *)from_fmri; 860 cbarg.to_fmri = (char *)fmri; 861 862 if (topo_vertex_iter(mod->tm_hdl, tdg, edge_cb, &cbarg) != 0) { 863 xmlFree(fmri); 864 return (-1); 865 } 866 xmlFree(fmri); 867 868 if (cbarg.from_vtx == NULL || cbarg.to_vtx == NULL) { 869 return (-1); 870 } 871 if (topo_edge_new(mod, cbarg.from_vtx, cbarg.to_vtx) != 0) { 872 return (-1); 873 } 874 } 875 876 return (0); 877 } 878 879 static int 880 add_edges(topo_hdl_t *thp, topo_mod_t *mod, topo_digraph_t *tdg, 881 xmlNodePtr xn) 882 { 883 int ret = -1; 884 nvlist_t *props = NULL; 885 xmlChar *name = NULL, *fmri = NULL; 886 xmlNodePtr cn; 887 uint64_t inst; 888 889 if ((name = xmlGetProp(xn, (xmlChar *)TDG_XML_NAME)) == NULL || 890 (fmri = xmlGetProp(xn, (xmlChar *)TDG_XML_FMRI)) == NULL || 891 xmlattr_to_int(mod, xn, TDG_XML_INSTANCE, &inst) != 0) { 892 goto fail; 893 } 894 895 if ((cn = get_child_by_name(xn, (xmlChar *)TDG_XML_OUTEDGES)) != 896 NULL) { 897 if (deserialize_edges(thp, mod, tdg, fmri, cn) != 0) 898 goto fail; 899 } 900 ret = 0; 901 902 fail: 903 if (ret != 0) { 904 topo_dprintf(thp, TOPO_DBG_XML, "%s: error parsing %s element", 905 __func__, TDG_XML_VERTEX); 906 dump_xml_node(thp, xn); 907 } 908 nvlist_free(props); 909 if (name != NULL) 910 xmlFree(name); 911 if (fmri != NULL) 912 xmlFree(fmri); 913 914 return (ret); 915 } 916 917 static topo_pgroup_info_t pginfo = { 918 NULL, 919 TOPO_STABILITY_PRIVATE, 920 TOPO_STABILITY_PRIVATE, 921 1 922 }; 923 924 static int 925 add_props(topo_hdl_t *thp, topo_vertex_t *vtx, nvlist_t *pgroups) 926 { 927 tnode_t *tn; 928 nvlist_t **pgs; 929 uint_t npgs = 0; 930 931 tn = topo_vertex_node(vtx); 932 if (nvlist_lookup_nvlist_array(pgroups, TDG_XML_PGROUPS, &pgs, 933 &npgs) != 0) { 934 goto fail; 935 } 936 937 for (uint_t i = 0; i < npgs; i++) { 938 char *pgname; 939 nvlist_t **props; 940 uint_t nprops; 941 int err; 942 943 if (nvlist_lookup_string(pgs[i], TDG_XML_PGROUP_NAME, 944 &pgname) != 0 || 945 nvlist_lookup_nvlist_array(pgs[i], TDG_XML_PVALS, &props, 946 &nprops) != 0) { 947 goto fail; 948 } 949 pginfo.tpi_name = pgname; 950 951 if (topo_pgroup_create(tn, &pginfo, &err) != 0) { 952 topo_dprintf(thp, TOPO_DBG_XML, "failed to create " 953 "pgroup: %s", pgname); 954 goto fail; 955 } 956 for (uint_t j = 0; j < nprops; j++) { 957 if (topo_prop_setprop(tn, pgname, props[j], 958 TOPO_PROP_IMMUTABLE, props[j], &err) != 0) { 959 topo_dprintf(thp, TOPO_DBG_XML, "failed to " 960 "set properties in pgroup: %s", pgname); 961 goto fail; 962 } 963 } 964 } 965 return (0); 966 fail: 967 topo_dprintf(thp, TOPO_DBG_XML, "%s: error decoding properties for " 968 "%s=%" PRIx64, __func__, topo_node_name(tn), 969 topo_node_instance(tn)); 970 if (thp->th_debug & TOPO_DBG_XML) 971 nvlist_print(stdout, pgroups); 972 973 return (-1); 974 } 975 976 static void 977 free_nvlist_array(topo_hdl_t *thp, nvlist_t **nvlarr, uint_t nelems) 978 { 979 for (uint_t i = 0; i < nelems; i++) { 980 if (nvlarr[i] != NULL) 981 nvlist_free(nvlarr[i]); 982 } 983 topo_hdl_free(thp, nvlarr, nelems * sizeof (nvlist_t *)); 984 } 985 986 static boolean_t 987 is_overflow(topo_hdl_t *thp, uint64_t val, uint_t nbits) 988 { 989 if ((val >> nbits) != 0) { 990 topo_dprintf(thp, TOPO_DBG_XML, "value exceeds %u bits", nbits); 991 return (B_TRUE); 992 } 993 return (B_FALSE); 994 } 995 996 /* 997 * Recursive function for parsing nvpair XML elements, which can contain 998 * nested nvlist and nvpair elements. 999 */ 1000 static int 1001 deserialize_nvpair(topo_hdl_t *thp, topo_mod_t *mod, nvlist_t *nvl, 1002 xmlNodePtr xn) 1003 { 1004 int ret = -1; 1005 xmlChar *name = NULL, *type = NULL, *sval = NULL; 1006 uint64_t val; 1007 1008 if ((name = xmlGetProp(xn, (xmlChar *)TDG_XML_NAME)) == NULL || 1009 (type = xmlGetProp(xn, (xmlChar *)TDG_XML_TYPE)) == NULL) { 1010 goto fail; 1011 } 1012 1013 if (xmlStrcmp(type, (xmlChar *)TDG_XML_NVLIST) == 0) { 1014 nvlist_t *cnvl = NULL; 1015 1016 if (topo_hdl_nvalloc(thp, &cnvl, NV_UNIQUE_NAME) != 0) { 1017 goto fail; 1018 } 1019 1020 for (xmlNodePtr cn = xn->xmlChildrenNode; 1021 cn != NULL; cn = cn->next) { 1022 1023 if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVLIST) != 0) 1024 continue; 1025 1026 for (xmlNodePtr gcn = cn->xmlChildrenNode; 1027 gcn != NULL; gcn = gcn->next) { 1028 1029 if (xmlStrcmp(gcn->name, 1030 (xmlChar *)TDG_XML_NVPAIR) != 0) 1031 continue; 1032 if (deserialize_nvpair(thp, mod, cnvl, gcn) != 1033 0) { 1034 nvlist_free(cnvl); 1035 goto fail; 1036 } 1037 } 1038 if (nvlist_add_nvlist(nvl, (char *)name, cnvl) != 0) { 1039 nvlist_free(cnvl); 1040 goto fail; 1041 } 1042 nvlist_free(cnvl); 1043 break; 1044 } 1045 } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_INT8) == 0) { 1046 if (xmlattr_to_int(mod, xn, TDG_XML_VALUE, &val) != 0 || 1047 is_overflow(thp, val, 8) || 1048 nvlist_add_int8(nvl, (char *)name, (int8_t)val) != 0) { 1049 goto fail; 1050 } 1051 } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_INT16) == 0) { 1052 if (xmlattr_to_int(mod, xn, TDG_XML_VALUE, &val) != 0 || 1053 is_overflow(thp, val, 16) || 1054 nvlist_add_int16(nvl, (char *)name, (int16_t)val) != 0) { 1055 goto fail; 1056 } 1057 } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_INT32) == 0) { 1058 if (xmlattr_to_int(mod, xn, TDG_XML_VALUE, &val) != 0 || 1059 is_overflow(thp, val, 32) || 1060 nvlist_add_int32(nvl, (char *)name, (int32_t)val) != 0) { 1061 goto fail; 1062 } 1063 } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_INT64) == 0) { 1064 if (xmlattr_to_int(mod, xn, TDG_XML_VALUE, &val) != 0 || 1065 nvlist_add_int64(nvl, (char *)name, (int64_t)val) != 0) { 1066 goto fail; 1067 } 1068 } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_UINT8) == 0) { 1069 if (xmlattr_to_int(mod, xn, TDG_XML_VALUE, &val) != 0 || 1070 is_overflow(thp, val, 8) || 1071 nvlist_add_uint8(nvl, (char *)name, (uint8_t)val) != 0) { 1072 goto fail; 1073 } 1074 } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_UINT16) == 0) { 1075 if (xmlattr_to_int(mod, xn, TDG_XML_VALUE, &val) != 0 || 1076 is_overflow(thp, val, 16) || 1077 nvlist_add_uint16(nvl, (char *)name, (uint16_t)val) != 0) { 1078 goto fail; 1079 } 1080 } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_UINT32) == 0) { 1081 if (xmlattr_to_int(mod, xn, TDG_XML_VALUE, &val) != 0 || 1082 is_overflow(thp, val, 32) || 1083 nvlist_add_uint32(nvl, (char *)name, (uint32_t)val) != 0) { 1084 goto fail; 1085 } 1086 } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_UINT64) == 0) { 1087 if (xmlattr_to_int(mod, xn, TDG_XML_VALUE, &val) != 0 || 1088 nvlist_add_uint64(nvl, (char *)name, (uint64_t)val) != 0) { 1089 goto fail; 1090 } 1091 } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_STRING) == 0) { 1092 if ((sval = xmlGetProp(xn, (xmlChar *)TDG_XML_VALUE)) == NULL || 1093 nvlist_add_string(nvl, (char *)name, (char *)sval) != 0) { 1094 goto fail; 1095 } 1096 } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_NVLIST_ARR) == 0) { 1097 uint64_t nelem = 0; 1098 nvlist_t **nvlarr = NULL; 1099 uint_t i = 0; 1100 xmlNodePtr cn = xn->xmlChildrenNode; 1101 1102 /* 1103 * Count the number of child nvlist elements 1104 */ 1105 while (cn != NULL) { 1106 if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVLIST) == 1107 0) { 1108 nelem++; 1109 } 1110 cn = cn->next; 1111 } 1112 1113 if ((nvlarr = topo_hdl_zalloc(thp, 1114 (nelem * sizeof (nvlist_t *)))) == NULL) { 1115 goto fail; 1116 } 1117 1118 for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) { 1119 if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVLIST) != 1120 0) 1121 continue; 1122 1123 if (topo_hdl_nvalloc(thp, &nvlarr[i], 1124 NV_UNIQUE_NAME) != 0) { 1125 free_nvlist_array(thp, nvlarr, nelem); 1126 goto fail; 1127 } 1128 1129 for (xmlNodePtr gcn = cn->xmlChildrenNode; 1130 gcn != NULL; gcn = gcn->next) { 1131 if (xmlStrcmp(gcn->name, 1132 (xmlChar *)TDG_XML_NVPAIR) != 0) 1133 continue; 1134 if (deserialize_nvpair(thp, mod, nvlarr[i], 1135 gcn) != 0) { 1136 free_nvlist_array(thp, nvlarr, nelem); 1137 goto fail; 1138 } 1139 } 1140 i++; 1141 } 1142 if (nvlist_add_nvlist_array(nvl, (char *)name, nvlarr, 1143 nelem) != 0) { 1144 free_nvlist_array(thp, nvlarr, nelem); 1145 goto fail; 1146 } 1147 free_nvlist_array(thp, nvlarr, nelem); 1148 } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_UINT32_ARR) == 0) { 1149 uint64_t nelem = 0; 1150 uint32_t *arr = NULL; 1151 uint_t i = 0; 1152 xmlNodePtr cn = xn->xmlChildrenNode; 1153 1154 /* 1155 * Count the number of child nvpair elements 1156 */ 1157 while (cn != NULL) { 1158 if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) == 1159 0) { 1160 nelem++; 1161 } 1162 cn = cn->next; 1163 } 1164 1165 if ((arr = topo_hdl_zalloc(thp, 1166 (nelem * sizeof (uint32_t)))) == NULL) { 1167 goto fail; 1168 } 1169 1170 for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) { 1171 if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) != 0) 1172 continue; 1173 1174 if (xmlattr_to_int(mod, cn, TDG_XML_VALUE, &val) != 0) { 1175 topo_hdl_free(thp, arr, 1176 (nelem * sizeof (uint32_t))); 1177 goto fail; 1178 } 1179 1180 arr[i] = val; 1181 i++; 1182 } 1183 if (nvlist_add_uint32_array(nvl, (char *)name, arr, 1184 nelem) != 0) { 1185 topo_hdl_free(thp, arr, (nelem * sizeof (uint32_t))); 1186 goto fail; 1187 } 1188 topo_hdl_free(thp, arr, (nelem * sizeof (uint32_t))); 1189 } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_INT32_ARR) == 0) { 1190 uint64_t nelem = 0; 1191 int32_t *arr = NULL; 1192 uint_t i = 0; 1193 xmlNodePtr cn = xn->xmlChildrenNode; 1194 1195 /* 1196 * Count the number of child nvpair elements 1197 */ 1198 while (cn != NULL) { 1199 if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) == 1200 0) { 1201 nelem++; 1202 } 1203 cn = cn->next; 1204 } 1205 1206 if ((arr = topo_hdl_zalloc(thp, 1207 (nelem * sizeof (int32_t)))) == NULL) { 1208 goto fail; 1209 } 1210 1211 for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) { 1212 if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) != 0) 1213 continue; 1214 1215 if (xmlattr_to_int(mod, cn, TDG_XML_VALUE, &val) != 0) { 1216 topo_hdl_free(thp, arr, 1217 (nelem * sizeof (int32_t))); 1218 goto fail; 1219 } 1220 1221 arr[i] = val; 1222 i++; 1223 } 1224 if (nvlist_add_int32_array(nvl, (char *)name, arr, 1225 nelem) != 0) { 1226 topo_hdl_free(thp, arr, (nelem * sizeof (int32_t))); 1227 goto fail; 1228 } 1229 topo_hdl_free(thp, arr, (nelem * sizeof (int32_t))); 1230 } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_UINT64_ARR) == 0) { 1231 uint64_t nelem = 0, *arr = NULL; 1232 uint_t i = 0; 1233 xmlNodePtr cn = xn->xmlChildrenNode; 1234 1235 /* 1236 * Count the number of child nvpair elements 1237 */ 1238 while (cn != NULL) { 1239 if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) == 1240 0) { 1241 nelem++; 1242 } 1243 cn = cn->next; 1244 } 1245 1246 if ((arr = topo_hdl_zalloc(thp, 1247 (nelem * sizeof (uint64_t)))) == NULL) { 1248 goto fail; 1249 } 1250 1251 for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) { 1252 if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) != 0) 1253 continue; 1254 1255 if (xmlattr_to_int(mod, cn, TDG_XML_VALUE, &val) != 0) { 1256 topo_hdl_free(thp, arr, 1257 (nelem * sizeof (uint64_t))); 1258 goto fail; 1259 } 1260 1261 arr[i] = val; 1262 i++; 1263 } 1264 if (nvlist_add_uint64_array(nvl, (char *)name, arr, 1265 nelem) != 0) { 1266 topo_hdl_free(thp, arr, (nelem * sizeof (uint64_t))); 1267 goto fail; 1268 } 1269 topo_hdl_free(thp, arr, (nelem * sizeof (uint64_t))); 1270 } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_INT64_ARR) == 0) { 1271 uint64_t nelem = 0; 1272 int64_t *arr = NULL; 1273 uint_t i = 0; 1274 xmlNodePtr cn = xn->xmlChildrenNode; 1275 1276 /* 1277 * Count the number of child nvpair elements 1278 */ 1279 while (cn != NULL) { 1280 if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) == 1281 0) { 1282 nelem++; 1283 } 1284 cn = cn->next; 1285 } 1286 1287 if ((arr = topo_hdl_zalloc(thp, 1288 (nelem * sizeof (int64_t)))) == NULL) { 1289 goto fail; 1290 } 1291 1292 for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) { 1293 if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) != 0) 1294 continue; 1295 1296 if (xmlattr_to_int(mod, cn, TDG_XML_VALUE, &val) != 0) { 1297 topo_hdl_free(thp, arr, 1298 (nelem * sizeof (int64_t))); 1299 goto fail; 1300 } 1301 1302 arr[i] = val; 1303 i++; 1304 } 1305 if (nvlist_add_int64_array(nvl, (char *)name, arr, 1306 nelem) != 0) { 1307 topo_hdl_free(thp, arr, (nelem * sizeof (int64_t))); 1308 goto fail; 1309 } 1310 topo_hdl_free(thp, arr, (nelem * sizeof (int64_t))); 1311 } 1312 ret = 0; 1313 fail: 1314 if (ret != 0) { 1315 topo_dprintf(thp, TOPO_DBG_XML, "%s: error parsing %s " 1316 "element: name: %s, type: %s, nvl: %p", __func__, xn->name, 1317 (name != NULL) ? (char *)name : "MISSING!", 1318 (type != NULL) ? (char *)type : "MISSING!", nvl); 1319 dump_xml_node(thp, xn); 1320 } 1321 if (name != NULL) 1322 xmlFree(name); 1323 if (type != NULL) 1324 xmlFree(type); 1325 if (sval != NULL) 1326 xmlFree(sval); 1327 1328 return (ret); 1329 } 1330 1331 static int 1332 deserialize_vertex(topo_hdl_t *thp, topo_mod_t *mod, topo_digraph_t *tdg, 1333 xmlNodePtr xn) 1334 { 1335 int ret = -1; 1336 topo_vertex_t *vtx = NULL; 1337 nvlist_t *props = NULL; 1338 xmlChar *name = NULL, *fmri = NULL; 1339 uint64_t inst; 1340 1341 if ((name = xmlGetProp(xn, (xmlChar *)TDG_XML_NAME)) == NULL || 1342 (fmri = xmlGetProp(xn, (xmlChar *)TDG_XML_FMRI)) == NULL || 1343 xmlattr_to_int(mod, xn, TDG_XML_INSTANCE, &inst) != 0) { 1344 goto fail; 1345 } 1346 1347 if ((vtx = topo_vertex_new(mod, (char *)name, inst)) == NULL) { 1348 goto fail; 1349 } 1350 1351 for (xmlNodePtr cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) { 1352 if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) == 0) { 1353 if (topo_hdl_nvalloc(thp, &props, NV_UNIQUE_NAME) != 0) 1354 goto fail; 1355 if (deserialize_nvpair(thp, mod, props, cn) != 0 || 1356 add_props(thp, vtx, props) != 0) 1357 goto fail; 1358 } 1359 } 1360 ret = 0; 1361 1362 fail: 1363 if (ret != 0) { 1364 topo_dprintf(thp, TOPO_DBG_XML, "%s: error parsing %s element", 1365 __func__, TDG_XML_VERTEX); 1366 dump_xml_node(thp, xn); 1367 } 1368 nvlist_free(props); 1369 if (name != NULL) 1370 xmlFree(name); 1371 if (fmri != NULL) 1372 xmlFree(fmri); 1373 1374 return (ret); 1375 } 1376 1377 /* 1378 * This function takes a buffer containing XML data describing a directed graph 1379 * topology. This data is parsed to the original directed graph is rehydrated. 1380 * 1381 * On success, a pointer to a topo_digraph_t representing the graph is 1382 * returned. The caller is responsible for destroying the graph via a call to 1383 * topo_digraph_destroy() 1384 * 1385 * On failure, NULL is returned. 1386 */ 1387 topo_digraph_t * 1388 topo_digraph_deserialize(topo_hdl_t *thp, const char *xml, size_t sz) 1389 { 1390 xmlDocPtr doc; 1391 xmlDtdPtr dtd = NULL; 1392 xmlNodePtr root, vertices; 1393 xmlChar *scheme = NULL; 1394 topo_mod_t *mod; 1395 topo_digraph_t *tdg, *ret = NULL; 1396 1397 if ((doc = xmlReadMemory(xml, sz, "", NULL, 0)) == NULL) { 1398 topo_dprintf(thp, TOPO_DBG_XML, "Failed to parse XML"); 1399 goto fail; 1400 } 1401 1402 /* 1403 * As a sanity check, extract the DTD from the XML and verify it 1404 * matches the DTD for a digraph topology. 1405 */ 1406 if ((dtd = xmlGetIntSubset(doc)) == NULL) { 1407 topo_dprintf(thp, TOPO_DBG_XML, "document has no DTD.\n"); 1408 goto fail; 1409 } 1410 1411 if (strcmp((const char *)dtd->SystemID, TDG_DTD) != 0) { 1412 topo_dprintf(thp, TOPO_DBG_XML, "unexpected DTD: %s", 1413 dtd->SystemID); 1414 goto fail; 1415 } 1416 1417 /* 1418 * Verify the root element is what we're expecting and then grab the 1419 * FMRI scheme from its attributes. 1420 */ 1421 if ((root = xmlDocGetRootElement(doc)) == NULL) { 1422 topo_dprintf(thp, TOPO_DBG_XML, "document is empty.\n"); 1423 goto fail; 1424 } 1425 1426 if (xmlStrcmp(root->name, (xmlChar *)TDG_XML_TOPO_DIGRAPH) != 0 || 1427 (scheme = xmlGetProp(root, (xmlChar *)TDG_XML_SCHEME)) == 1428 NULL) { 1429 topo_dprintf(thp, TOPO_DBG_XML, 1430 "failed to parse %s element", TDG_XML_TOPO_DIGRAPH); 1431 goto fail; 1432 } 1433 1434 /* 1435 * Load the topo module associated with this FMRI scheme. 1436 */ 1437 if ((mod = topo_mod_lookup(thp, (const char *)scheme, 1)) == NULL) { 1438 topo_dprintf(thp, TOPO_DBG_XML, "failed to load %s module", 1439 scheme); 1440 goto fail; 1441 } 1442 /* 1443 * If we have a builtin module for this scheme, then there will 1444 * already be an empty digraph attached to the handle. Otherwise, 1445 * create a new empty digraph and attach it to the handle. 1446 */ 1447 tdg = topo_digraph_get(mod->tm_hdl, mod->tm_info->tmi_scheme); 1448 if (tdg == NULL) { 1449 if ((tdg = topo_digraph_new(thp, mod, (const char *)scheme)) == 1450 NULL) { 1451 topo_dprintf(thp, TOPO_DBG_XML, "failed to create new " 1452 "digraph"); 1453 goto fail; 1454 } else { 1455 topo_list_append(&thp->th_digraphs, tdg); 1456 } 1457 } 1458 1459 /* 1460 * Iterate through the vertex XML elements to reconstruct the graph 1461 */ 1462 vertices = get_child_by_name(root, (xmlChar *)TDG_XML_VERTICES); 1463 if (vertices == NULL || 1464 xmlStrcmp(vertices->name, (xmlChar *)TDG_XML_VERTICES) != 0) { 1465 topo_dprintf(thp, TOPO_DBG_XML, "failed to parse %s element", 1466 TDG_XML_VERTICES); 1467 dump_xml_node(thp, root); 1468 goto fail; 1469 } 1470 1471 for (xmlNodePtr xn = vertices->xmlChildrenNode; xn != NULL; 1472 xn = xn->next) { 1473 if (xmlStrcmp(xn->name, (xmlChar *)TDG_XML_VERTEX) != 0) 1474 continue; 1475 if (deserialize_vertex(thp, mod, tdg, xn) != 0) 1476 goto fail; 1477 } 1478 1479 /* 1480 * Now that all of the vertices have been created, go back through 1481 * the vertex XML elements and add the edges. 1482 */ 1483 for (xmlNodePtr xn = vertices->xmlChildrenNode; xn != NULL; 1484 xn = xn->next) { 1485 if (xmlStrcmp(xn->name, (xmlChar *)TDG_XML_VERTEX) != 0) 1486 continue; 1487 if (add_edges(thp, mod, tdg, xn) != 0) 1488 goto fail; 1489 } 1490 1491 ret = tdg; 1492 1493 fail: 1494 if (scheme != NULL) 1495 xmlFree(scheme); 1496 1497 if (doc != NULL) 1498 xmlFreeDoc(doc); 1499 1500 (void) topo_hdl_seterrno(thp, ETOPO_MOD_XENUM); 1501 return (ret); 1502 } 1503