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 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <alloca.h> 29 #include <assert.h> 30 #include <errno.h> 31 #include <libintl.h> 32 #include <stdarg.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 37 #include "fru_tag.h" 38 #include "libfrup.h" 39 #include "libfrureg.h" 40 41 42 #define NUM_ITER_BYTES 4 43 44 #define HEAD_ITER 0 45 #define TAIL_ITER 1 /* not used */ 46 #define NUM_ITER 2 47 #define MAX_ITER 3 48 49 #define INDENT 3 50 #define TIMESTRINGLEN 128 51 #define TEMPERATURE_OFFSET 73 52 53 static void (*print_node)(fru_node_t fru_type, const char *path, 54 const char *name, end_node_fp_t *end_node, 55 void **end_args); 56 57 static char tagname[sizeof ("?_0123456789_0123456789_0123456789")]; 58 59 static int containers_only = 0, list_only = 0, saved_status = 0, xml = 0; 60 61 static FILE *errlog; 62 63 /* 64 * Definition for data elements found in devices but not found in 65 * the system's version of libfrureg 66 */ 67 static fru_regdef_t unknown = { 68 REGDEF_VERSION, 69 tagname, 70 -1, 71 -1, 72 -1, 73 -1, 74 FDTYPE_ByteArray, 75 FDISP_Hex, 76 FRU_WHICH_UNDEFINED, 77 FRU_WHICH_UNDEFINED, 78 0, 79 NULL, 80 0, 81 FRU_NOT_ITERATED, 82 NULL 83 }; 84 85 86 /* 87 * Write message to standard error and possibly the error log buffer 88 */ 89 static void 90 error(const char *format, ...) 91 { 92 va_list args; 93 94 95 /* make relevant output appear before error message */ 96 if (fflush(stdout) == EOF) { 97 (void) fprintf(stderr, "Error flushing output: %s\n", 98 strerror(errno)); 99 exit(1); 100 } 101 102 va_start(args, format); 103 if (vfprintf(stderr, format, args) < 0) exit(1); 104 if (errlog && (vfprintf(errlog, format, args) < 0)) exit(1); 105 } 106 107 /* 108 * Write message to standard output 109 */ 110 static void 111 output(const char *format, ...) 112 { 113 va_list args; 114 115 116 va_start(args, format); 117 if (vfprintf(stdout, format, args) < 0) { 118 error(gettext("Error writing output: %s\n"), 119 strerror(errno)); 120 exit(1); 121 } 122 } 123 124 /* 125 * Safe wrapper for putchar() 126 */ 127 static void 128 voidputchar(int c) 129 { 130 if (putchar(c) == EOF) { 131 error(gettext("Error writing output: %s\n"), 132 strerror(errno)); 133 exit(1); 134 } 135 } 136 137 static void (*safeputchar)(int c) = voidputchar; 138 139 /* 140 * Safe wrapper for puts() 141 */ 142 static void 143 voidputs(const char *s) 144 { 145 if (fputs(s, stdout) == EOF) { 146 error(gettext("Error writing output: %s\n"), 147 strerror(errno)); 148 exit(1); 149 } 150 } 151 152 static void (*safeputs)(const char *s) = voidputs; 153 154 /* 155 * XML-safe wrapper for putchar(): quotes XML-special characters 156 */ 157 static void 158 xputchar(int c) 159 { 160 switch (c) { 161 case '<': 162 c = fputs("<", stdout); 163 break; 164 case '>': 165 c = fputs(">", stdout); 166 break; 167 case '&': 168 c = fputs("&", stdout); 169 break; 170 case '"': 171 c = fputs(""", stdout); 172 break; 173 default: 174 c = putchar(c); 175 break; 176 } 177 178 if (c == EOF) { 179 error(gettext("Error writing output: %s\n"), 180 strerror(errno)); 181 exit(1); 182 } 183 } 184 185 /* 186 * XML-safe analog of puts(): quotes XML-special characters 187 */ 188 static void 189 xputs(const char *s) 190 { 191 char c; 192 193 for (/* */; ((c = *s) != 0); s++) 194 xputchar(c); 195 } 196 197 /* 198 * Output the XML DTD derived from the registry provided by libfrureg 199 */ 200 int 201 output_dtd(void) 202 { 203 char **element; 204 205 unsigned int i, j, num_elements = 0; 206 207 uint8_t *tagged; 208 209 const fru_regdef_t *def; 210 211 212 if (((element = fru_reg_list_entries(&num_elements)) == NULL) || 213 (num_elements == 0)) { 214 error(gettext("No FRU ID Registry elements")); 215 return (1); 216 } 217 218 if ((tagged = calloc(num_elements, sizeof (*tagged))) == NULL) { 219 error(gettext("Unable to get memory for tagged element list"), 220 strerror(errno)); 221 return (1); 222 } 223 224 /* 225 * Output the DTD preamble 226 */ 227 output("<!ELEMENT FRUID_XML_Tree (Parameter*, " 228 "(Fru | Location | Container)*,\n" 229 " Parameter*, ErrorLog?, Parameter*)>\n" 230 "<!ATTLIST FRUID_XML_Tree>\n" 231 "\n" 232 "<!ELEMENT Parameter EMPTY>\n" 233 "<!ATTLIST Parameter type CDATA #REQUIRED>\n" 234 "<!ATTLIST Parameter name CDATA #REQUIRED>\n" 235 "<!ATTLIST Parameter value CDATA #REQUIRED>\n" 236 "\n" 237 "<!ELEMENT Fru (Fru | Location | Container)*>\n" 238 "<!ATTLIST Fru name CDATA #REQUIRED>\n" 239 "\n" 240 "<!ELEMENT Location (Fru | Location | Container)*>\n" 241 "<!ATTLIST Location\n" 242 " name CDATA #IMPLIED\n" 243 " value CDATA #IMPLIED\n" 244 ">\n" 245 "\n" 246 "<!ELEMENT Container (ContainerData?, " 247 "(Fru | Location | Container)*)>\n" 248 "<!ATTLIST Container name CDATA #REQUIRED>\n" 249 "<!ATTLIST Container imagefile CDATA #IMPLIED>\n" 250 "\n" 251 "<!ELEMENT ContainerData (Segment*)>\n" 252 "<!ATTLIST ContainerData>\n" 253 "\n" 254 "<!ATTLIST Segment name CDATA #REQUIRED>\n" 255 "\n" 256 "<!ELEMENT Index EMPTY>\n" 257 "<!ATTLIST Index value CDATA #REQUIRED>\n" 258 "\n" 259 "<!ELEMENT ErrorLog (#PCDATA)>\n" 260 "<!ATTLIST ErrorLog>\n" 261 "\n"); 262 263 /* 264 * Output the definition for each element 265 */ 266 for (i = 0; i < num_elements; i++) { 267 assert(element[i] != NULL); 268 /* Prevent incompatible duplicate defn. from FRUID Registry. */ 269 if ((strcmp("Location", element[i])) == 0) continue; 270 if ((def = fru_reg_lookup_def_by_name(element[i])) == NULL) { 271 error(gettext("Error looking up registry " 272 "definition for \"%s\"\n"), 273 element[i]); 274 return (1); 275 } 276 277 if (def->tagType != FRU_X) tagged[i] = 1; 278 279 if (def->dataType == FDTYPE_Record) { 280 if (def->iterationType == FRU_NOT_ITERATED) 281 output("<!ELEMENT %s (%s", element[i], 282 def->enumTable[0].text); 283 else 284 output("<!ELEMENT %s (Index_%s*)>\n" 285 "<!ATTLIST Index_%s>\n" 286 "<!ELEMENT Index_%s (%s", 287 element[i], element[i], element[i], 288 element[i], def->enumTable[0].text); 289 290 for (j = 1; j < def->enumCount; j++) 291 output(",\n\t%s", def->enumTable[j].text); 292 293 output(")>\n"); 294 } else if (def->iterationType == FRU_NOT_ITERATED) { 295 output("<!ELEMENT %s EMPTY>\n" 296 "<!ATTLIST %s value CDATA #REQUIRED>\n", 297 element[i], element[i]); 298 299 if (def->dataType == FDTYPE_Enumeration) { 300 output("<!-- %s valid enumeration values\n"); 301 for (j = 0; j < def->enumCount; j++) { 302 output("\t\""); 303 xputs(def->enumTable[j].text); 304 output("\"\n"); 305 } 306 output("-->\n"); 307 } 308 } 309 else 310 output("<!ELEMENT %s (Index*)>\n", element[i]); 311 312 output("\n"); 313 } 314 315 /* Provide for returning the tag for an "unknown" element */ 316 output("<!ATTLIST UNKNOWN tag CDATA \"UNKNOWN\">\n\n"); 317 318 319 /* 320 * List all data elements as possible members of "Segment" 321 */ 322 output("<!ELEMENT Segment ((UNKNOWN"); 323 for (i = 0; i < num_elements; i++) { 324 if (tagged[i]) output("\n\t| %s", element[i]); 325 free(element[i]); 326 } 327 output(")*)>\n"); 328 free(element); 329 free(tagged); 330 331 return (0); 332 } 333 334 /* 335 * Safely pretty-print the value of a field 336 */ 337 static void 338 print_field(const uint8_t *field, const fru_regdef_t *def) 339 { 340 char *errmsg = NULL, timestring[TIMESTRINGLEN]; 341 342 int i; 343 344 uint64_t value; 345 346 time_t timefield; 347 348 349 switch (def->dataType) { 350 case FDTYPE_Binary: 351 assert(def->payloadLen <= sizeof (value)); 352 switch (def->dispType) { 353 case FDISP_Binary: 354 for (i = 0; i < def->payloadLen; i++) 355 output("%c%c%c%c%c%c%c%c", 356 ((field[i] & 0x80) ? '1' : '0'), 357 ((field[i] & 0x40) ? '1' : '0'), 358 ((field[i] & 0x20) ? '1' : '0'), 359 ((field[i] & 0x10) ? '1' : '0'), 360 ((field[i] & 0x08) ? '1' : '0'), 361 ((field[i] & 0x04) ? '1' : '0'), 362 ((field[i] & 0x02) ? '1' : '0'), 363 ((field[i] & 0x01) ? '1' : '0')); 364 return; 365 case FDISP_Octal: 366 case FDISP_Decimal: 367 value = 0; 368 (void) memcpy((((uint8_t *)&value) + 369 sizeof (value) - def->payloadLen), 370 field, def->payloadLen); 371 if ((value != 0) && 372 ((strcmp(def->name, "Lowest") == 0) || 373 (strcmp(def->name, "Highest") == 0) || 374 (strcmp(def->name, "Latest") == 0))) 375 output((def->dispType == FDISP_Octal) ? 376 "%llo" : "%lld (%lld degrees C)", 377 value, (value - TEMPERATURE_OFFSET)); 378 else 379 output((def->dispType == FDISP_Octal) ? 380 "%llo" : "%lld", value); 381 return; 382 case FDISP_Time: 383 if (def->payloadLen > sizeof (timefield)) { 384 errmsg = "time value too large for formatting"; 385 break; 386 } 387 (void) memcpy(&timefield, field, sizeof (timefield)); 388 if (strftime(timestring, sizeof (timestring), "%C", 389 localtime(&timefield)) == 0) { 390 errmsg = "formatted time would overflow buffer"; 391 break; 392 } 393 safeputs(timestring); 394 return; 395 } 396 break; 397 case FDTYPE_ASCII: 398 for (i = 0; i < def->payloadLen && field[i]; i++) 399 safeputchar(field[i]); 400 return; 401 case FDTYPE_Enumeration: 402 value = 0; 403 (void) memcpy((((uint8_t *)&value) + sizeof (value) 404 - def->payloadLen), 405 field, def->payloadLen); 406 for (i = 0; i < def->enumCount; i++) 407 if (def->enumTable[i].value == value) { 408 safeputs(def->enumTable[i].text); 409 return; 410 } 411 412 errmsg = "unrecognized value"; 413 break; 414 } 415 416 /* If nothing matched above, print the field in hex */ 417 for (i = 0; i < def->payloadLen; i++) 418 output("%2.2X", field[i]); 419 420 /* Safely print any error message associated with the field */ 421 if (errmsg) { 422 output(" ("); 423 safeputs(errmsg); 424 output(")\n"); 425 } 426 } 427 428 /* 429 * Recursively print the contents of a data element 430 */ 431 static void 432 print_element(const uint8_t *data, const fru_regdef_t *def, 433 const char *parent_path, int indent) 434 { 435 char *path; 436 size_t len; 437 438 int bytes = 0, i; 439 440 441 indent = (xml) ? (indent + INDENT) : (2*INDENT); 442 443 /* 444 * Construct the path, or, for XML, the name, for the current 445 * data element 446 */ 447 if ((def->iterationCount == 0) && 448 (def->iterationType != FRU_NOT_ITERATED)) { 449 if (xml) { 450 if (def->dataType == FDTYPE_Record) { 451 len = strlen("Index_") + strlen(def->name) + 1; 452 path = alloca(len); 453 (void) snprintf(path, len, 454 "Index_%s", def->name); 455 } 456 else 457 path = "Index"; 458 } 459 else 460 path = (char *)parent_path; 461 } else { 462 if (xml) 463 path = (char *)def->name; 464 else { 465 len = strlen(parent_path) + sizeof ("/") + 466 strlen(def->name) + 467 (def->iterationCount ? sizeof ("[255]") : 0); 468 path = alloca(len); 469 bytes = snprintf(path, len, 470 "%s/%s", parent_path, def->name); 471 } 472 } 473 474 /* 475 * Handle the various categories of data elements: iteration, 476 * record, and field 477 */ 478 if (def->iterationCount) { 479 int iterlen = (def->payloadLen - NUM_ITER_BYTES)/ 480 def->iterationCount, 481 n, valid = 1; 482 483 uint8_t head, num; 484 485 fru_regdef_t newdef; 486 487 488 /* 489 * Make a new element definition to describe the components 490 * of the iteration 491 */ 492 (void) memcpy(&newdef, def, sizeof (newdef)); 493 newdef.iterationCount = 0; 494 newdef.payloadLen = iterlen; 495 496 /* 497 * Validate the contents of the iteration control bytes 498 */ 499 if (data[HEAD_ITER] >= def->iterationCount) { 500 valid = 0; 501 error(gettext("%s: Invalid iteration head: %d " 502 "(should be less than %d)\n"), 503 path, data[HEAD_ITER], def->iterationCount); 504 } 505 506 if (data[NUM_ITER] > def->iterationCount) { 507 valid = 0; 508 error(gettext("%s: Invalid iteration count: %d " 509 "(should not be greater than %d)\n"), 510 path, data[NUM_ITER], def->iterationCount); 511 } 512 513 if (data[MAX_ITER] != def->iterationCount) { 514 valid = 0; 515 error(gettext("%s: Invalid iteration maximum: %d " 516 "(should equal %d)\n"), 517 path, data[MAX_ITER], def->iterationCount); 518 } 519 520 if (valid) { 521 head = data[HEAD_ITER]; 522 num = data[NUM_ITER]; 523 } else { 524 head = 0; 525 num = def->iterationCount; 526 error(gettext("%s: Showing all iterations\n"), path); 527 } 528 529 if (xml) 530 output("%*s<%s>\n", indent, "", path); 531 else 532 output("%*s%s (%d iterations)\n", indent, "", path, 533 num); 534 535 /* 536 * Print each component of the iteration 537 */ 538 for (i = head, n = 0, data += 4; 539 n < num; 540 i = ((i + 1) % def->iterationCount), n++) { 541 if (!xml) (void) sprintf((path + bytes), "[%d]", n); 542 print_element((data + i*iterlen), &newdef, path, 543 indent); 544 } 545 546 if (xml) output("%*s</%s>\n", indent, "", path); 547 548 } else if (def->dataType == FDTYPE_Record) { 549 const fru_regdef_t *component; 550 551 if (xml) 552 output("%*s<%s>\n", indent, "", path); 553 else 554 output("%*s%s\n", indent, "", path); 555 556 /* 557 * Print each component of the record 558 */ 559 for (i = 0; i < def->enumCount; 560 i++, data += component->payloadLen) { 561 component = fru_reg_lookup_def_by_name( 562 def->enumTable[i].text); 563 assert(component != NULL); 564 print_element(data, component, path, indent); 565 } 566 567 if (xml) output("%*s</%s>\n", indent, "", path); 568 } else if (xml) { 569 /* 570 * Base case: print the field formatted for XML 571 */ 572 char *format = ((def == &unknown) 573 ? "%*s<UNKNOWN tag=\"%s\" value=\"" 574 : "%*s<%s value=\""); 575 576 output(format, indent, "", path); 577 print_field(data, def); 578 /*CSTYLED*/ 579 output("\"/>\n"); /* \" confuses cstyle */ 580 } else { 581 /* 582 * Base case: print the field 583 */ 584 output("%*s%s: ", indent, "", path); 585 print_field(data, def); 586 output("\n"); 587 } 588 } 589 590 /* 591 * Print the contents of a packet (i.e., a tagged data element) 592 */ 593 /* ARGSUSED */ 594 static int 595 print_packet(fru_tag_t *tag, uint8_t *payload, size_t length, void *args) 596 { 597 int tag_type = get_tag_type(tag); 598 599 size_t payload_length = 0; 600 601 const fru_regdef_t *def; 602 603 604 /* 605 * Build a definition for unrecognized tags (e.g., not in libfrureg) 606 */ 607 if ((tag_type == -1) || 608 ((payload_length = get_payload_length(tag)) != length)) { 609 def = &unknown; 610 611 unknown.tagType = -1; 612 unknown.tagDense = -1; 613 unknown.payloadLen = length; 614 unknown.dataLength = unknown.payloadLen; 615 616 if (tag_type == -1) 617 (void) snprintf(tagname, sizeof (tagname), "INVALID"); 618 else 619 (void) snprintf(tagname, sizeof (tagname), 620 "%s_%u_%u_%u", get_tagtype_str(tag_type), 621 get_tag_dense(tag), payload_length, length); 622 } else if ((def = fru_reg_lookup_def_by_tag(*tag)) == NULL) { 623 def = &unknown; 624 625 unknown.tagType = tag_type; 626 unknown.tagDense = get_tag_dense(tag); 627 unknown.payloadLen = payload_length; 628 unknown.dataLength = unknown.payloadLen; 629 630 (void) snprintf(tagname, sizeof (tagname), "%s_%u_%u", 631 get_tagtype_str(unknown.tagType), 632 unknown.tagDense, payload_length); 633 } 634 635 636 /* 637 * Print the defined element 638 */ 639 print_element(payload, def, "", INDENT); 640 641 return (FRU_SUCCESS); 642 } 643 644 /* 645 * Print a segment's name and the contents of each data element in the segment 646 */ 647 static int 648 print_packets_in_segment(fru_seghdl_t segment, void *args) 649 { 650 char *name; 651 652 int status; 653 654 655 if ((status = fru_get_segment_name(segment, &name)) != FRU_SUCCESS) { 656 saved_status = status; 657 name = ""; 658 error(gettext("Error getting segment name: %s\n"), 659 fru_strerror(status)); 660 } 661 662 663 if (xml) 664 output("%*s<Segment name=\"%s\">\n", INDENT, "", name); 665 else 666 output("%*sSEGMENT: %s\n", INDENT, "", name); 667 668 /* Iterate over the packets in the segment, printing the contents */ 669 if ((status = fru_for_each_packet(segment, print_packet, args)) 670 != FRU_SUCCESS) { 671 saved_status = status; 672 error(gettext("Error processing data in segment \"%s\": %s\n"), 673 name, fru_strerror(status)); 674 } 675 676 if (xml) output("%*s</Segment>\n", INDENT, ""); 677 678 free(name); 679 680 return (FRU_SUCCESS); 681 } 682 683 /* ARGSUSED */ 684 static void 685 print_node_path(fru_node_t fru_type, const char *path, const char *name, 686 end_node_fp_t *end_node, void **end_args) 687 { 688 output("%s%s\n", path, 689 ((fru_type == FRU_NODE_CONTAINER) ? " (container)" 690 : ((fru_type == FRU_NODE_FRU) ? " (fru)" : ""))); 691 } 692 693 /* 694 * Close the XML element for a "location" node 695 */ 696 /* ARGSUSED */ 697 static void 698 end_location_xml(fru_nodehdl_t node, const char *path, const char *name, 699 void *args) 700 { 701 assert(args != NULL); 702 output("</Location> <!-- %s -->\n", args); 703 } 704 705 /* 706 * Close the XML element for a "fru" node 707 */ 708 /* ARGSUSED */ 709 static void 710 end_fru_xml(fru_nodehdl_t node, const char *path, const char *name, void *args) 711 { 712 assert(args != NULL); 713 output("</Fru> <!-- %s -->\n", args); 714 } 715 716 /* 717 * Close the XML element for a "container" node 718 */ 719 /* ARGSUSED */ 720 static void 721 end_container_xml(fru_nodehdl_t node, const char *path, const char *name, 722 void *args) 723 { 724 assert(args != NULL); 725 output("</Container> <!-- %s -->\n", args); 726 } 727 728 /* 729 * Introduce a node in XML and set the appropriate node-closing function 730 */ 731 /* ARGSUSED */ 732 static void 733 print_node_xml(fru_node_t fru_type, const char *path, const char *name, 734 end_node_fp_t *end_node, void **end_args) 735 { 736 switch (fru_type) { 737 case FRU_NODE_FRU: 738 output("<Fru name=\"%s\">\n", name); 739 *end_node = end_fru_xml; 740 break; 741 case FRU_NODE_CONTAINER: 742 output("<Container name=\"%s\">\n", name); 743 *end_node = end_container_xml; 744 break; 745 default: 746 output("<Location name=\"%s\">\n", name); 747 *end_node = end_location_xml; 748 break; 749 } 750 751 *end_args = (void *) name; 752 } 753 754 /* 755 * Print node info and, where appropriate, node contents 756 */ 757 /* ARGSUSED */ 758 static fru_errno_t 759 process_node(fru_nodehdl_t node, const char *path, const char *name, 760 void *args, end_node_fp_t *end_node, void **end_args) 761 { 762 int status; 763 764 fru_node_t fru_type = FRU_NODE_UNKNOWN; 765 766 767 if ((status = fru_get_node_type(node, &fru_type)) != FRU_SUCCESS) { 768 saved_status = status; 769 error(gettext("Error getting node type: %s\n"), 770 fru_strerror(status)); 771 } 772 773 if (containers_only) { 774 if (fru_type != FRU_NODE_CONTAINER) 775 return (FRU_SUCCESS); 776 name = path; 777 } 778 779 /* Introduce the node */ 780 assert(print_node != NULL); 781 print_node(fru_type, path, name, end_node, end_args); 782 783 if (list_only) 784 return (FRU_SUCCESS); 785 786 /* Print the contents of each packet in each segment of a container */ 787 if (fru_type == FRU_NODE_CONTAINER) { 788 if (xml) output("<ContainerData>\n"); 789 if ((status = 790 fru_for_each_segment(node, print_packets_in_segment, 791 NULL)) 792 != FRU_SUCCESS) { 793 saved_status = status; 794 error(gettext("Error processing node \"%s\": %s\n"), 795 name, fru_strerror(status)); 796 } 797 if (xml) output("</ContainerData>\n"); 798 } 799 800 return (FRU_SUCCESS); 801 } 802 803 /* 804 * Process the node if its path matches the search path in "args" 805 */ 806 /* ARGSUSED */ 807 static fru_errno_t 808 process_matching_node(fru_nodehdl_t node, const char *path, const char *name, 809 void *args, end_node_fp_t *end_node, void **end_args) 810 { 811 int status; 812 813 814 if (!fru_pathmatch(path, args)) 815 return (FRU_SUCCESS); 816 817 status = process_node(node, path, path, args, end_node, end_args); 818 819 return ((status == FRU_SUCCESS) ? FRU_WALK_TERMINATE : status); 820 } 821 822 /* 823 * Write the trailer required for well-formed DTD-compliant XML 824 */ 825 static void 826 terminate_xml() 827 { 828 errno = 0; 829 if (ftell(errlog) > 0) { 830 char c; 831 832 output("<ErrorLog>\n"); 833 rewind(errlog); 834 if (!errno) 835 while ((c = getc(errlog)) != EOF) 836 xputchar(c); 837 output("</ErrorLog>\n"); 838 } 839 840 if (errno) { 841 /*NOTREACHED*/ 842 errlog = NULL; 843 error(gettext("Error copying error messages to \"ErrorLog\""), 844 strerror(errno)); 845 } 846 847 output("</FRUID_XML_Tree>\n"); 848 } 849 850 /* 851 * Print available FRU ID information 852 */ 853 int 854 prtfru(const char *searchpath, int containers_only_flag, int list_only_flag, 855 int xml_flag) 856 { 857 fru_errno_t status; 858 859 fru_nodehdl_t frutree = 0; 860 861 862 /* Copy parameter flags to global flags */ 863 containers_only = containers_only_flag; 864 list_only = list_only_flag; 865 xml = xml_flag; 866 867 868 /* Help arrange for correct, efficient interleaving of output */ 869 (void) setvbuf(stderr, NULL, _IOLBF, 0); 870 871 872 /* Initialize for XML--or not */ 873 if (xml) { 874 safeputchar = xputchar; 875 safeputs = xputs; 876 877 print_node = print_node_xml; 878 879 if ((errlog = tmpfile()) == NULL) { 880 (void) fprintf(stderr, 881 "Error creating error log file: %s\n", 882 strerror(errno)); 883 return (1); 884 } 885 886 /* Output the XML preamble */ 887 output("<?xml version=\"1.0\" ?>\n" 888 "<!--\n" 889 " Copyright 2000-2002 Sun Microsystems, Inc. " 890 "All rights reserved.\n" 891 " Use is subject to license terms.\n" 892 "-->\n\n" 893 "<!DOCTYPE FRUID_XML_Tree SYSTEM \"prtfrureg.dtd\">\n\n" 894 "<FRUID_XML_Tree>\n"); 895 896 /* Arrange to always properly terminate XML */ 897 if (atexit(terminate_xml)) 898 error(gettext("Warning: XML will not be terminated: " 899 "%s\n"), strerror(errno)); 900 } else 901 print_node = print_node_path; 902 903 904 /* Get the root node */ 905 if ((status = fru_get_root(&frutree)) == FRU_NODENOTFOUND) { 906 error(gettext("This system does not provide FRU ID data\n")); 907 return (1); 908 } else if (status != FRU_SUCCESS) { 909 error(gettext("Unable to access FRU ID data: %s\n"), 910 fru_strerror(status)); 911 return (1); 912 } 913 914 /* Process the tree */ 915 if (searchpath == NULL) { 916 status = fru_walk_tree(frutree, "", process_node, NULL); 917 } else { 918 status = fru_walk_tree(frutree, "", process_matching_node, 919 (void *)searchpath); 920 if (status == FRU_WALK_TERMINATE) { 921 status = FRU_SUCCESS; 922 } else if (status == FRU_SUCCESS) { 923 error(gettext("\"%s\" not found\n"), searchpath); 924 return (1); 925 } 926 } 927 928 if (status != FRU_SUCCESS) 929 error(gettext("Error processing FRU tree: %s\n"), 930 fru_strerror(status)); 931 932 return (((status == FRU_SUCCESS) && (saved_status == 0)) ? 0 : 1); 933 } 934