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