Home | History | Annotate | Download | only in common
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <alloca.h>
     27 #include <unistd.h>
     28 #include <limits.h>
     29 #include <strings.h>
     30 #include <stdlib.h>
     31 #include <stdarg.h>
     32 #include <stdio.h>
     33 #include <errno.h>
     34 #include <time.h>
     35 #include <ctype.h>
     36 #include <regex.h>
     37 #include <dirent.h>
     38 
     39 #include <fmdump.h>
     40 
     41 #define	FMDUMP_EXIT_SUCCESS	0
     42 #define	FMDUMP_EXIT_FATAL	1
     43 #define	FMDUMP_EXIT_USAGE	2
     44 #define	FMDUMP_EXIT_ERROR	3
     45 
     46 const char *g_pname;
     47 ulong_t g_errs;
     48 ulong_t g_recs;
     49 char *g_root;
     50 
     51 struct topo_hdl *g_thp;
     52 fmd_msg_hdl_t *g_msg;
     53 
     54 /*PRINTFLIKE2*/
     55 void
     56 fmdump_printf(FILE *fp, const char *format, ...)
     57 {
     58 	va_list ap;
     59 
     60 	va_start(ap, format);
     61 
     62 	if (vfprintf(fp, format, ap) < 0) {
     63 		(void) fprintf(stderr, "%s: failed to print record: %s\n",
     64 		    g_pname, strerror(errno));
     65 		g_errs++;
     66 	}
     67 
     68 	va_end(ap);
     69 }
     70 
     71 void
     72 fmdump_vwarn(const char *format, va_list ap)
     73 {
     74 	int err = errno;
     75 
     76 	(void) fprintf(stderr, "%s: warning: ", g_pname);
     77 	(void) vfprintf(stderr, format, ap);
     78 
     79 	if (strchr(format, '\n') == NULL)
     80 		(void) fprintf(stderr, ": %s\n", strerror(err));
     81 
     82 	g_errs++;
     83 }
     84 
     85 /*PRINTFLIKE1*/
     86 void
     87 fmdump_warn(const char *format, ...)
     88 {
     89 	va_list ap;
     90 
     91 	va_start(ap, format);
     92 	fmdump_vwarn(format, ap);
     93 	va_end(ap);
     94 }
     95 
     96 char *
     97 fmdump_date(char *buf, size_t len, const fmd_log_record_t *rp)
     98 {
     99 	if (rp->rec_sec > LONG_MAX) {
    100 		fmdump_warn("record time is too large for 32-bit utility\n");
    101 		(void) snprintf(buf, len, "0x%llx", rp->rec_sec);
    102 	} else {
    103 		time_t tod = (time_t)rp->rec_sec;
    104 		time_t now = time(NULL);
    105 		if (tod > now+60 ||
    106 		    tod < now - 6L*30L*24L*60L*60L) { /* 6 months ago */
    107 			(void) strftime(buf, len, "%b %d %Y %T",
    108 			    localtime(&tod));
    109 		} else {
    110 			size_t sz;
    111 			sz = strftime(buf, len, "%b %d %T", localtime(&tod));
    112 			(void) snprintf(buf + sz, len - sz, ".%4.4llu",
    113 			    rp->rec_nsec / (NANOSEC / 10000));
    114 		}
    115 	}
    116 
    117 	return (buf);
    118 }
    119 
    120 char *
    121 fmdump_year(char *buf, size_t len, const fmd_log_record_t *rp)
    122 {
    123 #ifdef _ILP32
    124 	if (rp->rec_sec > LONG_MAX) {
    125 		fmdump_warn("record time is too large for 32-bit utility\n");
    126 		(void) snprintf(buf, len, "0x%llx", rp->rec_sec);
    127 	} else {
    128 #endif
    129 		time_t tod = (time_t)rp->rec_sec;
    130 		(void) strftime(buf, len, "%b %d %Y %T", localtime(&tod));
    131 #ifdef _ILP32
    132 	}
    133 #endif
    134 	return (buf);
    135 }
    136 
    137 static int
    138 usage(FILE *fp)
    139 {
    140 	(void) fprintf(fp, "Usage: %s [-efmvV] [-c class] [-R root] [-t time] "
    141 	    "[-T time] [-u uuid]\n\t\t[-n name[.name]*[=value]] [file]\n",
    142 	    g_pname);
    143 
    144 	(void) fprintf(fp,
    145 	    "\t-c  select events that match the specified class\n"
    146 	    "\t-e  display error log content instead of fault log content\n"
    147 	    "\t-f  follow growth of log file by waiting for additional data\n"
    148 	    "\t-m  display human-readable messages for the fault log\n"
    149 	    "\t-R  set root directory for pathname expansions\n"
    150 	    "\t-t  select events that occurred after the specified time\n"
    151 	    "\t-T  select events that occurred before the specified time\n"
    152 	    "\t-u  select events that match the specified uuid\n"
    153 	    "\t-n  select events containing named nvpair "
    154 	    "(with matching value)\n"
    155 	    "\t-v  set verbose mode: display additional event detail\n"
    156 	    "\t-V  set very verbose mode: display complete event contents\n");
    157 
    158 	return (FMDUMP_EXIT_USAGE);
    159 }
    160 
    161 /*ARGSUSED*/
    162 static int
    163 error(fmd_log_t *lp, void *private)
    164 {
    165 	fmdump_warn("skipping record: %s\n",
    166 	    fmd_log_errmsg(lp, fmd_log_errno(lp)));
    167 	return (0);
    168 }
    169 
    170 /*
    171  * Yet another disgusting argument parsing function (TM).  We attempt to parse
    172  * a time argument in a variety of strptime(3C) formats, in which case it is
    173  * interpreted as a local time and is converted to a timeval using mktime(3C).
    174  * If those formats fail, we look to see if the time is a decimal integer
    175  * followed by one of our magic suffixes, in which case the time is interpreted
    176  * as a time delta *before* the current time-of-day (i.e. "1h" = "1 hour ago").
    177  */
    178 static struct timeval *
    179 gettimeopt(const char *arg)
    180 {
    181 	const struct {
    182 		const char *name;
    183 		hrtime_t mul;
    184 	} suffix[] = {
    185 		{ "ns",		NANOSEC / NANOSEC },
    186 		{ "nsec",	NANOSEC / NANOSEC },
    187 		{ "us",		NANOSEC / MICROSEC },
    188 		{ "usec",	NANOSEC / MICROSEC },
    189 		{ "ms",		NANOSEC / MILLISEC },
    190 		{ "msec",	NANOSEC / MILLISEC },
    191 		{ "s",		NANOSEC / SEC },
    192 		{ "sec",	NANOSEC / SEC },
    193 		{ "m",		NANOSEC * (hrtime_t)60 },
    194 		{ "min",	NANOSEC * (hrtime_t)60 },
    195 		{ "h",		NANOSEC * (hrtime_t)(60 * 60) },
    196 		{ "hour",	NANOSEC * (hrtime_t)(60 * 60) },
    197 		{ "d",		NANOSEC * (hrtime_t)(24 * 60 * 60) },
    198 		{ "day",	NANOSEC * (hrtime_t)(24 * 60 * 60) },
    199 		{ NULL }
    200 	};
    201 
    202 	struct timeval *tvp = malloc(sizeof (struct timeval));
    203 	struct timeval tod;
    204 	struct tm tm;
    205 	char *p;
    206 
    207 	if (tvp == NULL) {
    208 		(void) fprintf(stderr, "%s: failed to allocate memory: %s\n",
    209 		    g_pname, strerror(errno));
    210 		exit(FMDUMP_EXIT_FATAL);
    211 	}
    212 
    213 	if (gettimeofday(&tod, NULL) != 0) {
    214 		(void) fprintf(stderr, "%s: failed to get tod: %s\n",
    215 		    g_pname, strerror(errno));
    216 		exit(FMDUMP_EXIT_FATAL);
    217 	}
    218 
    219 	/*
    220 	 * First try a variety of strptime() calls.  If these all fail, we'll
    221 	 * try parsing an integer followed by one of our suffix[] strings.
    222 	 * NOTE: any form using %y must appear *before* the equivalent %Y form;
    223 	 * otherwise %Y will accept the two year digits but infer century zero.
    224 	 * Any form ending in %y must additionally check isdigit(*p) to ensure
    225 	 * that it does not inadvertently match 2 digits of a 4-digit year.
    226 	 *
    227 	 * Beware: Any strptime() sequence containing consecutive %x sequences
    228 	 * may fall victim to SCCS expanding it as a keyword!  If this happens
    229 	 * we use separate string constant that ANSI C will concatenate.
    230 	 */
    231 	if ((p = strptime(arg, "%m/%d/%y" "%t" "%H:%M:%S", &tm)) == NULL &&
    232 	    (p = strptime(arg, "%m/%d/%Y" "%t" "%H:%M:%S", &tm)) == NULL &&
    233 	    (p = strptime(arg, "%m/%d/%y" "%t" "%H:%M", &tm)) == NULL &&
    234 	    (p = strptime(arg, "%m/%d/%Y" "%t" "%H:%M", &tm)) == NULL &&
    235 	    ((p = strptime(arg, "%m/%d/%y", &tm)) == NULL || isdigit(*p)) &&
    236 	    (p = strptime(arg, "%m/%d/%Y", &tm)) == NULL &&
    237 	    (p = strptime(arg, "%y-%m-%dT%H:%M:%S", &tm)) == NULL &&
    238 	    (p = strptime(arg, "%Y-%m-%dT%H:%M:%S", &tm)) == NULL &&
    239 	    (p = strptime(arg, "%y-%m-%dT%H:%M", &tm)) == NULL &&
    240 	    (p = strptime(arg, "%Y-%m-%dT%H:%M", &tm)) == NULL &&
    241 	    (p = strptime(arg, "%y-%m-%d", &tm)) == NULL &&
    242 	    (p = strptime(arg, "%Y-%m-%d", &tm)) == NULL &&
    243 	    (p = strptime(arg, "%d%b%y" "%t" "%H:%M:%S", &tm)) == NULL &&
    244 	    (p = strptime(arg, "%d%b%Y" "%t" "%H:%M:%S", &tm)) == NULL &&
    245 	    (p = strptime(arg, "%d%b%y" "%t" "%H:%M", &tm)) == NULL &&
    246 	    (p = strptime(arg, "%d%b%Y" "%t" "%H:%M", &tm)) == NULL &&
    247 	    ((p = strptime(arg, "%d%b%y", &tm)) == NULL || isdigit(*p)) &&
    248 	    (p = strptime(arg, "%d%b%Y", &tm)) == NULL &&
    249 	    (p = strptime(arg, "%b%t%d" "%t" "%H:%M:%S", &tm)) == NULL &&
    250 	    (p = strptime(arg, "%b%t%d" "%t" "%H:%M:%S", &tm)) == NULL &&
    251 	    (p = strptime(arg, "%H:%M:%S", &tm)) == NULL &&
    252 	    (p = strptime(arg, "%H:%M", &tm)) == NULL) {
    253 
    254 		hrtime_t nsec;
    255 		int i;
    256 
    257 		errno = 0;
    258 		nsec = strtol(arg, (char **)&p, 10);
    259 
    260 		if (errno != 0 || nsec == 0 || p == arg || *p == '\0') {
    261 			(void) fprintf(stderr, "%s: illegal time "
    262 			    "format -- %s\n", g_pname, arg);
    263 			exit(FMDUMP_EXIT_USAGE);
    264 		}
    265 
    266 		for (i = 0; suffix[i].name != NULL; i++) {
    267 			if (strcasecmp(suffix[i].name, p) == 0) {
    268 				nsec *= suffix[i].mul;
    269 				break;
    270 			}
    271 		}
    272 
    273 		if (suffix[i].name == NULL) {
    274 			(void) fprintf(stderr, "%s: illegal time "
    275 			    "format -- %s\n", g_pname, arg);
    276 			exit(FMDUMP_EXIT_USAGE);
    277 		}
    278 
    279 		tvp->tv_sec = nsec / NANOSEC;
    280 		tvp->tv_usec = (nsec % NANOSEC) / (NANOSEC / MICROSEC);
    281 
    282 		if (tvp->tv_sec > tod.tv_sec) {
    283 			(void) fprintf(stderr, "%s: time delta precedes "
    284 			    "UTC time origin -- %s\n", g_pname, arg);
    285 			exit(FMDUMP_EXIT_USAGE);
    286 		}
    287 
    288 		tvp->tv_sec = tod.tv_sec - tvp->tv_sec;
    289 
    290 	} else if (*p == '\0' || *p == '.') {
    291 		/*
    292 		 * If tm_year is zero, we matched [%b %d] %H:%M[:%S]; use
    293 		 * the result of localtime(&tod.tv_sec) to fill in the rest.
    294 		 */
    295 		if (tm.tm_year == 0) {
    296 			int h = tm.tm_hour;
    297 			int m = tm.tm_min;
    298 			int s = tm.tm_sec;
    299 			int b = tm.tm_mon;
    300 			int d = tm.tm_mday;
    301 
    302 			bcopy(localtime(&tod.tv_sec), &tm, sizeof (tm));
    303 			tm.tm_isdst = 0; /* see strptime(3C) and below */
    304 
    305 			if (d > 0) {
    306 				tm.tm_mon = b;
    307 				tm.tm_mday = d;
    308 			}
    309 
    310 			tm.tm_hour = h;
    311 			tm.tm_min = m;
    312 			tm.tm_sec = s;
    313 		}
    314 
    315 		errno = 0;
    316 		tvp->tv_sec = mktime(&tm);
    317 		tvp->tv_usec = 0;
    318 
    319 		if (tvp->tv_sec == -1L && errno != 0) {
    320 			(void) fprintf(stderr, "%s: failed to compose "
    321 			    "time %s: %s\n", g_pname, arg, strerror(errno));
    322 			exit(FMDUMP_EXIT_ERROR);
    323 		}
    324 
    325 		/*
    326 		 * If our mktime() set tm_isdst, adjust the result for DST by
    327 		 * subtracting the offset between the main and alternate zones.
    328 		 */
    329 		if (tm.tm_isdst)
    330 			tvp->tv_sec -= timezone - altzone;
    331 
    332 		if (p[0] == '.') {
    333 			arg = p;
    334 			errno = 0;
    335 			tvp->tv_usec =
    336 			    (suseconds_t)(strtod(arg, &p) * (double)MICROSEC);
    337 
    338 			if (errno != 0 || p == arg || *p != '\0') {
    339 				(void) fprintf(stderr, "%s: illegal time "
    340 				    "suffix -- .%s\n", g_pname, arg);
    341 				exit(FMDUMP_EXIT_USAGE);
    342 			}
    343 		}
    344 
    345 	} else {
    346 		(void) fprintf(stderr, "%s: unexpected suffix after "
    347 		    "time %s -- %s\n", g_pname, arg, p);
    348 		exit(FMDUMP_EXIT_USAGE);
    349 	}
    350 
    351 	return (tvp);
    352 }
    353 
    354 /*
    355  * If the -u option is specified in combination with the -e option, we iterate
    356  * over each record in the fault log with a matching UUID finding xrefs to the
    357  * error log, and then use this function to iterate over every xref'd record.
    358  */
    359 int
    360 xref_iter(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg)
    361 {
    362 	const fmd_log_record_t *xrp = rp->rec_xrefs;
    363 	fmdump_arg_t *dap = arg;
    364 	int i, rv = 0;
    365 
    366 	for (i = 0; rv == 0 && i < rp->rec_nrefs; i++, xrp++) {
    367 		if (fmd_log_filter(lp, dap->da_fc, dap->da_fv, xrp))
    368 			rv = dap->da_fmt->do_func(lp, xrp, dap->da_fp);
    369 	}
    370 
    371 	return (rv);
    372 }
    373 
    374 int
    375 xoff_iter(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg)
    376 {
    377 	fmdump_lyr_t *dyp = arg;
    378 
    379 	fmdump_printf(dyp->dy_fp, "%16llx ", (u_longlong_t)rp->rec_off);
    380 	return (dyp->dy_func(lp, rp, dyp->dy_arg));
    381 }
    382 
    383 /*
    384  * Initialize fmd_log_filter_nvarg_t from -n name=value argument string.
    385  */
    386 static fmd_log_filter_nvarg_t *
    387 setupnamevalue(char *namevalue)
    388 {
    389 	fmd_log_filter_nvarg_t	*argt;
    390 	char			*value;
    391 	regex_t			*value_regex = NULL;
    392 	char			errstr[128];
    393 	int			rv;
    394 
    395 	if ((value = strchr(namevalue, '=')) == NULL) {
    396 		value_regex = NULL;
    397 	} else {
    398 		*value++ = '\0';	/* separate name and value string */
    399 
    400 		/*
    401 		 * Skip white space before value to facilitate direct
    402 		 * cut/paste from previous fmdump output.
    403 		 */
    404 		while (isspace(*value))
    405 			value++;
    406 
    407 		if ((value_regex = malloc(sizeof (regex_t))) == NULL) {
    408 			(void) fprintf(stderr, "%s: failed to allocate memory: "
    409 			    "%s\n", g_pname, strerror(errno));
    410 			exit(FMDUMP_EXIT_FATAL);
    411 		}
    412 
    413 		/* compile regular expression for possible string match */
    414 		if ((rv = regcomp(value_regex, value,
    415 		    REG_NOSUB|REG_NEWLINE)) != 0) {
    416 			(void) regerror(rv, value_regex, errstr,
    417 			    sizeof (errstr));
    418 			(void) fprintf(stderr, "unexpected regular expression "
    419 			    "in %s: %s\n", value, errstr);
    420 			free(value_regex);
    421 			exit(FMDUMP_EXIT_USAGE);
    422 		}
    423 	}
    424 
    425 	if ((argt = malloc(sizeof (fmd_log_filter_nvarg_t))) == NULL) {
    426 		(void) fprintf(stderr, "%s: failed to allocate memory: %s\n",
    427 		    g_pname, strerror(errno));
    428 		exit(FMDUMP_EXIT_FATAL);
    429 	}
    430 	argt->nvarg_name = namevalue;		/* now just name */
    431 	argt->nvarg_value = value;
    432 	argt->nvarg_value_regex = value_regex;
    433 	return (argt);
    434 }
    435 
    436 /*
    437  * If the -a option is not present, filter out fault records that correspond
    438  * to events that the producer requested not be messaged for administrators.
    439  */
    440 /*ARGSUSED*/
    441 int
    442 log_filter_silent(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg)
    443 {
    444 	boolean_t msg;
    445 
    446 	return (nvlist_lookup_boolean_value(rp->rec_nvl,
    447 	    FM_SUSPECT_MESSAGE, &msg) != 0 || msg != 0);
    448 }
    449 
    450 struct loglink {
    451 	char 		*path;
    452 	long		suffix;
    453 	struct loglink	*next;
    454 };
    455 
    456 static void
    457 addlink(struct loglink **llp, char *dirname, char *logname, long suffix)
    458 {
    459 	struct loglink *newp;
    460 	size_t len;
    461 	char *str;
    462 
    463 	newp = malloc(sizeof (struct loglink));
    464 	len = strlen(dirname) + strlen(logname) + 2;
    465 	str = malloc(len);
    466 	if (newp == NULL || str == NULL) {
    467 		(void) fprintf(stderr, "%s: failed to allocate memory: %s\n",
    468 		    g_pname, strerror(errno));
    469 		exit(FMDUMP_EXIT_FATAL);
    470 	}
    471 
    472 	(void) snprintf(str, len, "%s/%s", dirname, logname);
    473 	newp->path = str;
    474 	newp->suffix = suffix;
    475 
    476 	while (*llp != NULL && suffix < (*llp)->suffix)
    477 		llp = &(*llp)->next;
    478 
    479 	newp->next = *llp;
    480 	*llp = newp;
    481 }
    482 
    483 /*
    484  * Find and return all the rotated logs.
    485  */
    486 static struct loglink *
    487 get_rotated_logs(char *logpath)
    488 {
    489 	char dirname[PATH_MAX], *logname, *endptr;
    490 	DIR *dirp;
    491 	struct dirent *dp;
    492 	long len, suffix;
    493 	struct loglink *head = NULL;
    494 
    495 	(void) strlcpy(dirname, logpath, sizeof (dirname));
    496 	logname = strrchr(dirname, '/');
    497 	*logname++ = '\0';
    498 	len = strlen(logname);
    499 
    500 	if ((dirp = opendir(dirname)) == NULL) {
    501 		(void) fprintf(stderr, "%s: failed to opendir `%s': %s\n",
    502 		    g_pname, dirname, strerror(errno));
    503 		return (NULL);
    504 	}
    505 
    506 	while ((dp = readdir(dirp)) != NULL) {
    507 		/*
    508 		 * Search the log directory for logs named "<logname>.0",
    509 		 * "<logname>.1", etc and add to the link in the
    510 		 * reverse numeric order.
    511 		 */
    512 		if (strlen(dp->d_name) < len + 2 ||
    513 		    strncmp(dp->d_name, logname, len) != 0 ||
    514 		    dp->d_name[len] != '.')
    515 			continue;
    516 
    517 		/*
    518 		 * "*.0-" file normally should not be seen.  It may
    519 		 * exist when user manually run 'fmadm rotate'.
    520 		 * In such case, we put it at the end of the list so
    521 		 * it'll be dumped after all the rotated logs, before
    522 		 * the current one.
    523 		 */
    524 		if (strcmp(dp->d_name + len + 1, "0-") == 0)
    525 			addlink(&head, dirname, dp->d_name, -1);
    526 		else if ((suffix = strtol(dp->d_name + len + 1,
    527 		    &endptr, 10)) >= 0 && *endptr == '\0')
    528 			addlink(&head, dirname, dp->d_name, suffix);
    529 	}
    530 
    531 	(void) closedir(dirp);
    532 
    533 	return (head);
    534 }
    535 
    536 int
    537 main(int argc, char *argv[])
    538 {
    539 	int opt_a = 0, opt_e = 0, opt_f = 0, opt_H = 0, opt_m = 0;
    540 	int opt_u = 0, opt_v = 0, opt_V = 0;
    541 
    542 	char ifile[PATH_MAX] = "";
    543 	int iflags = 0;
    544 
    545 	fmdump_arg_t arg;
    546 	fmdump_lyr_t lyr;
    547 	const fmdump_ops_t *ops;
    548 	fmd_log_filter_t *filtv;
    549 	uint_t filtc;
    550 
    551 	fmd_log_filter_t *errfv, *fltfv, *allfv;
    552 	uint_t errfc = 0, fltfc = 0, allfc = 0;
    553 
    554 	fmd_log_header_t log;
    555 	fmd_log_rec_f *func;
    556 	void *farg;
    557 	fmd_log_t *lp;
    558 	int c, err;
    559 	off64_t off = 0;
    560 	ulong_t recs;
    561 	struct loglink *rotated_logs = NULL, *llp;
    562 
    563 	g_pname = argv[0];
    564 
    565 	errfv = alloca(sizeof (fmd_log_filter_t) * argc);
    566 	fltfv = alloca(sizeof (fmd_log_filter_t) * argc);
    567 	allfv = alloca(sizeof (fmd_log_filter_t) * argc);
    568 
    569 	while (optind < argc) {
    570 		while ((c =
    571 		    getopt(argc, argv, "ac:efHmn:O:R:t:T:u:vV")) != EOF) {
    572 			switch (c) {
    573 			case 'a':
    574 				opt_a++;
    575 				break;
    576 			case 'c':
    577 				errfv[errfc].filt_func = fmd_log_filter_class;
    578 				errfv[errfc].filt_arg = optarg;
    579 				allfv[allfc++] = errfv[errfc++];
    580 				break;
    581 			case 'e':
    582 				opt_e++;
    583 				break;
    584 			case 'f':
    585 				opt_f++;
    586 				break;
    587 			case 'H':
    588 				opt_H++;
    589 				break;
    590 			case 'm':
    591 				opt_m++;
    592 				break;
    593 			case 'O':
    594 				off = strtoull(optarg, NULL, 16);
    595 				iflags |= FMD_LOG_XITER_OFFS;
    596 				break;
    597 			case 'R':
    598 				g_root = optarg;
    599 				break;
    600 			case 't':
    601 				errfv[errfc].filt_func = fmd_log_filter_after;
    602 				errfv[errfc].filt_arg = gettimeopt(optarg);
    603 				allfv[allfc++] = errfv[errfc++];
    604 				break;
    605 			case 'T':
    606 				errfv[errfc].filt_func = fmd_log_filter_before;
    607 				errfv[errfc].filt_arg = gettimeopt(optarg);
    608 				allfv[allfc++] = errfv[errfc++];
    609 				break;
    610 			case 'u':
    611 				fltfv[fltfc].filt_func = fmd_log_filter_uuid;
    612 				fltfv[fltfc].filt_arg = optarg;
    613 				allfv[allfc++] = fltfv[fltfc++];
    614 				opt_u++;
    615 				opt_a++; /* -u implies -a */
    616 				break;
    617 			case 'n': {
    618 				fltfv[fltfc].filt_func = fmd_log_filter_nv;
    619 				fltfv[fltfc].filt_arg = setupnamevalue(optarg);
    620 				allfv[allfc++] = fltfv[fltfc++];
    621 				break;
    622 			}
    623 			case 'v':
    624 				opt_v++;
    625 				break;
    626 			case 'V':
    627 				opt_V++;
    628 				break;
    629 			default:
    630 				return (usage(stderr));
    631 			}
    632 		}
    633 
    634 		if (optind < argc) {
    635 			if (*ifile != '\0') {
    636 				(void) fprintf(stderr, "%s: illegal "
    637 				    "argument -- %s\n", g_pname, argv[optind]);
    638 				return (FMDUMP_EXIT_USAGE);
    639 			} else {
    640 				(void) strlcpy(ifile,
    641 				    argv[optind++], sizeof (ifile));
    642 			}
    643 		}
    644 	}
    645 
    646 	if (*ifile == '\0') {
    647 		(void) snprintf(ifile, sizeof (ifile), "%s/var/fm/fmd/%slog",
    648 		    g_root ? g_root : "", opt_e && !opt_u ? "err" : "flt");
    649 		/*
    650 		 * logadm may rotate the logs.  When no input file is specified,
    651 		 * we try to dump all the rotated logs as well in the right
    652 		 * order.
    653 		 */
    654 		if (!opt_H && off == 0)
    655 			rotated_logs = get_rotated_logs(ifile);
    656 	} else if (g_root != NULL) {
    657 		(void) fprintf(stderr, "%s: -R option is not appropriate "
    658 		    "when file operand is present\n", g_pname);
    659 		return (FMDUMP_EXIT_USAGE);
    660 	}
    661 
    662 	if ((g_msg = fmd_msg_init(g_root, FMD_MSG_VERSION)) == NULL) {
    663 		(void) fprintf(stderr, "%s: failed to initialize "
    664 		    "libfmd_msg: %s\n", g_pname, strerror(errno));
    665 		return (FMDUMP_EXIT_FATAL);
    666 	}
    667 
    668 	if ((lp = fmd_log_open(FMD_LOG_VERSION, ifile, &err)) == NULL) {
    669 		(void) fprintf(stderr, "%s: failed to open %s: %s\n",
    670 		    g_pname, ifile, fmd_log_errmsg(NULL, err));
    671 		return (FMDUMP_EXIT_FATAL);
    672 	}
    673 
    674 	if (opt_H) {
    675 		fmd_log_header(lp, &log);
    676 
    677 		(void) printf("EXD_CREATOR = %s\n", log.log_creator);
    678 		(void) printf("EXD_HOSTNAME = %s\n", log.log_hostname);
    679 		(void) printf("EXD_FMA_LABEL = %s\n", log.log_label);
    680 		(void) printf("EXD_FMA_VERSION = %s\n", log.log_version);
    681 		(void) printf("EXD_FMA_OSREL = %s\n", log.log_osrelease);
    682 		(void) printf("EXD_FMA_OSVER = %s\n", log.log_osversion);
    683 		(void) printf("EXD_FMA_PLAT = %s\n", log.log_platform);
    684 		(void) printf("EXD_FMA_UUID = %s\n", log.log_uuid);
    685 
    686 		return (FMDUMP_EXIT_SUCCESS);
    687 	}
    688 
    689 	if (off != 0 && fmd_log_seek(lp, off) != 0) {
    690 		(void) fprintf(stderr, "%s: failed to seek %s: %s\n",
    691 		    g_pname, ifile, fmd_log_errmsg(lp, fmd_log_errno(lp)));
    692 		return (FMDUMP_EXIT_FATAL);
    693 	}
    694 
    695 	if (opt_e && opt_u)
    696 		ops = &fmdump_err_ops;
    697 	else if (strcmp(fmd_log_label(lp), fmdump_flt_ops.do_label) == 0)
    698 		ops = &fmdump_flt_ops;
    699 	else if (strcmp(fmd_log_label(lp), fmdump_asru_ops.do_label) == 0)
    700 		ops = &fmdump_asru_ops;
    701 	else
    702 		ops = &fmdump_err_ops;
    703 
    704 	if (!opt_a && ops == &fmdump_flt_ops) {
    705 		fltfv[fltfc].filt_func = log_filter_silent;
    706 		fltfv[fltfc].filt_arg = NULL;
    707 		allfv[allfc++] = fltfv[fltfc++];
    708 	}
    709 
    710 	if (opt_V) {
    711 		arg.da_fmt = &ops->do_formats[FMDUMP_VERB2];
    712 		iflags |= FMD_LOG_XITER_REFS;
    713 	} else if (opt_v) {
    714 		arg.da_fmt = &ops->do_formats[FMDUMP_VERB1];
    715 	} else if (opt_m) {
    716 		arg.da_fmt = &ops->do_formats[FMDUMP_MSG];
    717 	} else
    718 		arg.da_fmt = &ops->do_formats[FMDUMP_SHORT];
    719 
    720 	if (opt_m && arg.da_fmt->do_func == NULL) {
    721 		(void) fprintf(stderr, "%s: -m mode is not supported for "
    722 		    "log of type %s: %s\n", g_pname, fmd_log_label(lp), ifile);
    723 		return (FMDUMP_EXIT_USAGE);
    724 	}
    725 
    726 	arg.da_fv = errfv;
    727 	arg.da_fc = errfc;
    728 	arg.da_fp = stdout;
    729 
    730 	if (iflags & FMD_LOG_XITER_OFFS)
    731 		fmdump_printf(arg.da_fp, "%16s ", "OFFSET");
    732 
    733 	if (arg.da_fmt->do_hdr && !(opt_V && ops == &fmdump_flt_ops))
    734 		fmdump_printf(arg.da_fp, "%s\n", arg.da_fmt->do_hdr);
    735 
    736 	if (opt_e && opt_u) {
    737 		iflags |= FMD_LOG_XITER_REFS;
    738 		func = xref_iter;
    739 		farg = &arg;
    740 		filtc = fltfc;
    741 		filtv = fltfv;
    742 	} else {
    743 		func = arg.da_fmt->do_func;
    744 		farg = arg.da_fp;
    745 		filtc = allfc;
    746 		filtv = allfv;
    747 	}
    748 
    749 	if (iflags & FMD_LOG_XITER_OFFS) {
    750 		lyr.dy_func = func;
    751 		lyr.dy_arg = farg;
    752 		lyr.dy_fp = arg.da_fp;
    753 		func = xoff_iter;
    754 		farg = &lyr;
    755 	}
    756 
    757 	for (llp = rotated_logs; llp != NULL; llp = llp->next) {
    758 		fmd_log_t *rlp;
    759 
    760 		if ((rlp = fmd_log_open(FMD_LOG_VERSION, llp->path, &err))
    761 		    == NULL) {
    762 			(void) fprintf(stderr, "%s: failed to open %s: %s\n",
    763 			    g_pname, llp->path, fmd_log_errmsg(NULL, err));
    764 			g_errs++;
    765 			continue;
    766 		}
    767 
    768 		recs = 0;
    769 		if (fmd_log_xiter(rlp, iflags, filtc, filtv,
    770 		    func, error, farg, &recs) != 0) {
    771 			(void) fprintf(stderr,
    772 			    "%s: failed to dump %s: %s\n", g_pname, llp->path,
    773 			    fmd_log_errmsg(rlp, fmd_log_errno(rlp)));
    774 			g_errs++;
    775 		}
    776 		g_recs += recs;
    777 
    778 		fmd_log_close(rlp);
    779 	}
    780 
    781 	do {
    782 		recs = 0;
    783 		if (fmd_log_xiter(lp, iflags, filtc, filtv,
    784 		    func, error, farg, &recs) != 0) {
    785 			(void) fprintf(stderr,
    786 			    "%s: failed to dump %s: %s\n", g_pname, ifile,
    787 			    fmd_log_errmsg(lp, fmd_log_errno(lp)));
    788 			g_errs++;
    789 		}
    790 		g_recs += recs;
    791 
    792 		if (opt_f)
    793 			(void) sleep(1);
    794 
    795 	} while (opt_f);
    796 
    797 	if (!opt_f && g_recs == 0 && isatty(STDOUT_FILENO))
    798 		(void) fprintf(stderr, "%s: %s is empty\n", g_pname, ifile);
    799 
    800 	if (g_thp != NULL)
    801 		topo_close(g_thp);
    802 
    803 	fmd_log_close(lp);
    804 	fmd_msg_fini(g_msg);
    805 
    806 	return (g_errs ? FMDUMP_EXIT_ERROR : FMDUMP_EXIT_SUCCESS);
    807 }
    808