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 value -= TEMPERATURE_OFFSET; 376 output((def->dispType == FDISP_Octal) ? 377 "%llo" : "%lld", value); 378 return; 379 case FDISP_Time: 380 if (def->payloadLen > sizeof (timefield)) { 381 errmsg = "time value too large for formatting"; 382 break; 383 } 384 (void) memcpy(&timefield, field, sizeof (timefield)); 385 if (strftime(timestring, sizeof (timestring), "%C", 386 localtime(&timefield)) == 0) { 387 errmsg = "formatted time would overflow buffer"; 388 break; 389 } 390 safeputs(timestring); 391 return; 392 } 393 break; 394 case FDTYPE_ASCII: 395 for (i = 0; i < def->payloadLen && field[i]; i++) 396 safeputchar(field[i]); 397 return; 398 case FDTYPE_Enumeration: 399 value = 0; 400 (void) memcpy((((uint8_t *)&value) + sizeof (value) 401 - def->payloadLen), 402 field, def->payloadLen); 403 for (i = 0; i < def->enumCount; i++) 404 if (def->enumTable[i].value == value) { 405 safeputs(def->enumTable[i].text); 406 return; 407 } 408 409 errmsg = "unrecognized value"; 410 break; 411 } 412 413 /* If nothing matched above, print the field in hex */ 414 for (i = 0; i < def->payloadLen; i++) 415 output("%2.2X", field[i]); 416 417 /* Safely print any error message associated with the field */ 418 if (errmsg) { 419 output(" ("); 420 safeputs(errmsg); 421 output(")\n"); 422 } 423 } 424 425 /* 426 * Recursively print the contents of a data element 427 */ 428 static void 429 print_element(const uint8_t *data, const fru_regdef_t *def, 430 const char *parent_path, int indent) 431 { 432 char *path; 433 size_t len; 434 435 int bytes = 0, i; 436 437 438 indent = (xml) ? (indent + INDENT) : (2*INDENT); 439 440 /* 441 * Construct the path, or, for XML, the name, for the current 442 * data element 443 */ 444 if ((def->iterationCount == 0) && 445 (def->iterationType != FRU_NOT_ITERATED)) { 446 if (xml) { 447 if (def->dataType == FDTYPE_Record) { 448 len = strlen("Index_") + strlen(def->name) + 1; 449 path = alloca(len); 450 (void) snprintf(path, len, 451 "Index_%s", def->name); 452 } 453 else 454 path = "Index"; 455 } 456 else 457 path = (char *)parent_path; 458 } else { 459 if (xml) 460 path = (char *)def->name; 461 else { 462 len = strlen(parent_path) + sizeof ("/") + 463 strlen(def->name) + 464 (def->iterationCount ? sizeof ("[255]") : 0); 465 path = alloca(len); 466 bytes = snprintf(path, len, 467 "%s/%s", parent_path, def->name); 468 } 469 } 470 471 /* 472 * Handle the various categories of data elements: iteration, 473 * record, and field 474 */ 475 if (def->iterationCount) { 476 int iterlen = (def->payloadLen - NUM_ITER_BYTES)/ 477 def->iterationCount, 478 n, valid = 1; 479 480 uint8_t head, num; 481 482 fru_regdef_t newdef; 483 484 485 /* 486 * Make a new element definition to describe the components 487 * of the iteration 488 */ 489 (void) memcpy(&newdef, def, sizeof (newdef)); 490 newdef.iterationCount = 0; 491 newdef.payloadLen = iterlen; 492 493 /* 494 * Validate the contents of the iteration control bytes 495 */ 496 if (data[HEAD_ITER] >= def->iterationCount) { 497 valid = 0; 498 error(gettext("%s: Invalid iteration head: %d " 499 "(should be less than %d)\n"), 500 path, data[HEAD_ITER], def->iterationCount); 501 } 502 503 if (data[NUM_ITER] > def->iterationCount) { 504 valid = 0; 505 error(gettext("%s: Invalid iteration count: %d " 506 "(should not be greater than %d)\n"), 507 path, data[NUM_ITER], def->iterationCount); 508 } 509 510 if (data[MAX_ITER] != def->iterationCount) { 511 valid = 0; 512 error(gettext("%s: Invalid iteration maximum: %d " 513 "(should equal %d)\n"), 514 path, data[MAX_ITER], def->iterationCount); 515 } 516 517 if (valid) { 518 head = data[HEAD_ITER]; 519 num = data[NUM_ITER]; 520 } else { 521 head = 0; 522 num = def->iterationCount; 523 error(gettext("%s: Showing all iterations\n"), path); 524 } 525 526 if (xml) 527 output("%*s<%s>\n", indent, "", path); 528 else 529 output("%*s%s (%d iterations)\n", indent, "", path, 530 num); 531 532 /* 533 * Print each component of the iteration 534 */ 535 for (i = head, n = 0, data += 4; 536 n < num; 537 i = ((i + 1) % def->iterationCount), n++) { 538 if (!xml) (void) sprintf((path + bytes), "[%d]", n); 539 print_element((data + i*iterlen), &newdef, path, 540 indent); 541 } 542 543 if (xml) output("%*s</%s>\n", indent, "", path); 544 545 } else if (def->dataType == FDTYPE_Record) { 546 const fru_regdef_t *component; 547 548 if (xml) 549 output("%*s<%s>\n", indent, "", path); 550 else 551 output("%*s%s\n", indent, "", path); 552 553 /* 554 * Print each component of the record 555 */ 556 for (i = 0; i < def->enumCount; 557 i++, data += component->payloadLen) { 558 component = fru_reg_lookup_def_by_name( 559 def->enumTable[i].text); 560 assert(component != NULL); 561 print_element(data, component, path, indent); 562 } 563 564 if (xml) output("%*s</%s>\n", indent, "", path); 565 } else if (xml) { 566 /* 567 * Base case: print the field formatted for XML 568 */ 569 char *format = ((def == &unknown) 570 ? "%*s<UNKNOWN tag=\"%s\" value=\"" 571 : "%*s<%s value=\""); 572 573 output(format, indent, "", path); 574 print_field(data, def); 575 /*CSTYLED*/ 576 output("\"/>\n"); /* \" confuses cstyle */ 577 } else { 578 /* 579 * Base case: print the field 580 */ 581 output("%*s%s: ", indent, "", path); 582 print_field(data, def); 583 output("\n"); 584 } 585 } 586 587 /* 588 * Print the contents of a packet (i.e., a tagged data element) 589 */ 590 /* ARGSUSED */ 591 static int 592 print_packet(fru_tag_t *tag, uint8_t *payload, size_t length, void *args) 593 { 594 int tag_type = get_tag_type(tag); 595 596 size_t payload_length = 0; 597 598 const fru_regdef_t *def; 599 600 601 /* 602 * Build a definition for unrecognized tags (e.g., not in libfrureg) 603 */ 604 if ((tag_type == -1) || 605 ((payload_length = get_payload_length(tag)) != length)) { 606 def = &unknown; 607 608 unknown.tagType = -1; 609 unknown.tagDense = -1; 610 unknown.payloadLen = length; 611 unknown.dataLength = unknown.payloadLen; 612 613 if (tag_type == -1) 614 (void) snprintf(tagname, sizeof (tagname), "INVALID"); 615 else 616 (void) snprintf(tagname, sizeof (tagname), 617 "%s_%u_%u_%u", get_tagtype_str(tag_type), 618 get_tag_dense(tag), payload_length, length); 619 } else if ((def = fru_reg_lookup_def_by_tag(*tag)) == NULL) { 620 def = &unknown; 621 622 unknown.tagType = tag_type; 623 unknown.tagDense = get_tag_dense(tag); 624 unknown.payloadLen = payload_length; 625 unknown.dataLength = unknown.payloadLen; 626 627 (void) snprintf(tagname, sizeof (tagname), "%s_%u_%u", 628 get_tagtype_str(unknown.tagType), 629 unknown.tagDense, payload_length); 630 } 631 632 633 /* 634 * Print the defined element 635 */ 636 print_element(payload, def, "", INDENT); 637 638 return (FRU_SUCCESS); 639 } 640 641 /* 642 * Print a segment's name and the contents of each data element in the segment 643 */ 644 static int 645 print_packets_in_segment(fru_seghdl_t segment, void *args) 646 { 647 char *name; 648 649 int status; 650 651 652 if ((status = fru_get_segment_name(segment, &name)) != FRU_SUCCESS) { 653 saved_status = status; 654 name = ""; 655 error(gettext("Error getting segment name: %s\n"), 656 fru_strerror(status)); 657 } 658 659 660 if (xml) 661 output("%*s<Segment name=\"%s\">\n", INDENT, "", name); 662 else 663 output("%*sSEGMENT: %s\n", INDENT, "", name); 664 665 /* Iterate over the packets in the segment, printing the contents */ 666 if ((status = fru_for_each_packet(segment, print_packet, args)) 667 != FRU_SUCCESS) { 668 saved_status = status; 669 error(gettext("Error processing data in segment \"%s\": %s\n"), 670 name, fru_strerror(status)); 671 } 672 673 if (xml) output("%*s</Segment>\n", INDENT, ""); 674 675 free(name); 676 677 return (FRU_SUCCESS); 678 } 679 680 /* ARGSUSED */ 681 static void 682 print_node_path(fru_node_t fru_type, const char *path, const char *name, 683 end_node_fp_t *end_node, void **end_args) 684 { 685 output("%s%s\n", path, 686 ((fru_type == FRU_NODE_CONTAINER) ? " (container)" 687 : ((fru_type == FRU_NODE_FRU) ? " (fru)" : ""))); 688 } 689 690 /* 691 * Close the XML element for a "location" node 692 */ 693 /* ARGSUSED */ 694 static void 695 end_location_xml(fru_nodehdl_t node, const char *path, const char *name, 696 void *args) 697 { 698 assert(args != NULL); 699 output("</Location> <!-- %s -->\n", args); 700 } 701 702 /* 703 * Close the XML element for a "fru" node 704 */ 705 /* ARGSUSED */ 706 static void 707 end_fru_xml(fru_nodehdl_t node, const char *path, const char *name, void *args) 708 { 709 assert(args != NULL); 710 output("</Fru> <!-- %s -->\n", args); 711 } 712 713 /* 714 * Close the XML element for a "container" node 715 */ 716 /* ARGSUSED */ 717 static void 718 end_container_xml(fru_nodehdl_t node, const char *path, const char *name, 719 void *args) 720 { 721 assert(args != NULL); 722 output("</Container> <!-- %s -->\n", args); 723 } 724 725 /* 726 * Introduce a node in XML and set the appropriate node-closing function 727 */ 728 /* ARGSUSED */ 729 static void 730 print_node_xml(fru_node_t fru_type, const char *path, const char *name, 731 end_node_fp_t *end_node, void **end_args) 732 { 733 switch (fru_type) { 734 case FRU_NODE_FRU: 735 output("<Fru name=\"%s\">\n", name); 736 *end_node = end_fru_xml; 737 break; 738 case FRU_NODE_CONTAINER: 739 output("<Container name=\"%s\">\n", name); 740 *end_node = end_container_xml; 741 break; 742 default: 743 output("<Location name=\"%s\">\n", name); 744 *end_node = end_location_xml; 745 break; 746 } 747 748 *end_args = (void *) name; 749 } 750 751 /* 752 * Print node info and, where appropriate, node contents 753 */ 754 /* ARGSUSED */ 755 static fru_errno_t 756 process_node(fru_nodehdl_t node, const char *path, const char *name, 757 void *args, end_node_fp_t *end_node, void **end_args) 758 { 759 int status; 760 761 fru_node_t fru_type = FRU_NODE_UNKNOWN; 762 763 764 if ((status = fru_get_node_type(node, &fru_type)) != FRU_SUCCESS) { 765 saved_status = status; 766 error(gettext("Error getting node type: %s\n"), 767 fru_strerror(status)); 768 } 769 770 if (containers_only) { 771 if (fru_type != FRU_NODE_CONTAINER) 772 return (FRU_SUCCESS); 773 name = path; 774 } 775 776 /* Introduce the node */ 777 assert(print_node != NULL); 778 print_node(fru_type, path, name, end_node, end_args); 779 780 if (list_only) 781 return (FRU_SUCCESS); 782 783 /* Print the contents of each packet in each segment of a container */ 784 if (fru_type == FRU_NODE_CONTAINER) { 785 if (xml) output("<ContainerData>\n"); 786 if ((status = 787 fru_for_each_segment(node, print_packets_in_segment, 788 NULL)) 789 != FRU_SUCCESS) { 790 saved_status = status; 791 error(gettext("Error processing node \"%s\": %s\n"), 792 name, fru_strerror(status)); 793 } 794 if (xml) output("</ContainerData>\n"); 795 } 796 797 return (FRU_SUCCESS); 798 } 799 800 /* 801 * Process the node if its path matches the search path in "args" 802 */ 803 /* ARGSUSED */ 804 static fru_errno_t 805 process_matching_node(fru_nodehdl_t node, const char *path, const char *name, 806 void *args, end_node_fp_t *end_node, void **end_args) 807 { 808 int status; 809 810 811 if (!fru_pathmatch(path, args)) 812 return (FRU_SUCCESS); 813 814 status = process_node(node, path, path, args, end_node, end_args); 815 816 return ((status == FRU_SUCCESS) ? FRU_WALK_TERMINATE : status); 817 } 818 819 /* 820 * Write the trailer required for well-formed DTD-compliant XML 821 */ 822 static void 823 terminate_xml() 824 { 825 errno = 0; 826 if (ftell(errlog) > 0) { 827 char c; 828 829 output("<ErrorLog>\n"); 830 rewind(errlog); 831 if (!errno) 832 while ((c = getc(errlog)) != EOF) 833 xputchar(c); 834 output("</ErrorLog>\n"); 835 } 836 837 if (errno) { 838 /*NOTREACHED*/ 839 errlog = NULL; 840 error(gettext("Error copying error messages to \"ErrorLog\""), 841 strerror(errno)); 842 } 843 844 output("</FRUID_XML_Tree>\n"); 845 } 846 847 /* 848 * Print available FRU ID information 849 */ 850 int 851 prtfru(const char *searchpath, int containers_only_flag, int list_only_flag, 852 int xml_flag) 853 { 854 fru_errno_t status; 855 856 fru_nodehdl_t frutree = 0; 857 858 859 /* Copy parameter flags to global flags */ 860 containers_only = containers_only_flag; 861 list_only = list_only_flag; 862 xml = xml_flag; 863 864 865 /* Help arrange for correct, efficient interleaving of output */ 866 (void) setvbuf(stderr, NULL, _IOLBF, 0); 867 868 869 /* Initialize for XML--or not */ 870 if (xml) { 871 safeputchar = xputchar; 872 safeputs = xputs; 873 874 print_node = print_node_xml; 875 876 if ((errlog = tmpfile()) == NULL) { 877 (void) fprintf(stderr, 878 "Error creating error log file: %s\n", 879 strerror(errno)); 880 return (1); 881 } 882 883 /* Output the XML preamble */ 884 output("<?xml version=\"1.0\" ?>\n" 885 "<!--\n" 886 " Copyright 2000-2002 Sun Microsystems, Inc. " 887 "All rights reserved.\n" 888 " Use is subject to license terms.\n" 889 "-->\n\n" 890 "<!DOCTYPE FRUID_XML_Tree SYSTEM \"prtfrureg.dtd\">\n\n" 891 "<FRUID_XML_Tree>\n"); 892 893 /* Arrange to always properly terminate XML */ 894 if (atexit(terminate_xml)) 895 error(gettext("Warning: XML will not be terminated: " 896 "%s\n"), strerror(errno)); 897 } else 898 print_node = print_node_path; 899 900 901 /* Get the root node */ 902 if ((status = fru_get_root(&frutree)) == FRU_NODENOTFOUND) { 903 error(gettext("This system does not provide FRU ID data\n")); 904 return (1); 905 } else if (status != FRU_SUCCESS) { 906 error(gettext("Unable to access FRU ID data: %s\n"), 907 fru_strerror(status)); 908 return (1); 909 } 910 911 /* Process the tree */ 912 if (searchpath == NULL) { 913 status = fru_walk_tree(frutree, "", process_node, NULL); 914 } else { 915 status = fru_walk_tree(frutree, "", process_matching_node, 916 (void *)searchpath); 917 if (status == FRU_WALK_TERMINATE) { 918 status = FRU_SUCCESS; 919 } else if (status == FRU_SUCCESS) { 920 error(gettext("\"%s\" not found\n"), searchpath); 921 return (1); 922 } 923 } 924 925 if (status != FRU_SUCCESS) 926 error(gettext("Error processing FRU tree: %s\n"), 927 fru_strerror(status)); 928 929 return (((status == FRU_SUCCESS) && (saved_status == 0)) ? 0 : 1); 930 } 931