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