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