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