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