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 <sys/types.h> 29 #include <fmadm.h> 30 #include <errno.h> 31 #include <limits.h> 32 #include <strings.h> 33 #include <stdio.h> 34 #include <unistd.h> 35 #include <sys/wait.h> 36 #include <sys/stat.h> 37 #include <fcntl.h> 38 #include <fm/fmd_log.h> 39 #include <sys/fm/protocol.h> 40 #include <fm/libtopo.h> 41 #include <fm/fmd_adm.h> 42 #include <dlfcn.h> 43 #include <sys/systeminfo.h> 44 #include <sys/utsname.h> 45 #include <libintl.h> 46 #include <locale.h> 47 #include <sys/smbios.h> 48 #include <libdevinfo.h> 49 #include <stdlib.h> 50 51 #define offsetof(s, m) ((size_t)(&(((s*)0)->m))) 52 53 /* 54 * catalog_setup() must be called to setup support functions. 55 * Fault records are added to catalog by calling add_fault_record_to_catalog() 56 * records are stored in order of importance to the system. 57 * If -g flag is set or not_suppressed is not set and the class fru, fault, 58 * type are the same then details are merged into an existing record, with uuid 59 * records are stored in time order. 60 * For each record information is extracted from nvlist and merged into linked 61 * list each is checked for identical records for which percentage certainty are 62 * added together. 63 * print_catalog() is called to print out catalog and release external resources 64 * 65 * /---------------\ 66 * status_rec_list -> | | -| 67 * \---------------/ 68 * \/ 69 * /---------------\ /-------\ /-------\ 70 * status_fru_list | status_record | -> | uurec | -> | uurec | -| 71 * \/ | | |- | | <- | | 72 * /-------------\ | | \-------/ \-------/ 73 * | | -> | | \/ \/ 74 * \-------------/ | | /-------\ /-------\ 75 * \/ | | -> | asru | -> | asru | 76 * --- | | | | <- | | 77 * | | \-------/ \-------/ 78 * status_asru_list | class | 79 * \/ | resource | /-------\ /-------\ 80 * /-------------\ | fru | -> | list | -> | list | 81 * | | -> | serial | | | <- | | 82 * \-------------/ | | \-------/ \-------/ 83 * \/ \---------------/ 84 * --- \/ /\ 85 * /---------------\ 86 * | status_record | 87 * \---------------/ 88 * 89 * Fmadm faulty takes a number of options which affect the format of the 90 * output displayed. By default, the display reports the FRU and ASRU along 91 * with other information on per-case basis as in the example below. 92 * 93 * --------------- ------------------------------------ -------------- ------- 94 * TIME EVENT-ID MSG-ID SEVERITY 95 * --------------- ------------------------------------ -------------- ------- 96 * Sep 21 10:01:36 d482f935-5c8f-e9ab-9f25-d0aaafec1e6c AMD-8000-2F Major 97 * 98 * Fault class : fault.memory.dimm_sb 99 * Affects : mem:///motherboard=0/chip=0/memory-controller=0/dimm=0/rank=0 100 * degraded but still in service 101 * FRU : "CPU 0 DIMM 0" (hc://.../memory-controller=0/dimm=0) 102 * faulty 103 * 104 * Description : The number of errors associated with this memory module has 105 * exceeded acceptable levels. Refer to 106 * http://sun.com/msg/AMD-8000-2F for more information. 107 * 108 * Response : Pages of memory associated with this memory module are being 109 * removed from service as errors are reported. 110 * 111 * Impact : Total system memory capacity will be reduced as pages are 112 * retired. 113 * 114 * Action : Schedule a repair procedure to replace the affected memory 115 * module. Use fmdump -v -u <EVENT_ID> to identify the module. 116 * 117 * The -v flag is similar, but adds some additonal information such as the 118 * resource. The -s flag is also similar but just gives the top line summary. 119 * All these options (ie without the -f or -r flags) use the print_catalog() 120 * function to do the display. 121 * 122 * The -f flag changes the output so that it appears sorted on a per-fru basis. 123 * The output is somewhat cut down compared to the default output. If -f is 124 * used, then print_fru() is used to print the output. 125 * 126 * ----------------------------------------------------------------------------- 127 * "SLOT 2" (hc://.../hostbridge=3/pciexrc=3/pciexbus=4/pciexdev=0) faulty 128 * 5ca4aeb3-36...f6be-c2e8166dc484 2 suspects in this FRU total certainty 100% 129 * 130 * Description : A problem was detected for a PCI device. 131 * Refer to http://sun.com/msg/PCI-8000-7J for more information. 132 * 133 * Response : One or more device instances may be disabled 134 * 135 * Impact : Possible loss of services provided by the device instances 136 * associated with this fault 137 * 138 * Action : Schedule a repair procedure to replace the affected device. 139 * Use fmdump -v -u <EVENT_ID> to identify the device or contact 140 * Sun for support. 141 * 142 * The -r flag changes the output so that it appears sorted on a per-asru basis. 143 * The output is very much cut down compared to the default output, just giving 144 * the asru fmri and state. Here print_asru() is used to print the output. 145 * 146 * mem:///motherboard=0/chip=0/memory-controller=0/dimm=0/rank=0 degraded 147 * 148 * For all fmadm faulty options, the sequence of events is 149 * 150 * 1) Walk through all the cases in the system using fmd_adm_case_iter() and 151 * for each case call dfault_rec(). This will call add_fault_record_to_catalog() 152 * This will extract the data from the nvlist and call catalog_new_record() to 153 * save the data away in various linked lists in the catalogue. 154 * 155 * 2) Once this is done, the data can be supplemented by using 156 * fmd_adm_rsrc_iter(). However this is now only necessary for the -i option. 157 * 158 * 3) Finally print_catalog(), print_fru() or print_asru() are called as 159 * appropriate to display the information from the catalogue sorted in the 160 * requested way. 161 * 162 */ 163 164 typedef struct name_list { 165 struct name_list *next; 166 struct name_list *prev; 167 char *name; 168 uint8_t pct; 169 uint8_t max_pct; 170 ushort_t count; 171 int status; 172 } name_list_t; 173 174 typedef struct ari_list { 175 char *ari_uuid; 176 struct ari_list *next; 177 } ari_list_t; 178 179 typedef struct uurec { 180 struct uurec *next; 181 struct uurec *prev; 182 char *uuid; 183 ari_list_t *ari_uuid_list; 184 name_list_t *asru; 185 uint64_t sec; 186 } uurec_t; 187 188 typedef struct uurec_select { 189 struct uurec_select *next; 190 char *uuid; 191 } uurec_select_t; 192 193 typedef struct host_id { 194 char *chassis; 195 char *server; 196 char *platform; 197 } hostid_t; 198 199 typedef struct host_id_list { 200 hostid_t hostid; 201 struct host_id_list *next; 202 } host_id_list_t; 203 204 typedef struct status_record { 205 hostid_t *host; 206 int nrecs; 207 uurec_t *uurec; 208 char *severity; /* in C locale */ 209 char *msgid; 210 name_list_t *class; 211 name_list_t *resource; 212 name_list_t *asru; 213 name_list_t *fru; 214 name_list_t *serial; 215 char *url; 216 uint8_t not_suppressed; 217 } status_record_t; 218 219 typedef struct sr_list { 220 struct sr_list *next; 221 struct sr_list *prev; 222 struct status_record *status_record; 223 } sr_list_t; 224 225 typedef struct resource_list { 226 struct resource_list *next; 227 struct resource_list *prev; 228 sr_list_t *status_rec_list; 229 char *resource; 230 uint8_t not_suppressed; 231 uint8_t max_pct; 232 } resource_list_t; 233 234 typedef struct tgetlabel_data { 235 char *label; 236 char *fru; 237 } tgetlabel_data_t; 238 239 sr_list_t *status_rec_list; 240 resource_list_t *status_fru_list; 241 resource_list_t *status_asru_list; 242 243 static char *locale; 244 static char *nlspath; 245 static int max_display; 246 static int max_fault = 0; 247 static topo_hdl_t *topo_handle; 248 static char *topo_handle_uuid; 249 static host_id_list_t *host_list; 250 static int n_server; 251 static int opt_g; 252 253 static char * 254 format_date(char *buf, size_t len, uint64_t sec) 255 { 256 if (sec > LONG_MAX) { 257 (void) fprintf(stderr, 258 "record time is too large for 32-bit utility\n"); 259 (void) snprintf(buf, len, "0x%llx", sec); 260 } else { 261 time_t tod = (time_t)sec; 262 (void) strftime(buf, len, "%b %d %T", localtime(&tod)); 263 } 264 265 return (buf); 266 } 267 268 static hostid_t * 269 find_hostid_in_list(char *platform, char *chassis, char *server) 270 { 271 hostid_t *rt = NULL; 272 host_id_list_t *hostp; 273 274 if (platform == NULL) 275 platform = "-"; 276 if (server == NULL) 277 server = "-"; 278 hostp = host_list; 279 while (hostp) { 280 if (hostp->hostid.platform && 281 strcmp(hostp->hostid.platform, platform) == 0 && 282 hostp->hostid.server && 283 strcmp(hostp->hostid.server, server) == 0 && 284 (chassis == NULL || hostp->hostid.chassis == NULL || 285 strcmp(chassis, hostp->hostid.chassis) == 0)) { 286 rt = &hostp->hostid; 287 break; 288 } 289 hostp = hostp->next; 290 } 291 if (rt == NULL) { 292 hostp = malloc(sizeof (host_id_list_t)); 293 hostp->hostid.platform = strdup(platform); 294 hostp->hostid.server = strdup(server); 295 hostp->hostid.chassis = chassis ? strdup(chassis) : NULL; 296 hostp->next = host_list; 297 host_list = hostp; 298 rt = &hostp->hostid; 299 n_server++; 300 } 301 return (rt); 302 } 303 304 static hostid_t * 305 find_hostid(nvlist_t *nvl) 306 { 307 char *platform = NULL, *chassis = NULL, *server = NULL; 308 nvlist_t *auth, *fmri; 309 hostid_t *rt = NULL; 310 311 if (nvlist_lookup_nvlist(nvl, FM_SUSPECT_DE, &fmri) == 0 && 312 nvlist_lookup_nvlist(fmri, FM_FMRI_AUTHORITY, &auth) == 0) { 313 (void) nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT, 314 &platform); 315 (void) nvlist_lookup_string(auth, FM_FMRI_AUTH_SERVER, &server); 316 (void) nvlist_lookup_string(auth, FM_FMRI_AUTH_CHASSIS, 317 &chassis); 318 rt = find_hostid_in_list(platform, chassis, server); 319 } 320 return (rt); 321 } 322 323 static void 324 catalog_setup(void) 325 { 326 char *tp; 327 int pl; 328 329 /* 330 * All FMA event dictionaries use msgfmt(1) message objects to produce 331 * messages, even for the C locale. We therefore want to use dgettext 332 * for all message lookups, but its defined behavior in the C locale is 333 * to return the input string. Since our input strings are event codes 334 * and not format strings, this doesn't help us. We resolve this nit 335 * by setting NLSPATH to a non-existent file: the presence of NLSPATH 336 * is defined to force dgettext(3C) to do a full lookup even for C. 337 */ 338 nlspath = getenv("NLSPATH"); 339 if (nlspath == NULL) 340 putenv("NLSPATH=/usr/lib/fm/fmd/fmd.cat"); 341 else { 342 pl = strlen(nlspath) + sizeof ("NLSPATH=") + 1; 343 tp = malloc(pl); 344 (void) snprintf(tp, pl, "NLSPATH=%s", nlspath); 345 nlspath = tp; 346 } 347 348 locale = setlocale(LC_MESSAGES, ""); 349 } 350 351 static char * 352 get_dict_url(char *id) 353 { 354 char *url = "http://sun.com/msg/"; 355 int msz = sizeof (url) + strlen(id) + 1; 356 char *cp; 357 358 cp = malloc(msz); 359 (void) snprintf(cp, msz, "%s%s", url, id); 360 return (cp); 361 } 362 363 static char * 364 get_dict_msg(char *id, char *idx, int unknown, int translate) 365 { 366 char mbuf[128]; 367 char *msg; 368 char dbuf[32]; 369 char *p; 370 int restore_env = 0; 371 int restore_locale = 0; 372 373 p = strchr(id, '-'); 374 if (p == NULL || p == id || (p - id) >= 32) { 375 msg = mbuf; 376 } else { 377 strncpy(dbuf, id, (size_t)(p - id)); 378 dbuf[(size_t)(p - id)] = 0; 379 380 (void) snprintf(mbuf, sizeof (mbuf), "%s.%s", id, idx); 381 if (translate == 0 || nlspath == NULL) { 382 (void) setlocale(LC_MESSAGES, "C"); 383 restore_locale = 1; 384 } 385 bindtextdomain("FMD", "/usr/lib/locale"); 386 msg = dgettext(dbuf, mbuf); 387 if (msg == mbuf) { 388 (void) setlocale(LC_MESSAGES, "C"); 389 restore_locale = 1; 390 msg = dgettext(dbuf, mbuf); 391 } 392 if (msg == mbuf) { 393 putenv("NLSPATH=/usr/lib/fm/fmd/fmd.cat"); 394 restore_env = 1; 395 (void) setlocale(LC_MESSAGES, "C"); 396 msg = dgettext(dbuf, mbuf); 397 } 398 if (restore_locale) 399 (void) setlocale(LC_MESSAGES, locale); 400 if (restore_env && nlspath) 401 putenv(nlspath); 402 } 403 if (msg == mbuf) { 404 if (unknown) 405 msg = "unknown"; 406 else 407 msg = NULL; 408 } 409 return (msg); 410 } 411 412 /* 413 * compare two fru strings which are made up of substrings seperated by '/' 414 * return true if every substring is the same in the two strings, or if a 415 * substring is null in one. 416 */ 417 418 static int 419 frucmp(char *f1, char *f2) 420 { 421 char c1, c2; 422 int i = 0; 423 424 for (;;) { 425 c1 = *f1; 426 c2 = *f2; 427 if (c1 == c2) { 428 i = (c1 == '/') ? 0 : i + 1; 429 } else if (i == 0) { 430 if (c1 == '/') { 431 do { 432 f2++; 433 } while ((c2 = *f2) != 0 && c2 != '/'); 434 if (c2 == NULL) 435 break; 436 } else if (c2 == '/') { 437 do { 438 f1++; 439 } while ((c1 = *f1) != 0 && c1 != '/'); 440 if (c1 == NULL) 441 break; 442 } else 443 break; 444 } else 445 break; 446 if (c1 == NULL) 447 return (0); 448 f1++; 449 f2++; 450 } 451 return (1); 452 } 453 454 static int 455 tgetlabel(topo_hdl_t *thp, tnode_t *node, void *arg) 456 { 457 int err; 458 char *fru_name, *lname; 459 nvlist_t *fru = NULL; 460 int rt = TOPO_WALK_NEXT; 461 tgetlabel_data_t *tdp = (tgetlabel_data_t *)arg; 462 463 if (topo_node_fru(node, &fru, NULL, &err) == 0) { 464 if (topo_fmri_nvl2str(thp, fru, &fru_name, &err) == 0) { 465 if (frucmp(tdp->fru, fru_name) == 0 && 466 topo_node_label(node, &lname, &err) == 0) { 467 tdp->label = strdup(lname); 468 topo_hdl_strfree(thp, lname); 469 rt = TOPO_WALK_TERMINATE; 470 } 471 topo_hdl_strfree(thp, fru_name); 472 } 473 nvlist_free(fru); 474 } 475 return (rt); 476 } 477 478 static void 479 label_get_topo(void) 480 { 481 int err; 482 483 topo_handle = topo_open(TOPO_VERSION, 0, &err); 484 if (topo_handle) { 485 topo_handle_uuid = topo_snap_hold(topo_handle, NULL, &err); 486 } 487 } 488 489 static void 490 label_release_topo(void) 491 { 492 if (topo_handle_uuid) 493 topo_hdl_strfree(topo_handle, topo_handle_uuid); 494 if (topo_handle) { 495 topo_snap_release(topo_handle); 496 topo_close(topo_handle); 497 } 498 } 499 500 static char * 501 get_fmri_label(char *fru) 502 { 503 topo_walk_t *twp; 504 tgetlabel_data_t td; 505 int err; 506 507 td.label = NULL; 508 td.fru = fru; 509 if (topo_handle == NULL) 510 label_get_topo(); 511 if (topo_handle_uuid) { 512 twp = topo_walk_init(topo_handle, FM_FMRI_SCHEME_HC, 513 tgetlabel, &td, &err); 514 if (twp) { 515 topo_walk_step(twp, TOPO_WALK_CHILD); 516 topo_walk_fini(twp); 517 } 518 } 519 return (td.label); 520 } 521 522 static char * 523 get_nvl2str_topo(nvlist_t *nvl) 524 { 525 char *name = NULL; 526 char *tname; 527 int err; 528 char *scheme = NULL; 529 char *mod_name = NULL; 530 char buf[128]; 531 532 if (topo_handle == NULL) 533 label_get_topo(); 534 if (topo_fmri_nvl2str(topo_handle, nvl, &tname, &err) == 0) { 535 name = strdup(tname); 536 topo_hdl_strfree(topo_handle, tname); 537 } else { 538 (void) nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &scheme); 539 (void) nvlist_lookup_string(nvl, FM_FMRI_MOD_NAME, &mod_name); 540 if (scheme && strcmp(scheme, FM_FMRI_SCHEME_FMD) == 0 && 541 mod_name) { 542 (void) snprintf(buf, sizeof (buf), "%s:///module/%s", 543 scheme, mod_name); 544 name = strdup(buf); 545 } 546 } 547 return (name); 548 } 549 550 static int 551 set_priority(char *s) 552 { 553 int rt = 0; 554 555 if (s) { 556 if (strcmp(s, "Minor") == 0) 557 rt = 1; 558 else if (strcmp(s, "Major") == 0) 559 rt = 10; 560 else if (strcmp(s, "Critical") == 0) 561 rt = 100; 562 } 563 return (rt); 564 } 565 566 static int 567 cmp_priority(char *s1, char *s2, uint64_t t1, uint64_t t2, uint8_t p1, 568 uint8_t p2) 569 { 570 int r1, r2; 571 int rt; 572 573 r1 = set_priority(s1); 574 r2 = set_priority(s2); 575 rt = r1 - r2; 576 if (rt == 0) { 577 if (t1 > t2) 578 rt = 1; 579 else if (t1 < t2) 580 rt = -1; 581 else 582 rt = p1 - p2; 583 } 584 return (rt); 585 } 586 587 /* 588 * merge two lists into one, by comparing enties in new and moving into list if 589 * name is not there or free off memory for names which are already there 590 * add_pct indicates if pct is the sum or highest pct 591 */ 592 static name_list_t * 593 merge_name_list(name_list_t **list, name_list_t *new, int add_pct) 594 { 595 name_list_t *lp, *np, *sp, *rt = NULL; 596 int max_pct; 597 598 rt = *list; 599 np = new; 600 while (np) { 601 lp = *list; 602 while (lp) { 603 if (strcmp(lp->name, np->name) == 0) 604 break; 605 lp = lp->next; 606 if (lp == *list) 607 lp = NULL; 608 } 609 if (np->next == new) 610 sp = NULL; 611 else 612 sp = np->next; 613 if (lp) { 614 lp->status |= (np->status & FM_SUSPECT_FAULTY); 615 if (add_pct) { 616 lp->pct += np->pct; 617 lp->count += np->count; 618 } else if (np->pct > lp->pct) { 619 lp->pct = np->pct; 620 } 621 max_pct = np->max_pct; 622 free(np->name); 623 free(np); 624 np = NULL; 625 if (max_pct > lp->max_pct) { 626 lp->max_pct = max_pct; 627 if (lp->max_pct > lp->prev->max_pct && 628 lp != *list) { 629 lp->prev->next = lp->next; 630 lp->next->prev = lp->prev; 631 np = lp; 632 } 633 } 634 } 635 if (np) { 636 lp = *list; 637 if (lp) { 638 if (np->max_pct > lp->max_pct) { 639 np->next = lp; 640 np->prev = lp->prev; 641 lp->prev->next = np; 642 lp->prev = np; 643 *list = np; 644 rt = np; 645 } else { 646 lp = lp->next; 647 while (lp != *list && 648 np->max_pct < lp->max_pct) { 649 lp = lp->next; 650 } 651 np->next = lp; 652 np->prev = lp->prev; 653 lp->prev->next = np; 654 lp->prev = np; 655 } 656 } else { 657 *list = np; 658 np->next = np; 659 np->prev = np; 660 rt = np; 661 } 662 } 663 np = sp; 664 } 665 return (rt); 666 } 667 668 /* 669 * compare entries in two lists return true if the two lists have identical 670 * content. The two lists may not have entries in the same order, so we compare 671 * the size of the list as well as trying to find every entry from one list in 672 * the other. 673 */ 674 static int 675 cmp_name_list(name_list_t *lxp1, name_list_t *lxp2) 676 { 677 name_list_t *lp1, *lp2; 678 int l1 = 0, l2 = 0, common = 0; 679 680 lp2 = lxp2; 681 while (lp2) { 682 l2++; 683 lp2 = lp2->next; 684 if (lp2 == lxp2) 685 break; 686 } 687 lp1 = lxp1; 688 while (lp1) { 689 l1++; 690 lp2 = lxp2; 691 while (lp2) { 692 if (strcmp(lp2->name, lp1->name) == 0) { 693 common++; 694 break; 695 } 696 lp2 = lp2->next; 697 if (lp2 == lxp2) 698 break; 699 } 700 lp1 = lp1->next; 701 if (lp1 == lxp1) 702 break; 703 } 704 if (l1 == l2 && l2 == common) 705 return (0); 706 else 707 return (1); 708 } 709 710 static name_list_t * 711 alloc_name_list(char *name, uint8_t pct) 712 { 713 name_list_t *nlp; 714 715 nlp = malloc(sizeof (*nlp)); 716 nlp->name = strdup(name); 717 nlp->pct = pct; 718 nlp->max_pct = pct; 719 nlp->count = 1; 720 nlp->next = nlp; 721 nlp->prev = nlp; 722 nlp->status = 0; 723 return (nlp); 724 } 725 726 static void 727 free_name_list(name_list_t *list) 728 { 729 name_list_t *next = list; 730 name_list_t *lp; 731 732 if (list) { 733 do { 734 lp = next; 735 next = lp->next; 736 free(lp->name); 737 free(lp); 738 } while (next != list); 739 } 740 } 741 742 static status_record_t * 743 new_record_init(uurec_t *uurec_p, char *msgid, name_list_t *class, 744 name_list_t *fru, name_list_t *asru, name_list_t *resource, 745 name_list_t *serial, const char *url, boolean_t not_suppressed, 746 hostid_t *hostid) 747 { 748 status_record_t *status_rec_p; 749 750 status_rec_p = (status_record_t *)malloc(sizeof (status_record_t)); 751 status_rec_p->nrecs = 1; 752 status_rec_p->host = hostid; 753 status_rec_p->uurec = uurec_p; 754 uurec_p->next = NULL; 755 uurec_p->prev = NULL; 756 uurec_p->asru = asru; 757 status_rec_p->severity = get_dict_msg(msgid, "severity", 1, 0); 758 status_rec_p->class = class; 759 status_rec_p->fru = fru; 760 status_rec_p->asru = asru; 761 status_rec_p->resource = resource; 762 status_rec_p->serial = serial; 763 status_rec_p->url = url ? strdup(url) : NULL; 764 status_rec_p->msgid = strdup(msgid); 765 status_rec_p->not_suppressed = not_suppressed; 766 return (status_rec_p); 767 } 768 769 /* 770 * add record to given list maintaining order higher priority first. 771 */ 772 static void 773 add_rec_list(status_record_t *status_rec_p, sr_list_t **list_pp) 774 { 775 sr_list_t *tp, *np, *sp; 776 int order; 777 uint64_t sec; 778 779 np = malloc(sizeof (sr_list_t)); 780 np->status_record = status_rec_p; 781 sec = status_rec_p->uurec->sec; 782 if ((sp = *list_pp) == NULL) { 783 *list_pp = np; 784 np->next = np; 785 np->prev = np; 786 } else { 787 /* insert new record in front of lower priority */ 788 tp = sp; 789 order = cmp_priority(status_rec_p->severity, 790 sp->status_record->severity, sec, 791 tp->status_record->uurec->sec, 0, 0); 792 if (order > 0) { 793 *list_pp = np; 794 } else { 795 tp = sp->next; 796 while (tp != sp && 797 cmp_priority(status_rec_p->severity, 798 tp->status_record->severity, sec, 799 tp->status_record->uurec->sec, 0, 0)) { 800 tp = tp->next; 801 } 802 } 803 np->next = tp; 804 np->prev = tp->prev; 805 tp->prev->next = np; 806 tp->prev = np; 807 } 808 } 809 810 static void 811 add_resource(status_record_t *status_rec_p, resource_list_t **rp, 812 resource_list_t *np) 813 { 814 int order; 815 uint64_t sec; 816 resource_list_t *sp, *tp; 817 status_record_t *srp; 818 char *severity = status_rec_p->severity; 819 820 add_rec_list(status_rec_p, &np->status_rec_list); 821 if ((sp = *rp) == NULL) { 822 np->next = np; 823 np->prev = np; 824 *rp = np; 825 } else { 826 /* 827 * insert new record in front of lower priority 828 */ 829 tp = sp->next; 830 srp = sp->status_rec_list->status_record; 831 sec = status_rec_p->uurec->sec; 832 order = cmp_priority(severity, srp->severity, sec, 833 srp->uurec->sec, np->max_pct, sp->max_pct); 834 if (order > 0) { 835 *rp = np; 836 } else { 837 srp = tp->status_rec_list->status_record; 838 while (tp != sp && 839 cmp_priority(severity, srp->severity, sec, 840 srp->uurec->sec, np->max_pct, sp->max_pct) < 0) { 841 tp = tp->next; 842 srp = tp->status_rec_list->status_record; 843 } 844 } 845 np->next = tp; 846 np->prev = tp->prev; 847 tp->prev->next = np; 848 tp->prev = np; 849 } 850 } 851 852 static void 853 add_resource_list(status_record_t *status_rec_p, name_list_t *fp, 854 resource_list_t **rpp) 855 { 856 int order; 857 resource_list_t *np, *end; 858 status_record_t *srp; 859 860 np = *rpp; 861 end = np; 862 while (np) { 863 if (strcmp(fp->name, np->resource) == 0) { 864 np->not_suppressed |= status_rec_p->not_suppressed; 865 srp = np->status_rec_list->status_record; 866 order = cmp_priority(status_rec_p->severity, 867 srp->severity, status_rec_p->uurec->sec, 868 srp->uurec->sec, fp->max_pct, np->max_pct); 869 if (order > 0 && np != end) { 870 /* 871 * remove from list and add again using 872 * new priority 873 */ 874 np->prev->next = np->next; 875 np->next->prev = np->prev; 876 add_resource(status_rec_p, 877 rpp, np); 878 } else { 879 add_rec_list(status_rec_p, 880 &np->status_rec_list); 881 } 882 break; 883 } 884 np = np->next; 885 if (np == end) { 886 np = NULL; 887 break; 888 } 889 } 890 if (np == NULL) { 891 np = malloc(sizeof (resource_list_t)); 892 np->resource = fp->name; 893 np->not_suppressed = status_rec_p->not_suppressed; 894 np->status_rec_list = NULL; 895 np->max_pct = fp->max_pct; 896 add_resource(status_rec_p, rpp, np); 897 } 898 } 899 900 static void 901 add_list(status_record_t *status_rec_p, name_list_t *listp, 902 resource_list_t **glistp) 903 { 904 name_list_t *fp, *end; 905 906 fp = listp; 907 end = fp; 908 while (fp) { 909 add_resource_list(status_rec_p, fp, glistp); 910 fp = fp->next; 911 if (fp == end) 912 break; 913 } 914 } 915 916 /* 917 * add record to rec, fru and asru lists. 918 */ 919 static void 920 catalog_new_record(uurec_t *uurec_p, char *msgid, name_list_t *class, 921 name_list_t *fru, name_list_t *asru, name_list_t *resource, 922 name_list_t *serial, const char *url, boolean_t not_suppressed, 923 hostid_t *hostid) 924 { 925 status_record_t *status_rec_p; 926 927 status_rec_p = new_record_init(uurec_p, msgid, class, fru, asru, 928 resource, serial, url, not_suppressed, hostid); 929 add_rec_list(status_rec_p, &status_rec_list); 930 if (status_rec_p->fru) 931 add_list(status_rec_p, status_rec_p->fru, &status_fru_list); 932 if (status_rec_p->asru) 933 add_list(status_rec_p, status_rec_p->asru, &status_asru_list); 934 } 935 936 /* 937 * add uuid and diagnoses time to an existing record for similar fault on the 938 * same fru 939 */ 940 static void 941 catalog_merge_record(status_record_t *status_rec_p, uurec_t *uurec_p, 942 name_list_t *asru, name_list_t *resource, name_list_t *serial, 943 const char *url, boolean_t not_suppressed) 944 { 945 uurec_t *uurec1_p; 946 947 status_rec_p->nrecs++; 948 /* add uurec in time order */ 949 if (status_rec_p->uurec->sec > uurec_p->sec) { 950 uurec_p->next = status_rec_p->uurec; 951 uurec_p->prev = NULL; 952 status_rec_p->uurec = uurec_p; 953 } else { 954 uurec1_p = status_rec_p->uurec; 955 while (uurec1_p->next && uurec1_p->next->sec <= uurec_p->sec) 956 uurec1_p = uurec1_p->next; 957 if (uurec1_p->next) 958 uurec1_p->next->prev = uurec_p; 959 uurec_p->next = uurec1_p->next; 960 uurec_p->prev = uurec1_p; 961 uurec1_p->next = uurec_p; 962 } 963 if (status_rec_p->url == NULL && url != NULL) 964 status_rec_p->url = strdup(url); 965 status_rec_p->not_suppressed |= not_suppressed; 966 uurec_p->asru = merge_name_list(&status_rec_p->asru, asru, 0); 967 (void) merge_name_list(&status_rec_p->resource, resource, 0); 968 (void) merge_name_list(&status_rec_p->serial, serial, 0); 969 } 970 971 static status_record_t * 972 record_in_catalog(name_list_t *class, name_list_t *fru, 973 char *msgid, hostid_t *host) 974 { 975 sr_list_t *status_rec_p; 976 status_record_t *srp = NULL; 977 978 status_rec_p = status_rec_list; 979 while (status_rec_p) { 980 srp = status_rec_p->status_record; 981 if (host == srp->host && 982 cmp_name_list(class, srp->class) == 0 && 983 cmp_name_list(fru, srp->fru) == 0 && 984 strcmp(msgid, srp->msgid) == 0) 985 break; 986 if (status_rec_p->next == status_rec_list) { 987 srp = NULL; 988 break; 989 } else { 990 status_rec_p = status_rec_p->next; 991 } 992 } 993 return (srp); 994 } 995 996 static void 997 get_serial_no(nvlist_t *nvl, name_list_t **serial_p, uint8_t pct) 998 { 999 char *name; 1000 char *serial = NULL; 1001 char **lserial = NULL; 1002 uint64_t serint; 1003 name_list_t *nlp; 1004 int j; 1005 uint_t nelem; 1006 char buf[64]; 1007 1008 if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &name) == 0) { 1009 if (strcmp(name, FM_FMRI_SCHEME_CPU) == 0) { 1010 if (nvlist_lookup_uint64(nvl, FM_FMRI_CPU_SERIAL_ID, 1011 &serint) == 0) { 1012 (void) snprintf(buf, sizeof (buf), "%llX", 1013 serint); 1014 nlp = alloc_name_list(buf, pct); 1015 (void) merge_name_list(serial_p, nlp, 1); 1016 } 1017 } else if (strcmp(name, FM_FMRI_SCHEME_MEM) == 0) { 1018 if (nvlist_lookup_string_array(nvl, 1019 FM_FMRI_MEM_SERIAL_ID, &lserial, &nelem) == 0) { 1020 nlp = alloc_name_list(lserial[0], pct); 1021 for (j = 1; j < nelem; j++) { 1022 name_list_t *n1lp; 1023 n1lp = alloc_name_list(lserial[j], pct); 1024 (void) merge_name_list(&nlp, n1lp, 1); 1025 } 1026 (void) merge_name_list(serial_p, nlp, 1); 1027 } 1028 } else if (strcmp(name, FM_FMRI_SCHEME_HC) == 0) { 1029 if (nvlist_lookup_string(nvl, FM_FMRI_HC_SERIAL_ID, 1030 &serial) == 0) { 1031 nlp = alloc_name_list(serial, pct); 1032 (void) merge_name_list(serial_p, nlp, 1); 1033 } 1034 } 1035 } 1036 } 1037 1038 static void 1039 extract_record_info(nvlist_t *nvl, name_list_t **class_p, 1040 name_list_t **fru_p, name_list_t **serial_p, 1041 name_list_t **resource_p, name_list_t **asru_p, uint8_t status) 1042 { 1043 nvlist_t *lfru, *lasru, *rsrc; 1044 name_list_t *nlp; 1045 char *name; 1046 uint8_t lpct = 0; 1047 char *lclass = NULL; 1048 1049 (void) nvlist_lookup_uint8(nvl, FM_FAULT_CERTAINTY, &lpct); 1050 if (nvlist_lookup_string(nvl, FM_CLASS, &lclass) == 0) { 1051 nlp = alloc_name_list(lclass, lpct); 1052 (void) merge_name_list(class_p, nlp, 1); 1053 } 1054 if (nvlist_lookup_nvlist(nvl, FM_FAULT_FRU, &lfru) == 0) { 1055 name = get_nvl2str_topo(lfru); 1056 if (name != NULL) { 1057 nlp = alloc_name_list(name, lpct); 1058 free(name); 1059 (void) merge_name_list(fru_p, nlp, 1); 1060 } 1061 get_serial_no(lfru, serial_p, lpct); 1062 } 1063 if (nvlist_lookup_nvlist(nvl, FM_FAULT_ASRU, &lasru) == 0) { 1064 name = get_nvl2str_topo(lasru); 1065 if (name != NULL) { 1066 nlp = alloc_name_list(name, lpct); 1067 nlp->status = status & ~FM_SUSPECT_NOT_PRESENT; 1068 free(name); 1069 (void) merge_name_list(asru_p, nlp, 1); 1070 } 1071 get_serial_no(lasru, serial_p, lpct); 1072 } 1073 if (nvlist_lookup_nvlist(nvl, FM_FAULT_RESOURCE, &rsrc) == 0) { 1074 name = get_nvl2str_topo(rsrc); 1075 if (name != NULL) { 1076 nlp = alloc_name_list(name, lpct); 1077 free(name); 1078 (void) merge_name_list(resource_p, nlp, 1); 1079 } 1080 } 1081 } 1082 1083 static void 1084 add_fault_record_to_catalog(nvlist_t *nvl, uint64_t sec, char *uuid, 1085 const char *url) 1086 { 1087 char *msgid = "-"; 1088 uint_t i, size = 0; 1089 name_list_t *class = NULL, *resource = NULL; 1090 name_list_t *asru = NULL, *fru = NULL, *serial = NULL; 1091 nvlist_t **nva; 1092 uint8_t *ba; 1093 status_record_t *status_rec_p; 1094 uurec_t *uurec_p; 1095 hostid_t *host; 1096 boolean_t not_suppressed = 1; 1097 boolean_t any_present = 0; 1098 1099 (void) nvlist_lookup_string(nvl, FM_SUSPECT_DIAG_CODE, &msgid); 1100 (void) nvlist_lookup_uint32(nvl, FM_SUSPECT_FAULT_SZ, &size); 1101 (void) nvlist_lookup_boolean_value(nvl, FM_SUSPECT_MESSAGE, 1102 ¬_suppressed); 1103 1104 if (size != 0) { 1105 (void) nvlist_lookup_nvlist_array(nvl, FM_SUSPECT_FAULT_LIST, 1106 &nva, &size); 1107 (void) nvlist_lookup_uint8_array(nvl, FM_SUSPECT_FAULT_STATUS, 1108 &ba, &size); 1109 for (i = 0; i < size; i++) { 1110 extract_record_info(nva[i], &class, &fru, &serial, 1111 &resource, &asru, ba[i]); 1112 if (!(ba[i] & FM_SUSPECT_NOT_PRESENT) && 1113 (ba[i] & FM_SUSPECT_FAULTY)) 1114 any_present = 1; 1115 } 1116 /* 1117 * also suppress if no resources present 1118 */ 1119 if (any_present == 0) 1120 not_suppressed = 0; 1121 } 1122 1123 uurec_p = (uurec_t *)malloc(sizeof (uurec_t)); 1124 uurec_p->uuid = strdup(uuid); 1125 uurec_p->sec = sec; 1126 uurec_p->ari_uuid_list = NULL; 1127 host = find_hostid(nvl); 1128 if (not_suppressed && !opt_g) 1129 status_rec_p = NULL; 1130 else 1131 status_rec_p = record_in_catalog(class, fru, msgid, host); 1132 if (status_rec_p) { 1133 catalog_merge_record(status_rec_p, uurec_p, asru, resource, 1134 serial, url, not_suppressed); 1135 free_name_list(class); 1136 free_name_list(fru); 1137 } else { 1138 catalog_new_record(uurec_p, msgid, class, fru, asru, 1139 resource, serial, url, not_suppressed, host); 1140 } 1141 } 1142 1143 static void 1144 update_asru_state_in_catalog(const char *uuid, const char *ari_uuid) 1145 { 1146 sr_list_t *srp; 1147 uurec_t *uurp; 1148 ari_list_t *ari_list; 1149 1150 srp = status_rec_list; 1151 if (srp) { 1152 for (;;) { 1153 uurp = srp->status_record->uurec; 1154 while (uurp) { 1155 if (strcmp(uuid, uurp->uuid) == 0) { 1156 ari_list = (ari_list_t *) 1157 malloc(sizeof (ari_list_t)); 1158 ari_list->ari_uuid = strdup(ari_uuid); 1159 ari_list->next = uurp->ari_uuid_list; 1160 uurp->ari_uuid_list = ari_list; 1161 return; 1162 } 1163 uurp = uurp->next; 1164 } 1165 if (srp->next == status_rec_list) 1166 break; 1167 srp = srp->next; 1168 } 1169 } 1170 } 1171 1172 static void 1173 print_line(char *label, char *buf) 1174 { 1175 char *cp, *ep, *wp; 1176 char c; 1177 int i; 1178 int lsz; 1179 char *padding; 1180 1181 lsz = strlen(label); 1182 padding = malloc(lsz + 1); 1183 for (i = 0; i < lsz; i++) 1184 padding[i] = ' '; 1185 padding[i] = 0; 1186 cp = buf; 1187 ep = buf; 1188 c = *ep; 1189 (void) printf("\n"); 1190 while (c) { 1191 i = lsz; 1192 wp = NULL; 1193 while ((c = *ep) != NULL && (wp == NULL || i < 80)) { 1194 if (c == ' ') 1195 wp = ep; 1196 else if (c == '\n') { 1197 i = 0; 1198 *ep = 0; 1199 do { 1200 ep++; 1201 } while ((c = *ep) != NULL && c == ' '); 1202 break; 1203 } 1204 ep++; 1205 i++; 1206 } 1207 if (i >= 80 && wp) { 1208 *wp = 0; 1209 ep = wp + 1; 1210 c = *ep; 1211 } 1212 (void) printf("%s%s\n", label, cp); 1213 cp = ep; 1214 label = padding; 1215 } 1216 free(padding); 1217 } 1218 1219 static void 1220 print_dict_info(char *msgid, char *url) 1221 { 1222 const char *cp; 1223 char *l_url; 1224 char *buf; 1225 int bufsz; 1226 1227 cp = get_dict_msg(msgid, "description", 0, 1); 1228 if (cp) { 1229 if (url) 1230 l_url = url; 1231 else 1232 l_url = get_dict_url(msgid); 1233 bufsz = strlen(cp) + strlen(l_url) + 1; 1234 buf = malloc(bufsz); 1235 (void) snprintf(buf, bufsz, cp, l_url); 1236 print_line(dgettext("FMD", "Description : "), buf); 1237 free(buf); 1238 if (!url) 1239 free(l_url); 1240 } 1241 cp = get_dict_msg(msgid, "response", 0, 1); 1242 if (cp) { 1243 buf = strdup(cp); 1244 print_line(dgettext("FMD", "Response : "), buf); 1245 free(buf); 1246 } 1247 cp = get_dict_msg(msgid, "impact", 0, 1); 1248 if (cp) { 1249 buf = strdup(cp); 1250 print_line(dgettext("FMD", "Impact : "), buf); 1251 free(buf); 1252 } 1253 cp = get_dict_msg(msgid, "action", 0, 1); 1254 if (cp) { 1255 buf = strdup(cp); 1256 print_line(dgettext("FMD", "Action : "), buf); 1257 free(buf); 1258 } 1259 } 1260 1261 static void 1262 print_name(name_list_t *list, char *(func)(char *), char *padding, int *np, 1263 int pct, int full) 1264 { 1265 char *name, *fru = NULL; 1266 1267 name = list->name; 1268 if (func) 1269 fru = func(list->name); 1270 if (fru) { 1271 (void) printf("%s \"%s\" (%s)", padding, fru, name); 1272 *np += 1; 1273 free(fru); 1274 } else { 1275 (void) printf("%s %s", padding, name); 1276 *np += 1; 1277 } 1278 if (list->pct && pct > 0 && pct < 100) { 1279 if (list->count > 1) { 1280 if (full) { 1281 (void) printf(" %d @ %s %d%%\n", list->count, 1282 dgettext("FMD", "max"), 1283 list->max_pct); 1284 } else { 1285 (void) printf(" %s %d%%\n", 1286 dgettext("FMD", "max"), 1287 list->max_pct); 1288 } 1289 } else { 1290 (void) printf(" %d%%\n", list->pct); 1291 } 1292 } else { 1293 (void) printf("\n"); 1294 } 1295 } 1296 1297 static void 1298 print_asru_status(int status, char *label) 1299 { 1300 char *msg = NULL; 1301 1302 switch (status) { 1303 case 0: 1304 msg = dgettext("FMD", "ok and in service"); 1305 break; 1306 case FM_SUSPECT_FAULTY: 1307 msg = dgettext("FMD", "degraded but still in service"); 1308 break; 1309 case FM_SUSPECT_UNUSABLE: 1310 msg = dgettext("FMD", "unknown, not present or disabled"); 1311 break; 1312 case FM_SUSPECT_FAULTY | FM_SUSPECT_UNUSABLE: 1313 msg = dgettext("FMD", "faulted and taken out of service"); 1314 break; 1315 default: 1316 break; 1317 } 1318 if (msg) { 1319 (void) printf("%s %s\n", label, msg); 1320 } 1321 } 1322 1323 static void 1324 print_name_list(name_list_t *list, char *label, char *(func)(char *), 1325 int limit, int pct, void (func1)(int, char *), int full) 1326 { 1327 char *name, *fru = NULL; 1328 char *padding; 1329 int i, j, l, n; 1330 name_list_t *end = list; 1331 1332 l = strlen(label); 1333 padding = malloc(l + 1); 1334 for (i = 0; i < l; i++) 1335 padding[i] = ' '; 1336 padding[l] = 0; 1337 (void) printf("%s", label); 1338 name = list->name; 1339 if (func) 1340 fru = func(list->name); 1341 if (fru) { 1342 (void) printf(" \"%s\" (%s)", fru, name); 1343 free(fru); 1344 } else { 1345 (void) printf(" %s", name); 1346 } 1347 if (list->pct && pct > 0 && pct < 100) { 1348 if (list->count > 1) { 1349 if (full) { 1350 (void) printf(" %d @ %s %d%%\n", list->count, 1351 dgettext("FMD", "max"), list->max_pct); 1352 } else { 1353 (void) printf(" %s %d%%\n", 1354 dgettext("FMD", "max"), list->max_pct); 1355 } 1356 } else { 1357 (void) printf(" %d%%\n", list->pct); 1358 } 1359 } else { 1360 (void) printf("\n"); 1361 } 1362 if (func1) 1363 func1(list->status, padding); 1364 n = 1; 1365 j = 0; 1366 while ((list = list->next) != end) { 1367 if (limit == 0 || n < limit) { 1368 print_name(list, func, padding, &n, pct, full); 1369 if (func1) 1370 func1(list->status, padding); 1371 } else 1372 j++; 1373 } 1374 if (j == 1) { 1375 print_name(list->prev, func, padding, &n, pct, full); 1376 } else if (j > 1) { 1377 (void) printf("%s... %d %s\n", padding, j, 1378 dgettext("FMD", "more entries suppressed," 1379 " use -v option for full list")); 1380 } 1381 free(padding); 1382 } 1383 1384 static int 1385 asru_same_status(name_list_t *list) 1386 { 1387 name_list_t *end = list; 1388 int status = list->status; 1389 1390 while ((list = list->next) != end) { 1391 if (status == -1) { 1392 status = list->status; 1393 continue; 1394 } 1395 if (list->status != -1 && status != list->status) { 1396 status = -1; 1397 break; 1398 } 1399 } 1400 return (status); 1401 } 1402 1403 static int 1404 serial_in_fru(name_list_t *fru, name_list_t *serial) 1405 { 1406 name_list_t *sp = serial; 1407 name_list_t *fp; 1408 int nserial = 0; 1409 int found = 0; 1410 char buf[128]; 1411 1412 while (sp) { 1413 fp = fru; 1414 nserial++; 1415 (void) snprintf(buf, sizeof (buf), "serial=%s", sp->name); 1416 buf[sizeof (buf) - 1] = 0; 1417 while (fp) { 1418 if (strstr(fp->name, buf) != NULL) { 1419 found++; 1420 break; 1421 } 1422 fp = fp->next; 1423 if (fp == fru) 1424 break; 1425 } 1426 sp = sp->next; 1427 if (sp == serial) 1428 break; 1429 } 1430 return (found == nserial ? 1 : 0); 1431 } 1432 1433 static void 1434 print_server_name(hostid_t *host, char *label) 1435 { 1436 (void) printf("%s %s %s %s\n", label, host->server, host->platform, 1437 host->chassis ? host->chassis : ""); 1438 } 1439 1440 static void 1441 print_sup_record(status_record_t *srp, int opt_i, int full) 1442 { 1443 char buf[32]; 1444 uurec_t *uurp = srp->uurec; 1445 int n, j, k, max; 1446 int status; 1447 ari_list_t *ari_list; 1448 1449 n = 0; 1450 max = max_fault; 1451 if (max < 0) { 1452 max = 0; 1453 } 1454 j = max / 2; 1455 max -= j; 1456 k = srp->nrecs - max; 1457 while ((uurp = uurp->next) != NULL) { 1458 if (full || n < j || n >= k || max_fault == 0 || 1459 srp->nrecs == max_fault+1) { 1460 if (opt_i) { 1461 ari_list = uurp->ari_uuid_list; 1462 while (ari_list) { 1463 (void) printf("%-15s %s\n", 1464 format_date(buf, sizeof (buf), 1465 uurp->sec), ari_list->ari_uuid); 1466 ari_list = ari_list->next; 1467 } 1468 } else { 1469 (void) printf("%-15s %s\n", 1470 format_date(buf, sizeof (buf), uurp->sec), 1471 uurp->uuid); 1472 } 1473 } else if (n == j) 1474 (void) printf("... %d %s\n", srp->nrecs - max_fault, 1475 dgettext("FMD", "more entries suppressed")); 1476 n++; 1477 } 1478 (void) printf("\n"); 1479 if (n_server > 1) 1480 print_server_name(srp->host, dgettext("FMD", "Host :")); 1481 if (srp->class) 1482 print_name_list(srp->class, 1483 dgettext("FMD", "Fault class :"), NULL, 0, srp->class->pct, 1484 NULL, full); 1485 if (srp->asru) { 1486 status = asru_same_status(srp->asru); 1487 if (status != -1) { 1488 print_name_list(srp->asru, 1489 dgettext("FMD", "Affects :"), NULL, 1490 full ? 0 : max_display, 0, NULL, full); 1491 print_asru_status(status, " "); 1492 } else 1493 print_name_list(srp->asru, 1494 dgettext("FMD", "Affects :"), NULL, 1495 full ? 0 : max_display, 0, print_asru_status, full); 1496 } 1497 if (full || srp->fru == NULL) { 1498 if (srp->resource) { 1499 print_name_list(srp->resource, 1500 dgettext("FMD", "Problem in :"), 1501 NULL, full ? 0 : max_display, 0, NULL, full); 1502 } 1503 } 1504 if (srp->fru) { 1505 print_name_list(srp->fru, dgettext("FMD", "FRU :"), 1506 get_fmri_label, 0, 1507 srp->fru->pct == 100 ? 100 : srp->fru->max_pct, 1508 NULL, full); 1509 } 1510 if (srp->serial && !serial_in_fru(srp->fru, srp->serial) && 1511 !serial_in_fru(srp->asru, srp->serial)) { 1512 print_name_list(srp->serial, dgettext("FMD", "Serial ID. :"), 1513 NULL, 0, 0, NULL, full); 1514 } 1515 print_dict_info(srp->msgid, srp->url); 1516 (void) printf("\n"); 1517 } 1518 1519 static void 1520 print_status_record(status_record_t *srp, int summary, int opt_i, int full) 1521 { 1522 char buf[32]; 1523 uurec_t *uurp = srp->uurec; 1524 char *severity; 1525 static int header = 0; 1526 char *head; 1527 ari_list_t *ari_list; 1528 1529 if (nlspath) 1530 severity = get_dict_msg(srp->msgid, "severity", 1, 1); 1531 else 1532 severity = srp->severity; 1533 1534 if (!summary || !header) { 1535 if (opt_i) { 1536 head = "--------------- " 1537 "------------------------------------ " 1538 "-------------- ---------\n" 1539 "TIME CACHE-ID" 1540 " MSG-ID" 1541 " SEVERITY\n--------------- " 1542 "------------------------------------ " 1543 " -------------- ---------"; 1544 } else { 1545 head = "--------------- " 1546 "------------------------------------ " 1547 "-------------- ---------\n" 1548 "TIME EVENT-ID" 1549 " MSG-ID" 1550 " SEVERITY\n--------------- " 1551 "------------------------------------ " 1552 " -------------- ---------"; 1553 } 1554 (void) printf("%s\n", dgettext("FMD", head)); 1555 header = 1; 1556 } 1557 if (opt_i) { 1558 ari_list = uurp->ari_uuid_list; 1559 while (ari_list) { 1560 (void) printf("%-15s %-37s %-14s %-9s\n", 1561 format_date(buf, sizeof (buf), uurp->sec), 1562 ari_list->ari_uuid, srp->msgid, severity); 1563 ari_list = ari_list->next; 1564 } 1565 } else { 1566 (void) printf("%-15s %-37s %-14s %-9s\n", 1567 format_date(buf, sizeof (buf), uurp->sec), 1568 uurp->uuid, srp->msgid, severity); 1569 } 1570 1571 if (!summary) 1572 print_sup_record(srp, opt_i, full); 1573 } 1574 1575 static void 1576 print_catalog(int summary, int opt_a, int full, int opt_i, int page_feed) 1577 { 1578 status_record_t *srp; 1579 sr_list_t *slp; 1580 1581 slp = status_rec_list; 1582 if (slp) { 1583 for (;;) { 1584 srp = slp->status_record; 1585 if (opt_a || srp->not_suppressed) { 1586 if (page_feed) 1587 (void) printf("\f\n"); 1588 print_status_record(srp, summary, opt_i, full); 1589 } 1590 if (slp->next == status_rec_list) 1591 break; 1592 slp = slp->next; 1593 } 1594 } 1595 } 1596 1597 static name_list_t * 1598 find_fru(status_record_t *srp, char *resource) 1599 { 1600 name_list_t *rt = NULL; 1601 name_list_t *fru = srp->fru; 1602 1603 while (fru) { 1604 if (strcmp(resource, fru->name) == 0) { 1605 rt = fru; 1606 break; 1607 } 1608 fru = fru->next; 1609 if (fru == srp->fru) 1610 break; 1611 } 1612 return (rt); 1613 } 1614 1615 static void 1616 print_fru_line(name_list_t *fru, char *uuid) 1617 { 1618 if (fru->pct == 100) { 1619 (void) printf("%s %d %s %d%%\n", uuid, fru->count, 1620 dgettext("FMD", "suspects in this FRU total certainty"), 1621 100); 1622 } else { 1623 (void) printf("%s %d %s %d%%\n", uuid, fru->count, 1624 dgettext("FMD", "suspects in this FRU max certainty"), 1625 fru->max_pct); 1626 } 1627 } 1628 1629 static void 1630 print_fru(int summary, int opt_a, int opt_i, int page_feed) 1631 { 1632 resource_list_t *tp = status_fru_list; 1633 status_record_t *srp; 1634 sr_list_t *slp, *end; 1635 char *msgid, *fru_label; 1636 uurec_t *uurp; 1637 name_list_t *fru; 1638 ari_list_t *ari_list; 1639 1640 while (tp) { 1641 if (opt_a || tp->not_suppressed) { 1642 if (page_feed) 1643 (void) printf("\f\n"); 1644 if (!summary) 1645 (void) printf("-----------------------------" 1646 "---------------------------------------" 1647 "----------\n"); 1648 fru_label = get_fmri_label(tp->resource); 1649 if (fru_label) { 1650 (void) printf("\"%s\" (%s)\n", fru_label, 1651 tp->resource); 1652 free(fru_label); 1653 } else { 1654 (void) printf("%s\n", tp->resource); 1655 } 1656 slp = tp->status_rec_list; 1657 end = slp; 1658 do { 1659 srp = slp->status_record; 1660 uurp = srp->uurec; 1661 fru = find_fru(srp, tp->resource); 1662 if (fru) { 1663 if (opt_i) { 1664 ari_list = uurp->ari_uuid_list; 1665 while (ari_list) { 1666 print_fru_line(fru, 1667 ari_list->ari_uuid); 1668 ari_list = 1669 ari_list->next; 1670 } 1671 } else { 1672 print_fru_line(fru, uurp->uuid); 1673 } 1674 } 1675 slp = slp->next; 1676 } while (slp != end); 1677 if (!summary) { 1678 slp = tp->status_rec_list; 1679 end = slp; 1680 srp = slp->status_record; 1681 if (srp->serial && 1682 !serial_in_fru(srp->fru, srp->serial)) { 1683 print_name_list(srp->serial, 1684 dgettext("FMD", "Serial ID. :"), 1685 NULL, 0, 0, NULL, 1); 1686 } 1687 msgid = NULL; 1688 do { 1689 if (msgid == NULL || 1690 strcmp(msgid, srp->msgid) != 0) { 1691 msgid = srp->msgid; 1692 print_dict_info(srp->msgid, 1693 srp->url); 1694 } 1695 slp = slp->next; 1696 } while (slp != end); 1697 } 1698 } 1699 tp = tp->next; 1700 if (tp == status_fru_list) 1701 break; 1702 } 1703 } 1704 1705 static void 1706 print_asru(int opt_a) 1707 { 1708 resource_list_t *tp = status_asru_list; 1709 status_record_t *srp; 1710 sr_list_t *slp, *end; 1711 char *msg; 1712 int status; 1713 name_list_t *asru; 1714 1715 while (tp) { 1716 if (opt_a || tp->not_suppressed) { 1717 status = 0; 1718 slp = tp->status_rec_list; 1719 end = slp; 1720 do { 1721 srp = slp->status_record; 1722 asru = srp->asru; 1723 while (asru) { 1724 if (strcmp(tp->resource, 1725 asru->name) == 0) 1726 status |= asru->status; 1727 asru = asru->next; 1728 if (asru == srp->asru) 1729 break; 1730 } 1731 slp = slp->next; 1732 } while (slp != end); 1733 switch (status) { 1734 case 0: 1735 msg = dgettext("FMD", "ok"); 1736 break; 1737 case FM_SUSPECT_FAULTY: 1738 msg = dgettext("FMD", "degraded"); 1739 break; 1740 case FM_SUSPECT_UNUSABLE: 1741 msg = dgettext("FMD", "unknown"); 1742 break; 1743 case FM_SUSPECT_FAULTY | FM_SUSPECT_UNUSABLE: 1744 msg = dgettext("FMD", "faulted"); 1745 break; 1746 default: 1747 msg = ""; 1748 break; 1749 } 1750 (void) printf("%-69s %s\n", tp->resource, msg); 1751 } 1752 tp = tp->next; 1753 if (tp == status_asru_list) 1754 break; 1755 } 1756 } 1757 1758 static int 1759 uuid_in_list(char *uuid, uurec_select_t *uurecp) 1760 { 1761 while (uurecp) { 1762 if (strcmp(uuid, uurecp->uuid) == 0) 1763 return (1); 1764 uurecp = uurecp->next; 1765 } 1766 return (0); 1767 } 1768 1769 static int 1770 dfault_rec(const fmd_adm_caseinfo_t *acp, void *arg) 1771 { 1772 int64_t *diag_time; 1773 uint_t nelem; 1774 int rt = 0; 1775 char *uuid = "-"; 1776 uurec_select_t *uurecp = (uurec_select_t *)arg; 1777 1778 if (nvlist_lookup_int64_array(acp->aci_event, FM_SUSPECT_DIAG_TIME, 1779 &diag_time, &nelem) == 0 && nelem >= 2) { 1780 (void) nvlist_lookup_string(acp->aci_event, FM_SUSPECT_UUID, 1781 &uuid); 1782 if (uurecp == NULL || uuid_in_list(uuid, uurecp)) 1783 add_fault_record_to_catalog(acp->aci_event, *diag_time, 1784 uuid, acp->aci_url); 1785 } else { 1786 rt = -1; 1787 } 1788 return (rt); 1789 } 1790 1791 /*ARGSUSED*/ 1792 static int 1793 dstatus_rec(const fmd_adm_rsrcinfo_t *ari, void *unused) 1794 { 1795 update_asru_state_in_catalog(ari->ari_case, ari->ari_uuid); 1796 return (0); 1797 } 1798 1799 static int 1800 get_cases_from_fmd(fmd_adm_t *adm, uurec_select_t *uurecp, int opt_i) 1801 { 1802 int rt = FMADM_EXIT_SUCCESS; 1803 1804 /* 1805 * These calls may fail with Protocol error if message payload is to big 1806 */ 1807 if (fmd_adm_case_iter(adm, NULL, dfault_rec, uurecp) != 0) 1808 die("failed to get case list from fmd"); 1809 if (opt_i && fmd_adm_rsrc_iter(adm, 1, dstatus_rec, NULL) != 0) 1810 die("failed to get case status from fmd"); 1811 return (rt); 1812 } 1813 1814 /* 1815 * fmadm faulty command 1816 * 1817 * -a show hidden fault records 1818 * -f show faulty fru's 1819 * -g force grouping of similar faults on the same fru 1820 * -n number of fault records to display 1821 * -p pipe output through pager 1822 * -r show faulty asru's 1823 * -s print summary of first fault 1824 * -u print listed uuid's only 1825 * -v full output 1826 */ 1827 1828 int 1829 cmd_faulty(fmd_adm_t *adm, int argc, char *argv[]) 1830 { 1831 int opt_a = 0, opt_v = 0, opt_p = 0, opt_s = 0, opt_r = 0, opt_f = 0; 1832 int opt_i = 0; 1833 char *pager; 1834 FILE *fp; 1835 int rt, c, stat; 1836 uurec_select_t *tp; 1837 uurec_select_t *uurecp = NULL; 1838 1839 catalog_setup(); 1840 while ((c = getopt(argc, argv, "afgin:prsu:v")) != EOF) { 1841 switch (c) { 1842 case 'a': 1843 opt_a++; 1844 break; 1845 case 'f': 1846 opt_f++; 1847 break; 1848 case 'g': 1849 opt_g++; 1850 break; 1851 case 'i': 1852 opt_i++; 1853 break; 1854 case 'n': 1855 max_fault = atoi(optarg); 1856 break; 1857 case 'p': 1858 opt_p++; 1859 break; 1860 case 'r': 1861 opt_r++; 1862 break; 1863 case 's': 1864 opt_s++; 1865 break; 1866 case 'u': 1867 tp = (uurec_select_t *)malloc(sizeof (uurec_select_t)); 1868 tp->uuid = optarg; 1869 tp->next = uurecp; 1870 uurecp = tp; 1871 opt_a = 1; 1872 break; 1873 case 'v': 1874 opt_v++; 1875 break; 1876 default: 1877 return (FMADM_EXIT_USAGE); 1878 } 1879 } 1880 if (optind < argc) 1881 return (FMADM_EXIT_USAGE); 1882 1883 rt = get_cases_from_fmd(adm, uurecp, opt_i); 1884 if (opt_p) { 1885 if ((pager = getenv("PAGER")) == NULL) 1886 pager = "/usr/bin/more"; 1887 fp = popen(pager, "w"); 1888 if (fp == NULL) { 1889 rt = FMADM_EXIT_ERROR; 1890 opt_p = 0; 1891 } else { 1892 dup2(fileno(fp), 1); 1893 setbuf(stdout, NULL); 1894 (void) fclose(fp); 1895 } 1896 } 1897 max_display = max_fault; 1898 if (opt_f) 1899 print_fru(opt_s, opt_a, opt_i, opt_p && !opt_s); 1900 if (opt_r) 1901 print_asru(opt_a); 1902 if (opt_f == 0 && opt_r == 0) 1903 print_catalog(opt_s, opt_a, opt_v, opt_i, opt_p && !opt_s); 1904 label_release_topo(); 1905 if (opt_p) { 1906 (void) fclose(stdout); 1907 (void) wait(&stat); 1908 } 1909 return (rt); 1910 } 1911 1912 int 1913 cmd_flush(fmd_adm_t *adm, int argc, char *argv[]) 1914 { 1915 int i, status = FMADM_EXIT_SUCCESS; 1916 1917 if (argc < 2 || (i = getopt(argc, argv, "")) != EOF) 1918 return (FMADM_EXIT_USAGE); 1919 1920 for (i = 1; i < argc; i++) { 1921 if (fmd_adm_rsrc_flush(adm, argv[i]) != 0) { 1922 warn("failed to flush %s", argv[i]); 1923 status = FMADM_EXIT_ERROR; 1924 } else 1925 note("flushed resource history for %s\n", argv[i]); 1926 } 1927 1928 return (status); 1929 } 1930 1931 int 1932 cmd_repair(fmd_adm_t *adm, int argc, char *argv[]) 1933 { 1934 int err; 1935 1936 if (getopt(argc, argv, "") != EOF) 1937 return (FMADM_EXIT_USAGE); 1938 1939 if (argc - optind != 1) 1940 return (FMADM_EXIT_USAGE); 1941 1942 /* 1943 * For now, we assume that if the input string contains a colon, it is 1944 * an FMRI and if it does not it is a UUID. If things get more complex 1945 * in the future with multiple UUID formats, an FMRI parser can be 1946 * added here to differentiate the input argument appropriately. 1947 */ 1948 if (strchr(argv[optind], ':') != NULL) 1949 err = fmd_adm_rsrc_repair(adm, argv[optind]); 1950 else 1951 err = fmd_adm_case_repair(adm, argv[optind]); 1952 1953 if (err != 0) 1954 die("failed to record repair to %s", argv[optind]); 1955 1956 note("recorded repair to %s\n", argv[optind]); 1957 return (FMADM_EXIT_SUCCESS); 1958 } 1959