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