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