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