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