xref: /onnv-gate/usr/src/cmd/fm/fmdump/common/fmdump.c (revision 12967:ab9ae749152f)
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
55609Scy152378  * Common Development and Distribution License (the "License").
65609Scy152378  * 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*12967Sgavin.maltby@oracle.com  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
230Sstevel@tonic-gate  */
240Sstevel@tonic-gate 
250Sstevel@tonic-gate #include <alloca.h>
260Sstevel@tonic-gate #include <unistd.h>
270Sstevel@tonic-gate #include <limits.h>
280Sstevel@tonic-gate #include <strings.h>
290Sstevel@tonic-gate #include <stdlib.h>
300Sstevel@tonic-gate #include <stdarg.h>
310Sstevel@tonic-gate #include <stdio.h>
320Sstevel@tonic-gate #include <errno.h>
330Sstevel@tonic-gate #include <time.h>
340Sstevel@tonic-gate #include <ctype.h>
356640Scth #include <regex.h>
368740SSean.Ye@Sun.COM #include <dirent.h>
37*12967Sgavin.maltby@oracle.com #include <pthread.h>
380Sstevel@tonic-gate 
390Sstevel@tonic-gate #include <fmdump.h>
400Sstevel@tonic-gate 
410Sstevel@tonic-gate #define	FMDUMP_EXIT_SUCCESS	0
420Sstevel@tonic-gate #define	FMDUMP_EXIT_FATAL	1
430Sstevel@tonic-gate #define	FMDUMP_EXIT_USAGE	2
440Sstevel@tonic-gate #define	FMDUMP_EXIT_ERROR	3
450Sstevel@tonic-gate 
460Sstevel@tonic-gate const char *g_pname;
470Sstevel@tonic-gate ulong_t g_errs;
480Sstevel@tonic-gate ulong_t g_recs;
490Sstevel@tonic-gate char *g_root;
509501SRobert.Johnston@Sun.COM 
511414Scindi struct topo_hdl *g_thp;
529501SRobert.Johnston@Sun.COM fmd_msg_hdl_t *g_msg;
530Sstevel@tonic-gate 
540Sstevel@tonic-gate /*PRINTFLIKE2*/
550Sstevel@tonic-gate void
fmdump_printf(FILE * fp,const char * format,...)560Sstevel@tonic-gate fmdump_printf(FILE *fp, const char *format, ...)
570Sstevel@tonic-gate {
580Sstevel@tonic-gate 	va_list ap;
590Sstevel@tonic-gate 
600Sstevel@tonic-gate 	va_start(ap, format);
610Sstevel@tonic-gate 
620Sstevel@tonic-gate 	if (vfprintf(fp, format, ap) < 0) {
630Sstevel@tonic-gate 		(void) fprintf(stderr, "%s: failed to print record: %s\n",
640Sstevel@tonic-gate 		    g_pname, strerror(errno));
650Sstevel@tonic-gate 		g_errs++;
660Sstevel@tonic-gate 	}
670Sstevel@tonic-gate 
680Sstevel@tonic-gate 	va_end(ap);
690Sstevel@tonic-gate }
700Sstevel@tonic-gate 
710Sstevel@tonic-gate void
fmdump_vwarn(const char * format,va_list ap)720Sstevel@tonic-gate fmdump_vwarn(const char *format, va_list ap)
730Sstevel@tonic-gate {
740Sstevel@tonic-gate 	int err = errno;
750Sstevel@tonic-gate 
760Sstevel@tonic-gate 	(void) fprintf(stderr, "%s: warning: ", g_pname);
770Sstevel@tonic-gate 	(void) vfprintf(stderr, format, ap);
780Sstevel@tonic-gate 
790Sstevel@tonic-gate 	if (strchr(format, '\n') == NULL)
800Sstevel@tonic-gate 		(void) fprintf(stderr, ": %s\n", strerror(err));
810Sstevel@tonic-gate 
820Sstevel@tonic-gate 	g_errs++;
830Sstevel@tonic-gate }
840Sstevel@tonic-gate 
850Sstevel@tonic-gate /*PRINTFLIKE1*/
860Sstevel@tonic-gate void
fmdump_warn(const char * format,...)870Sstevel@tonic-gate fmdump_warn(const char *format, ...)
880Sstevel@tonic-gate {
890Sstevel@tonic-gate 	va_list ap;
900Sstevel@tonic-gate 
910Sstevel@tonic-gate 	va_start(ap, format);
920Sstevel@tonic-gate 	fmdump_vwarn(format, ap);
930Sstevel@tonic-gate 	va_end(ap);
940Sstevel@tonic-gate }
950Sstevel@tonic-gate 
96*12967Sgavin.maltby@oracle.com static void
fmdump_exit(int err,int exitcode,const char * format,va_list ap)97*12967Sgavin.maltby@oracle.com fmdump_exit(int err, int exitcode, const char *format, va_list ap)
98*12967Sgavin.maltby@oracle.com {
99*12967Sgavin.maltby@oracle.com 	(void) fprintf(stderr, "%s: ", g_pname);
100*12967Sgavin.maltby@oracle.com 
101*12967Sgavin.maltby@oracle.com 	(void) vfprintf(stderr, format, ap);
102*12967Sgavin.maltby@oracle.com 
103*12967Sgavin.maltby@oracle.com 	if (strchr(format, '\n') == NULL)
104*12967Sgavin.maltby@oracle.com 		(void) fprintf(stderr, ": %s\n", strerror(err));
105*12967Sgavin.maltby@oracle.com 
106*12967Sgavin.maltby@oracle.com 	exit(exitcode);
107*12967Sgavin.maltby@oracle.com }
108*12967Sgavin.maltby@oracle.com 
109*12967Sgavin.maltby@oracle.com /*PRINTFLIKE1*/
110*12967Sgavin.maltby@oracle.com static void
fmdump_fatal(const char * format,...)111*12967Sgavin.maltby@oracle.com fmdump_fatal(const char *format, ...)
112*12967Sgavin.maltby@oracle.com {
113*12967Sgavin.maltby@oracle.com 	int err = errno;
114*12967Sgavin.maltby@oracle.com 
115*12967Sgavin.maltby@oracle.com 	va_list ap;
116*12967Sgavin.maltby@oracle.com 
117*12967Sgavin.maltby@oracle.com 	va_start(ap, format);
118*12967Sgavin.maltby@oracle.com 	fmdump_exit(err, FMDUMP_EXIT_FATAL, format, ap);
119*12967Sgavin.maltby@oracle.com 	va_end(ap);
120*12967Sgavin.maltby@oracle.com }
121*12967Sgavin.maltby@oracle.com 
122*12967Sgavin.maltby@oracle.com /*PRINTFLIKE1*/
123*12967Sgavin.maltby@oracle.com static void
fmdump_usage(const char * format,...)124*12967Sgavin.maltby@oracle.com fmdump_usage(const char *format, ...)
125*12967Sgavin.maltby@oracle.com {
126*12967Sgavin.maltby@oracle.com 
127*12967Sgavin.maltby@oracle.com 	int err = errno;
128*12967Sgavin.maltby@oracle.com 
129*12967Sgavin.maltby@oracle.com 	va_list ap;
130*12967Sgavin.maltby@oracle.com 
131*12967Sgavin.maltby@oracle.com 	va_start(ap, format);
132*12967Sgavin.maltby@oracle.com 	fmdump_exit(err, FMDUMP_EXIT_USAGE, format, ap);
133*12967Sgavin.maltby@oracle.com 	va_end(ap);
134*12967Sgavin.maltby@oracle.com }
135*12967Sgavin.maltby@oracle.com 
1360Sstevel@tonic-gate char *
fmdump_date(char * buf,size_t len,const fmd_log_record_t * rp)1370Sstevel@tonic-gate fmdump_date(char *buf, size_t len, const fmd_log_record_t *rp)
1380Sstevel@tonic-gate {
1390Sstevel@tonic-gate 	if (rp->rec_sec > LONG_MAX) {
1400Sstevel@tonic-gate 		fmdump_warn("record time is too large for 32-bit utility\n");
1410Sstevel@tonic-gate 		(void) snprintf(buf, len, "0x%llx", rp->rec_sec);
1420Sstevel@tonic-gate 	} else {
1430Sstevel@tonic-gate 		time_t tod = (time_t)rp->rec_sec;
1445609Scy152378 		time_t now = time(NULL);
1455609Scy152378 		if (tod > now+60 ||
1465609Scy152378 		    tod < now - 6L*30L*24L*60L*60L) { /* 6 months ago */
1475609Scy152378 			(void) strftime(buf, len, "%b %d %Y %T",
1485609Scy152378 			    localtime(&tod));
1495609Scy152378 		} else {
1505609Scy152378 			size_t sz;
1515609Scy152378 			sz = strftime(buf, len, "%b %d %T", localtime(&tod));
1525609Scy152378 			(void) snprintf(buf + sz, len - sz, ".%4.4llu",
1535609Scy152378 			    rp->rec_nsec / (NANOSEC / 10000));
1545609Scy152378 		}
1550Sstevel@tonic-gate 	}
1560Sstevel@tonic-gate 
1570Sstevel@tonic-gate 	return (buf);
1580Sstevel@tonic-gate }
1590Sstevel@tonic-gate 
1600Sstevel@tonic-gate char *
fmdump_year(char * buf,size_t len,const fmd_log_record_t * rp)1610Sstevel@tonic-gate fmdump_year(char *buf, size_t len, const fmd_log_record_t *rp)
1620Sstevel@tonic-gate {
1630Sstevel@tonic-gate #ifdef _ILP32
1640Sstevel@tonic-gate 	if (rp->rec_sec > LONG_MAX) {
1650Sstevel@tonic-gate 		fmdump_warn("record time is too large for 32-bit utility\n");
1660Sstevel@tonic-gate 		(void) snprintf(buf, len, "0x%llx", rp->rec_sec);
1670Sstevel@tonic-gate 	} else {
1680Sstevel@tonic-gate #endif
1690Sstevel@tonic-gate 		time_t tod = (time_t)rp->rec_sec;
1700Sstevel@tonic-gate 		(void) strftime(buf, len, "%b %d %Y %T", localtime(&tod));
1710Sstevel@tonic-gate #ifdef _ILP32
1720Sstevel@tonic-gate 	}
1730Sstevel@tonic-gate #endif
1740Sstevel@tonic-gate 	return (buf);
1750Sstevel@tonic-gate }
1760Sstevel@tonic-gate 
177*12967Sgavin.maltby@oracle.com /* BEGIN CSTYLED */
178*12967Sgavin.maltby@oracle.com static const char *synopsis =
179*12967Sgavin.maltby@oracle.com "Usage: %s [[-e | -i | -I] | -A ] [-f] [-mvVp] [-c class] [-R root]\n"
180*12967Sgavin.maltby@oracle.com 	"\t      [-t time ][-T time] [-u uuid] [-n name[.name]*[=value]] "
181*12967Sgavin.maltby@oracle.com 							"[file]...\n    "
182*12967Sgavin.maltby@oracle.com     "Log selection: [-e | -i | -I] or one [file]; default is the fault log\n"
183*12967Sgavin.maltby@oracle.com 	"\t-e  display error log content\n"
184*12967Sgavin.maltby@oracle.com 	"\t-i  display infolog content\n"
185*12967Sgavin.maltby@oracle.com 	"\t-I  display the high-value-infolog content\n"
186*12967Sgavin.maltby@oracle.com 	"\t-R  set root directory for pathname expansions\n    "
187*12967Sgavin.maltby@oracle.com     "Command behaviour:\n"
188*12967Sgavin.maltby@oracle.com 	"\t-A  Aggregate specified [file]s or, if no [file], all known logs\n"
189*12967Sgavin.maltby@oracle.com 	"\t-f  follow growth of log file by waiting for additional data\n    "
190*12967Sgavin.maltby@oracle.com     "Output options:\n"
191*12967Sgavin.maltby@oracle.com 	"\t-m  display human-readable messages (only for fault logs)\n"
192*12967Sgavin.maltby@oracle.com 	"\t-v  set verbose mode: display additional event detail\n"
193*12967Sgavin.maltby@oracle.com 	"\t-V  set very verbose mode: display complete event contents\n"
194*12967Sgavin.maltby@oracle.com 	"\t-p  Used with -V: apply some output prettification\n    "
195*12967Sgavin.maltby@oracle.com     "Selection filters:\n"
196*12967Sgavin.maltby@oracle.com 	"\t-c  select events that match the specified class\n"
197*12967Sgavin.maltby@oracle.com 	"\t-t  select events that occurred after the specified time\n"
198*12967Sgavin.maltby@oracle.com 	"\t-T  select events that occurred before the specified time\n"
199*12967Sgavin.maltby@oracle.com 	"\t-u  select events that match the specified diagnosis uuid\n"
200*12967Sgavin.maltby@oracle.com 	"\t-n  select events containing named nvpair (with matching value)\n";
201*12967Sgavin.maltby@oracle.com /* END CSTYLED */
202*12967Sgavin.maltby@oracle.com 
2030Sstevel@tonic-gate static int
usage(FILE * fp)2040Sstevel@tonic-gate usage(FILE *fp)
2050Sstevel@tonic-gate {
206*12967Sgavin.maltby@oracle.com 	(void) fprintf(fp, synopsis, g_pname);
2070Sstevel@tonic-gate 	return (FMDUMP_EXIT_USAGE);
2080Sstevel@tonic-gate }
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate /*ARGSUSED*/
2110Sstevel@tonic-gate static int
error(fmd_log_t * lp,void * private)2120Sstevel@tonic-gate error(fmd_log_t *lp, void *private)
2130Sstevel@tonic-gate {
2140Sstevel@tonic-gate 	fmdump_warn("skipping record: %s\n",
2150Sstevel@tonic-gate 	    fmd_log_errmsg(lp, fmd_log_errno(lp)));
2160Sstevel@tonic-gate 	return (0);
2170Sstevel@tonic-gate }
2180Sstevel@tonic-gate 
2190Sstevel@tonic-gate /*
2200Sstevel@tonic-gate  * Yet another disgusting argument parsing function (TM).  We attempt to parse
2210Sstevel@tonic-gate  * a time argument in a variety of strptime(3C) formats, in which case it is
2220Sstevel@tonic-gate  * interpreted as a local time and is converted to a timeval using mktime(3C).
2230Sstevel@tonic-gate  * If those formats fail, we look to see if the time is a decimal integer
2240Sstevel@tonic-gate  * followed by one of our magic suffixes, in which case the time is interpreted
2250Sstevel@tonic-gate  * as a time delta *before* the current time-of-day (i.e. "1h" = "1 hour ago").
2260Sstevel@tonic-gate  */
2270Sstevel@tonic-gate static struct timeval *
gettimeopt(const char * arg)2280Sstevel@tonic-gate gettimeopt(const char *arg)
2290Sstevel@tonic-gate {
2300Sstevel@tonic-gate 	const struct {
2310Sstevel@tonic-gate 		const char *name;
2320Sstevel@tonic-gate 		hrtime_t mul;
2330Sstevel@tonic-gate 	} suffix[] = {
2346640Scth 		{ "ns",		NANOSEC / NANOSEC },
2350Sstevel@tonic-gate 		{ "nsec",	NANOSEC / NANOSEC },
2360Sstevel@tonic-gate 		{ "us",		NANOSEC / MICROSEC },
2370Sstevel@tonic-gate 		{ "usec",	NANOSEC / MICROSEC },
2380Sstevel@tonic-gate 		{ "ms",		NANOSEC / MILLISEC },
2390Sstevel@tonic-gate 		{ "msec",	NANOSEC / MILLISEC },
2400Sstevel@tonic-gate 		{ "s",		NANOSEC / SEC },
2410Sstevel@tonic-gate 		{ "sec",	NANOSEC / SEC },
2420Sstevel@tonic-gate 		{ "m",		NANOSEC * (hrtime_t)60 },
2430Sstevel@tonic-gate 		{ "min",	NANOSEC * (hrtime_t)60 },
2440Sstevel@tonic-gate 		{ "h",		NANOSEC * (hrtime_t)(60 * 60) },
2450Sstevel@tonic-gate 		{ "hour",	NANOSEC * (hrtime_t)(60 * 60) },
2460Sstevel@tonic-gate 		{ "d",		NANOSEC * (hrtime_t)(24 * 60 * 60) },
2470Sstevel@tonic-gate 		{ "day",	NANOSEC * (hrtime_t)(24 * 60 * 60) },
2480Sstevel@tonic-gate 		{ NULL }
2490Sstevel@tonic-gate 	};
2500Sstevel@tonic-gate 
2510Sstevel@tonic-gate 	struct timeval *tvp = malloc(sizeof (struct timeval));
2520Sstevel@tonic-gate 	struct timeval tod;
2530Sstevel@tonic-gate 	struct tm tm;
2540Sstevel@tonic-gate 	char *p;
2550Sstevel@tonic-gate 
256*12967Sgavin.maltby@oracle.com 	if (tvp == NULL)
257*12967Sgavin.maltby@oracle.com 		fmdump_fatal("failed to allocate memory");
2580Sstevel@tonic-gate 
259*12967Sgavin.maltby@oracle.com 	if (gettimeofday(&tod, NULL) != 0)
260*12967Sgavin.maltby@oracle.com 		fmdump_fatal("failed to get tod");
2610Sstevel@tonic-gate 
2620Sstevel@tonic-gate 	/*
2630Sstevel@tonic-gate 	 * First try a variety of strptime() calls.  If these all fail, we'll
2640Sstevel@tonic-gate 	 * try parsing an integer followed by one of our suffix[] strings.
2650Sstevel@tonic-gate 	 * NOTE: any form using %y must appear *before* the equivalent %Y form;
2660Sstevel@tonic-gate 	 * otherwise %Y will accept the two year digits but infer century zero.
2670Sstevel@tonic-gate 	 * Any form ending in %y must additionally check isdigit(*p) to ensure
2680Sstevel@tonic-gate 	 * that it does not inadvertently match 2 digits of a 4-digit year.
2690Sstevel@tonic-gate 	 *
2700Sstevel@tonic-gate 	 * Beware: Any strptime() sequence containing consecutive %x sequences
2710Sstevel@tonic-gate 	 * may fall victim to SCCS expanding it as a keyword!  If this happens
2720Sstevel@tonic-gate 	 * we use separate string constant that ANSI C will concatenate.
2730Sstevel@tonic-gate 	 */
2740Sstevel@tonic-gate 	if ((p = strptime(arg, "%m/%d/%y" "%t" "%H:%M:%S", &tm)) == NULL &&
2750Sstevel@tonic-gate 	    (p = strptime(arg, "%m/%d/%Y" "%t" "%H:%M:%S", &tm)) == NULL &&
2760Sstevel@tonic-gate 	    (p = strptime(arg, "%m/%d/%y" "%t" "%H:%M", &tm)) == NULL &&
2770Sstevel@tonic-gate 	    (p = strptime(arg, "%m/%d/%Y" "%t" "%H:%M", &tm)) == NULL &&
2780Sstevel@tonic-gate 	    ((p = strptime(arg, "%m/%d/%y", &tm)) == NULL || isdigit(*p)) &&
2790Sstevel@tonic-gate 	    (p = strptime(arg, "%m/%d/%Y", &tm)) == NULL &&
2800Sstevel@tonic-gate 	    (p = strptime(arg, "%y-%m-%dT%H:%M:%S", &tm)) == NULL &&
2810Sstevel@tonic-gate 	    (p = strptime(arg, "%Y-%m-%dT%H:%M:%S", &tm)) == NULL &&
2820Sstevel@tonic-gate 	    (p = strptime(arg, "%y-%m-%dT%H:%M", &tm)) == NULL &&
2830Sstevel@tonic-gate 	    (p = strptime(arg, "%Y-%m-%dT%H:%M", &tm)) == NULL &&
2840Sstevel@tonic-gate 	    (p = strptime(arg, "%y-%m-%d", &tm)) == NULL &&
2850Sstevel@tonic-gate 	    (p = strptime(arg, "%Y-%m-%d", &tm)) == NULL &&
2860Sstevel@tonic-gate 	    (p = strptime(arg, "%d%b%y" "%t" "%H:%M:%S", &tm)) == NULL &&
2870Sstevel@tonic-gate 	    (p = strptime(arg, "%d%b%Y" "%t" "%H:%M:%S", &tm)) == NULL &&
2880Sstevel@tonic-gate 	    (p = strptime(arg, "%d%b%y" "%t" "%H:%M", &tm)) == NULL &&
2890Sstevel@tonic-gate 	    (p = strptime(arg, "%d%b%Y" "%t" "%H:%M", &tm)) == NULL &&
2900Sstevel@tonic-gate 	    ((p = strptime(arg, "%d%b%y", &tm)) == NULL || isdigit(*p)) &&
2910Sstevel@tonic-gate 	    (p = strptime(arg, "%d%b%Y", &tm)) == NULL &&
2920Sstevel@tonic-gate 	    (p = strptime(arg, "%b%t%d" "%t" "%H:%M:%S", &tm)) == NULL &&
2930Sstevel@tonic-gate 	    (p = strptime(arg, "%b%t%d" "%t" "%H:%M:%S", &tm)) == NULL &&
2940Sstevel@tonic-gate 	    (p = strptime(arg, "%H:%M:%S", &tm)) == NULL &&
2950Sstevel@tonic-gate 	    (p = strptime(arg, "%H:%M", &tm)) == NULL) {
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate 		hrtime_t nsec;
2980Sstevel@tonic-gate 		int i;
2990Sstevel@tonic-gate 
3000Sstevel@tonic-gate 		errno = 0;
3010Sstevel@tonic-gate 		nsec = strtol(arg, (char **)&p, 10);
3020Sstevel@tonic-gate 
303*12967Sgavin.maltby@oracle.com 		if (errno != 0 || nsec == 0 || p == arg || *p == '\0')
304*12967Sgavin.maltby@oracle.com 			fmdump_usage("illegal time format -- %s\n", arg);
3050Sstevel@tonic-gate 
3060Sstevel@tonic-gate 		for (i = 0; suffix[i].name != NULL; i++) {
3070Sstevel@tonic-gate 			if (strcasecmp(suffix[i].name, p) == 0) {
3080Sstevel@tonic-gate 				nsec *= suffix[i].mul;
3090Sstevel@tonic-gate 				break;
3100Sstevel@tonic-gate 			}
3110Sstevel@tonic-gate 		}
3120Sstevel@tonic-gate 
313*12967Sgavin.maltby@oracle.com 		if (suffix[i].name == NULL)
314*12967Sgavin.maltby@oracle.com 			fmdump_usage("illegal time format -- %s\n", arg);
3150Sstevel@tonic-gate 
3160Sstevel@tonic-gate 		tvp->tv_sec = nsec / NANOSEC;
3170Sstevel@tonic-gate 		tvp->tv_usec = (nsec % NANOSEC) / (NANOSEC / MICROSEC);
3180Sstevel@tonic-gate 
319*12967Sgavin.maltby@oracle.com 		if (tvp->tv_sec > tod.tv_sec)
320*12967Sgavin.maltby@oracle.com 			fmdump_usage("time delta precedes UTC time origin "
321*12967Sgavin.maltby@oracle.com 			    "-- %s\n", arg);
3220Sstevel@tonic-gate 
3230Sstevel@tonic-gate 		tvp->tv_sec = tod.tv_sec - tvp->tv_sec;
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 	} else if (*p == '\0' || *p == '.') {
3260Sstevel@tonic-gate 		/*
3270Sstevel@tonic-gate 		 * If tm_year is zero, we matched [%b %d] %H:%M[:%S]; use
3280Sstevel@tonic-gate 		 * the result of localtime(&tod.tv_sec) to fill in the rest.
3290Sstevel@tonic-gate 		 */
3300Sstevel@tonic-gate 		if (tm.tm_year == 0) {
3310Sstevel@tonic-gate 			int h = tm.tm_hour;
3320Sstevel@tonic-gate 			int m = tm.tm_min;
3330Sstevel@tonic-gate 			int s = tm.tm_sec;
3340Sstevel@tonic-gate 			int b = tm.tm_mon;
3350Sstevel@tonic-gate 			int d = tm.tm_mday;
3360Sstevel@tonic-gate 
3370Sstevel@tonic-gate 			bcopy(localtime(&tod.tv_sec), &tm, sizeof (tm));
3380Sstevel@tonic-gate 			tm.tm_isdst = 0; /* see strptime(3C) and below */
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate 			if (d > 0) {
3410Sstevel@tonic-gate 				tm.tm_mon = b;
3420Sstevel@tonic-gate 				tm.tm_mday = d;
3430Sstevel@tonic-gate 			}
3440Sstevel@tonic-gate 
3450Sstevel@tonic-gate 			tm.tm_hour = h;
3460Sstevel@tonic-gate 			tm.tm_min = m;
3470Sstevel@tonic-gate 			tm.tm_sec = s;
3480Sstevel@tonic-gate 		}
3490Sstevel@tonic-gate 
3500Sstevel@tonic-gate 		errno = 0;
3510Sstevel@tonic-gate 		tvp->tv_sec = mktime(&tm);
3520Sstevel@tonic-gate 		tvp->tv_usec = 0;
3530Sstevel@tonic-gate 
354*12967Sgavin.maltby@oracle.com 		if (tvp->tv_sec == -1L && errno != 0)
355*12967Sgavin.maltby@oracle.com 			fmdump_fatal("failed to compose time %s", arg);
3560Sstevel@tonic-gate 
3570Sstevel@tonic-gate 		/*
3580Sstevel@tonic-gate 		 * If our mktime() set tm_isdst, adjust the result for DST by
3590Sstevel@tonic-gate 		 * subtracting the offset between the main and alternate zones.
3600Sstevel@tonic-gate 		 */
3610Sstevel@tonic-gate 		if (tm.tm_isdst)
3620Sstevel@tonic-gate 			tvp->tv_sec -= timezone - altzone;
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate 		if (p[0] == '.') {
3650Sstevel@tonic-gate 			arg = p;
3660Sstevel@tonic-gate 			errno = 0;
3670Sstevel@tonic-gate 			tvp->tv_usec =
3680Sstevel@tonic-gate 			    (suseconds_t)(strtod(arg, &p) * (double)MICROSEC);
3690Sstevel@tonic-gate 
370*12967Sgavin.maltby@oracle.com 			if (errno != 0 || p == arg || *p != '\0')
371*12967Sgavin.maltby@oracle.com 				fmdump_usage("illegal time suffix -- .%s\n",
372*12967Sgavin.maltby@oracle.com 				    arg);
3730Sstevel@tonic-gate 		}
3740Sstevel@tonic-gate 
3750Sstevel@tonic-gate 	} else {
376*12967Sgavin.maltby@oracle.com 		fmdump_usage("unexpected suffix after time %s -- %s\n", arg, p);
3770Sstevel@tonic-gate 	}
3780Sstevel@tonic-gate 
3790Sstevel@tonic-gate 	return (tvp);
3800Sstevel@tonic-gate }
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate /*
3830Sstevel@tonic-gate  * If the -u option is specified in combination with the -e option, we iterate
3840Sstevel@tonic-gate  * over each record in the fault log with a matching UUID finding xrefs to the
3850Sstevel@tonic-gate  * error log, and then use this function to iterate over every xref'd record.
3860Sstevel@tonic-gate  */
3870Sstevel@tonic-gate int
xref_iter(fmd_log_t * lp,const fmd_log_record_t * rp,void * arg)3880Sstevel@tonic-gate xref_iter(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg)
3890Sstevel@tonic-gate {
3900Sstevel@tonic-gate 	const fmd_log_record_t *xrp = rp->rec_xrefs;
3910Sstevel@tonic-gate 	fmdump_arg_t *dap = arg;
3920Sstevel@tonic-gate 	int i, rv = 0;
3930Sstevel@tonic-gate 
3940Sstevel@tonic-gate 	for (i = 0; rv == 0 && i < rp->rec_nrefs; i++, xrp++) {
3950Sstevel@tonic-gate 		if (fmd_log_filter(lp, dap->da_fc, dap->da_fv, xrp))
3960Sstevel@tonic-gate 			rv = dap->da_fmt->do_func(lp, xrp, dap->da_fp);
3970Sstevel@tonic-gate 	}
3980Sstevel@tonic-gate 
3990Sstevel@tonic-gate 	return (rv);
4000Sstevel@tonic-gate }
4010Sstevel@tonic-gate 
4020Sstevel@tonic-gate int
xoff_iter(fmd_log_t * lp,const fmd_log_record_t * rp,void * arg)4030Sstevel@tonic-gate xoff_iter(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg)
4040Sstevel@tonic-gate {
4050Sstevel@tonic-gate 	fmdump_lyr_t *dyp = arg;
4060Sstevel@tonic-gate 
4070Sstevel@tonic-gate 	fmdump_printf(dyp->dy_fp, "%16llx ", (u_longlong_t)rp->rec_off);
4080Sstevel@tonic-gate 	return (dyp->dy_func(lp, rp, dyp->dy_arg));
4090Sstevel@tonic-gate }
4100Sstevel@tonic-gate 
4110Sstevel@tonic-gate /*
4126640Scth  * Initialize fmd_log_filter_nvarg_t from -n name=value argument string.
4136640Scth  */
4146640Scth static fmd_log_filter_nvarg_t *
setupnamevalue(char * namevalue)4156640Scth setupnamevalue(char *namevalue)
4166640Scth {
4176640Scth 	fmd_log_filter_nvarg_t	*argt;
4186640Scth 	char			*value;
4196640Scth 	regex_t			*value_regex = NULL;
4206640Scth 	char			errstr[128];
4216640Scth 	int			rv;
4226640Scth 
4236640Scth 	if ((value = strchr(namevalue, '=')) == NULL) {
4246640Scth 		value_regex = NULL;
4256640Scth 	} else {
4266640Scth 		*value++ = '\0';	/* separate name and value string */
4276640Scth 
4286640Scth 		/*
4296640Scth 		 * Skip white space before value to facilitate direct
4306640Scth 		 * cut/paste from previous fmdump output.
4316640Scth 		 */
4326640Scth 		while (isspace(*value))
4336640Scth 			value++;
4346640Scth 
435*12967Sgavin.maltby@oracle.com 		if ((value_regex = malloc(sizeof (regex_t))) == NULL)
436*12967Sgavin.maltby@oracle.com 			fmdump_fatal("failed to allocate memory");
4376640Scth 
4386640Scth 		/* compile regular expression for possible string match */
4396640Scth 		if ((rv = regcomp(value_regex, value,
4406640Scth 		    REG_NOSUB|REG_NEWLINE)) != 0) {
4416640Scth 			(void) regerror(rv, value_regex, errstr,
4426640Scth 			    sizeof (errstr));
4436640Scth 			free(value_regex);
444*12967Sgavin.maltby@oracle.com 			fmdump_usage("unexpected regular expression in "
445*12967Sgavin.maltby@oracle.com 			    "%s: %s\n", value, errstr);
4466640Scth 		}
4476640Scth 	}
4486640Scth 
449*12967Sgavin.maltby@oracle.com 	if ((argt = malloc(sizeof (fmd_log_filter_nvarg_t))) == NULL)
450*12967Sgavin.maltby@oracle.com 		fmdump_fatal("failed to allocate memory");
451*12967Sgavin.maltby@oracle.com 
4526640Scth 	argt->nvarg_name = namevalue;		/* now just name */
4536640Scth 	argt->nvarg_value = value;
4546640Scth 	argt->nvarg_value_regex = value_regex;
4556640Scth 	return (argt);
4566640Scth }
4576640Scth 
4586640Scth /*
4590Sstevel@tonic-gate  * If the -a option is not present, filter out fault records that correspond
4600Sstevel@tonic-gate  * to events that the producer requested not be messaged for administrators.
4610Sstevel@tonic-gate  */
4620Sstevel@tonic-gate /*ARGSUSED*/
4630Sstevel@tonic-gate int
log_filter_silent(fmd_log_t * lp,const fmd_log_record_t * rp,void * arg)4640Sstevel@tonic-gate log_filter_silent(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg)
4650Sstevel@tonic-gate {
466*12967Sgavin.maltby@oracle.com 	int opt_A = (arg != NULL);
4670Sstevel@tonic-gate 	boolean_t msg;
468*12967Sgavin.maltby@oracle.com 	char *class;
469*12967Sgavin.maltby@oracle.com 
470*12967Sgavin.maltby@oracle.com 	/*
471*12967Sgavin.maltby@oracle.com 	 * If -A was used then apply this filter only to events of list class
472*12967Sgavin.maltby@oracle.com 	 */
473*12967Sgavin.maltby@oracle.com 	if (opt_A) {
474*12967Sgavin.maltby@oracle.com 		if (nvlist_lookup_string(rp->rec_nvl, FM_CLASS, &class) != 0 ||
475*12967Sgavin.maltby@oracle.com 		    strncmp(class, FM_LIST_EVENT ".",
476*12967Sgavin.maltby@oracle.com 		    sizeof (FM_LIST_EVENT)) != 0)
477*12967Sgavin.maltby@oracle.com 			return (1);
478*12967Sgavin.maltby@oracle.com 	}
4790Sstevel@tonic-gate 
4800Sstevel@tonic-gate 	return (nvlist_lookup_boolean_value(rp->rec_nvl,
4810Sstevel@tonic-gate 	    FM_SUSPECT_MESSAGE, &msg) != 0 || msg != 0);
4820Sstevel@tonic-gate }
4830Sstevel@tonic-gate 
4848740SSean.Ye@Sun.COM struct loglink {
485*12967Sgavin.maltby@oracle.com 	char		*path;
4868740SSean.Ye@Sun.COM 	long		suffix;
4878740SSean.Ye@Sun.COM 	struct loglink	*next;
4888740SSean.Ye@Sun.COM };
4898740SSean.Ye@Sun.COM 
4908740SSean.Ye@Sun.COM static void
addlink(struct loglink ** llp,char * dirname,char * logname,long suffix)4918740SSean.Ye@Sun.COM addlink(struct loglink **llp, char *dirname, char *logname, long suffix)
4928740SSean.Ye@Sun.COM {
4938740SSean.Ye@Sun.COM 	struct loglink *newp;
4948740SSean.Ye@Sun.COM 	size_t len;
4958740SSean.Ye@Sun.COM 	char *str;
4968740SSean.Ye@Sun.COM 
4978740SSean.Ye@Sun.COM 	newp = malloc(sizeof (struct loglink));
4988740SSean.Ye@Sun.COM 	len = strlen(dirname) + strlen(logname) + 2;
4998740SSean.Ye@Sun.COM 	str = malloc(len);
500*12967Sgavin.maltby@oracle.com 	if (newp == NULL || str == NULL)
501*12967Sgavin.maltby@oracle.com 		fmdump_fatal("failed to allocate memory");
5028740SSean.Ye@Sun.COM 
5038740SSean.Ye@Sun.COM 	(void) snprintf(str, len, "%s/%s", dirname, logname);
5048740SSean.Ye@Sun.COM 	newp->path = str;
5058740SSean.Ye@Sun.COM 	newp->suffix = suffix;
5068740SSean.Ye@Sun.COM 
5078740SSean.Ye@Sun.COM 	while (*llp != NULL && suffix < (*llp)->suffix)
5088740SSean.Ye@Sun.COM 		llp = &(*llp)->next;
5098740SSean.Ye@Sun.COM 
5108740SSean.Ye@Sun.COM 	newp->next = *llp;
5118740SSean.Ye@Sun.COM 	*llp = newp;
5128740SSean.Ye@Sun.COM }
5138740SSean.Ye@Sun.COM 
5148740SSean.Ye@Sun.COM /*
5158740SSean.Ye@Sun.COM  * Find and return all the rotated logs.
5168740SSean.Ye@Sun.COM  */
5178740SSean.Ye@Sun.COM static struct loglink *
get_rotated_logs(char * logpath)5188740SSean.Ye@Sun.COM get_rotated_logs(char *logpath)
5198740SSean.Ye@Sun.COM {
5208740SSean.Ye@Sun.COM 	char dirname[PATH_MAX], *logname, *endptr;
5218740SSean.Ye@Sun.COM 	DIR *dirp;
5228740SSean.Ye@Sun.COM 	struct dirent *dp;
5238740SSean.Ye@Sun.COM 	long len, suffix;
5248740SSean.Ye@Sun.COM 	struct loglink *head = NULL;
5258740SSean.Ye@Sun.COM 
5268740SSean.Ye@Sun.COM 	(void) strlcpy(dirname, logpath, sizeof (dirname));
5278740SSean.Ye@Sun.COM 	logname = strrchr(dirname, '/');
5288740SSean.Ye@Sun.COM 	*logname++ = '\0';
5298740SSean.Ye@Sun.COM 	len = strlen(logname);
5308740SSean.Ye@Sun.COM 
5318740SSean.Ye@Sun.COM 	if ((dirp = opendir(dirname)) == NULL) {
532*12967Sgavin.maltby@oracle.com 		fmdump_warn("failed to opendir `%s'", dirname);
533*12967Sgavin.maltby@oracle.com 		g_errs++;
5348740SSean.Ye@Sun.COM 		return (NULL);
5358740SSean.Ye@Sun.COM 	}
5368740SSean.Ye@Sun.COM 
5378740SSean.Ye@Sun.COM 	while ((dp = readdir(dirp)) != NULL) {
5388740SSean.Ye@Sun.COM 		/*
5398740SSean.Ye@Sun.COM 		 * Search the log directory for logs named "<logname>.0",
5408740SSean.Ye@Sun.COM 		 * "<logname>.1", etc and add to the link in the
5418740SSean.Ye@Sun.COM 		 * reverse numeric order.
5428740SSean.Ye@Sun.COM 		 */
5438740SSean.Ye@Sun.COM 		if (strlen(dp->d_name) < len + 2 ||
5448740SSean.Ye@Sun.COM 		    strncmp(dp->d_name, logname, len) != 0 ||
5458740SSean.Ye@Sun.COM 		    dp->d_name[len] != '.')
5468740SSean.Ye@Sun.COM 			continue;
5478740SSean.Ye@Sun.COM 
5488740SSean.Ye@Sun.COM 		/*
5498740SSean.Ye@Sun.COM 		 * "*.0-" file normally should not be seen.  It may
5508740SSean.Ye@Sun.COM 		 * exist when user manually run 'fmadm rotate'.
5518740SSean.Ye@Sun.COM 		 * In such case, we put it at the end of the list so
5528740SSean.Ye@Sun.COM 		 * it'll be dumped after all the rotated logs, before
5538740SSean.Ye@Sun.COM 		 * the current one.
5548740SSean.Ye@Sun.COM 		 */
5558740SSean.Ye@Sun.COM 		if (strcmp(dp->d_name + len + 1, "0-") == 0)
5568740SSean.Ye@Sun.COM 			addlink(&head, dirname, dp->d_name, -1);
5578740SSean.Ye@Sun.COM 		else if ((suffix = strtol(dp->d_name + len + 1,
5588740SSean.Ye@Sun.COM 		    &endptr, 10)) >= 0 && *endptr == '\0')
5598740SSean.Ye@Sun.COM 			addlink(&head, dirname, dp->d_name, suffix);
5608740SSean.Ye@Sun.COM 	}
5618740SSean.Ye@Sun.COM 
5628740SSean.Ye@Sun.COM 	(void) closedir(dirp);
5638740SSean.Ye@Sun.COM 
5648740SSean.Ye@Sun.COM 	return (head);
5658740SSean.Ye@Sun.COM }
5668740SSean.Ye@Sun.COM 
567*12967Sgavin.maltby@oracle.com /*
568*12967Sgavin.maltby@oracle.com  * Aggregate log files.  If ifiles is not NULL then one or more files
569*12967Sgavin.maltby@oracle.com  * were listed on the command line, and we will merge just those files.
570*12967Sgavin.maltby@oracle.com  * Otherwise we will merge all known log file types, and include the
571*12967Sgavin.maltby@oracle.com  * rotated logs for each type (you can suppress the inclusion of
572*12967Sgavin.maltby@oracle.com  * some logtypes through use of FMDUMP_AGGREGATE_IGNORE in the process
573*12967Sgavin.maltby@oracle.com  * environment, setting it to a comma-separated list of log labels and/or
574*12967Sgavin.maltby@oracle.com  * log filenames to ignore).
575*12967Sgavin.maltby@oracle.com  *
576*12967Sgavin.maltby@oracle.com  * We will not attempt to perform a chronological sort across all log records
577*12967Sgavin.maltby@oracle.com  * of all files.  Indeed, we won't even sort individual log files -
578*12967Sgavin.maltby@oracle.com  * we will not re-order events differently to how they appeared in their
579*12967Sgavin.maltby@oracle.com  * original log file.  This is because log files are already inherently
580*12967Sgavin.maltby@oracle.com  * ordered by the order in which fmd receives and processes events.
581*12967Sgavin.maltby@oracle.com  * So we determine the output order by comparing the "next" record
582*12967Sgavin.maltby@oracle.com  * off the top of each log file.
583*12967Sgavin.maltby@oracle.com  *
584*12967Sgavin.maltby@oracle.com  * We will construct a number of log record source "pipelines".  As above,
585*12967Sgavin.maltby@oracle.com  * the next record to render in the overall output is that from the
586*12967Sgavin.maltby@oracle.com  * pipeline with the oldest event.
587*12967Sgavin.maltby@oracle.com  *
588*12967Sgavin.maltby@oracle.com  * For the case that input logfiles were listed on the command line, each
589*12967Sgavin.maltby@oracle.com  * pipeline will process exactly one of those logfiles.  Distinct pipelines
590*12967Sgavin.maltby@oracle.com  * may process logfiles of the same "type" - eg if two "error" logs and
591*12967Sgavin.maltby@oracle.com  * one "fault" logs are specified then there'll be two pipelines producing
592*12967Sgavin.maltby@oracle.com  * events from "error" logs.
593*12967Sgavin.maltby@oracle.com  *
594*12967Sgavin.maltby@oracle.com  * If we are merging all known log types then we will construct exactly
595*12967Sgavin.maltby@oracle.com  * one pipeline for each known log type - one for error, one for fault, etc.
596*12967Sgavin.maltby@oracle.com  * Each pipeline will process first the rotated logs of that type and then
597*12967Sgavin.maltby@oracle.com  * move on to the current log of that type.
598*12967Sgavin.maltby@oracle.com  *
599*12967Sgavin.maltby@oracle.com  * The output from all pipelines flows into a serializer which selects
600*12967Sgavin.maltby@oracle.com  * the next record once all pipelines have asserted their output state.
601*12967Sgavin.maltby@oracle.com  * The output state of a pipeline is one of:
602*12967Sgavin.maltby@oracle.com  *
603*12967Sgavin.maltby@oracle.com  *	- record available: the next record from this pipeline is available
604*12967Sgavin.maltby@oracle.com  *	  for comparison and consumption
605*12967Sgavin.maltby@oracle.com  *
606*12967Sgavin.maltby@oracle.com  *	- done: this pipeline will produce no more records
607*12967Sgavin.maltby@oracle.com  *
608*12967Sgavin.maltby@oracle.com  *	- polling: this pipeline is polling for new records and will
609*12967Sgavin.maltby@oracle.com  *	  make them available as output if/when any are observed
610*12967Sgavin.maltby@oracle.com  *
611*12967Sgavin.maltby@oracle.com  *	- processing: output state will be updated shortly
612*12967Sgavin.maltby@oracle.com  *
613*12967Sgavin.maltby@oracle.com  * A pipeline iterates over each file queued to it using fmd_log_xiter.
614*12967Sgavin.maltby@oracle.com  * We do this in a separate thread for each pipeline.  The callback on
615*12967Sgavin.maltby@oracle.com  * each iteration must update the serializer to let it know that
616*12967Sgavin.maltby@oracle.com  * a new record is available.  In the serializer thread we decide whether
617*12967Sgavin.maltby@oracle.com  * we have all records expected have arrived and it is time to choose
618*12967Sgavin.maltby@oracle.com  * the next output record.
619*12967Sgavin.maltby@oracle.com  */
620*12967Sgavin.maltby@oracle.com 
621*12967Sgavin.maltby@oracle.com /*
622*12967Sgavin.maltby@oracle.com  * A pipeline descriptor.  The pl_cv condition variable is used together
623*12967Sgavin.maltby@oracle.com  * with pl_lock for initial synchronisation, and thereafter with the
624*12967Sgavin.maltby@oracle.com  * lock for the serializer for pausing and continuing this pipeline.
625*12967Sgavin.maltby@oracle.com  */
626*12967Sgavin.maltby@oracle.com struct fmdump_pipeline {
627*12967Sgavin.maltby@oracle.com 	pthread_mutex_t pl_lock;	/* used only in pipeline startup */
628*12967Sgavin.maltby@oracle.com 	int pl_started;			/* sync with main thread on startup */
629*12967Sgavin.maltby@oracle.com 	pthread_t pl_thr;		/* our processing thread */
630*12967Sgavin.maltby@oracle.com 	pthread_cond_t pl_cv;		/* see above */
631*12967Sgavin.maltby@oracle.com 	struct loglink *pl_rotated;	/* rotated logs to process first */
632*12967Sgavin.maltby@oracle.com 	char *pl_logpath;		/* target path to process */
633*12967Sgavin.maltby@oracle.com 	char *pl_processing;		/* path currently being processed */
634*12967Sgavin.maltby@oracle.com 	struct fmdump_srlzer *pl_srlzer;	/* link to serializer */
635*12967Sgavin.maltby@oracle.com 	int pl_srlzeridx;		/* serializer index for this pipeline */
636*12967Sgavin.maltby@oracle.com 	const fmdump_ops_t *pl_ops;	/* ops for the log type we're given */
637*12967Sgavin.maltby@oracle.com 	int pl_fmt;			/* FMDUMP_{SHORT,VERB1,VERB2,PRETTY} */
638*12967Sgavin.maltby@oracle.com 	boolean_t pl_follow;		/* go into poll mode at log end */
639*12967Sgavin.maltby@oracle.com 	fmdump_arg_t pl_arg;		/* arguments */
640*12967Sgavin.maltby@oracle.com };
641*12967Sgavin.maltby@oracle.com 
642*12967Sgavin.maltby@oracle.com enum fmdump_pipestate {
643*12967Sgavin.maltby@oracle.com 	FMDUMP_PIPE_PROCESSING = 0x1000,
644*12967Sgavin.maltby@oracle.com 	FMDUMP_PIPE_RECORDAVAIL,
645*12967Sgavin.maltby@oracle.com 	FMDUMP_PIPE_POLLING,
646*12967Sgavin.maltby@oracle.com 	FMDUMP_PIPE_DONE
647*12967Sgavin.maltby@oracle.com };
648*12967Sgavin.maltby@oracle.com 
649*12967Sgavin.maltby@oracle.com /*
650*12967Sgavin.maltby@oracle.com  * Each pipeline has an associated output slot in the serializer.  This
651*12967Sgavin.maltby@oracle.com  * must be updated with the serializer locked.  After update evaluate
652*12967Sgavin.maltby@oracle.com  * whether there are enough slots decided that we should select a
653*12967Sgavin.maltby@oracle.com  * record to output.
654*12967Sgavin.maltby@oracle.com  */
655*12967Sgavin.maltby@oracle.com struct fmdump_srlzer_slot {
656*12967Sgavin.maltby@oracle.com 	enum fmdump_pipestate ss_state;
657*12967Sgavin.maltby@oracle.com 	uint64_t ss_sec;
658*12967Sgavin.maltby@oracle.com 	uint64_t ss_nsec;
659*12967Sgavin.maltby@oracle.com };
660*12967Sgavin.maltby@oracle.com 
661*12967Sgavin.maltby@oracle.com /*
662*12967Sgavin.maltby@oracle.com  * All pipelines are linked to a single serializer.  The serializer
663*12967Sgavin.maltby@oracle.com  * structure must be updated under the ds_lock; this mutex is also
664*12967Sgavin.maltby@oracle.com  * paired with the pl_cv of individual pipelines (one mutex, many condvars)
665*12967Sgavin.maltby@oracle.com  * in pausing and continuing individual pipelines.
666*12967Sgavin.maltby@oracle.com  */
667*12967Sgavin.maltby@oracle.com struct fmdump_srlzer {
668*12967Sgavin.maltby@oracle.com 	struct fmdump_pipeline *ds_pipearr;	/* pipeline array */
669*12967Sgavin.maltby@oracle.com 	pthread_mutex_t ds_lock;		/* see above */
670*12967Sgavin.maltby@oracle.com 	uint32_t ds_pipecnt;			/* number of pipelines */
671*12967Sgavin.maltby@oracle.com 	uint32_t ds_pollcnt;			/* pipelines in poll mode */
672*12967Sgavin.maltby@oracle.com 	uint32_t ds_nrecordavail;		/* pipelines with a record */
673*12967Sgavin.maltby@oracle.com 	uint32_t ds_ndone;			/* completed pipelines */
674*12967Sgavin.maltby@oracle.com 	struct fmdump_srlzer_slot *ds_slot;	/* slot array */
675*12967Sgavin.maltby@oracle.com };
676*12967Sgavin.maltby@oracle.com 
677*12967Sgavin.maltby@oracle.com /*
678*12967Sgavin.maltby@oracle.com  * All known log types.  When aggregation is requested an no file list
679*12967Sgavin.maltby@oracle.com  * is provided we will process the logs identified here (if lt_enabled
680*12967Sgavin.maltby@oracle.com  * is true and not over-ridden by environment settings).  We also
681*12967Sgavin.maltby@oracle.com  * use this in determining the appropriate ops structure for each distinct
682*12967Sgavin.maltby@oracle.com  * label.
683*12967Sgavin.maltby@oracle.com  */
684*12967Sgavin.maltby@oracle.com static struct fmdump_logtype {
685*12967Sgavin.maltby@oracle.com 	const char *lt_label;		/* label from log header */
686*12967Sgavin.maltby@oracle.com 	boolean_t lt_enabled;		/* include in merge? */
687*12967Sgavin.maltby@oracle.com 	const char *lt_logname;		/* var/fm/fmd/%s */
688*12967Sgavin.maltby@oracle.com 	const fmdump_ops_t *lt_ops;
689*12967Sgavin.maltby@oracle.com } logtypes[] = {
690*12967Sgavin.maltby@oracle.com 	{
691*12967Sgavin.maltby@oracle.com 		"error",
692*12967Sgavin.maltby@oracle.com 		B_TRUE,
693*12967Sgavin.maltby@oracle.com 		"errlog",
694*12967Sgavin.maltby@oracle.com 		&fmdump_err_ops
695*12967Sgavin.maltby@oracle.com 	},
696*12967Sgavin.maltby@oracle.com 	{
697*12967Sgavin.maltby@oracle.com 		"fault",
698*12967Sgavin.maltby@oracle.com 		B_TRUE,
699*12967Sgavin.maltby@oracle.com 		"fltlog",
700*12967Sgavin.maltby@oracle.com 		&fmdump_flt_ops
701*12967Sgavin.maltby@oracle.com 	},
702*12967Sgavin.maltby@oracle.com 	{
703*12967Sgavin.maltby@oracle.com 		"info",
704*12967Sgavin.maltby@oracle.com 		B_TRUE,
705*12967Sgavin.maltby@oracle.com 		"infolog",
706*12967Sgavin.maltby@oracle.com 		&fmdump_info_ops
707*12967Sgavin.maltby@oracle.com 	},
708*12967Sgavin.maltby@oracle.com 	{
709*12967Sgavin.maltby@oracle.com 		"info",
710*12967Sgavin.maltby@oracle.com 		B_TRUE,
711*12967Sgavin.maltby@oracle.com 		"infolog_hival",
712*12967Sgavin.maltby@oracle.com 		&fmdump_info_ops
713*12967Sgavin.maltby@oracle.com 	},
714*12967Sgavin.maltby@oracle.com 	{
715*12967Sgavin.maltby@oracle.com 		"asru",
716*12967Sgavin.maltby@oracle.com 		B_FALSE,		/* not included unless in file list */
717*12967Sgavin.maltby@oracle.com 		NULL,
718*12967Sgavin.maltby@oracle.com 		&fmdump_asru_ops	/* but we need ops when it is */
719*12967Sgavin.maltby@oracle.com 	}
720*12967Sgavin.maltby@oracle.com };
721*12967Sgavin.maltby@oracle.com 
722*12967Sgavin.maltby@oracle.com /*
723*12967Sgavin.maltby@oracle.com  * Disable logtypes per environment setting.  Does not apply when a list
724*12967Sgavin.maltby@oracle.com  * of logs is provided on the command line.
725*12967Sgavin.maltby@oracle.com  */
726*12967Sgavin.maltby@oracle.com static void
do_disables(void)727*12967Sgavin.maltby@oracle.com do_disables(void)
728*12967Sgavin.maltby@oracle.com {
729*12967Sgavin.maltby@oracle.com 	char *env = getenv("FMDUMP_AGGREGATE_IGNORE");
730*12967Sgavin.maltby@oracle.com 	char *dup, *start, *tofree;
731*12967Sgavin.maltby@oracle.com 	int i;
732*12967Sgavin.maltby@oracle.com 
733*12967Sgavin.maltby@oracle.com 	if (env == NULL)
734*12967Sgavin.maltby@oracle.com 		return;
735*12967Sgavin.maltby@oracle.com 
736*12967Sgavin.maltby@oracle.com 	tofree = dup = strdup(env);
737*12967Sgavin.maltby@oracle.com 
738*12967Sgavin.maltby@oracle.com 	while (dup != NULL) {
739*12967Sgavin.maltby@oracle.com 		start = strsep(&dup, ",");
740*12967Sgavin.maltby@oracle.com 		for (i = 0; i < sizeof (logtypes) / sizeof (logtypes[0]); i++) {
741*12967Sgavin.maltby@oracle.com 			if (logtypes[i].lt_logname == NULL)
742*12967Sgavin.maltby@oracle.com 				continue;
743*12967Sgavin.maltby@oracle.com 
744*12967Sgavin.maltby@oracle.com 			if (strcmp(start, logtypes[i].lt_label) == 0 ||
745*12967Sgavin.maltby@oracle.com 			    strcmp(start, logtypes[i].lt_logname) == 0) {
746*12967Sgavin.maltby@oracle.com 				logtypes[i].lt_enabled = B_FALSE;
747*12967Sgavin.maltby@oracle.com 			}
748*12967Sgavin.maltby@oracle.com 		}
749*12967Sgavin.maltby@oracle.com 	}
750*12967Sgavin.maltby@oracle.com 
751*12967Sgavin.maltby@oracle.com 	free(tofree);
752*12967Sgavin.maltby@oracle.com }
753*12967Sgavin.maltby@oracle.com 
754*12967Sgavin.maltby@oracle.com static void
srlzer_enter(struct fmdump_pipeline * pl)755*12967Sgavin.maltby@oracle.com srlzer_enter(struct fmdump_pipeline *pl)
756*12967Sgavin.maltby@oracle.com {
757*12967Sgavin.maltby@oracle.com 	struct fmdump_srlzer *srlzer = pl->pl_srlzer;
758*12967Sgavin.maltby@oracle.com 
759*12967Sgavin.maltby@oracle.com 	(void) pthread_mutex_lock(&srlzer->ds_lock);
760*12967Sgavin.maltby@oracle.com }
761*12967Sgavin.maltby@oracle.com 
762*12967Sgavin.maltby@oracle.com static void
srlzer_exit(struct fmdump_pipeline * pl)763*12967Sgavin.maltby@oracle.com srlzer_exit(struct fmdump_pipeline *pl)
764*12967Sgavin.maltby@oracle.com {
765*12967Sgavin.maltby@oracle.com 	struct fmdump_srlzer *srlzer = pl->pl_srlzer;
766*12967Sgavin.maltby@oracle.com 
767*12967Sgavin.maltby@oracle.com 	ASSERT(MUTEX_HELD(&srlzer->ds_lock));
768*12967Sgavin.maltby@oracle.com 	(void) pthread_mutex_unlock(&srlzer->ds_lock);
769*12967Sgavin.maltby@oracle.com }
770*12967Sgavin.maltby@oracle.com 
771*12967Sgavin.maltby@oracle.com static struct fmdump_pipeline *
srlzer_choose(struct fmdump_srlzer * srlzer)772*12967Sgavin.maltby@oracle.com srlzer_choose(struct fmdump_srlzer *srlzer)
773*12967Sgavin.maltby@oracle.com {
774*12967Sgavin.maltby@oracle.com 	struct fmdump_srlzer_slot *slot, *oldest;
775*12967Sgavin.maltby@oracle.com 	int oldestidx = -1;
776*12967Sgavin.maltby@oracle.com 	int first = 1;
777*12967Sgavin.maltby@oracle.com 	int i;
778*12967Sgavin.maltby@oracle.com 
779*12967Sgavin.maltby@oracle.com 	ASSERT(MUTEX_HELD(&srlzer->ds_lock));
780*12967Sgavin.maltby@oracle.com 
781*12967Sgavin.maltby@oracle.com 	for (i = 0, slot = &srlzer->ds_slot[0]; i < srlzer->ds_pipecnt;
782*12967Sgavin.maltby@oracle.com 	    i++, slot++) {
783*12967Sgavin.maltby@oracle.com 		if (slot->ss_state != FMDUMP_PIPE_RECORDAVAIL)
784*12967Sgavin.maltby@oracle.com 			continue;
785*12967Sgavin.maltby@oracle.com 
786*12967Sgavin.maltby@oracle.com 		if (first) {
787*12967Sgavin.maltby@oracle.com 			oldest = slot;
788*12967Sgavin.maltby@oracle.com 			oldestidx = i;
789*12967Sgavin.maltby@oracle.com 			first = 0;
790*12967Sgavin.maltby@oracle.com 			continue;
791*12967Sgavin.maltby@oracle.com 		}
792*12967Sgavin.maltby@oracle.com 
793*12967Sgavin.maltby@oracle.com 		if (slot->ss_sec < oldest->ss_sec ||
794*12967Sgavin.maltby@oracle.com 		    slot->ss_sec == oldest->ss_sec &&
795*12967Sgavin.maltby@oracle.com 		    slot->ss_nsec < oldest->ss_nsec) {
796*12967Sgavin.maltby@oracle.com 			oldest = slot;
797*12967Sgavin.maltby@oracle.com 			oldestidx = i;
798*12967Sgavin.maltby@oracle.com 		}
799*12967Sgavin.maltby@oracle.com 	}
800*12967Sgavin.maltby@oracle.com 
801*12967Sgavin.maltby@oracle.com 	return (oldestidx >= 0 ? &srlzer->ds_pipearr[oldestidx] : NULL);
802*12967Sgavin.maltby@oracle.com }
803*12967Sgavin.maltby@oracle.com 
804*12967Sgavin.maltby@oracle.com static void
pipeline_stall(struct fmdump_pipeline * pl)805*12967Sgavin.maltby@oracle.com pipeline_stall(struct fmdump_pipeline *pl)
806*12967Sgavin.maltby@oracle.com {
807*12967Sgavin.maltby@oracle.com 	struct fmdump_srlzer *srlzer = pl->pl_srlzer;
808*12967Sgavin.maltby@oracle.com 
809*12967Sgavin.maltby@oracle.com 	ASSERT(MUTEX_HELD(&srlzer->ds_lock));
810*12967Sgavin.maltby@oracle.com 	(void) pthread_cond_wait(&pl->pl_cv, &srlzer->ds_lock);
811*12967Sgavin.maltby@oracle.com }
812*12967Sgavin.maltby@oracle.com 
813*12967Sgavin.maltby@oracle.com static void
pipeline_continue(struct fmdump_pipeline * pl)814*12967Sgavin.maltby@oracle.com pipeline_continue(struct fmdump_pipeline *pl)
815*12967Sgavin.maltby@oracle.com {
816*12967Sgavin.maltby@oracle.com 	struct fmdump_srlzer *srlzer = pl->pl_srlzer;
817*12967Sgavin.maltby@oracle.com 
818*12967Sgavin.maltby@oracle.com 	ASSERT(MUTEX_HELD(&srlzer->ds_lock));
819*12967Sgavin.maltby@oracle.com 	(void) pthread_cond_signal(&srlzer->ds_pipearr[pl->pl_srlzeridx].pl_cv);
820*12967Sgavin.maltby@oracle.com }
821*12967Sgavin.maltby@oracle.com 
822*12967Sgavin.maltby@oracle.com /*
823*12967Sgavin.maltby@oracle.com  * Called on each pipeline record iteration to make a new record
824*12967Sgavin.maltby@oracle.com  * available for input to the serializer.  Returns 0 to indicate that
825*12967Sgavin.maltby@oracle.com  * the caller must stall the pipeline, or 1 to indicate that the
826*12967Sgavin.maltby@oracle.com  * caller should go ahead and render their record.  If this record
827*12967Sgavin.maltby@oracle.com  * addition fills the serializer then choose a pipeline that must
828*12967Sgavin.maltby@oracle.com  * render output.
829*12967Sgavin.maltby@oracle.com  */
830*12967Sgavin.maltby@oracle.com static int
pipeline_output(struct fmdump_pipeline * pl,const fmd_log_record_t * rp)831*12967Sgavin.maltby@oracle.com pipeline_output(struct fmdump_pipeline *pl, const fmd_log_record_t *rp)
832*12967Sgavin.maltby@oracle.com {
833*12967Sgavin.maltby@oracle.com 	struct fmdump_srlzer *srlzer = pl->pl_srlzer;
834*12967Sgavin.maltby@oracle.com 	struct fmdump_srlzer_slot *slot;
835*12967Sgavin.maltby@oracle.com 	struct fmdump_pipeline *wpl;
836*12967Sgavin.maltby@oracle.com 	int thisidx = pl->pl_srlzeridx;
837*12967Sgavin.maltby@oracle.com 
838*12967Sgavin.maltby@oracle.com 	ASSERT(MUTEX_HELD(&srlzer->ds_lock));
839*12967Sgavin.maltby@oracle.com 
840*12967Sgavin.maltby@oracle.com 	slot = &srlzer->ds_slot[thisidx];
841*12967Sgavin.maltby@oracle.com 	slot->ss_state = FMDUMP_PIPE_RECORDAVAIL;
842*12967Sgavin.maltby@oracle.com 	slot->ss_sec = rp->rec_sec;
843*12967Sgavin.maltby@oracle.com 	slot->ss_nsec = rp->rec_nsec;
844*12967Sgavin.maltby@oracle.com 	srlzer->ds_nrecordavail++;
845*12967Sgavin.maltby@oracle.com 
846*12967Sgavin.maltby@oracle.com 	/*
847*12967Sgavin.maltby@oracle.com 	 * Once all pipelines are polling we just render in arrival order.
848*12967Sgavin.maltby@oracle.com 	 */
849*12967Sgavin.maltby@oracle.com 	if (srlzer->ds_pollcnt == srlzer->ds_pipecnt)
850*12967Sgavin.maltby@oracle.com 		return (1);
851*12967Sgavin.maltby@oracle.com 
852*12967Sgavin.maltby@oracle.com 	/*
853*12967Sgavin.maltby@oracle.com 	 * If not all pipelines have asserted an output yet then the
854*12967Sgavin.maltby@oracle.com 	 * caller must block.
855*12967Sgavin.maltby@oracle.com 	 */
856*12967Sgavin.maltby@oracle.com 	if (srlzer->ds_nrecordavail + srlzer->ds_ndone +
857*12967Sgavin.maltby@oracle.com 	    srlzer->ds_pollcnt < srlzer->ds_pipecnt)
858*12967Sgavin.maltby@oracle.com 		return (0);
859*12967Sgavin.maltby@oracle.com 
860*12967Sgavin.maltby@oracle.com 	/*
861*12967Sgavin.maltby@oracle.com 	 * Right so it's time to turn the crank by choosing which of the
862*12967Sgavin.maltby@oracle.com 	 * filled line of slots should produce output.  If it is the slot
863*12967Sgavin.maltby@oracle.com 	 * for our caller then return their index to them, otherwise return
864*12967Sgavin.maltby@oracle.com 	 * -1 to the caller to make them block and cv_signal the winner.
865*12967Sgavin.maltby@oracle.com 	 */
866*12967Sgavin.maltby@oracle.com 	wpl = srlzer_choose(srlzer);
867*12967Sgavin.maltby@oracle.com 	ASSERT(wpl != NULL);
868*12967Sgavin.maltby@oracle.com 
869*12967Sgavin.maltby@oracle.com 	if (wpl == pl)
870*12967Sgavin.maltby@oracle.com 		return (1);
871*12967Sgavin.maltby@oracle.com 
872*12967Sgavin.maltby@oracle.com 	/* Wake the oldest, and return 0 to put the caller to sleep */
873*12967Sgavin.maltby@oracle.com 	pipeline_continue(wpl);
874*12967Sgavin.maltby@oracle.com 
875*12967Sgavin.maltby@oracle.com 	return (0);
876*12967Sgavin.maltby@oracle.com }
877*12967Sgavin.maltby@oracle.com 
878*12967Sgavin.maltby@oracle.com static void
pipeline_mark_consumed(struct fmdump_pipeline * pl)879*12967Sgavin.maltby@oracle.com pipeline_mark_consumed(struct fmdump_pipeline *pl)
880*12967Sgavin.maltby@oracle.com {
881*12967Sgavin.maltby@oracle.com 	struct fmdump_srlzer *srlzer = pl->pl_srlzer;
882*12967Sgavin.maltby@oracle.com 
883*12967Sgavin.maltby@oracle.com 	ASSERT(MUTEX_HELD(&srlzer->ds_lock));
884*12967Sgavin.maltby@oracle.com 	srlzer->ds_slot[pl->pl_srlzeridx].ss_state = FMDUMP_PIPE_PROCESSING;
885*12967Sgavin.maltby@oracle.com 	srlzer->ds_nrecordavail--;
886*12967Sgavin.maltby@oracle.com }
887*12967Sgavin.maltby@oracle.com 
888*12967Sgavin.maltby@oracle.com static void
pipeline_done(struct fmdump_pipeline * pl)889*12967Sgavin.maltby@oracle.com pipeline_done(struct fmdump_pipeline *pl)
890*12967Sgavin.maltby@oracle.com {
891*12967Sgavin.maltby@oracle.com 	struct fmdump_srlzer *srlzer = pl->pl_srlzer;
892*12967Sgavin.maltby@oracle.com 	struct fmdump_pipeline *wpl;
893*12967Sgavin.maltby@oracle.com 
894*12967Sgavin.maltby@oracle.com 	srlzer_enter(pl);
895*12967Sgavin.maltby@oracle.com 
896*12967Sgavin.maltby@oracle.com 	srlzer->ds_slot[pl->pl_srlzeridx].ss_state = FMDUMP_PIPE_DONE;
897*12967Sgavin.maltby@oracle.com 	srlzer->ds_ndone++;
898*12967Sgavin.maltby@oracle.com 	wpl = srlzer_choose(srlzer);
899*12967Sgavin.maltby@oracle.com 	if (wpl != NULL)
900*12967Sgavin.maltby@oracle.com 		pipeline_continue(wpl);
901*12967Sgavin.maltby@oracle.com 
902*12967Sgavin.maltby@oracle.com 	srlzer_exit(pl);
903*12967Sgavin.maltby@oracle.com }
904*12967Sgavin.maltby@oracle.com 
905*12967Sgavin.maltby@oracle.com static void
pipeline_pollmode(struct fmdump_pipeline * pl)906*12967Sgavin.maltby@oracle.com pipeline_pollmode(struct fmdump_pipeline *pl)
907*12967Sgavin.maltby@oracle.com {
908*12967Sgavin.maltby@oracle.com 	struct fmdump_srlzer *srlzer = pl->pl_srlzer;
909*12967Sgavin.maltby@oracle.com 	struct fmdump_pipeline *wpl;
910*12967Sgavin.maltby@oracle.com 
911*12967Sgavin.maltby@oracle.com 	if (srlzer->ds_slot[pl->pl_srlzeridx].ss_state == FMDUMP_PIPE_POLLING)
912*12967Sgavin.maltby@oracle.com 		return;
913*12967Sgavin.maltby@oracle.com 
914*12967Sgavin.maltby@oracle.com 	srlzer_enter(pl);
915*12967Sgavin.maltby@oracle.com 
916*12967Sgavin.maltby@oracle.com 	srlzer->ds_slot[pl->pl_srlzeridx].ss_state = FMDUMP_PIPE_POLLING;
917*12967Sgavin.maltby@oracle.com 	if (++srlzer->ds_pollcnt + srlzer->ds_nrecordavail ==
918*12967Sgavin.maltby@oracle.com 	    srlzer->ds_pipecnt && (wpl = srlzer_choose(srlzer)) != NULL)
919*12967Sgavin.maltby@oracle.com 		pipeline_continue(wpl);
920*12967Sgavin.maltby@oracle.com 
921*12967Sgavin.maltby@oracle.com 	srlzer_exit(pl);
922*12967Sgavin.maltby@oracle.com }
923*12967Sgavin.maltby@oracle.com 
924*12967Sgavin.maltby@oracle.com static int
pipeline_err(fmd_log_t * lp,void * arg)925*12967Sgavin.maltby@oracle.com pipeline_err(fmd_log_t *lp, void *arg)
926*12967Sgavin.maltby@oracle.com {
927*12967Sgavin.maltby@oracle.com 	struct fmdump_pipeline *pl = (struct fmdump_pipeline *)arg;
928*12967Sgavin.maltby@oracle.com 
929*12967Sgavin.maltby@oracle.com 	fmdump_warn("skipping record in %s: %s\n", pl->pl_processing,
930*12967Sgavin.maltby@oracle.com 	    fmd_log_errmsg(lp, fmd_log_errno(lp)));
931*12967Sgavin.maltby@oracle.com 	g_errs++;
932*12967Sgavin.maltby@oracle.com 
933*12967Sgavin.maltby@oracle.com 	return (0);
934*12967Sgavin.maltby@oracle.com }
935*12967Sgavin.maltby@oracle.com 
936*12967Sgavin.maltby@oracle.com static int
pipeline_cb(fmd_log_t * lp,const fmd_log_record_t * rp,void * arg)937*12967Sgavin.maltby@oracle.com pipeline_cb(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg)
938*12967Sgavin.maltby@oracle.com {
939*12967Sgavin.maltby@oracle.com 	struct fmdump_pipeline *pl = (struct fmdump_pipeline *)arg;
940*12967Sgavin.maltby@oracle.com 	int rc;
941*12967Sgavin.maltby@oracle.com 
942*12967Sgavin.maltby@oracle.com 	fmd_log_rec_f *func = pl->pl_arg.da_fmt->do_func;
943*12967Sgavin.maltby@oracle.com 
944*12967Sgavin.maltby@oracle.com 	srlzer_enter(pl);
945*12967Sgavin.maltby@oracle.com 
946*12967Sgavin.maltby@oracle.com 	if (!pipeline_output(pl, rp))
947*12967Sgavin.maltby@oracle.com 		pipeline_stall(pl);
948*12967Sgavin.maltby@oracle.com 
949*12967Sgavin.maltby@oracle.com 	rc = func(lp, rp, pl->pl_arg.da_fp);
950*12967Sgavin.maltby@oracle.com 	pipeline_mark_consumed(pl);
951*12967Sgavin.maltby@oracle.com 
952*12967Sgavin.maltby@oracle.com 	srlzer_exit(pl);
953*12967Sgavin.maltby@oracle.com 
954*12967Sgavin.maltby@oracle.com 	return (rc);
955*12967Sgavin.maltby@oracle.com }
956*12967Sgavin.maltby@oracle.com 
957*12967Sgavin.maltby@oracle.com static void
pipeline_process(struct fmdump_pipeline * pl,char * logpath,boolean_t follow)958*12967Sgavin.maltby@oracle.com pipeline_process(struct fmdump_pipeline *pl, char *logpath, boolean_t follow)
959*12967Sgavin.maltby@oracle.com {
960*12967Sgavin.maltby@oracle.com 	fmd_log_header_t log;
961*12967Sgavin.maltby@oracle.com 	fmd_log_t *lp;
962*12967Sgavin.maltby@oracle.com 	int err;
963*12967Sgavin.maltby@oracle.com 	int i;
964*12967Sgavin.maltby@oracle.com 
965*12967Sgavin.maltby@oracle.com 	pl->pl_processing = logpath;
966*12967Sgavin.maltby@oracle.com 
967*12967Sgavin.maltby@oracle.com 	if ((lp = fmd_log_open(FMD_LOG_VERSION, logpath, &err)) == NULL) {
968*12967Sgavin.maltby@oracle.com 		fmdump_warn("failed to open %s: %s\n",
969*12967Sgavin.maltby@oracle.com 		    logpath, fmd_log_errmsg(NULL, err));
970*12967Sgavin.maltby@oracle.com 		g_errs++;
971*12967Sgavin.maltby@oracle.com 		return;
972*12967Sgavin.maltby@oracle.com 	}
973*12967Sgavin.maltby@oracle.com 
974*12967Sgavin.maltby@oracle.com 	fmd_log_header(lp, &log);
975*12967Sgavin.maltby@oracle.com 	for (i = 0; i < sizeof (logtypes) / sizeof (logtypes[0]); i++) {
976*12967Sgavin.maltby@oracle.com 		if (strcmp(log.log_label, logtypes[i].lt_label) == 0) {
977*12967Sgavin.maltby@oracle.com 			pl->pl_ops = logtypes[i].lt_ops;
978*12967Sgavin.maltby@oracle.com 			pl->pl_arg.da_fmt =
979*12967Sgavin.maltby@oracle.com 			    &pl->pl_ops->do_formats[pl->pl_fmt];
980*12967Sgavin.maltby@oracle.com 			break;
981*12967Sgavin.maltby@oracle.com 		}
982*12967Sgavin.maltby@oracle.com 	}
983*12967Sgavin.maltby@oracle.com 
984*12967Sgavin.maltby@oracle.com 	if (pl->pl_ops == NULL) {
985*12967Sgavin.maltby@oracle.com 		fmdump_warn("unknown log type %s for %s\n",
986*12967Sgavin.maltby@oracle.com 		    log.log_label, logpath);
987*12967Sgavin.maltby@oracle.com 		g_errs++;
988*12967Sgavin.maltby@oracle.com 		return;
989*12967Sgavin.maltby@oracle.com 	}
990*12967Sgavin.maltby@oracle.com 
991*12967Sgavin.maltby@oracle.com 	do {
992*12967Sgavin.maltby@oracle.com 		if (fmd_log_xiter(lp, FMD_LOG_XITER_REFS, pl->pl_arg.da_fc,
993*12967Sgavin.maltby@oracle.com 		    pl->pl_arg.da_fv, pipeline_cb, pipeline_err, (void *)pl,
994*12967Sgavin.maltby@oracle.com 		    NULL) != 0) {
995*12967Sgavin.maltby@oracle.com 			fmdump_warn("failed to dump %s: %s\n",
996*12967Sgavin.maltby@oracle.com 			    logpath, fmd_log_errmsg(lp, fmd_log_errno(lp)));
997*12967Sgavin.maltby@oracle.com 			g_errs++;
998*12967Sgavin.maltby@oracle.com 			fmd_log_close(lp);
999*12967Sgavin.maltby@oracle.com 			return;
1000*12967Sgavin.maltby@oracle.com 		}
1001*12967Sgavin.maltby@oracle.com 
1002*12967Sgavin.maltby@oracle.com 		if (follow) {
1003*12967Sgavin.maltby@oracle.com 			pipeline_pollmode(pl);
1004*12967Sgavin.maltby@oracle.com 			(void) sleep(1);
1005*12967Sgavin.maltby@oracle.com 		}
1006*12967Sgavin.maltby@oracle.com 
1007*12967Sgavin.maltby@oracle.com 	} while (follow);
1008*12967Sgavin.maltby@oracle.com 
1009*12967Sgavin.maltby@oracle.com 	fmd_log_close(lp);
1010*12967Sgavin.maltby@oracle.com }
1011*12967Sgavin.maltby@oracle.com 
1012*12967Sgavin.maltby@oracle.com static void *
pipeline_thr(void * arg)1013*12967Sgavin.maltby@oracle.com pipeline_thr(void *arg)
1014*12967Sgavin.maltby@oracle.com {
1015*12967Sgavin.maltby@oracle.com 	struct fmdump_pipeline *pl = (struct fmdump_pipeline *)arg;
1016*12967Sgavin.maltby@oracle.com 	struct loglink *ll;
1017*12967Sgavin.maltby@oracle.com 
1018*12967Sgavin.maltby@oracle.com 	(void) pthread_mutex_lock(&pl->pl_lock);
1019*12967Sgavin.maltby@oracle.com 	pl->pl_started = 1;
1020*12967Sgavin.maltby@oracle.com 	(void) pthread_mutex_unlock(&pl->pl_lock);
1021*12967Sgavin.maltby@oracle.com 	(void) pthread_cond_signal(&pl->pl_cv);
1022*12967Sgavin.maltby@oracle.com 
1023*12967Sgavin.maltby@oracle.com 	for (ll = pl->pl_rotated; ll != NULL; ll = ll->next)
1024*12967Sgavin.maltby@oracle.com 		pipeline_process(pl, ll->path, B_FALSE);
1025*12967Sgavin.maltby@oracle.com 
1026*12967Sgavin.maltby@oracle.com 	pipeline_process(pl, pl->pl_logpath, pl->pl_follow);
1027*12967Sgavin.maltby@oracle.com 	pipeline_done(pl);
1028*12967Sgavin.maltby@oracle.com 
1029*12967Sgavin.maltby@oracle.com 	return (NULL);
1030*12967Sgavin.maltby@oracle.com }
1031*12967Sgavin.maltby@oracle.com 
1032*12967Sgavin.maltby@oracle.com 
1033*12967Sgavin.maltby@oracle.com static int
aggregate(char ** ifiles,int n_ifiles,int opt_f,fmd_log_filter_t * fv,uint_t fc,int opt_v,int opt_V,int opt_p)1034*12967Sgavin.maltby@oracle.com aggregate(char **ifiles, int n_ifiles, int opt_f,
1035*12967Sgavin.maltby@oracle.com     fmd_log_filter_t *fv, uint_t fc,
1036*12967Sgavin.maltby@oracle.com     int opt_v, int opt_V, int opt_p)
1037*12967Sgavin.maltby@oracle.com {
1038*12967Sgavin.maltby@oracle.com 	struct fmdump_pipeline *pipeline, *pl;
1039*12967Sgavin.maltby@oracle.com 	struct fmdump_srlzer srlzer;
1040*12967Sgavin.maltby@oracle.com 	uint32_t npipe;
1041*12967Sgavin.maltby@oracle.com 	int fmt;
1042*12967Sgavin.maltby@oracle.com 	int i;
1043*12967Sgavin.maltby@oracle.com 
1044*12967Sgavin.maltby@oracle.com 	if (ifiles != NULL) {
1045*12967Sgavin.maltby@oracle.com 		npipe = n_ifiles;
1046*12967Sgavin.maltby@oracle.com 		pipeline = calloc(npipe, sizeof (struct fmdump_pipeline));
1047*12967Sgavin.maltby@oracle.com 		if (!pipeline)
1048*12967Sgavin.maltby@oracle.com 			fmdump_fatal("failed to allocate memory");
1049*12967Sgavin.maltby@oracle.com 
1050*12967Sgavin.maltby@oracle.com 		for (i = 0; i < n_ifiles; i++)
1051*12967Sgavin.maltby@oracle.com 			pipeline[i].pl_logpath = ifiles[i];
1052*12967Sgavin.maltby@oracle.com 	} else {
1053*12967Sgavin.maltby@oracle.com 		pipeline = calloc(sizeof (logtypes) / sizeof (logtypes[0]),
1054*12967Sgavin.maltby@oracle.com 		    sizeof (struct fmdump_pipeline));
1055*12967Sgavin.maltby@oracle.com 		if (!pipeline)
1056*12967Sgavin.maltby@oracle.com 			fmdump_fatal("failed to allocate memory");
1057*12967Sgavin.maltby@oracle.com 
1058*12967Sgavin.maltby@oracle.com 		do_disables();
1059*12967Sgavin.maltby@oracle.com 
1060*12967Sgavin.maltby@oracle.com 		npipe = 0;
1061*12967Sgavin.maltby@oracle.com 		for (i = 0; i < sizeof (logtypes) / sizeof (logtypes[0]); i++) {
1062*12967Sgavin.maltby@oracle.com 			struct fmdump_logtype *ltp = &logtypes[i];
1063*12967Sgavin.maltby@oracle.com 			char *logpath;
1064*12967Sgavin.maltby@oracle.com 
1065*12967Sgavin.maltby@oracle.com 			if (ltp->lt_enabled == B_FALSE)
1066*12967Sgavin.maltby@oracle.com 				continue;
1067*12967Sgavin.maltby@oracle.com 
1068*12967Sgavin.maltby@oracle.com 			if ((logpath = malloc(PATH_MAX)) == NULL)
1069*12967Sgavin.maltby@oracle.com 				fmdump_fatal("failed to allocate memory");
1070*12967Sgavin.maltby@oracle.com 
1071*12967Sgavin.maltby@oracle.com 			(void) snprintf(logpath, PATH_MAX,
1072*12967Sgavin.maltby@oracle.com 			    "%s/var/fm/fmd/%s",
1073*12967Sgavin.maltby@oracle.com 			    g_root ? g_root : "", ltp->lt_logname);
1074*12967Sgavin.maltby@oracle.com 
1075*12967Sgavin.maltby@oracle.com 			pipeline[npipe].pl_rotated =
1076*12967Sgavin.maltby@oracle.com 			    get_rotated_logs(logpath);
1077*12967Sgavin.maltby@oracle.com 
1078*12967Sgavin.maltby@oracle.com 			pipeline[npipe++].pl_logpath = logpath;
1079*12967Sgavin.maltby@oracle.com 		}
1080*12967Sgavin.maltby@oracle.com 	}
1081*12967Sgavin.maltby@oracle.com 
1082*12967Sgavin.maltby@oracle.com 	if (opt_V)
1083*12967Sgavin.maltby@oracle.com 		fmt = opt_p ? FMDUMP_PRETTY : FMDUMP_VERB2;
1084*12967Sgavin.maltby@oracle.com 	else if (opt_v)
1085*12967Sgavin.maltby@oracle.com 		fmt = FMDUMP_VERB1;
1086*12967Sgavin.maltby@oracle.com 	else
1087*12967Sgavin.maltby@oracle.com 		fmt = FMDUMP_SHORT;
1088*12967Sgavin.maltby@oracle.com 
1089*12967Sgavin.maltby@oracle.com 	bzero(&srlzer, sizeof (srlzer));
1090*12967Sgavin.maltby@oracle.com 	srlzer.ds_pipearr = pipeline;
1091*12967Sgavin.maltby@oracle.com 	srlzer.ds_pipecnt = npipe;
1092*12967Sgavin.maltby@oracle.com 	srlzer.ds_slot = calloc(npipe, sizeof (struct fmdump_srlzer_slot));
1093*12967Sgavin.maltby@oracle.com 	if (!srlzer.ds_slot)
1094*12967Sgavin.maltby@oracle.com 		fmdump_fatal("failed to allocate memory");
1095*12967Sgavin.maltby@oracle.com 	(void) pthread_mutex_init(&srlzer.ds_lock, NULL);
1096*12967Sgavin.maltby@oracle.com 
1097*12967Sgavin.maltby@oracle.com 	for (i = 0, pl = &pipeline[0]; i < npipe; i++, pl++) {
1098*12967Sgavin.maltby@oracle.com 		(void) pthread_mutex_init(&pl->pl_lock, NULL);
1099*12967Sgavin.maltby@oracle.com 		(void) pthread_cond_init(&pl->pl_cv, NULL);
1100*12967Sgavin.maltby@oracle.com 		srlzer.ds_slot[i].ss_state = FMDUMP_PIPE_PROCESSING;
1101*12967Sgavin.maltby@oracle.com 		pl->pl_srlzer = &srlzer;
1102*12967Sgavin.maltby@oracle.com 		pl->pl_srlzeridx = i;
1103*12967Sgavin.maltby@oracle.com 		pl->pl_follow = opt_f ? B_TRUE : B_FALSE;
1104*12967Sgavin.maltby@oracle.com 		pl->pl_fmt = fmt;
1105*12967Sgavin.maltby@oracle.com 		pl->pl_arg.da_fv = fv;
1106*12967Sgavin.maltby@oracle.com 		pl->pl_arg.da_fc = fc;
1107*12967Sgavin.maltby@oracle.com 		pl->pl_arg.da_fp = stdout;
1108*12967Sgavin.maltby@oracle.com 
1109*12967Sgavin.maltby@oracle.com 		(void) pthread_mutex_lock(&pl->pl_lock);
1110*12967Sgavin.maltby@oracle.com 
1111*12967Sgavin.maltby@oracle.com 		if (pthread_create(&pl->pl_thr, NULL,
1112*12967Sgavin.maltby@oracle.com 		    pipeline_thr, (void *)pl) != 0)
1113*12967Sgavin.maltby@oracle.com 			fmdump_fatal("pthread_create for pipeline %d failed",
1114*12967Sgavin.maltby@oracle.com 			    i);
1115*12967Sgavin.maltby@oracle.com 	}
1116*12967Sgavin.maltby@oracle.com 
1117*12967Sgavin.maltby@oracle.com 	for (i = 0, pl = &pipeline[0]; i < npipe; i++, pl++) {
1118*12967Sgavin.maltby@oracle.com 		while (!pl->pl_started)
1119*12967Sgavin.maltby@oracle.com 			(void) pthread_cond_wait(&pl->pl_cv, &pl->pl_lock);
1120*12967Sgavin.maltby@oracle.com 
1121*12967Sgavin.maltby@oracle.com 		(void) pthread_mutex_unlock(&pl->pl_lock);
1122*12967Sgavin.maltby@oracle.com 	}
1123*12967Sgavin.maltby@oracle.com 
1124*12967Sgavin.maltby@oracle.com 	for (i = 0, pl = &pipeline[0]; i < npipe; i++, pl++)
1125*12967Sgavin.maltby@oracle.com 		(void) pthread_join(pl->pl_thr, NULL);
1126*12967Sgavin.maltby@oracle.com 
1127*12967Sgavin.maltby@oracle.com 	if (ifiles == NULL) {
1128*12967Sgavin.maltby@oracle.com 		for (i = 0; i < npipe; i++)
1129*12967Sgavin.maltby@oracle.com 			free(pipeline[i].pl_logpath);
1130*12967Sgavin.maltby@oracle.com 	}
1131*12967Sgavin.maltby@oracle.com 
1132*12967Sgavin.maltby@oracle.com 	free(srlzer.ds_slot);
1133*12967Sgavin.maltby@oracle.com 
1134*12967Sgavin.maltby@oracle.com 	free(pipeline);
1135*12967Sgavin.maltby@oracle.com 
1136*12967Sgavin.maltby@oracle.com 	return (FMDUMP_EXIT_SUCCESS);
1137*12967Sgavin.maltby@oracle.com }
1138*12967Sgavin.maltby@oracle.com 
1139*12967Sgavin.maltby@oracle.com static void
cleanup(char ** ifiles,int n_ifiles)1140*12967Sgavin.maltby@oracle.com cleanup(char **ifiles, int n_ifiles)
1141*12967Sgavin.maltby@oracle.com {
1142*12967Sgavin.maltby@oracle.com 	int i;
1143*12967Sgavin.maltby@oracle.com 
1144*12967Sgavin.maltby@oracle.com 	if (ifiles == NULL)
1145*12967Sgavin.maltby@oracle.com 		return;
1146*12967Sgavin.maltby@oracle.com 
1147*12967Sgavin.maltby@oracle.com 	for (i = 0; i < n_ifiles; i++) {
1148*12967Sgavin.maltby@oracle.com 		if (ifiles[i] != NULL) {
1149*12967Sgavin.maltby@oracle.com 			free(ifiles[i]);
1150*12967Sgavin.maltby@oracle.com 			ifiles[i] = NULL;
1151*12967Sgavin.maltby@oracle.com 		}
1152*12967Sgavin.maltby@oracle.com 	}
1153*12967Sgavin.maltby@oracle.com 
1154*12967Sgavin.maltby@oracle.com 	free(ifiles);
1155*12967Sgavin.maltby@oracle.com }
1156*12967Sgavin.maltby@oracle.com 
11570Sstevel@tonic-gate int
main(int argc,char * argv[])11580Sstevel@tonic-gate main(int argc, char *argv[])
11590Sstevel@tonic-gate {
1160*12967Sgavin.maltby@oracle.com 	int opt_a = 0, opt_e = 0, opt_f = 0, opt_H = 0, opt_m = 0, opt_p = 0;
11610Sstevel@tonic-gate 	int opt_u = 0, opt_v = 0, opt_V = 0;
1162*12967Sgavin.maltby@oracle.com 	int opt_i = 0, opt_I = 0;
1163*12967Sgavin.maltby@oracle.com 	int opt_A = 0;
1164*12967Sgavin.maltby@oracle.com 	char **ifiles = NULL;
1165*12967Sgavin.maltby@oracle.com 	char *ifile = NULL;
1166*12967Sgavin.maltby@oracle.com 	int n_ifiles;
1167*12967Sgavin.maltby@oracle.com 	int ifileidx = 0;
11680Sstevel@tonic-gate 	int iflags = 0;
11690Sstevel@tonic-gate 
11700Sstevel@tonic-gate 	fmdump_arg_t arg;
11710Sstevel@tonic-gate 	fmdump_lyr_t lyr;
11720Sstevel@tonic-gate 	const fmdump_ops_t *ops;
11730Sstevel@tonic-gate 	fmd_log_filter_t *filtv;
11740Sstevel@tonic-gate 	uint_t filtc;
11750Sstevel@tonic-gate 
11760Sstevel@tonic-gate 	fmd_log_filter_t *errfv, *fltfv, *allfv;
11770Sstevel@tonic-gate 	uint_t errfc = 0, fltfc = 0, allfc = 0;
11780Sstevel@tonic-gate 
11790Sstevel@tonic-gate 	fmd_log_header_t log;
11800Sstevel@tonic-gate 	fmd_log_rec_f *func;
11810Sstevel@tonic-gate 	void *farg;
11820Sstevel@tonic-gate 	fmd_log_t *lp;
11830Sstevel@tonic-gate 	int c, err;
11840Sstevel@tonic-gate 	off64_t off = 0;
11858740SSean.Ye@Sun.COM 	ulong_t recs;
11868740SSean.Ye@Sun.COM 	struct loglink *rotated_logs = NULL, *llp;
11870Sstevel@tonic-gate 
11880Sstevel@tonic-gate 	g_pname = argv[0];
11890Sstevel@tonic-gate 
11900Sstevel@tonic-gate 	errfv = alloca(sizeof (fmd_log_filter_t) * argc);
11910Sstevel@tonic-gate 	fltfv = alloca(sizeof (fmd_log_filter_t) * argc);
11920Sstevel@tonic-gate 	allfv = alloca(sizeof (fmd_log_filter_t) * argc);
11930Sstevel@tonic-gate 
11940Sstevel@tonic-gate 	while (optind < argc) {
11956640Scth 		while ((c =
1196*12967Sgavin.maltby@oracle.com 		    getopt(argc, argv, "Aac:efHiImn:O:pR:t:T:u:vV")) != EOF) {
11970Sstevel@tonic-gate 			switch (c) {
1198*12967Sgavin.maltby@oracle.com 			case 'A':
1199*12967Sgavin.maltby@oracle.com 				opt_A++;
1200*12967Sgavin.maltby@oracle.com 				break;
12010Sstevel@tonic-gate 			case 'a':
12020Sstevel@tonic-gate 				opt_a++;
12030Sstevel@tonic-gate 				break;
12040Sstevel@tonic-gate 			case 'c':
12050Sstevel@tonic-gate 				errfv[errfc].filt_func = fmd_log_filter_class;
12060Sstevel@tonic-gate 				errfv[errfc].filt_arg = optarg;
12070Sstevel@tonic-gate 				allfv[allfc++] = errfv[errfc++];
12080Sstevel@tonic-gate 				break;
12090Sstevel@tonic-gate 			case 'e':
1210*12967Sgavin.maltby@oracle.com 				if (opt_i)
1211*12967Sgavin.maltby@oracle.com 					return (usage(stderr));
12120Sstevel@tonic-gate 				opt_e++;
12130Sstevel@tonic-gate 				break;
12140Sstevel@tonic-gate 			case 'f':
12150Sstevel@tonic-gate 				opt_f++;
12160Sstevel@tonic-gate 				break;
12170Sstevel@tonic-gate 			case 'H':
12180Sstevel@tonic-gate 				opt_H++;
12190Sstevel@tonic-gate 				break;
1220*12967Sgavin.maltby@oracle.com 			case 'i':
1221*12967Sgavin.maltby@oracle.com 				if (opt_e || opt_I)
1222*12967Sgavin.maltby@oracle.com 					return (usage(stderr));
1223*12967Sgavin.maltby@oracle.com 				opt_i++;
1224*12967Sgavin.maltby@oracle.com 				break;
1225*12967Sgavin.maltby@oracle.com 			case 'I':
1226*12967Sgavin.maltby@oracle.com 				if (opt_e || opt_i)
1227*12967Sgavin.maltby@oracle.com 					return (usage(stderr));
1228*12967Sgavin.maltby@oracle.com 				opt_I++;
1229*12967Sgavin.maltby@oracle.com 				break;
12309501SRobert.Johnston@Sun.COM 			case 'm':
12319501SRobert.Johnston@Sun.COM 				opt_m++;
12329501SRobert.Johnston@Sun.COM 				break;
12330Sstevel@tonic-gate 			case 'O':
12340Sstevel@tonic-gate 				off = strtoull(optarg, NULL, 16);
12350Sstevel@tonic-gate 				iflags |= FMD_LOG_XITER_OFFS;
12360Sstevel@tonic-gate 				break;
1237*12967Sgavin.maltby@oracle.com 			case 'p':
1238*12967Sgavin.maltby@oracle.com 				opt_p++;
1239*12967Sgavin.maltby@oracle.com 				break;
12400Sstevel@tonic-gate 			case 'R':
12410Sstevel@tonic-gate 				g_root = optarg;
12420Sstevel@tonic-gate 				break;
12430Sstevel@tonic-gate 			case 't':
12440Sstevel@tonic-gate 				errfv[errfc].filt_func = fmd_log_filter_after;
12450Sstevel@tonic-gate 				errfv[errfc].filt_arg = gettimeopt(optarg);
12460Sstevel@tonic-gate 				allfv[allfc++] = errfv[errfc++];
12470Sstevel@tonic-gate 				break;
12480Sstevel@tonic-gate 			case 'T':
12490Sstevel@tonic-gate 				errfv[errfc].filt_func = fmd_log_filter_before;
12500Sstevel@tonic-gate 				errfv[errfc].filt_arg = gettimeopt(optarg);
12510Sstevel@tonic-gate 				allfv[allfc++] = errfv[errfc++];
12520Sstevel@tonic-gate 				break;
12530Sstevel@tonic-gate 			case 'u':
12540Sstevel@tonic-gate 				fltfv[fltfc].filt_func = fmd_log_filter_uuid;
12550Sstevel@tonic-gate 				fltfv[fltfc].filt_arg = optarg;
12560Sstevel@tonic-gate 				allfv[allfc++] = fltfv[fltfc++];
12570Sstevel@tonic-gate 				opt_u++;
12580Sstevel@tonic-gate 				opt_a++; /* -u implies -a */
12590Sstevel@tonic-gate 				break;
12606640Scth 			case 'n': {
12616640Scth 				fltfv[fltfc].filt_func = fmd_log_filter_nv;
12626640Scth 				fltfv[fltfc].filt_arg = setupnamevalue(optarg);
12636640Scth 				allfv[allfc++] = fltfv[fltfc++];
12646640Scth 				break;
12656640Scth 			}
12660Sstevel@tonic-gate 			case 'v':
12670Sstevel@tonic-gate 				opt_v++;
12680Sstevel@tonic-gate 				break;
12690Sstevel@tonic-gate 			case 'V':
12700Sstevel@tonic-gate 				opt_V++;
12710Sstevel@tonic-gate 				break;
12720Sstevel@tonic-gate 			default:
12730Sstevel@tonic-gate 				return (usage(stderr));
12740Sstevel@tonic-gate 			}
12750Sstevel@tonic-gate 		}
12760Sstevel@tonic-gate 
1277*12967Sgavin.maltby@oracle.com 		if (opt_A && (opt_e || opt_i || opt_I || opt_m || opt_u))
1278*12967Sgavin.maltby@oracle.com 			fmdump_usage("-A excludes all of "
1279*12967Sgavin.maltby@oracle.com 			    "-e, -i, -I, -m and -u\n");
1280*12967Sgavin.maltby@oracle.com 
12810Sstevel@tonic-gate 		if (optind < argc) {
1282*12967Sgavin.maltby@oracle.com 			char *dest;
1283*12967Sgavin.maltby@oracle.com 
1284*12967Sgavin.maltby@oracle.com 			if (ifiles == NULL) {
1285*12967Sgavin.maltby@oracle.com 				n_ifiles = argc - optind;
1286*12967Sgavin.maltby@oracle.com 				ifiles = calloc(n_ifiles, sizeof (char *));
1287*12967Sgavin.maltby@oracle.com 				if (ifiles == NULL) {
1288*12967Sgavin.maltby@oracle.com 					fmdump_fatal(
1289*12967Sgavin.maltby@oracle.com 					    "failed to allocate memory for "
1290*12967Sgavin.maltby@oracle.com 					    "%d input file%s", n_ifiles,
1291*12967Sgavin.maltby@oracle.com 					    n_ifiles > 1 ? "s" : "");
1292*12967Sgavin.maltby@oracle.com 				}
12930Sstevel@tonic-gate 			}
1294*12967Sgavin.maltby@oracle.com 
1295*12967Sgavin.maltby@oracle.com 			if (ifileidx > 0 && !opt_A)
1296*12967Sgavin.maltby@oracle.com 				fmdump_usage("illegal argument -- %s\n",
1297*12967Sgavin.maltby@oracle.com 				    argv[optind]);
1298*12967Sgavin.maltby@oracle.com 
1299*12967Sgavin.maltby@oracle.com 			if ((dest = malloc(PATH_MAX)) == NULL)
1300*12967Sgavin.maltby@oracle.com 				fmdump_fatal("failed to allocate memory");
1301*12967Sgavin.maltby@oracle.com 
1302*12967Sgavin.maltby@oracle.com 			(void) strlcpy(dest, argv[optind++], PATH_MAX);
1303*12967Sgavin.maltby@oracle.com 			ifiles[ifileidx++] = dest;
13040Sstevel@tonic-gate 		}
13050Sstevel@tonic-gate 	}
13060Sstevel@tonic-gate 
1307*12967Sgavin.maltby@oracle.com 	if (opt_A) {
1308*12967Sgavin.maltby@oracle.com 		int rc;
1309*12967Sgavin.maltby@oracle.com 
1310*12967Sgavin.maltby@oracle.com 		if (!opt_a) {
1311*12967Sgavin.maltby@oracle.com 			fltfv[fltfc].filt_func = log_filter_silent;
1312*12967Sgavin.maltby@oracle.com 			fltfv[fltfc].filt_arg = (void *)1;
1313*12967Sgavin.maltby@oracle.com 			allfv[allfc++] = fltfv[fltfc++];
1314*12967Sgavin.maltby@oracle.com 		}
1315*12967Sgavin.maltby@oracle.com 
1316*12967Sgavin.maltby@oracle.com 		rc = aggregate(ifiles, n_ifiles, opt_f,
1317*12967Sgavin.maltby@oracle.com 		    allfv, allfc,
1318*12967Sgavin.maltby@oracle.com 		    opt_v, opt_V, opt_p);
1319*12967Sgavin.maltby@oracle.com 
1320*12967Sgavin.maltby@oracle.com 		cleanup(ifiles, n_ifiles);
1321*12967Sgavin.maltby@oracle.com 		return (rc);
1322*12967Sgavin.maltby@oracle.com 	} else {
1323*12967Sgavin.maltby@oracle.com 		if (ifiles == NULL) {
1324*12967Sgavin.maltby@oracle.com 			if ((ifile = calloc(1, PATH_MAX)) == NULL)
1325*12967Sgavin.maltby@oracle.com 				fmdump_fatal("failed to allocate memory");
1326*12967Sgavin.maltby@oracle.com 		} else {
1327*12967Sgavin.maltby@oracle.com 			ifile = ifiles[0];
1328*12967Sgavin.maltby@oracle.com 		}
1329*12967Sgavin.maltby@oracle.com 	}
1330*12967Sgavin.maltby@oracle.com 
1331*12967Sgavin.maltby@oracle.com 
13320Sstevel@tonic-gate 	if (*ifile == '\0') {
1333*12967Sgavin.maltby@oracle.com 		const char *pfx, *sfx;
1334*12967Sgavin.maltby@oracle.com 
1335*12967Sgavin.maltby@oracle.com 		if (opt_u || (!opt_e && !opt_i && !opt_I)) {
1336*12967Sgavin.maltby@oracle.com 			pfx = "flt";
1337*12967Sgavin.maltby@oracle.com 			sfx = "";
1338*12967Sgavin.maltby@oracle.com 		} else {
1339*12967Sgavin.maltby@oracle.com 			if (opt_e) {
1340*12967Sgavin.maltby@oracle.com 				pfx = "err";
1341*12967Sgavin.maltby@oracle.com 				sfx = "";
1342*12967Sgavin.maltby@oracle.com 			} else {
1343*12967Sgavin.maltby@oracle.com 				pfx = "info";
1344*12967Sgavin.maltby@oracle.com 				sfx = opt_I ? "_hival" : "";
1345*12967Sgavin.maltby@oracle.com 			}
1346*12967Sgavin.maltby@oracle.com 		}
1347*12967Sgavin.maltby@oracle.com 
1348*12967Sgavin.maltby@oracle.com 		(void) snprintf(ifile, PATH_MAX, "%s/var/fm/fmd/%slog%s",
1349*12967Sgavin.maltby@oracle.com 		    g_root ? g_root : "", pfx, sfx);
13508740SSean.Ye@Sun.COM 		/*
13518740SSean.Ye@Sun.COM 		 * logadm may rotate the logs.  When no input file is specified,
13528740SSean.Ye@Sun.COM 		 * we try to dump all the rotated logs as well in the right
13538740SSean.Ye@Sun.COM 		 * order.
13548740SSean.Ye@Sun.COM 		 */
13558740SSean.Ye@Sun.COM 		if (!opt_H && off == 0)
13568740SSean.Ye@Sun.COM 			rotated_logs = get_rotated_logs(ifile);
13570Sstevel@tonic-gate 	} else if (g_root != NULL) {
1358*12967Sgavin.maltby@oracle.com 		fmdump_usage("-R option is not appropriate "
1359*12967Sgavin.maltby@oracle.com 		    "when file operand is present\n");
13600Sstevel@tonic-gate 	}
13610Sstevel@tonic-gate 
1362*12967Sgavin.maltby@oracle.com 	if ((g_msg = fmd_msg_init(g_root, FMD_MSG_VERSION)) == NULL)
1363*12967Sgavin.maltby@oracle.com 		fmdump_fatal("failed to initialize libfmd_msg");
13649501SRobert.Johnston@Sun.COM 
13650Sstevel@tonic-gate 	if ((lp = fmd_log_open(FMD_LOG_VERSION, ifile, &err)) == NULL) {
1366*12967Sgavin.maltby@oracle.com 		fmdump_fatal("failed to open %s: %s\n", ifile,
1367*12967Sgavin.maltby@oracle.com 		    fmd_log_errmsg(NULL, err));
13680Sstevel@tonic-gate 	}
13690Sstevel@tonic-gate 
13700Sstevel@tonic-gate 	if (opt_H) {
13710Sstevel@tonic-gate 		fmd_log_header(lp, &log);
13720Sstevel@tonic-gate 
13730Sstevel@tonic-gate 		(void) printf("EXD_CREATOR = %s\n", log.log_creator);
13740Sstevel@tonic-gate 		(void) printf("EXD_HOSTNAME = %s\n", log.log_hostname);
13750Sstevel@tonic-gate 		(void) printf("EXD_FMA_LABEL = %s\n", log.log_label);
13760Sstevel@tonic-gate 		(void) printf("EXD_FMA_VERSION = %s\n", log.log_version);
13770Sstevel@tonic-gate 		(void) printf("EXD_FMA_OSREL = %s\n", log.log_osrelease);
13780Sstevel@tonic-gate 		(void) printf("EXD_FMA_OSVER = %s\n", log.log_osversion);
13790Sstevel@tonic-gate 		(void) printf("EXD_FMA_PLAT = %s\n", log.log_platform);
13801052Sdilpreet 		(void) printf("EXD_FMA_UUID = %s\n", log.log_uuid);
13810Sstevel@tonic-gate 
13820Sstevel@tonic-gate 		return (FMDUMP_EXIT_SUCCESS);
13830Sstevel@tonic-gate 	}
13840Sstevel@tonic-gate 
13850Sstevel@tonic-gate 	if (off != 0 && fmd_log_seek(lp, off) != 0) {
1386*12967Sgavin.maltby@oracle.com 		fmdump_fatal("failed to seek %s: %s\n", ifile,
1387*12967Sgavin.maltby@oracle.com 		    fmd_log_errmsg(lp, fmd_log_errno(lp)));
13880Sstevel@tonic-gate 	}
13890Sstevel@tonic-gate 
13900Sstevel@tonic-gate 	if (opt_e && opt_u)
13910Sstevel@tonic-gate 		ops = &fmdump_err_ops;
13920Sstevel@tonic-gate 	else if (strcmp(fmd_log_label(lp), fmdump_flt_ops.do_label) == 0)
13930Sstevel@tonic-gate 		ops = &fmdump_flt_ops;
13940Sstevel@tonic-gate 	else if (strcmp(fmd_log_label(lp), fmdump_asru_ops.do_label) == 0)
13950Sstevel@tonic-gate 		ops = &fmdump_asru_ops;
1396*12967Sgavin.maltby@oracle.com 	else if (strcmp(fmd_log_label(lp), fmdump_info_ops.do_label) == 0)
1397*12967Sgavin.maltby@oracle.com 		ops = &fmdump_info_ops;
13980Sstevel@tonic-gate 	else
13990Sstevel@tonic-gate 		ops = &fmdump_err_ops;
14000Sstevel@tonic-gate 
14010Sstevel@tonic-gate 	if (!opt_a && ops == &fmdump_flt_ops) {
14020Sstevel@tonic-gate 		fltfv[fltfc].filt_func = log_filter_silent;
14030Sstevel@tonic-gate 		fltfv[fltfc].filt_arg = NULL;
14040Sstevel@tonic-gate 		allfv[allfc++] = fltfv[fltfc++];
14050Sstevel@tonic-gate 	}
14060Sstevel@tonic-gate 
14070Sstevel@tonic-gate 	if (opt_V) {
1408*12967Sgavin.maltby@oracle.com 		arg.da_fmt =
1409*12967Sgavin.maltby@oracle.com 		    &ops->do_formats[opt_p ? FMDUMP_PRETTY : FMDUMP_VERB2];
14100Sstevel@tonic-gate 		iflags |= FMD_LOG_XITER_REFS;
14110Sstevel@tonic-gate 	} else if (opt_v) {
14120Sstevel@tonic-gate 		arg.da_fmt = &ops->do_formats[FMDUMP_VERB1];
14139501SRobert.Johnston@Sun.COM 	} else if (opt_m) {
14149501SRobert.Johnston@Sun.COM 		arg.da_fmt = &ops->do_formats[FMDUMP_MSG];
14150Sstevel@tonic-gate 	} else
14160Sstevel@tonic-gate 		arg.da_fmt = &ops->do_formats[FMDUMP_SHORT];
14170Sstevel@tonic-gate 
14189501SRobert.Johnston@Sun.COM 	if (opt_m && arg.da_fmt->do_func == NULL) {
1419*12967Sgavin.maltby@oracle.com 		fmdump_usage("-m mode is not supported for "
1420*12967Sgavin.maltby@oracle.com 		    "log of type %s: %s\n", fmd_log_label(lp), ifile);
14219501SRobert.Johnston@Sun.COM 	}
14229501SRobert.Johnston@Sun.COM 
14230Sstevel@tonic-gate 	arg.da_fv = errfv;
14240Sstevel@tonic-gate 	arg.da_fc = errfc;
14250Sstevel@tonic-gate 	arg.da_fp = stdout;
14260Sstevel@tonic-gate 
14270Sstevel@tonic-gate 	if (iflags & FMD_LOG_XITER_OFFS)
14280Sstevel@tonic-gate 		fmdump_printf(arg.da_fp, "%16s ", "OFFSET");
14290Sstevel@tonic-gate 
143010928SStephen.Hanson@Sun.COM 	if (arg.da_fmt->do_hdr && !(opt_V && ops == &fmdump_flt_ops))
14310Sstevel@tonic-gate 		fmdump_printf(arg.da_fp, "%s\n", arg.da_fmt->do_hdr);
14320Sstevel@tonic-gate 
14330Sstevel@tonic-gate 	if (opt_e && opt_u) {
14340Sstevel@tonic-gate 		iflags |= FMD_LOG_XITER_REFS;
14350Sstevel@tonic-gate 		func = xref_iter;
14360Sstevel@tonic-gate 		farg = &arg;
14370Sstevel@tonic-gate 		filtc = fltfc;
14380Sstevel@tonic-gate 		filtv = fltfv;
14390Sstevel@tonic-gate 	} else {
14400Sstevel@tonic-gate 		func = arg.da_fmt->do_func;
14410Sstevel@tonic-gate 		farg = arg.da_fp;
14420Sstevel@tonic-gate 		filtc = allfc;
14430Sstevel@tonic-gate 		filtv = allfv;
14440Sstevel@tonic-gate 	}
14450Sstevel@tonic-gate 
14460Sstevel@tonic-gate 	if (iflags & FMD_LOG_XITER_OFFS) {
14470Sstevel@tonic-gate 		lyr.dy_func = func;
14480Sstevel@tonic-gate 		lyr.dy_arg = farg;
14490Sstevel@tonic-gate 		lyr.dy_fp = arg.da_fp;
14500Sstevel@tonic-gate 		func = xoff_iter;
14510Sstevel@tonic-gate 		farg = &lyr;
14520Sstevel@tonic-gate 	}
14530Sstevel@tonic-gate 
14548740SSean.Ye@Sun.COM 	for (llp = rotated_logs; llp != NULL; llp = llp->next) {
14558740SSean.Ye@Sun.COM 		fmd_log_t *rlp;
14568740SSean.Ye@Sun.COM 
14578740SSean.Ye@Sun.COM 		if ((rlp = fmd_log_open(FMD_LOG_VERSION, llp->path, &err))
14588740SSean.Ye@Sun.COM 		    == NULL) {
1459*12967Sgavin.maltby@oracle.com 			fmdump_warn("failed to open %s: %s\n",
1460*12967Sgavin.maltby@oracle.com 			    llp->path, fmd_log_errmsg(NULL, err));
14618740SSean.Ye@Sun.COM 			g_errs++;
14628740SSean.Ye@Sun.COM 			continue;
14638740SSean.Ye@Sun.COM 		}
14648740SSean.Ye@Sun.COM 
14658740SSean.Ye@Sun.COM 		recs = 0;
14668740SSean.Ye@Sun.COM 		if (fmd_log_xiter(rlp, iflags, filtc, filtv,
14678740SSean.Ye@Sun.COM 		    func, error, farg, &recs) != 0) {
1468*12967Sgavin.maltby@oracle.com 			fmdump_warn("failed to dump %s: %s\n", llp->path,
14698740SSean.Ye@Sun.COM 			    fmd_log_errmsg(rlp, fmd_log_errno(rlp)));
14708740SSean.Ye@Sun.COM 			g_errs++;
14718740SSean.Ye@Sun.COM 		}
14728740SSean.Ye@Sun.COM 		g_recs += recs;
14738740SSean.Ye@Sun.COM 
14748740SSean.Ye@Sun.COM 		fmd_log_close(rlp);
14758740SSean.Ye@Sun.COM 	}
14768740SSean.Ye@Sun.COM 
14770Sstevel@tonic-gate 	do {
14788740SSean.Ye@Sun.COM 		recs = 0;
14790Sstevel@tonic-gate 		if (fmd_log_xiter(lp, iflags, filtc, filtv,
14808740SSean.Ye@Sun.COM 		    func, error, farg, &recs) != 0) {
1481*12967Sgavin.maltby@oracle.com 			fmdump_warn("failed to dump %s: %s\n", ifile,
14820Sstevel@tonic-gate 			    fmd_log_errmsg(lp, fmd_log_errno(lp)));
14830Sstevel@tonic-gate 			g_errs++;
14840Sstevel@tonic-gate 		}
14858740SSean.Ye@Sun.COM 		g_recs += recs;
14860Sstevel@tonic-gate 
14870Sstevel@tonic-gate 		if (opt_f)
14880Sstevel@tonic-gate 			(void) sleep(1);
14890Sstevel@tonic-gate 
14900Sstevel@tonic-gate 	} while (opt_f);
14910Sstevel@tonic-gate 
14920Sstevel@tonic-gate 	if (!opt_f && g_recs == 0 && isatty(STDOUT_FILENO))
1493*12967Sgavin.maltby@oracle.com 		fmdump_warn("%s is empty\n", ifile);
14940Sstevel@tonic-gate 
14959501SRobert.Johnston@Sun.COM 	if (g_thp != NULL)
14969501SRobert.Johnston@Sun.COM 		topo_close(g_thp);
14979501SRobert.Johnston@Sun.COM 
14980Sstevel@tonic-gate 	fmd_log_close(lp);
14999501SRobert.Johnston@Sun.COM 	fmd_msg_fini(g_msg);
15009501SRobert.Johnston@Sun.COM 
1501*12967Sgavin.maltby@oracle.com 	if (ifiles == NULL)
1502*12967Sgavin.maltby@oracle.com 		free(ifile);
1503*12967Sgavin.maltby@oracle.com 	else
1504*12967Sgavin.maltby@oracle.com 		cleanup(ifiles, n_ifiles);
1505*12967Sgavin.maltby@oracle.com 
15060Sstevel@tonic-gate 	return (g_errs ? FMDUMP_EXIT_ERROR : FMDUMP_EXIT_SUCCESS);
15070Sstevel@tonic-gate }
1508