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