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