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