xref: /illumos-gate/usr/src/cmd/fm/fmdump/common/fmdump.c (revision 8efd981e)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
547911a7dScy  * Common Development and Distribution License (the "License").
647911a7dScy  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
2169fcd3d4SYuri Pankov 
227c478bd9Sstevel@tonic-gate /*
23f6e214c7SGavin Maltby  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
2469fcd3d4SYuri Pankov  * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
252db6d663SJoshua M. Clulow  * Copyright (c) 2013, Joyent, Inc. All rights reserved.
26*8efd981eSKeith M Wesolowski  * Copyright 2024 Oxide Computer Co.
277c478bd9Sstevel@tonic-gate  */
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate #include <alloca.h>
307c478bd9Sstevel@tonic-gate #include <unistd.h>
317c478bd9Sstevel@tonic-gate #include <limits.h>
327c478bd9Sstevel@tonic-gate #include <strings.h>
337c478bd9Sstevel@tonic-gate #include <stdlib.h>
347c478bd9Sstevel@tonic-gate #include <stdarg.h>
357c478bd9Sstevel@tonic-gate #include <stdio.h>
367c478bd9Sstevel@tonic-gate #include <errno.h>
377c478bd9Sstevel@tonic-gate #include <time.h>
387c478bd9Sstevel@tonic-gate #include <ctype.h>
39602ca9eaScth #include <regex.h>
40c93c462eSCheng Sean Ye #include <dirent.h>
41f6e214c7SGavin Maltby #include <pthread.h>
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate #include <fmdump.h>
447c478bd9Sstevel@tonic-gate 
457c478bd9Sstevel@tonic-gate #define	FMDUMP_EXIT_SUCCESS	0
467c478bd9Sstevel@tonic-gate #define	FMDUMP_EXIT_FATAL	1
477c478bd9Sstevel@tonic-gate #define	FMDUMP_EXIT_USAGE	2
487c478bd9Sstevel@tonic-gate #define	FMDUMP_EXIT_ERROR	3
497c478bd9Sstevel@tonic-gate 
507c478bd9Sstevel@tonic-gate const char *g_pname;
517c478bd9Sstevel@tonic-gate ulong_t g_errs;
527c478bd9Sstevel@tonic-gate ulong_t g_recs;
537c478bd9Sstevel@tonic-gate char *g_root;
54b6955755SRobert Johnston 
557aec1d6eScindi struct topo_hdl *g_thp;
56b6955755SRobert Johnston fmd_msg_hdl_t *g_msg;
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate /*PRINTFLIKE2*/
597c478bd9Sstevel@tonic-gate void
fmdump_printf(FILE * fp,const char * format,...)607c478bd9Sstevel@tonic-gate fmdump_printf(FILE *fp, const char *format, ...)
617c478bd9Sstevel@tonic-gate {
627c478bd9Sstevel@tonic-gate 	va_list ap;
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate 	va_start(ap, format);
657c478bd9Sstevel@tonic-gate 
667c478bd9Sstevel@tonic-gate 	if (vfprintf(fp, format, ap) < 0) {
677c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "%s: failed to print record: %s\n",
687c478bd9Sstevel@tonic-gate 		    g_pname, strerror(errno));
697c478bd9Sstevel@tonic-gate 		g_errs++;
707c478bd9Sstevel@tonic-gate 	}
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate 	va_end(ap);
737c478bd9Sstevel@tonic-gate }
747c478bd9Sstevel@tonic-gate 
757c478bd9Sstevel@tonic-gate void
fmdump_vwarn(const char * format,va_list ap)767c478bd9Sstevel@tonic-gate fmdump_vwarn(const char *format, va_list ap)
777c478bd9Sstevel@tonic-gate {
787c478bd9Sstevel@tonic-gate 	int err = errno;
797c478bd9Sstevel@tonic-gate 
807c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, "%s: warning: ", g_pname);
817c478bd9Sstevel@tonic-gate 	(void) vfprintf(stderr, format, ap);
827c478bd9Sstevel@tonic-gate 
837c478bd9Sstevel@tonic-gate 	if (strchr(format, '\n') == NULL)
847c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, ": %s\n", strerror(err));
857c478bd9Sstevel@tonic-gate 
867c478bd9Sstevel@tonic-gate 	g_errs++;
877c478bd9Sstevel@tonic-gate }
887c478bd9Sstevel@tonic-gate 
897c478bd9Sstevel@tonic-gate /*PRINTFLIKE1*/
907c478bd9Sstevel@tonic-gate void
fmdump_warn(const char * format,...)917c478bd9Sstevel@tonic-gate fmdump_warn(const char *format, ...)
927c478bd9Sstevel@tonic-gate {
937c478bd9Sstevel@tonic-gate 	va_list ap;
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate 	va_start(ap, format);
967c478bd9Sstevel@tonic-gate 	fmdump_vwarn(format, ap);
977c478bd9Sstevel@tonic-gate 	va_end(ap);
987c478bd9Sstevel@tonic-gate }
997c478bd9Sstevel@tonic-gate 
100f6e214c7SGavin Maltby static void
fmdump_exit(int err,int exitcode,const char * format,va_list ap)101f6e214c7SGavin Maltby fmdump_exit(int err, int exitcode, const char *format, va_list ap)
102f6e214c7SGavin Maltby {
103f6e214c7SGavin Maltby 	(void) fprintf(stderr, "%s: ", g_pname);
104f6e214c7SGavin Maltby 
105f6e214c7SGavin Maltby 	(void) vfprintf(stderr, format, ap);
106f6e214c7SGavin Maltby 
107f6e214c7SGavin Maltby 	if (strchr(format, '\n') == NULL)
108f6e214c7SGavin Maltby 		(void) fprintf(stderr, ": %s\n", strerror(err));
109f6e214c7SGavin Maltby 
110f6e214c7SGavin Maltby 	exit(exitcode);
111f6e214c7SGavin Maltby }
112f6e214c7SGavin Maltby 
113f6e214c7SGavin Maltby /*PRINTFLIKE1*/
114f6e214c7SGavin Maltby static void
fmdump_fatal(const char * format,...)115f6e214c7SGavin Maltby fmdump_fatal(const char *format, ...)
116f6e214c7SGavin Maltby {
117f6e214c7SGavin Maltby 	int err = errno;
118f6e214c7SGavin Maltby 
119f6e214c7SGavin Maltby 	va_list ap;
120f6e214c7SGavin Maltby 
121f6e214c7SGavin Maltby 	va_start(ap, format);
122f6e214c7SGavin Maltby 	fmdump_exit(err, FMDUMP_EXIT_FATAL, format, ap);
123f6e214c7SGavin Maltby 	va_end(ap);
124f6e214c7SGavin Maltby }
125f6e214c7SGavin Maltby 
126f6e214c7SGavin Maltby /*PRINTFLIKE1*/
127f6e214c7SGavin Maltby static void
fmdump_usage(const char * format,...)128f6e214c7SGavin Maltby fmdump_usage(const char *format, ...)
129f6e214c7SGavin Maltby {
130f6e214c7SGavin Maltby 
131f6e214c7SGavin Maltby 	int err = errno;
132f6e214c7SGavin Maltby 
133f6e214c7SGavin Maltby 	va_list ap;
134f6e214c7SGavin Maltby 
135f6e214c7SGavin Maltby 	va_start(ap, format);
136f6e214c7SGavin Maltby 	fmdump_exit(err, FMDUMP_EXIT_USAGE, format, ap);
137f6e214c7SGavin Maltby 	va_end(ap);
138f6e214c7SGavin Maltby }
139f6e214c7SGavin Maltby 
1407c478bd9Sstevel@tonic-gate char *
fmdump_date(char * buf,size_t len,const fmd_log_record_t * rp)1417c478bd9Sstevel@tonic-gate fmdump_date(char *buf, size_t len, const fmd_log_record_t *rp)
1427c478bd9Sstevel@tonic-gate {
1437c478bd9Sstevel@tonic-gate 	if (rp->rec_sec > LONG_MAX) {
1447c478bd9Sstevel@tonic-gate 		fmdump_warn("record time is too large for 32-bit utility\n");
1457c478bd9Sstevel@tonic-gate 		(void) snprintf(buf, len, "0x%llx", rp->rec_sec);
1467c478bd9Sstevel@tonic-gate 	} else {
1477c478bd9Sstevel@tonic-gate 		time_t tod = (time_t)rp->rec_sec;
14847911a7dScy 		time_t now = time(NULL);
14947911a7dScy 		if (tod > now+60 ||
15047911a7dScy 		    tod < now - 6L*30L*24L*60L*60L) { /* 6 months ago */
15147911a7dScy 			(void) strftime(buf, len, "%b %d %Y %T",
15247911a7dScy 			    localtime(&tod));
15347911a7dScy 		} else {
15447911a7dScy 			size_t sz;
15547911a7dScy 			sz = strftime(buf, len, "%b %d %T", localtime(&tod));
15647911a7dScy 			(void) snprintf(buf + sz, len - sz, ".%4.4llu",
15747911a7dScy 			    rp->rec_nsec / (NANOSEC / 10000));
15847911a7dScy 		}
1597c478bd9Sstevel@tonic-gate 	}
1607c478bd9Sstevel@tonic-gate 
1617c478bd9Sstevel@tonic-gate 	return (buf);
1627c478bd9Sstevel@tonic-gate }
1637c478bd9Sstevel@tonic-gate 
1647c478bd9Sstevel@tonic-gate char *
fmdump_year(char * buf,size_t len,const fmd_log_record_t * rp)1657c478bd9Sstevel@tonic-gate fmdump_year(char *buf, size_t len, const fmd_log_record_t *rp)
1667c478bd9Sstevel@tonic-gate {
1677c478bd9Sstevel@tonic-gate #ifdef _ILP32
1687c478bd9Sstevel@tonic-gate 	if (rp->rec_sec > LONG_MAX) {
1697c478bd9Sstevel@tonic-gate 		fmdump_warn("record time is too large for 32-bit utility\n");
1707c478bd9Sstevel@tonic-gate 		(void) snprintf(buf, len, "0x%llx", rp->rec_sec);
1717c478bd9Sstevel@tonic-gate 	} else {
1727c478bd9Sstevel@tonic-gate #endif
1737c478bd9Sstevel@tonic-gate 		time_t tod = (time_t)rp->rec_sec;
1747c478bd9Sstevel@tonic-gate 		(void) strftime(buf, len, "%b %d %Y %T", localtime(&tod));
1757c478bd9Sstevel@tonic-gate #ifdef _ILP32
1767c478bd9Sstevel@tonic-gate 	}
1777c478bd9Sstevel@tonic-gate #endif
1787c478bd9Sstevel@tonic-gate 	return (buf);
1797c478bd9Sstevel@tonic-gate }
1807c478bd9Sstevel@tonic-gate 
181f6e214c7SGavin Maltby /* BEGIN CSTYLED */
182f6e214c7SGavin Maltby static const char *synopsis =
183*8efd981eSKeith M Wesolowski "Usage: %s [[-e | -i | -I | -u] | -A ] [-f] [-aHmvVp] [-c class] [-R root]\n"
184*8efd981eSKeith M Wesolowski 	"\t      [-t time] [-T time] [-u uuid] [-n name[.name]*[=value]]\n"
185*8efd981eSKeith M Wesolowski 	"\t      [-N name[.name]*[=value][;name[.name]*[=value]]*] "
186f6e214c7SGavin Maltby 							"[file]...\n    "
187f6e214c7SGavin Maltby     "Log selection: [-e | -i | -I] or one [file]; default is the fault log\n"
188f6e214c7SGavin Maltby 	"\t-e  display error log content\n"
189f6e214c7SGavin Maltby 	"\t-i  display infolog content\n"
190f6e214c7SGavin Maltby 	"\t-I  display the high-value-infolog content\n"
191f6e214c7SGavin Maltby 	"\t-R  set root directory for pathname expansions\n    "
192f6e214c7SGavin Maltby     "Command behaviour:\n"
193f6e214c7SGavin Maltby 	"\t-A  Aggregate specified [file]s or, if no [file], all known logs\n"
194*8efd981eSKeith M Wesolowski 	"\t-H  display the log's header attributes instead of contents\n"
195f6e214c7SGavin Maltby 	"\t-f  follow growth of log file by waiting for additional data\n    "
196f6e214c7SGavin Maltby     "Output options:\n"
197*8efd981eSKeith M Wesolowski 	"\t-j  Used with -V: emit JSON-formatted output\n"
198f6e214c7SGavin Maltby 	"\t-m  display human-readable messages (only for fault logs)\n"
1992db6d663SJoshua M. Clulow 	"\t-p  Used with -V: apply some output prettification\n"
200*8efd981eSKeith M Wesolowski 	"\t-v  set verbose mode: display additional event detail\n"
201*8efd981eSKeith M Wesolowski 	"\t-V  set very verbose mode: display complete event contents\n    "
202f6e214c7SGavin Maltby     "Selection filters:\n"
203*8efd981eSKeith M Wesolowski 	"\t-a  select all events, including normally silent events\n"
204f6e214c7SGavin Maltby 	"\t-c  select events that match the specified class\n"
205*8efd981eSKeith M Wesolowski 	"\t-n  select events containing named nvpair (with matching value)\n"
206*8efd981eSKeith M Wesolowski 	"\t-N  select events matching multiple property names (or nvpairs)\n"
207f6e214c7SGavin Maltby 	"\t-t  select events that occurred after the specified time\n"
208f6e214c7SGavin Maltby 	"\t-T  select events that occurred before the specified time\n"
209*8efd981eSKeith M Wesolowski 	"\t-u  select events that match the specified diagnosis uuid\n";
210f6e214c7SGavin Maltby /* END CSTYLED */
211f6e214c7SGavin Maltby 
2127c478bd9Sstevel@tonic-gate static int
usage(FILE * fp)2137c478bd9Sstevel@tonic-gate usage(FILE *fp)
2147c478bd9Sstevel@tonic-gate {
215f6e214c7SGavin Maltby 	(void) fprintf(fp, synopsis, g_pname);
2167c478bd9Sstevel@tonic-gate 	return (FMDUMP_EXIT_USAGE);
2177c478bd9Sstevel@tonic-gate }
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2207c478bd9Sstevel@tonic-gate static int
error(fmd_log_t * lp,void * private)2217c478bd9Sstevel@tonic-gate error(fmd_log_t *lp, void *private)
2227c478bd9Sstevel@tonic-gate {
2237c478bd9Sstevel@tonic-gate 	fmdump_warn("skipping record: %s\n",
2247c478bd9Sstevel@tonic-gate 	    fmd_log_errmsg(lp, fmd_log_errno(lp)));
2257c478bd9Sstevel@tonic-gate 	return (0);
2267c478bd9Sstevel@tonic-gate }
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate /*
2297c478bd9Sstevel@tonic-gate  * Yet another disgusting argument parsing function (TM).  We attempt to parse
2307c478bd9Sstevel@tonic-gate  * a time argument in a variety of strptime(3C) formats, in which case it is
2317c478bd9Sstevel@tonic-gate  * interpreted as a local time and is converted to a timeval using mktime(3C).
2327c478bd9Sstevel@tonic-gate  * If those formats fail, we look to see if the time is a decimal integer
2337c478bd9Sstevel@tonic-gate  * followed by one of our magic suffixes, in which case the time is interpreted
2347c478bd9Sstevel@tonic-gate  * as a time delta *before* the current time-of-day (i.e. "1h" = "1 hour ago").
2357c478bd9Sstevel@tonic-gate  */
2367c478bd9Sstevel@tonic-gate static struct timeval *
gettimeopt(const char * arg)2377c478bd9Sstevel@tonic-gate gettimeopt(const char *arg)
2387c478bd9Sstevel@tonic-gate {
2397c478bd9Sstevel@tonic-gate 	const struct {
2407c478bd9Sstevel@tonic-gate 		const char *name;
2417c478bd9Sstevel@tonic-gate 		hrtime_t mul;
2427c478bd9Sstevel@tonic-gate 	} suffix[] = {
243602ca9eaScth 		{ "ns",		NANOSEC / NANOSEC },
2447c478bd9Sstevel@tonic-gate 		{ "nsec",	NANOSEC / NANOSEC },
2457c478bd9Sstevel@tonic-gate 		{ "us",		NANOSEC / MICROSEC },
2467c478bd9Sstevel@tonic-gate 		{ "usec",	NANOSEC / MICROSEC },
2477c478bd9Sstevel@tonic-gate 		{ "ms",		NANOSEC / MILLISEC },
2487c478bd9Sstevel@tonic-gate 		{ "msec",	NANOSEC / MILLISEC },
2497c478bd9Sstevel@tonic-gate 		{ "s",		NANOSEC / SEC },
2507c478bd9Sstevel@tonic-gate 		{ "sec",	NANOSEC / SEC },
2517c478bd9Sstevel@tonic-gate 		{ "m",		NANOSEC * (hrtime_t)60 },
2527c478bd9Sstevel@tonic-gate 		{ "min",	NANOSEC * (hrtime_t)60 },
2537c478bd9Sstevel@tonic-gate 		{ "h",		NANOSEC * (hrtime_t)(60 * 60) },
2547c478bd9Sstevel@tonic-gate 		{ "hour",	NANOSEC * (hrtime_t)(60 * 60) },
2557c478bd9Sstevel@tonic-gate 		{ "d",		NANOSEC * (hrtime_t)(24 * 60 * 60) },
2567c478bd9Sstevel@tonic-gate 		{ "day",	NANOSEC * (hrtime_t)(24 * 60 * 60) },
2577c478bd9Sstevel@tonic-gate 		{ NULL }
2587c478bd9Sstevel@tonic-gate 	};
2597c478bd9Sstevel@tonic-gate 
2607c478bd9Sstevel@tonic-gate 	struct timeval *tvp = malloc(sizeof (struct timeval));
2617c478bd9Sstevel@tonic-gate 	struct timeval tod;
2627c478bd9Sstevel@tonic-gate 	struct tm tm;
2637c478bd9Sstevel@tonic-gate 	char *p;
2647c478bd9Sstevel@tonic-gate 
265f6e214c7SGavin Maltby 	if (tvp == NULL)
266f6e214c7SGavin Maltby 		fmdump_fatal("failed to allocate memory");
2677c478bd9Sstevel@tonic-gate 
268f6e214c7SGavin Maltby 	if (gettimeofday(&tod, NULL) != 0)
269f6e214c7SGavin Maltby 		fmdump_fatal("failed to get tod");
2707c478bd9Sstevel@tonic-gate 
2717c478bd9Sstevel@tonic-gate 	/*
2727c478bd9Sstevel@tonic-gate 	 * First try a variety of strptime() calls.  If these all fail, we'll
2737c478bd9Sstevel@tonic-gate 	 * try parsing an integer followed by one of our suffix[] strings.
2747c478bd9Sstevel@tonic-gate 	 */
27569fcd3d4SYuri Pankov 	if ((p = strptime(arg, "%m/%d/%Y %H:%M:%S", &tm)) == NULL &&
27669fcd3d4SYuri Pankov 	    (p = strptime(arg, "%m/%d/%y %H:%M:%S", &tm)) == NULL &&
27769fcd3d4SYuri Pankov 	    (p = strptime(arg, "%m/%d/%Y %H:%M", &tm)) == NULL &&
27869fcd3d4SYuri Pankov 	    (p = strptime(arg, "%m/%d/%y %H:%M", &tm)) == NULL &&
2797c478bd9Sstevel@tonic-gate 	    (p = strptime(arg, "%m/%d/%Y", &tm)) == NULL &&
28069fcd3d4SYuri Pankov 	    (p = strptime(arg, "%m/%d/%y", &tm)) == NULL &&
2817c478bd9Sstevel@tonic-gate 	    (p = strptime(arg, "%Y-%m-%dT%H:%M:%S", &tm)) == NULL &&
28269fcd3d4SYuri Pankov 	    (p = strptime(arg, "%y-%m-%dT%H:%M:%S", &tm)) == NULL &&
2837c478bd9Sstevel@tonic-gate 	    (p = strptime(arg, "%Y-%m-%dT%H:%M", &tm)) == NULL &&
28469fcd3d4SYuri Pankov 	    (p = strptime(arg, "%y-%m-%dT%H:%M", &tm)) == NULL &&
2857c478bd9Sstevel@tonic-gate 	    (p = strptime(arg, "%Y-%m-%d", &tm)) == NULL &&
28669fcd3d4SYuri Pankov 	    (p = strptime(arg, "%y-%m-%d", &tm)) == NULL &&
28769fcd3d4SYuri Pankov 	    (p = strptime(arg, "%d%b%Y %H:%M:%S", &tm)) == NULL &&
28869fcd3d4SYuri Pankov 	    (p = strptime(arg, "%d%b%y %H:%M:%S", &tm)) == NULL &&
28969fcd3d4SYuri Pankov 	    (p = strptime(arg, "%d%b%Y %H:%M", &tm)) == NULL &&
29069fcd3d4SYuri Pankov 	    (p = strptime(arg, "%d%b%y %H:%M", &tm)) == NULL &&
2917c478bd9Sstevel@tonic-gate 	    (p = strptime(arg, "%d%b%Y", &tm)) == NULL &&
29269fcd3d4SYuri Pankov 	    (p = strptime(arg, "%d%b%y", &tm)) == NULL &&
29369fcd3d4SYuri Pankov 	    (p = strptime(arg, "%b %d %H:%M:%S", &tm)) == NULL &&
29469fcd3d4SYuri Pankov 	    (p = strptime(arg, "%b %d %H:%M:%S", &tm)) == NULL &&
2957c478bd9Sstevel@tonic-gate 	    (p = strptime(arg, "%H:%M:%S", &tm)) == NULL &&
2967c478bd9Sstevel@tonic-gate 	    (p = strptime(arg, "%H:%M", &tm)) == NULL) {
2977c478bd9Sstevel@tonic-gate 
2987c478bd9Sstevel@tonic-gate 		hrtime_t nsec;
2997c478bd9Sstevel@tonic-gate 		int i;
3007c478bd9Sstevel@tonic-gate 
3017c478bd9Sstevel@tonic-gate 		errno = 0;
3027c478bd9Sstevel@tonic-gate 		nsec = strtol(arg, (char **)&p, 10);
3037c478bd9Sstevel@tonic-gate 
304f6e214c7SGavin Maltby 		if (errno != 0 || nsec == 0 || p == arg || *p == '\0')
305f6e214c7SGavin Maltby 			fmdump_usage("illegal time format -- %s\n", arg);
3067c478bd9Sstevel@tonic-gate 
3077c478bd9Sstevel@tonic-gate 		for (i = 0; suffix[i].name != NULL; i++) {
3087c478bd9Sstevel@tonic-gate 			if (strcasecmp(suffix[i].name, p) == 0) {
3097c478bd9Sstevel@tonic-gate 				nsec *= suffix[i].mul;
3107c478bd9Sstevel@tonic-gate 				break;
3117c478bd9Sstevel@tonic-gate 			}
3127c478bd9Sstevel@tonic-gate 		}
3137c478bd9Sstevel@tonic-gate 
314f6e214c7SGavin Maltby 		if (suffix[i].name == NULL)
315f6e214c7SGavin Maltby 			fmdump_usage("illegal time format -- %s\n", arg);
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate 		tvp->tv_sec = nsec / NANOSEC;
3187c478bd9Sstevel@tonic-gate 		tvp->tv_usec = (nsec % NANOSEC) / (NANOSEC / MICROSEC);
3197c478bd9Sstevel@tonic-gate 
320f6e214c7SGavin Maltby 		if (tvp->tv_sec > tod.tv_sec)
321f6e214c7SGavin Maltby 			fmdump_usage("time delta precedes UTC time origin "
322f6e214c7SGavin Maltby 			    "-- %s\n", arg);
3237c478bd9Sstevel@tonic-gate 
3247c478bd9Sstevel@tonic-gate 		tvp->tv_sec = tod.tv_sec - tvp->tv_sec;
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate 	} else if (*p == '\0' || *p == '.') {
3277c478bd9Sstevel@tonic-gate 		/*
3287c478bd9Sstevel@tonic-gate 		 * If tm_year is zero, we matched [%b %d] %H:%M[:%S]; use
3297c478bd9Sstevel@tonic-gate 		 * the result of localtime(&tod.tv_sec) to fill in the rest.
3307c478bd9Sstevel@tonic-gate 		 */
3317c478bd9Sstevel@tonic-gate 		if (tm.tm_year == 0) {
3327c478bd9Sstevel@tonic-gate 			int h = tm.tm_hour;
3337c478bd9Sstevel@tonic-gate 			int m = tm.tm_min;
3347c478bd9Sstevel@tonic-gate 			int s = tm.tm_sec;
3357c478bd9Sstevel@tonic-gate 			int b = tm.tm_mon;
3367c478bd9Sstevel@tonic-gate 			int d = tm.tm_mday;
3377c478bd9Sstevel@tonic-gate 
3387c478bd9Sstevel@tonic-gate 			bcopy(localtime(&tod.tv_sec), &tm, sizeof (tm));
3397c478bd9Sstevel@tonic-gate 			tm.tm_isdst = 0; /* see strptime(3C) and below */
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate 			if (d > 0) {
3427c478bd9Sstevel@tonic-gate 				tm.tm_mon = b;
3437c478bd9Sstevel@tonic-gate 				tm.tm_mday = d;
3447c478bd9Sstevel@tonic-gate 			}
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate 			tm.tm_hour = h;
3477c478bd9Sstevel@tonic-gate 			tm.tm_min = m;
3487c478bd9Sstevel@tonic-gate 			tm.tm_sec = s;
3497c478bd9Sstevel@tonic-gate 		}
3507c478bd9Sstevel@tonic-gate 
3517c478bd9Sstevel@tonic-gate 		errno = 0;
3527c478bd9Sstevel@tonic-gate 		tvp->tv_sec = mktime(&tm);
3537c478bd9Sstevel@tonic-gate 		tvp->tv_usec = 0;
3547c478bd9Sstevel@tonic-gate 
355f6e214c7SGavin Maltby 		if (tvp->tv_sec == -1L && errno != 0)
356f6e214c7SGavin Maltby 			fmdump_fatal("failed to compose time %s", arg);
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate 		/*
3597c478bd9Sstevel@tonic-gate 		 * If our mktime() set tm_isdst, adjust the result for DST by
3607c478bd9Sstevel@tonic-gate 		 * subtracting the offset between the main and alternate zones.
3617c478bd9Sstevel@tonic-gate 		 */
3627c478bd9Sstevel@tonic-gate 		if (tm.tm_isdst)
3637c478bd9Sstevel@tonic-gate 			tvp->tv_sec -= timezone - altzone;
3647c478bd9Sstevel@tonic-gate 
3657c478bd9Sstevel@tonic-gate 		if (p[0] == '.') {
3667c478bd9Sstevel@tonic-gate 			arg = p;
3677c478bd9Sstevel@tonic-gate 			errno = 0;
3687c478bd9Sstevel@tonic-gate 			tvp->tv_usec =
3697c478bd9Sstevel@tonic-gate 			    (suseconds_t)(strtod(arg, &p) * (double)MICROSEC);
3707c478bd9Sstevel@tonic-gate 
371f6e214c7SGavin Maltby 			if (errno != 0 || p == arg || *p != '\0')
372f6e214c7SGavin Maltby 				fmdump_usage("illegal time suffix -- .%s\n",
373f6e214c7SGavin Maltby 				    arg);
3747c478bd9Sstevel@tonic-gate 		}
3757c478bd9Sstevel@tonic-gate 
3767c478bd9Sstevel@tonic-gate 	} else {
377f6e214c7SGavin Maltby 		fmdump_usage("unexpected suffix after time %s -- %s\n", arg, p);
3787c478bd9Sstevel@tonic-gate 	}
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate 	return (tvp);
3817c478bd9Sstevel@tonic-gate }
3827c478bd9Sstevel@tonic-gate 
3837c478bd9Sstevel@tonic-gate /*
3847c478bd9Sstevel@tonic-gate  * If the -u option is specified in combination with the -e option, we iterate
3857c478bd9Sstevel@tonic-gate  * over each record in the fault log with a matching UUID finding xrefs to the
3867c478bd9Sstevel@tonic-gate  * error log, and then use this function to iterate over every xref'd record.
3877c478bd9Sstevel@tonic-gate  */
3887c478bd9Sstevel@tonic-gate int
xref_iter(fmd_log_t * lp,const fmd_log_record_t * rp,void * arg)3897c478bd9Sstevel@tonic-gate xref_iter(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg)
3907c478bd9Sstevel@tonic-gate {
3917c478bd9Sstevel@tonic-gate 	const fmd_log_record_t *xrp = rp->rec_xrefs;
3927c478bd9Sstevel@tonic-gate 	fmdump_arg_t *dap = arg;
3937c478bd9Sstevel@tonic-gate 	int i, rv = 0;
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate 	for (i = 0; rv == 0 && i < rp->rec_nrefs; i++, xrp++) {
3967c478bd9Sstevel@tonic-gate 		if (fmd_log_filter(lp, dap->da_fc, dap->da_fv, xrp))
3977c478bd9Sstevel@tonic-gate 			rv = dap->da_fmt->do_func(lp, xrp, dap->da_fp);
3987c478bd9Sstevel@tonic-gate 	}
3997c478bd9Sstevel@tonic-gate 
4007c478bd9Sstevel@tonic-gate 	return (rv);
4017c478bd9Sstevel@tonic-gate }
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate int
xoff_iter(fmd_log_t * lp,const fmd_log_record_t * rp,void * arg)4047c478bd9Sstevel@tonic-gate xoff_iter(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg)
4057c478bd9Sstevel@tonic-gate {
4067c478bd9Sstevel@tonic-gate 	fmdump_lyr_t *dyp = arg;
4077c478bd9Sstevel@tonic-gate 
4087c478bd9Sstevel@tonic-gate 	fmdump_printf(dyp->dy_fp, "%16llx ", (u_longlong_t)rp->rec_off);
4097c478bd9Sstevel@tonic-gate 	return (dyp->dy_func(lp, rp, dyp->dy_arg));
4107c478bd9Sstevel@tonic-gate }
4117c478bd9Sstevel@tonic-gate 
412602ca9eaScth /*
413602ca9eaScth  * Initialize fmd_log_filter_nvarg_t from -n name=value argument string.
414602ca9eaScth  */
415602ca9eaScth static fmd_log_filter_nvarg_t *
setupnamevalue(char * namevalue)416602ca9eaScth setupnamevalue(char *namevalue)
417602ca9eaScth {
418602ca9eaScth 	fmd_log_filter_nvarg_t	*argt;
419602ca9eaScth 	char			*value;
420602ca9eaScth 	regex_t			*value_regex = NULL;
421602ca9eaScth 	char			errstr[128];
422602ca9eaScth 	int			rv;
423602ca9eaScth 
424602ca9eaScth 	if ((value = strchr(namevalue, '=')) == NULL) {
425602ca9eaScth 		value_regex = NULL;
426602ca9eaScth 	} else {
427602ca9eaScth 		*value++ = '\0';	/* separate name and value string */
428602ca9eaScth 
429602ca9eaScth 		/*
430602ca9eaScth 		 * Skip white space before value to facilitate direct
431602ca9eaScth 		 * cut/paste from previous fmdump output.
432602ca9eaScth 		 */
433602ca9eaScth 		while (isspace(*value))
434602ca9eaScth 			value++;
435602ca9eaScth 
436f6e214c7SGavin Maltby 		if ((value_regex = malloc(sizeof (regex_t))) == NULL)
437f6e214c7SGavin Maltby 			fmdump_fatal("failed to allocate memory");
438602ca9eaScth 
439602ca9eaScth 		/* compile regular expression for possible string match */
440602ca9eaScth 		if ((rv = regcomp(value_regex, value,
441602ca9eaScth 		    REG_NOSUB|REG_NEWLINE)) != 0) {
442602ca9eaScth 			(void) regerror(rv, value_regex, errstr,
443602ca9eaScth 			    sizeof (errstr));
444602ca9eaScth 			free(value_regex);
445f6e214c7SGavin Maltby 			fmdump_usage("unexpected regular expression in "
446f6e214c7SGavin Maltby 			    "%s: %s\n", value, errstr);
447602ca9eaScth 		}
448602ca9eaScth 	}
449602ca9eaScth 
450*8efd981eSKeith M Wesolowski 	if ((argt = calloc(1, sizeof (fmd_log_filter_nvarg_t))) == NULL)
451f6e214c7SGavin Maltby 		fmdump_fatal("failed to allocate memory");
452f6e214c7SGavin Maltby 
453602ca9eaScth 	argt->nvarg_name = namevalue;		/* now just name */
454602ca9eaScth 	argt->nvarg_value = value;
455602ca9eaScth 	argt->nvarg_value_regex = value_regex;
456602ca9eaScth 	return (argt);
457602ca9eaScth }
458602ca9eaScth 
459*8efd981eSKeith M Wesolowski /*
460*8efd981eSKeith M Wesolowski  * As for setupnamevalue() above, create our chain of filter arguments for -N
461*8efd981eSKeith M Wesolowski  * [name[=value][;name[=value]]*.  This would be simple except for the problems
462*8efd981eSKeith M Wesolowski  * of escaping something in a string.  To accommodate the use of the ; within
463*8efd981eSKeith M Wesolowski  * the chain, we allow it to be escaped.  One might imagine that the backslash
464*8efd981eSKeith M Wesolowski  * character should be used to escape it, but that opens Pandora's box because
465*8efd981eSKeith M Wesolowski  * the value portion of each entry (if present) is allowed to be a regex.  The
466*8efd981eSKeith M Wesolowski  * treatment of backslashes within regexes is not something we want to replicate
467*8efd981eSKeith M Wesolowski  * here, which would be necessary if we wanted to allow escaping the ; with a
468*8efd981eSKeith M Wesolowski  * backslash.  Specifically, consider how we treat the sequence of characters
469*8efd981eSKeith M Wesolowski  * '\\;x' (two backslash characters followed by a semicolon and then some other
470*8efd981eSKeith M Wesolowski  * character x).  In the name portion of the entry, this would be a backslash
471*8efd981eSKeith M Wesolowski  * followed by an escaped semicolon, so that we would treat this as '\;' and
472*8efd981eSKeith M Wesolowski  * include x and subsequent characters in this entry.  In the value portion (if
473*8efd981eSKeith M Wesolowski  * present), we would have to treat it as a pair of backslashes followed by the
474*8efd981eSKeith M Wesolowski  * terminating ; and the next entry would begin with 'x'...  except that we
475*8efd981eSKeith M Wesolowski  * might be inside [] where the backslash is not special, and so on.
476*8efd981eSKeith M Wesolowski  *
477*8efd981eSKeith M Wesolowski  * Let's not do that.  Instead, we allow the user to 'escape' the ; by repeating
478*8efd981eSKeith M Wesolowski  * it, and we interpret that before any regex interpretation is done.  Therefore
479*8efd981eSKeith M Wesolowski  * *every* pair of consecutive semicolons, regardless of where it appears, is
480*8efd981eSKeith M Wesolowski  * replaced by a literal semicolon.  This allows the semicolon to appear any
481*8efd981eSKeith M Wesolowski  * number of times in either the name or, if present, the value, including as
482*8efd981eSKeith M Wesolowski  * part of a regex (see regexp(7)), simply by doubling it.  A non-doubled
483*8efd981eSKeith M Wesolowski  * semicolon always terminates the entry.  This now creates one more problem:
484*8efd981eSKeith M Wesolowski  * whether to treat ';;;' as a literal semicolon followed by the entry
485*8efd981eSKeith M Wesolowski  * terminator, or the entry terminator followed by a literal semicolon to start
486*8efd981eSKeith M Wesolowski  * the next entry.  Here we have to cheat a little: it's clear from the FMD PRM
487*8efd981eSKeith M Wesolowski  * (especially chapter 10 as well as the schema for module properties, buffers,
488*8efd981eSKeith M Wesolowski  * statistics, and other entities) that the event member namespace is intended
489*8efd981eSKeith M Wesolowski  * to exclude both the semicolon and whitespace.  A value, or a regex intended
490*8efd981eSKeith M Wesolowski  * to match values, might well include anything.  Therefore, a semicolon at the
491*8efd981eSKeith M Wesolowski  * beginning of an entry is unlikely to be useful, while one at the end of an
492*8efd981eSKeith M Wesolowski  * entry may well be intentional.  We'll allow either or both when unambiguous,
493*8efd981eSKeith M Wesolowski  * but a sequence containing an odd number of consecutive ';' characters will be
494*8efd981eSKeith M Wesolowski  * interpreted as half that number of literal semicolons (rounded down) followed
495*8efd981eSKeith M Wesolowski  * by the terminator.  If the user wishes to begin an event property name with a
496*8efd981eSKeith M Wesolowski  * semicolon, it needs to be the first property in the chain.  Chains with
497*8efd981eSKeith M Wesolowski  * multiple properties whose names begin with a literal semicolon are not
498*8efd981eSKeith M Wesolowski  * supported.  Again, this almost certainly can never matter as no event should
499*8efd981eSKeith M Wesolowski  * ever have a property whose name contains a semicolon.
500*8efd981eSKeith M Wesolowski  *
501*8efd981eSKeith M Wesolowski  * We choose the semicolon because the comma is very likely to be present in
502*8efd981eSKeith M Wesolowski  * some property values on which the user may want to filter, especially the
503*8efd981eSKeith M Wesolowski  * name of device paths.  The semicolon may itself appear in values, especially
504*8efd981eSKeith M Wesolowski  * if the property is a URI, though it is likely much less common.  We have to
505*8efd981eSKeith M Wesolowski  * pick something.  If this proves unwieldy or insufficiently expressive, it
506*8efd981eSKeith M Wesolowski  * will need to be replaced by a full-on logical expression parser with
507*8efd981eSKeith M Wesolowski  * first-class support for internal quoting, escaping, and regexes.  One might
508*8efd981eSKeith M Wesolowski  * be better off dumping JSON and importing it into a SQL database if that level
509*8efd981eSKeith M Wesolowski  * of complexity is required.
510*8efd981eSKeith M Wesolowski  */
511*8efd981eSKeith M Wesolowski 
512*8efd981eSKeith M Wesolowski static fmd_log_filter_nvarg_t *
setupnamevalue_multi(char * chainstr)513*8efd981eSKeith M Wesolowski setupnamevalue_multi(char *chainstr)
514*8efd981eSKeith M Wesolowski {
515*8efd981eSKeith M Wesolowski 	fmd_log_filter_nvarg_t *argchain = NULL;
516*8efd981eSKeith M Wesolowski 	size_t rem = strlen(chainstr) + 1;
517*8efd981eSKeith M Wesolowski 	fmd_log_filter_nvarg_t *argt;
518*8efd981eSKeith M Wesolowski 
519*8efd981eSKeith M Wesolowski 	/*
520*8efd981eSKeith M Wesolowski 	 * Here, rem holds the number of characters remaining that we are
521*8efd981eSKeith M Wesolowski 	 * permitted to examine, including the terminating NUL.  If the first
522*8efd981eSKeith M Wesolowski 	 * entry begins with a single semicolon, it is considered empty and
523*8efd981eSKeith M Wesolowski 	 * ignored.  Similarly, a trailing semicolon is optional and ignored if
524*8efd981eSKeith M Wesolowski 	 * present.  We won't create empty filter entries for any input.
525*8efd981eSKeith M Wesolowski 	 */
526*8efd981eSKeith M Wesolowski 	for (char *nv = chainstr; rem > 0; ++chainstr, --rem) {
527*8efd981eSKeith M Wesolowski 		switch (*chainstr) {
528*8efd981eSKeith M Wesolowski 		case ';':
529*8efd981eSKeith M Wesolowski 			ASSERT(rem > 1);
530*8efd981eSKeith M Wesolowski 
531*8efd981eSKeith M Wesolowski 			/*
532*8efd981eSKeith M Wesolowski 			 * Check for double-semicolon.  If found,
533*8efd981eSKeith M Wesolowski 			 * de-duplicate it and advance past, then continue the
534*8efd981eSKeith M Wesolowski 			 * loop: we can't be done yet.
535*8efd981eSKeith M Wesolowski 			 */
536*8efd981eSKeith M Wesolowski 			if (chainstr[1] == ';') {
537*8efd981eSKeith M Wesolowski 				ASSERT(rem > 2);
538*8efd981eSKeith M Wesolowski 				--rem;
539*8efd981eSKeith M Wesolowski 				(void) memmove(chainstr, chainstr + 1, rem);
540*8efd981eSKeith M Wesolowski 				break;
541*8efd981eSKeith M Wesolowski 			}
542*8efd981eSKeith M Wesolowski 
543*8efd981eSKeith M Wesolowski 			*chainstr = '\0';
544*8efd981eSKeith M Wesolowski 
545*8efd981eSKeith M Wesolowski 			/*FALLTHROUGH*/
546*8efd981eSKeith M Wesolowski 		case '\0':
547*8efd981eSKeith M Wesolowski 			if (chainstr != nv) {
548*8efd981eSKeith M Wesolowski 				argt = setupnamevalue(nv);
549*8efd981eSKeith M Wesolowski 				argt->nvarg_next = argchain;
550*8efd981eSKeith M Wesolowski 				argchain = argt;
551*8efd981eSKeith M Wesolowski 			}
552*8efd981eSKeith M Wesolowski 			nv = chainstr + 1;
553*8efd981eSKeith M Wesolowski 
554*8efd981eSKeith M Wesolowski 			/*FALLTHROUGH*/
555*8efd981eSKeith M Wesolowski 		default:
556*8efd981eSKeith M Wesolowski 			ASSERT(rem > 0);
557*8efd981eSKeith M Wesolowski 		}
558*8efd981eSKeith M Wesolowski 	}
559*8efd981eSKeith M Wesolowski 
560*8efd981eSKeith M Wesolowski 	return (argchain);
561*8efd981eSKeith M Wesolowski }
562*8efd981eSKeith M Wesolowski 
5637c478bd9Sstevel@tonic-gate /*
5647c478bd9Sstevel@tonic-gate  * If the -a option is not present, filter out fault records that correspond
5657c478bd9Sstevel@tonic-gate  * to events that the producer requested not be messaged for administrators.
5667c478bd9Sstevel@tonic-gate  */
5677c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5687c478bd9Sstevel@tonic-gate int
log_filter_silent(fmd_log_t * lp,const fmd_log_record_t * rp,void * arg)5697c478bd9Sstevel@tonic-gate log_filter_silent(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg)
5707c478bd9Sstevel@tonic-gate {
571f6e214c7SGavin Maltby 	int opt_A = (arg != NULL);
5727c478bd9Sstevel@tonic-gate 	boolean_t msg;
573f6e214c7SGavin Maltby 	char *class;
574f6e214c7SGavin Maltby 
575f6e214c7SGavin Maltby 	/*
576f6e214c7SGavin Maltby 	 * If -A was used then apply this filter only to events of list class
577f6e214c7SGavin Maltby 	 */
578f6e214c7SGavin Maltby 	if (opt_A) {
579f6e214c7SGavin Maltby 		if (nvlist_lookup_string(rp->rec_nvl, FM_CLASS, &class) != 0 ||
580f6e214c7SGavin Maltby 		    strncmp(class, FM_LIST_EVENT ".",
581f6e214c7SGavin Maltby 		    sizeof (FM_LIST_EVENT)) != 0)
582f6e214c7SGavin Maltby 			return (1);
583f6e214c7SGavin Maltby 	}
5847c478bd9Sstevel@tonic-gate 
5857c478bd9Sstevel@tonic-gate 	return (nvlist_lookup_boolean_value(rp->rec_nvl,
5867c478bd9Sstevel@tonic-gate 	    FM_SUSPECT_MESSAGE, &msg) != 0 || msg != 0);
5877c478bd9Sstevel@tonic-gate }
5887c478bd9Sstevel@tonic-gate 
589c93c462eSCheng Sean Ye struct loglink {
590f6e214c7SGavin Maltby 	char		*path;
591c93c462eSCheng Sean Ye 	long		suffix;
592c93c462eSCheng Sean Ye 	struct loglink	*next;
593c93c462eSCheng Sean Ye };
594c93c462eSCheng Sean Ye 
595c93c462eSCheng Sean Ye static void
addlink(struct loglink ** llp,char * dirname,char * logname,long suffix)596c93c462eSCheng Sean Ye addlink(struct loglink **llp, char *dirname, char *logname, long suffix)
597c93c462eSCheng Sean Ye {
598c93c462eSCheng Sean Ye 	struct loglink *newp;
599c93c462eSCheng Sean Ye 	size_t len;
600c93c462eSCheng Sean Ye 	char *str;
601c93c462eSCheng Sean Ye 
602c93c462eSCheng Sean Ye 	newp = malloc(sizeof (struct loglink));
603c93c462eSCheng Sean Ye 	len = strlen(dirname) + strlen(logname) + 2;
604c93c462eSCheng Sean Ye 	str = malloc(len);
605f6e214c7SGavin Maltby 	if (newp == NULL || str == NULL)
606f6e214c7SGavin Maltby 		fmdump_fatal("failed to allocate memory");
607c93c462eSCheng Sean Ye 
608c93c462eSCheng Sean Ye 	(void) snprintf(str, len, "%s/%s", dirname, logname);
609c93c462eSCheng Sean Ye 	newp->path = str;
610c93c462eSCheng Sean Ye 	newp->suffix = suffix;
611c93c462eSCheng Sean Ye 
612c93c462eSCheng Sean Ye 	while (*llp != NULL && suffix < (*llp)->suffix)
613c93c462eSCheng Sean Ye 		llp = &(*llp)->next;
614c93c462eSCheng Sean Ye 
615c93c462eSCheng Sean Ye 	newp->next = *llp;
616c93c462eSCheng Sean Ye 	*llp = newp;
617c93c462eSCheng Sean Ye }
618c93c462eSCheng Sean Ye 
619c93c462eSCheng Sean Ye /*
620c93c462eSCheng Sean Ye  * Find and return all the rotated logs.
621c93c462eSCheng Sean Ye  */
622c93c462eSCheng Sean Ye static struct loglink *
get_rotated_logs(char * logpath)623c93c462eSCheng Sean Ye get_rotated_logs(char *logpath)
624c93c462eSCheng Sean Ye {
625c93c462eSCheng Sean Ye 	char dirname[PATH_MAX], *logname, *endptr;
626c93c462eSCheng Sean Ye 	DIR *dirp;
627c93c462eSCheng Sean Ye 	struct dirent *dp;
628c93c462eSCheng Sean Ye 	long len, suffix;
629c93c462eSCheng Sean Ye 	struct loglink *head = NULL;
630c93c462eSCheng Sean Ye 
631c93c462eSCheng Sean Ye 	(void) strlcpy(dirname, logpath, sizeof (dirname));
632c93c462eSCheng Sean Ye 	logname = strrchr(dirname, '/');
633c93c462eSCheng Sean Ye 	*logname++ = '\0';
634c93c462eSCheng Sean Ye 	len = strlen(logname);
635c93c462eSCheng Sean Ye 
636c93c462eSCheng Sean Ye 	if ((dirp = opendir(dirname)) == NULL) {
637f6e214c7SGavin Maltby 		fmdump_warn("failed to opendir `%s'", dirname);
638f6e214c7SGavin Maltby 		g_errs++;
639c93c462eSCheng Sean Ye 		return (NULL);
640c93c462eSCheng Sean Ye 	}
641c93c462eSCheng Sean Ye 
642c93c462eSCheng Sean Ye 	while ((dp = readdir(dirp)) != NULL) {
643c93c462eSCheng Sean Ye 		/*
644c93c462eSCheng Sean Ye 		 * Search the log directory for logs named "<logname>.0",
645c93c462eSCheng Sean Ye 		 * "<logname>.1", etc and add to the link in the
646c93c462eSCheng Sean Ye 		 * reverse numeric order.
647c93c462eSCheng Sean Ye 		 */
648c93c462eSCheng Sean Ye 		if (strlen(dp->d_name) < len + 2 ||
649c93c462eSCheng Sean Ye 		    strncmp(dp->d_name, logname, len) != 0 ||
650c93c462eSCheng Sean Ye 		    dp->d_name[len] != '.')
651c93c462eSCheng Sean Ye 			continue;
652c93c462eSCheng Sean Ye 
653c93c462eSCheng Sean Ye 		/*
654c93c462eSCheng Sean Ye 		 * "*.0-" file normally should not be seen.  It may
655c93c462eSCheng Sean Ye 		 * exist when user manually run 'fmadm rotate'.
656c93c462eSCheng Sean Ye 		 * In such case, we put it at the end of the list so
657c93c462eSCheng Sean Ye 		 * it'll be dumped after all the rotated logs, before
658c93c462eSCheng Sean Ye 		 * the current one.
659c93c462eSCheng Sean Ye 		 */
660c93c462eSCheng Sean Ye 		if (strcmp(dp->d_name + len + 1, "0-") == 0)
661c93c462eSCheng Sean Ye 			addlink(&head, dirname, dp->d_name, -1);
662c93c462eSCheng Sean Ye 		else if ((suffix = strtol(dp->d_name + len + 1,
663c93c462eSCheng Sean Ye 		    &endptr, 10)) >= 0 && *endptr == '\0')
664c93c462eSCheng Sean Ye 			addlink(&head, dirname, dp->d_name, suffix);
665c93c462eSCheng Sean Ye 	}
666c93c462eSCheng Sean Ye 
667c93c462eSCheng Sean Ye 	(void) closedir(dirp);
668c93c462eSCheng Sean Ye 
669c93c462eSCheng Sean Ye 	return (head);
670c93c462eSCheng Sean Ye }
671c93c462eSCheng Sean Ye 
672f6e214c7SGavin Maltby /*
673f6e214c7SGavin Maltby  * Aggregate log files.  If ifiles is not NULL then one or more files
674f6e214c7SGavin Maltby  * were listed on the command line, and we will merge just those files.
675f6e214c7SGavin Maltby  * Otherwise we will merge all known log file types, and include the
676f6e214c7SGavin Maltby  * rotated logs for each type (you can suppress the inclusion of
677f6e214c7SGavin Maltby  * some logtypes through use of FMDUMP_AGGREGATE_IGNORE in the process
678f6e214c7SGavin Maltby  * environment, setting it to a comma-separated list of log labels and/or
679f6e214c7SGavin Maltby  * log filenames to ignore).
680f6e214c7SGavin Maltby  *
681f6e214c7SGavin Maltby  * We will not attempt to perform a chronological sort across all log records
682f6e214c7SGavin Maltby  * of all files.  Indeed, we won't even sort individual log files -
683f6e214c7SGavin Maltby  * we will not re-order events differently to how they appeared in their
684f6e214c7SGavin Maltby  * original log file.  This is because log files are already inherently
685f6e214c7SGavin Maltby  * ordered by the order in which fmd receives and processes events.
686f6e214c7SGavin Maltby  * So we determine the output order by comparing the "next" record
687f6e214c7SGavin Maltby  * off the top of each log file.
688f6e214c7SGavin Maltby  *
689f6e214c7SGavin Maltby  * We will construct a number of log record source "pipelines".  As above,
690f6e214c7SGavin Maltby  * the next record to render in the overall output is that from the
691f6e214c7SGavin Maltby  * pipeline with the oldest event.
692f6e214c7SGavin Maltby  *
693f6e214c7SGavin Maltby  * For the case that input logfiles were listed on the command line, each
694f6e214c7SGavin Maltby  * pipeline will process exactly one of those logfiles.  Distinct pipelines
695f6e214c7SGavin Maltby  * may process logfiles of the same "type" - eg if two "error" logs and
696f6e214c7SGavin Maltby  * one "fault" logs are specified then there'll be two pipelines producing
697f6e214c7SGavin Maltby  * events from "error" logs.
698f6e214c7SGavin Maltby  *
699f6e214c7SGavin Maltby  * If we are merging all known log types then we will construct exactly
700f6e214c7SGavin Maltby  * one pipeline for each known log type - one for error, one for fault, etc.
701f6e214c7SGavin Maltby  * Each pipeline will process first the rotated logs of that type and then
702f6e214c7SGavin Maltby  * move on to the current log of that type.
703f6e214c7SGavin Maltby  *
704f6e214c7SGavin Maltby  * The output from all pipelines flows into a serializer which selects
705f6e214c7SGavin Maltby  * the next record once all pipelines have asserted their output state.
706f6e214c7SGavin Maltby  * The output state of a pipeline is one of:
707f6e214c7SGavin Maltby  *
708f6e214c7SGavin Maltby  *	- record available: the next record from this pipeline is available
709f6e214c7SGavin Maltby  *	  for comparison and consumption
710f6e214c7SGavin Maltby  *
711f6e214c7SGavin Maltby  *	- done: this pipeline will produce no more records
712f6e214c7SGavin Maltby  *
713f6e214c7SGavin Maltby  *	- polling: this pipeline is polling for new records and will
714f6e214c7SGavin Maltby  *	  make them available as output if/when any are observed
715f6e214c7SGavin Maltby  *
716f6e214c7SGavin Maltby  *	- processing: output state will be updated shortly
717f6e214c7SGavin Maltby  *
718f6e214c7SGavin Maltby  * A pipeline iterates over each file queued to it using fmd_log_xiter.
719f6e214c7SGavin Maltby  * We do this in a separate thread for each pipeline.  The callback on
720f6e214c7SGavin Maltby  * each iteration must update the serializer to let it know that
721f6e214c7SGavin Maltby  * a new record is available.  In the serializer thread we decide whether
722f6e214c7SGavin Maltby  * we have all records expected have arrived and it is time to choose
723f6e214c7SGavin Maltby  * the next output record.
724f6e214c7SGavin Maltby  */
725f6e214c7SGavin Maltby 
726f6e214c7SGavin Maltby /*
727f6e214c7SGavin Maltby  * A pipeline descriptor.  The pl_cv condition variable is used together
728f6e214c7SGavin Maltby  * with pl_lock for initial synchronisation, and thereafter with the
729f6e214c7SGavin Maltby  * lock for the serializer for pausing and continuing this pipeline.
730f6e214c7SGavin Maltby  */
731f6e214c7SGavin Maltby struct fmdump_pipeline {
732f6e214c7SGavin Maltby 	pthread_mutex_t pl_lock;	/* used only in pipeline startup */
733f6e214c7SGavin Maltby 	int pl_started;			/* sync with main thread on startup */
734f6e214c7SGavin Maltby 	pthread_t pl_thr;		/* our processing thread */
735f6e214c7SGavin Maltby 	pthread_cond_t pl_cv;		/* see above */
736f6e214c7SGavin Maltby 	struct loglink *pl_rotated;	/* rotated logs to process first */
737f6e214c7SGavin Maltby 	char *pl_logpath;		/* target path to process */
738f6e214c7SGavin Maltby 	char *pl_processing;		/* path currently being processed */
739f6e214c7SGavin Maltby 	struct fmdump_srlzer *pl_srlzer;	/* link to serializer */
740f6e214c7SGavin Maltby 	int pl_srlzeridx;		/* serializer index for this pipeline */
741f6e214c7SGavin Maltby 	const fmdump_ops_t *pl_ops;	/* ops for the log type we're given */
742f6e214c7SGavin Maltby 	int pl_fmt;			/* FMDUMP_{SHORT,VERB1,VERB2,PRETTY} */
743f6e214c7SGavin Maltby 	boolean_t pl_follow;		/* go into poll mode at log end */
744f6e214c7SGavin Maltby 	fmdump_arg_t pl_arg;		/* arguments */
745f6e214c7SGavin Maltby };
746f6e214c7SGavin Maltby 
747f6e214c7SGavin Maltby enum fmdump_pipestate {
748f6e214c7SGavin Maltby 	FMDUMP_PIPE_PROCESSING = 0x1000,
749f6e214c7SGavin Maltby 	FMDUMP_PIPE_RECORDAVAIL,
750f6e214c7SGavin Maltby 	FMDUMP_PIPE_POLLING,
751f6e214c7SGavin Maltby 	FMDUMP_PIPE_DONE
752f6e214c7SGavin Maltby };
753f6e214c7SGavin Maltby 
754f6e214c7SGavin Maltby /*
755f6e214c7SGavin Maltby  * Each pipeline has an associated output slot in the serializer.  This
756f6e214c7SGavin Maltby  * must be updated with the serializer locked.  After update evaluate
757f6e214c7SGavin Maltby  * whether there are enough slots decided that we should select a
758f6e214c7SGavin Maltby  * record to output.
759f6e214c7SGavin Maltby  */
760f6e214c7SGavin Maltby struct fmdump_srlzer_slot {
761f6e214c7SGavin Maltby 	enum fmdump_pipestate ss_state;
762f6e214c7SGavin Maltby 	uint64_t ss_sec;
763f6e214c7SGavin Maltby 	uint64_t ss_nsec;
764f6e214c7SGavin Maltby };
765f6e214c7SGavin Maltby 
766f6e214c7SGavin Maltby /*
767f6e214c7SGavin Maltby  * All pipelines are linked to a single serializer.  The serializer
768f6e214c7SGavin Maltby  * structure must be updated under the ds_lock; this mutex is also
769f6e214c7SGavin Maltby  * paired with the pl_cv of individual pipelines (one mutex, many condvars)
770f6e214c7SGavin Maltby  * in pausing and continuing individual pipelines.
771f6e214c7SGavin Maltby  */
772f6e214c7SGavin Maltby struct fmdump_srlzer {
773f6e214c7SGavin Maltby 	struct fmdump_pipeline *ds_pipearr;	/* pipeline array */
774f6e214c7SGavin Maltby 	pthread_mutex_t ds_lock;		/* see above */
775f6e214c7SGavin Maltby 	uint32_t ds_pipecnt;			/* number of pipelines */
776f6e214c7SGavin Maltby 	uint32_t ds_pollcnt;			/* pipelines in poll mode */
777f6e214c7SGavin Maltby 	uint32_t ds_nrecordavail;		/* pipelines with a record */
778f6e214c7SGavin Maltby 	uint32_t ds_ndone;			/* completed pipelines */
779f6e214c7SGavin Maltby 	struct fmdump_srlzer_slot *ds_slot;	/* slot array */
780f6e214c7SGavin Maltby };
781f6e214c7SGavin Maltby 
782f6e214c7SGavin Maltby /*
783f6e214c7SGavin Maltby  * All known log types.  When aggregation is requested an no file list
784f6e214c7SGavin Maltby  * is provided we will process the logs identified here (if lt_enabled
785f6e214c7SGavin Maltby  * is true and not over-ridden by environment settings).  We also
786f6e214c7SGavin Maltby  * use this in determining the appropriate ops structure for each distinct
787f6e214c7SGavin Maltby  * label.
788f6e214c7SGavin Maltby  */
789f6e214c7SGavin Maltby static struct fmdump_logtype {
790f6e214c7SGavin Maltby 	const char *lt_label;		/* label from log header */
791f6e214c7SGavin Maltby 	boolean_t lt_enabled;		/* include in merge? */
792f6e214c7SGavin Maltby 	const char *lt_logname;		/* var/fm/fmd/%s */
793f6e214c7SGavin Maltby 	const fmdump_ops_t *lt_ops;
794f6e214c7SGavin Maltby } logtypes[] = {
795f6e214c7SGavin Maltby 	{
796f6e214c7SGavin Maltby 		"error",
797f6e214c7SGavin Maltby 		B_TRUE,
798f6e214c7SGavin Maltby 		"errlog",
799f6e214c7SGavin Maltby 		&fmdump_err_ops
800f6e214c7SGavin Maltby 	},
801f6e214c7SGavin Maltby 	{
802f6e214c7SGavin Maltby 		"fault",
803f6e214c7SGavin Maltby 		B_TRUE,
804f6e214c7SGavin Maltby 		"fltlog",
805f6e214c7SGavin Maltby 		&fmdump_flt_ops
806f6e214c7SGavin Maltby 	},
807f6e214c7SGavin Maltby 	{
808f6e214c7SGavin Maltby 		"info",
809f6e214c7SGavin Maltby 		B_TRUE,
810f6e214c7SGavin Maltby 		"infolog",
811f6e214c7SGavin Maltby 		&fmdump_info_ops
812f6e214c7SGavin Maltby 	},
813f6e214c7SGavin Maltby 	{
814f6e214c7SGavin Maltby 		"info",
815f6e214c7SGavin Maltby 		B_TRUE,
816f6e214c7SGavin Maltby 		"infolog_hival",
817f6e214c7SGavin Maltby 		&fmdump_info_ops
818f6e214c7SGavin Maltby 	},
819f6e214c7SGavin Maltby 	{
820f6e214c7SGavin Maltby 		"asru",
821f6e214c7SGavin Maltby 		B_FALSE,		/* not included unless in file list */
822f6e214c7SGavin Maltby 		NULL,
823f6e214c7SGavin Maltby 		&fmdump_asru_ops	/* but we need ops when it is */
824f6e214c7SGavin Maltby 	}
825f6e214c7SGavin Maltby };
826f6e214c7SGavin Maltby 
827f6e214c7SGavin Maltby /*
828f6e214c7SGavin Maltby  * Disable logtypes per environment setting.  Does not apply when a list
829f6e214c7SGavin Maltby  * of logs is provided on the command line.
830f6e214c7SGavin Maltby  */
831f6e214c7SGavin Maltby static void
do_disables(void)832f6e214c7SGavin Maltby do_disables(void)
833f6e214c7SGavin Maltby {
834f6e214c7SGavin Maltby 	char *env = getenv("FMDUMP_AGGREGATE_IGNORE");
835f6e214c7SGavin Maltby 	char *dup, *start, *tofree;
836f6e214c7SGavin Maltby 	int i;
837f6e214c7SGavin Maltby 
838f6e214c7SGavin Maltby 	if (env == NULL)
839f6e214c7SGavin Maltby 		return;
840f6e214c7SGavin Maltby 
841f6e214c7SGavin Maltby 	tofree = dup = strdup(env);
842f6e214c7SGavin Maltby 
843f6e214c7SGavin Maltby 	while (dup != NULL) {
844f6e214c7SGavin Maltby 		start = strsep(&dup, ",");
845f6e214c7SGavin Maltby 		for (i = 0; i < sizeof (logtypes) / sizeof (logtypes[0]); i++) {
846f6e214c7SGavin Maltby 			if (logtypes[i].lt_logname == NULL)
847f6e214c7SGavin Maltby 				continue;
848f6e214c7SGavin Maltby 
849f6e214c7SGavin Maltby 			if (strcmp(start, logtypes[i].lt_label) == 0 ||
850f6e214c7SGavin Maltby 			    strcmp(start, logtypes[i].lt_logname) == 0) {
851f6e214c7SGavin Maltby 				logtypes[i].lt_enabled = B_FALSE;
852f6e214c7SGavin Maltby 			}
853f6e214c7SGavin Maltby 		}
854f6e214c7SGavin Maltby 	}
855f6e214c7SGavin Maltby 
856f6e214c7SGavin Maltby 	free(tofree);
857f6e214c7SGavin Maltby }
858f6e214c7SGavin Maltby 
859f6e214c7SGavin Maltby static void
srlzer_enter(struct fmdump_pipeline * pl)860f6e214c7SGavin Maltby srlzer_enter(struct fmdump_pipeline *pl)
861f6e214c7SGavin Maltby {
862f6e214c7SGavin Maltby 	struct fmdump_srlzer *srlzer = pl->pl_srlzer;
863f6e214c7SGavin Maltby 
864f6e214c7SGavin Maltby 	(void) pthread_mutex_lock(&srlzer->ds_lock);
865f6e214c7SGavin Maltby }
866f6e214c7SGavin Maltby 
867f6e214c7SGavin Maltby static void
srlzer_exit(struct fmdump_pipeline * pl)868f6e214c7SGavin Maltby srlzer_exit(struct fmdump_pipeline *pl)
869f6e214c7SGavin Maltby {
870f6e214c7SGavin Maltby 	struct fmdump_srlzer *srlzer = pl->pl_srlzer;
871f6e214c7SGavin Maltby 
872f6e214c7SGavin Maltby 	ASSERT(MUTEX_HELD(&srlzer->ds_lock));
873f6e214c7SGavin Maltby 	(void) pthread_mutex_unlock(&srlzer->ds_lock);
874f6e214c7SGavin Maltby }
875f6e214c7SGavin Maltby 
876f6e214c7SGavin Maltby static struct fmdump_pipeline *
srlzer_choose(struct fmdump_srlzer * srlzer)877f6e214c7SGavin Maltby srlzer_choose(struct fmdump_srlzer *srlzer)
878f6e214c7SGavin Maltby {
879f6e214c7SGavin Maltby 	struct fmdump_srlzer_slot *slot, *oldest;
880f6e214c7SGavin Maltby 	int oldestidx = -1;
881f6e214c7SGavin Maltby 	int first = 1;
882f6e214c7SGavin Maltby 	int i;
883f6e214c7SGavin Maltby 
884f6e214c7SGavin Maltby 	ASSERT(MUTEX_HELD(&srlzer->ds_lock));
885f6e214c7SGavin Maltby 
886f6e214c7SGavin Maltby 	for (i = 0, slot = &srlzer->ds_slot[0]; i < srlzer->ds_pipecnt;
887f6e214c7SGavin Maltby 	    i++, slot++) {
888f6e214c7SGavin Maltby 		if (slot->ss_state != FMDUMP_PIPE_RECORDAVAIL)
889f6e214c7SGavin Maltby 			continue;
890f6e214c7SGavin Maltby 
891f6e214c7SGavin Maltby 		if (first) {
892f6e214c7SGavin Maltby 			oldest = slot;
893f6e214c7SGavin Maltby 			oldestidx = i;
894f6e214c7SGavin Maltby 			first = 0;
895f6e214c7SGavin Maltby 			continue;
896f6e214c7SGavin Maltby 		}
897f6e214c7SGavin Maltby 
898f6e214c7SGavin Maltby 		if (slot->ss_sec < oldest->ss_sec ||
899f6e214c7SGavin Maltby 		    slot->ss_sec == oldest->ss_sec &&
900f6e214c7SGavin Maltby 		    slot->ss_nsec < oldest->ss_nsec) {
901f6e214c7SGavin Maltby 			oldest = slot;
902f6e214c7SGavin Maltby 			oldestidx = i;
903f6e214c7SGavin Maltby 		}
904f6e214c7SGavin Maltby 	}
905f6e214c7SGavin Maltby 
906f6e214c7SGavin Maltby 	return (oldestidx >= 0 ? &srlzer->ds_pipearr[oldestidx] : NULL);
907f6e214c7SGavin Maltby }
908f6e214c7SGavin Maltby 
909f6e214c7SGavin Maltby static void
pipeline_stall(struct fmdump_pipeline * pl)910f6e214c7SGavin Maltby pipeline_stall(struct fmdump_pipeline *pl)
911f6e214c7SGavin Maltby {
912f6e214c7SGavin Maltby 	struct fmdump_srlzer *srlzer = pl->pl_srlzer;
913f6e214c7SGavin Maltby 
914f6e214c7SGavin Maltby 	ASSERT(MUTEX_HELD(&srlzer->ds_lock));
915f6e214c7SGavin Maltby 	(void) pthread_cond_wait(&pl->pl_cv, &srlzer->ds_lock);
916f6e214c7SGavin Maltby }
917f6e214c7SGavin Maltby 
918f6e214c7SGavin Maltby static void
pipeline_continue(struct fmdump_pipeline * pl)919f6e214c7SGavin Maltby pipeline_continue(struct fmdump_pipeline *pl)
920f6e214c7SGavin Maltby {
921f6e214c7SGavin Maltby 	struct fmdump_srlzer *srlzer = pl->pl_srlzer;
922f6e214c7SGavin Maltby 
923f6e214c7SGavin Maltby 	ASSERT(MUTEX_HELD(&srlzer->ds_lock));
924f6e214c7SGavin Maltby 	(void) pthread_cond_signal(&srlzer->ds_pipearr[pl->pl_srlzeridx].pl_cv);
925f6e214c7SGavin Maltby }
926f6e214c7SGavin Maltby 
927f6e214c7SGavin Maltby /*
928f6e214c7SGavin Maltby  * Called on each pipeline record iteration to make a new record
929f6e214c7SGavin Maltby  * available for input to the serializer.  Returns 0 to indicate that
930f6e214c7SGavin Maltby  * the caller must stall the pipeline, or 1 to indicate that the
931f6e214c7SGavin Maltby  * caller should go ahead and render their record.  If this record
932f6e214c7SGavin Maltby  * addition fills the serializer then choose a pipeline that must
933f6e214c7SGavin Maltby  * render output.
934f6e214c7SGavin Maltby  */
935f6e214c7SGavin Maltby static int
pipeline_output(struct fmdump_pipeline * pl,const fmd_log_record_t * rp)936f6e214c7SGavin Maltby pipeline_output(struct fmdump_pipeline *pl, const fmd_log_record_t *rp)
937f6e214c7SGavin Maltby {
938f6e214c7SGavin Maltby 	struct fmdump_srlzer *srlzer = pl->pl_srlzer;
939f6e214c7SGavin Maltby 	struct fmdump_srlzer_slot *slot;
940f6e214c7SGavin Maltby 	struct fmdump_pipeline *wpl;
941f6e214c7SGavin Maltby 	int thisidx = pl->pl_srlzeridx;
942f6e214c7SGavin Maltby 
943f6e214c7SGavin Maltby 	ASSERT(MUTEX_HELD(&srlzer->ds_lock));
944f6e214c7SGavin Maltby 
945f6e214c7SGavin Maltby 	slot = &srlzer->ds_slot[thisidx];
946f6e214c7SGavin Maltby 	slot->ss_state = FMDUMP_PIPE_RECORDAVAIL;
947f6e214c7SGavin Maltby 	slot->ss_sec = rp->rec_sec;
948f6e214c7SGavin Maltby 	slot->ss_nsec = rp->rec_nsec;
949f6e214c7SGavin Maltby 	srlzer->ds_nrecordavail++;
950f6e214c7SGavin Maltby 
951f6e214c7SGavin Maltby 	/*
952f6e214c7SGavin Maltby 	 * Once all pipelines are polling we just render in arrival order.
953f6e214c7SGavin Maltby 	 */
954f6e214c7SGavin Maltby 	if (srlzer->ds_pollcnt == srlzer->ds_pipecnt)
955f6e214c7SGavin Maltby 		return (1);
956f6e214c7SGavin Maltby 
957f6e214c7SGavin Maltby 	/*
958f6e214c7SGavin Maltby 	 * If not all pipelines have asserted an output yet then the
959f6e214c7SGavin Maltby 	 * caller must block.
960f6e214c7SGavin Maltby 	 */
961f6e214c7SGavin Maltby 	if (srlzer->ds_nrecordavail + srlzer->ds_ndone +
962f6e214c7SGavin Maltby 	    srlzer->ds_pollcnt < srlzer->ds_pipecnt)
963f6e214c7SGavin Maltby 		return (0);
964f6e214c7SGavin Maltby 
965f6e214c7SGavin Maltby 	/*
966f6e214c7SGavin Maltby 	 * Right so it's time to turn the crank by choosing which of the
967f6e214c7SGavin Maltby 	 * filled line of slots should produce output.  If it is the slot
968f6e214c7SGavin Maltby 	 * for our caller then return their index to them, otherwise return
969f6e214c7SGavin Maltby 	 * -1 to the caller to make them block and cv_signal the winner.
970f6e214c7SGavin Maltby 	 */
971f6e214c7SGavin Maltby 	wpl = srlzer_choose(srlzer);
972f6e214c7SGavin Maltby 	ASSERT(wpl != NULL);
973f6e214c7SGavin Maltby 
974f6e214c7SGavin Maltby 	if (wpl == pl)
975f6e214c7SGavin Maltby 		return (1);
976f6e214c7SGavin Maltby 
977f6e214c7SGavin Maltby 	/* Wake the oldest, and return 0 to put the caller to sleep */
978f6e214c7SGavin Maltby 	pipeline_continue(wpl);
979f6e214c7SGavin Maltby 
980f6e214c7SGavin Maltby 	return (0);
981f6e214c7SGavin Maltby }
982f6e214c7SGavin Maltby 
983f6e214c7SGavin Maltby static void
pipeline_mark_consumed(struct fmdump_pipeline * pl)984f6e214c7SGavin Maltby pipeline_mark_consumed(struct fmdump_pipeline *pl)
985f6e214c7SGavin Maltby {
986f6e214c7SGavin Maltby 	struct fmdump_srlzer *srlzer = pl->pl_srlzer;
987f6e214c7SGavin Maltby 
988f6e214c7SGavin Maltby 	ASSERT(MUTEX_HELD(&srlzer->ds_lock));
989f6e214c7SGavin Maltby 	srlzer->ds_slot[pl->pl_srlzeridx].ss_state = FMDUMP_PIPE_PROCESSING;
990f6e214c7SGavin Maltby 	srlzer->ds_nrecordavail--;
991f6e214c7SGavin Maltby }
992f6e214c7SGavin Maltby 
993f6e214c7SGavin Maltby static void
pipeline_done(struct fmdump_pipeline * pl)994f6e214c7SGavin Maltby pipeline_done(struct fmdump_pipeline *pl)
995f6e214c7SGavin Maltby {
996f6e214c7SGavin Maltby 	struct fmdump_srlzer *srlzer = pl->pl_srlzer;
997f6e214c7SGavin Maltby 	struct fmdump_pipeline *wpl;
998f6e214c7SGavin Maltby 
999f6e214c7SGavin Maltby 	srlzer_enter(pl);
1000f6e214c7SGavin Maltby 
1001f6e214c7SGavin Maltby 	srlzer->ds_slot[pl->pl_srlzeridx].ss_state = FMDUMP_PIPE_DONE;
1002f6e214c7SGavin Maltby 	srlzer->ds_ndone++;
1003f6e214c7SGavin Maltby 	wpl = srlzer_choose(srlzer);
1004f6e214c7SGavin Maltby 	if (wpl != NULL)
1005f6e214c7SGavin Maltby 		pipeline_continue(wpl);
1006f6e214c7SGavin Maltby 
1007f6e214c7SGavin Maltby 	srlzer_exit(pl);
1008f6e214c7SGavin Maltby }
1009f6e214c7SGavin Maltby 
1010f6e214c7SGavin Maltby static void
pipeline_pollmode(struct fmdump_pipeline * pl)1011f6e214c7SGavin Maltby pipeline_pollmode(struct fmdump_pipeline *pl)
1012f6e214c7SGavin Maltby {
1013f6e214c7SGavin Maltby 	struct fmdump_srlzer *srlzer = pl->pl_srlzer;
1014f6e214c7SGavin Maltby 	struct fmdump_pipeline *wpl;
1015f6e214c7SGavin Maltby 
1016f6e214c7SGavin Maltby 	if (srlzer->ds_slot[pl->pl_srlzeridx].ss_state == FMDUMP_PIPE_POLLING)
1017f6e214c7SGavin Maltby 		return;
1018f6e214c7SGavin Maltby 
1019f6e214c7SGavin Maltby 	srlzer_enter(pl);
1020f6e214c7SGavin Maltby 
1021f6e214c7SGavin Maltby 	srlzer->ds_slot[pl->pl_srlzeridx].ss_state = FMDUMP_PIPE_POLLING;
1022f6e214c7SGavin Maltby 	if (++srlzer->ds_pollcnt + srlzer->ds_nrecordavail ==
1023f6e214c7SGavin Maltby 	    srlzer->ds_pipecnt && (wpl = srlzer_choose(srlzer)) != NULL)
1024f6e214c7SGavin Maltby 		pipeline_continue(wpl);
1025f6e214c7SGavin Maltby 
1026f6e214c7SGavin Maltby 	srlzer_exit(pl);
1027f6e214c7SGavin Maltby }
1028f6e214c7SGavin Maltby 
1029f6e214c7SGavin Maltby static int
pipeline_err(fmd_log_t * lp,void * arg)1030f6e214c7SGavin Maltby pipeline_err(fmd_log_t *lp, void *arg)
1031f6e214c7SGavin Maltby {
1032f6e214c7SGavin Maltby 	struct fmdump_pipeline *pl = (struct fmdump_pipeline *)arg;
1033f6e214c7SGavin Maltby 
1034f6e214c7SGavin Maltby 	fmdump_warn("skipping record in %s: %s\n", pl->pl_processing,
1035f6e214c7SGavin Maltby 	    fmd_log_errmsg(lp, fmd_log_errno(lp)));
1036f6e214c7SGavin Maltby 	g_errs++;
1037f6e214c7SGavin Maltby 
1038f6e214c7SGavin Maltby 	return (0);
1039f6e214c7SGavin Maltby }
1040f6e214c7SGavin Maltby 
1041f6e214c7SGavin Maltby static int
pipeline_cb(fmd_log_t * lp,const fmd_log_record_t * rp,void * arg)1042f6e214c7SGavin Maltby pipeline_cb(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg)
1043f6e214c7SGavin Maltby {
1044f6e214c7SGavin Maltby 	struct fmdump_pipeline *pl = (struct fmdump_pipeline *)arg;
1045f6e214c7SGavin Maltby 	int rc;
1046f6e214c7SGavin Maltby 
1047f6e214c7SGavin Maltby 	fmd_log_rec_f *func = pl->pl_arg.da_fmt->do_func;
1048f6e214c7SGavin Maltby 
1049f6e214c7SGavin Maltby 	srlzer_enter(pl);
1050f6e214c7SGavin Maltby 
1051f6e214c7SGavin Maltby 	if (!pipeline_output(pl, rp))
1052f6e214c7SGavin Maltby 		pipeline_stall(pl);
1053f6e214c7SGavin Maltby 
1054f6e214c7SGavin Maltby 	rc = func(lp, rp, pl->pl_arg.da_fp);
1055f6e214c7SGavin Maltby 	pipeline_mark_consumed(pl);
1056f6e214c7SGavin Maltby 
1057f6e214c7SGavin Maltby 	srlzer_exit(pl);
1058f6e214c7SGavin Maltby 
1059f6e214c7SGavin Maltby 	return (rc);
1060f6e214c7SGavin Maltby }
1061f6e214c7SGavin Maltby 
1062f6e214c7SGavin Maltby static void
pipeline_process(struct fmdump_pipeline * pl,char * logpath,boolean_t follow)1063f6e214c7SGavin Maltby pipeline_process(struct fmdump_pipeline *pl, char *logpath, boolean_t follow)
1064f6e214c7SGavin Maltby {
1065f6e214c7SGavin Maltby 	fmd_log_header_t log;
1066f6e214c7SGavin Maltby 	fmd_log_t *lp;
1067f6e214c7SGavin Maltby 	int err;
1068f6e214c7SGavin Maltby 	int i;
1069f6e214c7SGavin Maltby 
1070f6e214c7SGavin Maltby 	pl->pl_processing = logpath;
1071f6e214c7SGavin Maltby 
1072f6e214c7SGavin Maltby 	if ((lp = fmd_log_open(FMD_LOG_VERSION, logpath, &err)) == NULL) {
1073f6e214c7SGavin Maltby 		fmdump_warn("failed to open %s: %s\n",
1074f6e214c7SGavin Maltby 		    logpath, fmd_log_errmsg(NULL, err));
1075f6e214c7SGavin Maltby 		g_errs++;
1076f6e214c7SGavin Maltby 		return;
1077f6e214c7SGavin Maltby 	}
1078f6e214c7SGavin Maltby 
1079f6e214c7SGavin Maltby 	fmd_log_header(lp, &log);
1080f6e214c7SGavin Maltby 	for (i = 0; i < sizeof (logtypes) / sizeof (logtypes[0]); i++) {
1081f6e214c7SGavin Maltby 		if (strcmp(log.log_label, logtypes[i].lt_label) == 0) {
1082f6e214c7SGavin Maltby 			pl->pl_ops = logtypes[i].lt_ops;
1083f6e214c7SGavin Maltby 			pl->pl_arg.da_fmt =
1084f6e214c7SGavin Maltby 			    &pl->pl_ops->do_formats[pl->pl_fmt];
1085f6e214c7SGavin Maltby 			break;
1086f6e214c7SGavin Maltby 		}
1087f6e214c7SGavin Maltby 	}
1088f6e214c7SGavin Maltby 
1089f6e214c7SGavin Maltby 	if (pl->pl_ops == NULL) {
1090f6e214c7SGavin Maltby 		fmdump_warn("unknown log type %s for %s\n",
1091f6e214c7SGavin Maltby 		    log.log_label, logpath);
1092f6e214c7SGavin Maltby 		g_errs++;
1093f6e214c7SGavin Maltby 		return;
1094f6e214c7SGavin Maltby 	}
1095f6e214c7SGavin Maltby 
1096f6e214c7SGavin Maltby 	do {
1097f6e214c7SGavin Maltby 		if (fmd_log_xiter(lp, FMD_LOG_XITER_REFS, pl->pl_arg.da_fc,
1098f6e214c7SGavin Maltby 		    pl->pl_arg.da_fv, pipeline_cb, pipeline_err, (void *)pl,
1099f6e214c7SGavin Maltby 		    NULL) != 0) {
1100f6e214c7SGavin Maltby 			fmdump_warn("failed to dump %s: %s\n",
1101f6e214c7SGavin Maltby 			    logpath, fmd_log_errmsg(lp, fmd_log_errno(lp)));
1102f6e214c7SGavin Maltby 			g_errs++;
1103f6e214c7SGavin Maltby 			fmd_log_close(lp);
1104f6e214c7SGavin Maltby 			return;
1105f6e214c7SGavin Maltby 		}
1106f6e214c7SGavin Maltby 
1107f6e214c7SGavin Maltby 		if (follow) {
1108f6e214c7SGavin Maltby 			pipeline_pollmode(pl);
1109f6e214c7SGavin Maltby 			(void) sleep(1);
1110f6e214c7SGavin Maltby 		}
1111f6e214c7SGavin Maltby 
1112f6e214c7SGavin Maltby 	} while (follow);
1113f6e214c7SGavin Maltby 
1114f6e214c7SGavin Maltby 	fmd_log_close(lp);
1115f6e214c7SGavin Maltby }
1116f6e214c7SGavin Maltby 
1117f6e214c7SGavin Maltby static void *
pipeline_thr(void * arg)1118f6e214c7SGavin Maltby pipeline_thr(void *arg)
1119f6e214c7SGavin Maltby {
1120f6e214c7SGavin Maltby 	struct fmdump_pipeline *pl = (struct fmdump_pipeline *)arg;
1121f6e214c7SGavin Maltby 	struct loglink *ll;
1122f6e214c7SGavin Maltby 
1123f6e214c7SGavin Maltby 	(void) pthread_mutex_lock(&pl->pl_lock);
1124f6e214c7SGavin Maltby 	pl->pl_started = 1;
1125f6e214c7SGavin Maltby 	(void) pthread_mutex_unlock(&pl->pl_lock);
1126f6e214c7SGavin Maltby 	(void) pthread_cond_signal(&pl->pl_cv);
1127f6e214c7SGavin Maltby 
1128f6e214c7SGavin Maltby 	for (ll = pl->pl_rotated; ll != NULL; ll = ll->next)
1129f6e214c7SGavin Maltby 		pipeline_process(pl, ll->path, B_FALSE);
1130f6e214c7SGavin Maltby 
1131f6e214c7SGavin Maltby 	pipeline_process(pl, pl->pl_logpath, pl->pl_follow);
1132f6e214c7SGavin Maltby 	pipeline_done(pl);
1133f6e214c7SGavin Maltby 
1134f6e214c7SGavin Maltby 	return (NULL);
1135f6e214c7SGavin Maltby }
1136f6e214c7SGavin Maltby 
1137f6e214c7SGavin Maltby 
1138f6e214c7SGavin Maltby 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,int opt_j)1139f6e214c7SGavin Maltby aggregate(char **ifiles, int n_ifiles, int opt_f,
1140f6e214c7SGavin Maltby     fmd_log_filter_t *fv, uint_t fc,
11412db6d663SJoshua M. Clulow     int opt_v, int opt_V, int opt_p, int opt_j)
1142f6e214c7SGavin Maltby {
1143f6e214c7SGavin Maltby 	struct fmdump_pipeline *pipeline, *pl;
1144f6e214c7SGavin Maltby 	struct fmdump_srlzer srlzer;
1145f6e214c7SGavin Maltby 	uint32_t npipe;
1146f6e214c7SGavin Maltby 	int fmt;
1147f6e214c7SGavin Maltby 	int i;
1148f6e214c7SGavin Maltby 
1149f6e214c7SGavin Maltby 	if (ifiles != NULL) {
1150f6e214c7SGavin Maltby 		npipe = n_ifiles;
1151f6e214c7SGavin Maltby 		pipeline = calloc(npipe, sizeof (struct fmdump_pipeline));
1152f6e214c7SGavin Maltby 		if (!pipeline)
1153f6e214c7SGavin Maltby 			fmdump_fatal("failed to allocate memory");
1154f6e214c7SGavin Maltby 
1155f6e214c7SGavin Maltby 		for (i = 0; i < n_ifiles; i++)
1156f6e214c7SGavin Maltby 			pipeline[i].pl_logpath = ifiles[i];
1157f6e214c7SGavin Maltby 	} else {
1158f6e214c7SGavin Maltby 		pipeline = calloc(sizeof (logtypes) / sizeof (logtypes[0]),
1159f6e214c7SGavin Maltby 		    sizeof (struct fmdump_pipeline));
1160f6e214c7SGavin Maltby 		if (!pipeline)
1161f6e214c7SGavin Maltby 			fmdump_fatal("failed to allocate memory");
1162f6e214c7SGavin Maltby 
1163f6e214c7SGavin Maltby 		do_disables();
1164f6e214c7SGavin Maltby 
1165f6e214c7SGavin Maltby 		npipe = 0;
1166f6e214c7SGavin Maltby 		for (i = 0; i < sizeof (logtypes) / sizeof (logtypes[0]); i++) {
1167f6e214c7SGavin Maltby 			struct fmdump_logtype *ltp = &logtypes[i];
1168f6e214c7SGavin Maltby 			char *logpath;
1169f6e214c7SGavin Maltby 
1170f6e214c7SGavin Maltby 			if (ltp->lt_enabled == B_FALSE)
1171f6e214c7SGavin Maltby 				continue;
1172f6e214c7SGavin Maltby 
1173f6e214c7SGavin Maltby 			if ((logpath = malloc(PATH_MAX)) == NULL)
1174f6e214c7SGavin Maltby 				fmdump_fatal("failed to allocate memory");
1175f6e214c7SGavin Maltby 
1176f6e214c7SGavin Maltby 			(void) snprintf(logpath, PATH_MAX,
1177f6e214c7SGavin Maltby 			    "%s/var/fm/fmd/%s",
1178f6e214c7SGavin Maltby 			    g_root ? g_root : "", ltp->lt_logname);
1179f6e214c7SGavin Maltby 
1180f6e214c7SGavin Maltby 			pipeline[npipe].pl_rotated =
1181f6e214c7SGavin Maltby 			    get_rotated_logs(logpath);
1182f6e214c7SGavin Maltby 
1183f6e214c7SGavin Maltby 			pipeline[npipe++].pl_logpath = logpath;
1184f6e214c7SGavin Maltby 		}
1185f6e214c7SGavin Maltby 	}
1186f6e214c7SGavin Maltby 
1187f6e214c7SGavin Maltby 	if (opt_V)
11882db6d663SJoshua M. Clulow 		fmt = opt_p ? FMDUMP_PRETTY : opt_j ? FMDUMP_JSON :
11892db6d663SJoshua M. Clulow 		    FMDUMP_VERB2;
1190f6e214c7SGavin Maltby 	else if (opt_v)
1191f6e214c7SGavin Maltby 		fmt = FMDUMP_VERB1;
1192f6e214c7SGavin Maltby 	else
1193f6e214c7SGavin Maltby 		fmt = FMDUMP_SHORT;
1194f6e214c7SGavin Maltby 
1195f6e214c7SGavin Maltby 	bzero(&srlzer, sizeof (srlzer));
1196f6e214c7SGavin Maltby 	srlzer.ds_pipearr = pipeline;
1197f6e214c7SGavin Maltby 	srlzer.ds_pipecnt = npipe;
1198f6e214c7SGavin Maltby 	srlzer.ds_slot = calloc(npipe, sizeof (struct fmdump_srlzer_slot));
1199f6e214c7SGavin Maltby 	if (!srlzer.ds_slot)
1200f6e214c7SGavin Maltby 		fmdump_fatal("failed to allocate memory");
1201f6e214c7SGavin Maltby 	(void) pthread_mutex_init(&srlzer.ds_lock, NULL);
1202f6e214c7SGavin Maltby 
1203f6e214c7SGavin Maltby 	for (i = 0, pl = &pipeline[0]; i < npipe; i++, pl++) {
1204f6e214c7SGavin Maltby 		(void) pthread_mutex_init(&pl->pl_lock, NULL);
1205f6e214c7SGavin Maltby 		(void) pthread_cond_init(&pl->pl_cv, NULL);
1206f6e214c7SGavin Maltby 		srlzer.ds_slot[i].ss_state = FMDUMP_PIPE_PROCESSING;
1207f6e214c7SGavin Maltby 		pl->pl_srlzer = &srlzer;
1208f6e214c7SGavin Maltby 		pl->pl_srlzeridx = i;
1209f6e214c7SGavin Maltby 		pl->pl_follow = opt_f ? B_TRUE : B_FALSE;
1210f6e214c7SGavin Maltby 		pl->pl_fmt = fmt;
1211f6e214c7SGavin Maltby 		pl->pl_arg.da_fv = fv;
1212f6e214c7SGavin Maltby 		pl->pl_arg.da_fc = fc;
1213f6e214c7SGavin Maltby 		pl->pl_arg.da_fp = stdout;
1214f6e214c7SGavin Maltby 
1215f6e214c7SGavin Maltby 		(void) pthread_mutex_lock(&pl->pl_lock);
1216f6e214c7SGavin Maltby 
1217f6e214c7SGavin Maltby 		if (pthread_create(&pl->pl_thr, NULL,
1218f6e214c7SGavin Maltby 		    pipeline_thr, (void *)pl) != 0)
1219f6e214c7SGavin Maltby 			fmdump_fatal("pthread_create for pipeline %d failed",
1220f6e214c7SGavin Maltby 			    i);
1221f6e214c7SGavin Maltby 	}
1222f6e214c7SGavin Maltby 
1223f6e214c7SGavin Maltby 	for (i = 0, pl = &pipeline[0]; i < npipe; i++, pl++) {
1224f6e214c7SGavin Maltby 		while (!pl->pl_started)
1225f6e214c7SGavin Maltby 			(void) pthread_cond_wait(&pl->pl_cv, &pl->pl_lock);
1226f6e214c7SGavin Maltby 
1227f6e214c7SGavin Maltby 		(void) pthread_mutex_unlock(&pl->pl_lock);
1228f6e214c7SGavin Maltby 	}
1229f6e214c7SGavin Maltby 
1230f6e214c7SGavin Maltby 	for (i = 0, pl = &pipeline[0]; i < npipe; i++, pl++)
1231f6e214c7SGavin Maltby 		(void) pthread_join(pl->pl_thr, NULL);
1232f6e214c7SGavin Maltby 
1233f6e214c7SGavin Maltby 	if (ifiles == NULL) {
1234f6e214c7SGavin Maltby 		for (i = 0; i < npipe; i++)
1235f6e214c7SGavin Maltby 			free(pipeline[i].pl_logpath);
1236f6e214c7SGavin Maltby 	}
1237f6e214c7SGavin Maltby 
1238f6e214c7SGavin Maltby 	free(srlzer.ds_slot);
1239f6e214c7SGavin Maltby 
1240f6e214c7SGavin Maltby 	free(pipeline);
1241f6e214c7SGavin Maltby 
1242f6e214c7SGavin Maltby 	return (FMDUMP_EXIT_SUCCESS);
1243f6e214c7SGavin Maltby }
1244f6e214c7SGavin Maltby 
1245f6e214c7SGavin Maltby static void
cleanup(char ** ifiles,int n_ifiles)1246f6e214c7SGavin Maltby cleanup(char **ifiles, int n_ifiles)
1247f6e214c7SGavin Maltby {
1248f6e214c7SGavin Maltby 	int i;
1249f6e214c7SGavin Maltby 
1250f6e214c7SGavin Maltby 	if (ifiles == NULL)
1251f6e214c7SGavin Maltby 		return;
1252f6e214c7SGavin Maltby 
1253f6e214c7SGavin Maltby 	for (i = 0; i < n_ifiles; i++) {
1254f6e214c7SGavin Maltby 		if (ifiles[i] != NULL) {
1255f6e214c7SGavin Maltby 			free(ifiles[i]);
1256f6e214c7SGavin Maltby 			ifiles[i] = NULL;
1257f6e214c7SGavin Maltby 		}
1258f6e214c7SGavin Maltby 	}
1259f6e214c7SGavin Maltby 
1260f6e214c7SGavin Maltby 	free(ifiles);
1261f6e214c7SGavin Maltby }
1262f6e214c7SGavin Maltby 
12637c478bd9Sstevel@tonic-gate int
main(int argc,char * argv[])12647c478bd9Sstevel@tonic-gate main(int argc, char *argv[])
12657c478bd9Sstevel@tonic-gate {
1266f6e214c7SGavin Maltby 	int opt_a = 0, opt_e = 0, opt_f = 0, opt_H = 0, opt_m = 0, opt_p = 0;
12672db6d663SJoshua M. Clulow 	int opt_u = 0, opt_v = 0, opt_V = 0, opt_j = 0;
1268f6e214c7SGavin Maltby 	int opt_i = 0, opt_I = 0;
1269f6e214c7SGavin Maltby 	int opt_A = 0;
1270f6e214c7SGavin Maltby 	char **ifiles = NULL;
1271f6e214c7SGavin Maltby 	char *ifile = NULL;
1272f6e214c7SGavin Maltby 	int n_ifiles;
1273f6e214c7SGavin Maltby 	int ifileidx = 0;
12747c478bd9Sstevel@tonic-gate 	int iflags = 0;
12757c478bd9Sstevel@tonic-gate 
12767c478bd9Sstevel@tonic-gate 	fmdump_arg_t arg;
12777c478bd9Sstevel@tonic-gate 	fmdump_lyr_t lyr;
12787c478bd9Sstevel@tonic-gate 	const fmdump_ops_t *ops;
12797c478bd9Sstevel@tonic-gate 	fmd_log_filter_t *filtv;
12807c478bd9Sstevel@tonic-gate 	uint_t filtc;
12817c478bd9Sstevel@tonic-gate 
12827c478bd9Sstevel@tonic-gate 	fmd_log_filter_t *errfv, *fltfv, *allfv;
12837c478bd9Sstevel@tonic-gate 	uint_t errfc = 0, fltfc = 0, allfc = 0;
12847c478bd9Sstevel@tonic-gate 
12857c478bd9Sstevel@tonic-gate 	fmd_log_header_t log;
12867c478bd9Sstevel@tonic-gate 	fmd_log_rec_f *func;
12877c478bd9Sstevel@tonic-gate 	void *farg;
12887c478bd9Sstevel@tonic-gate 	fmd_log_t *lp;
12897c478bd9Sstevel@tonic-gate 	int c, err;
12907c478bd9Sstevel@tonic-gate 	off64_t off = 0;
1291c93c462eSCheng Sean Ye 	ulong_t recs;
1292c93c462eSCheng Sean Ye 	struct loglink *rotated_logs = NULL, *llp;
12937c478bd9Sstevel@tonic-gate 
12947c478bd9Sstevel@tonic-gate 	g_pname = argv[0];
12957c478bd9Sstevel@tonic-gate 
12967c478bd9Sstevel@tonic-gate 	errfv = alloca(sizeof (fmd_log_filter_t) * argc);
12977c478bd9Sstevel@tonic-gate 	fltfv = alloca(sizeof (fmd_log_filter_t) * argc);
12987c478bd9Sstevel@tonic-gate 	allfv = alloca(sizeof (fmd_log_filter_t) * argc);
12997c478bd9Sstevel@tonic-gate 
13007c478bd9Sstevel@tonic-gate 	while (optind < argc) {
1301*8efd981eSKeith M Wesolowski 		while ((c = getopt(argc, argv,
1302*8efd981eSKeith M Wesolowski 		    "Aac:efHiIjmN:n:O:pR:t:T:u:vV")) != EOF) {
13037c478bd9Sstevel@tonic-gate 			switch (c) {
1304f6e214c7SGavin Maltby 			case 'A':
1305f6e214c7SGavin Maltby 				opt_A++;
1306f6e214c7SGavin Maltby 				break;
13077c478bd9Sstevel@tonic-gate 			case 'a':
13087c478bd9Sstevel@tonic-gate 				opt_a++;
13097c478bd9Sstevel@tonic-gate 				break;
13107c478bd9Sstevel@tonic-gate 			case 'c':
13117c478bd9Sstevel@tonic-gate 				errfv[errfc].filt_func = fmd_log_filter_class;
13127c478bd9Sstevel@tonic-gate 				errfv[errfc].filt_arg = optarg;
13137c478bd9Sstevel@tonic-gate 				allfv[allfc++] = errfv[errfc++];
13147c478bd9Sstevel@tonic-gate 				break;
13157c478bd9Sstevel@tonic-gate 			case 'e':
1316f6e214c7SGavin Maltby 				if (opt_i)
1317f6e214c7SGavin Maltby 					return (usage(stderr));
13187c478bd9Sstevel@tonic-gate 				opt_e++;
13197c478bd9Sstevel@tonic-gate 				break;
13207c478bd9Sstevel@tonic-gate 			case 'f':
13217c478bd9Sstevel@tonic-gate 				opt_f++;
13227c478bd9Sstevel@tonic-gate 				break;
13237c478bd9Sstevel@tonic-gate 			case 'H':
13247c478bd9Sstevel@tonic-gate 				opt_H++;
13257c478bd9Sstevel@tonic-gate 				break;
1326f6e214c7SGavin Maltby 			case 'i':
1327f6e214c7SGavin Maltby 				if (opt_e || opt_I)
1328f6e214c7SGavin Maltby 					return (usage(stderr));
1329f6e214c7SGavin Maltby 				opt_i++;
1330f6e214c7SGavin Maltby 				break;
1331f6e214c7SGavin Maltby 			case 'I':
1332f6e214c7SGavin Maltby 				if (opt_e || opt_i)
1333f6e214c7SGavin Maltby 					return (usage(stderr));
1334f6e214c7SGavin Maltby 				opt_I++;
1335f6e214c7SGavin Maltby 				break;
13362db6d663SJoshua M. Clulow 			case 'j':
13372db6d663SJoshua M. Clulow 				if (opt_p)
13382db6d663SJoshua M. Clulow 					return (usage(stderr));
13392db6d663SJoshua M. Clulow 				opt_j++;
13402db6d663SJoshua M. Clulow 				break;
1341b6955755SRobert Johnston 			case 'm':
1342b6955755SRobert Johnston 				opt_m++;
1343b6955755SRobert Johnston 				break;
1344*8efd981eSKeith M Wesolowski 			case 'N':
1345*8efd981eSKeith M Wesolowski 				fltfv[fltfc].filt_func =
1346*8efd981eSKeith M Wesolowski 				    fmd_log_filter_nv_multi;
1347*8efd981eSKeith M Wesolowski 				fltfv[fltfc].filt_arg =
1348*8efd981eSKeith M Wesolowski 				    setupnamevalue_multi(optarg);
1349*8efd981eSKeith M Wesolowski 				allfv[allfc++] = fltfv[fltfc++];
1350*8efd981eSKeith M Wesolowski 				break;
1351*8efd981eSKeith M Wesolowski 			case 'n':
1352*8efd981eSKeith M Wesolowski 				fltfv[fltfc].filt_func = fmd_log_filter_nv;
1353*8efd981eSKeith M Wesolowski 				fltfv[fltfc].filt_arg = setupnamevalue(optarg);
1354*8efd981eSKeith M Wesolowski 				allfv[allfc++] = fltfv[fltfc++];
1355*8efd981eSKeith M Wesolowski 				break;
1356*8efd981eSKeith M Wesolowski 			case 'O': {
1357*8efd981eSKeith M Wesolowski 				char *p;
1358*8efd981eSKeith M Wesolowski 
1359*8efd981eSKeith M Wesolowski 				errno = 0;
1360*8efd981eSKeith M Wesolowski 				off = strtoull(optarg, &p, 16);
1361*8efd981eSKeith M Wesolowski 
1362*8efd981eSKeith M Wesolowski 				if (errno != 0 || p == optarg || *p != '\0') {
1363*8efd981eSKeith M Wesolowski 					fmdump_usage(
1364*8efd981eSKeith M Wesolowski 					    "illegal offset format -- %s\n",
1365*8efd981eSKeith M Wesolowski 					    optarg);
1366*8efd981eSKeith M Wesolowski 				}
13677c478bd9Sstevel@tonic-gate 				iflags |= FMD_LOG_XITER_OFFS;
13687c478bd9Sstevel@tonic-gate 				break;
1369*8efd981eSKeith M Wesolowski 			}
1370f6e214c7SGavin Maltby 			case 'p':
13712db6d663SJoshua M. Clulow 				if (opt_j)
13722db6d663SJoshua M. Clulow 					return (usage(stderr));
1373f6e214c7SGavin Maltby 				opt_p++;
1374f6e214c7SGavin Maltby 				break;
13757c478bd9Sstevel@tonic-gate 			case 'R':
13767c478bd9Sstevel@tonic-gate 				g_root = optarg;
13777c478bd9Sstevel@tonic-gate 				break;
13787c478bd9Sstevel@tonic-gate 			case 't':
13797c478bd9Sstevel@tonic-gate 				errfv[errfc].filt_func = fmd_log_filter_after;
13807c478bd9Sstevel@tonic-gate 				errfv[errfc].filt_arg = gettimeopt(optarg);
13817c478bd9Sstevel@tonic-gate 				allfv[allfc++] = errfv[errfc++];
13827c478bd9Sstevel@tonic-gate 				break;
13837c478bd9Sstevel@tonic-gate 			case 'T':
13847c478bd9Sstevel@tonic-gate 				errfv[errfc].filt_func = fmd_log_filter_before;
13857c478bd9Sstevel@tonic-gate 				errfv[errfc].filt_arg = gettimeopt(optarg);
13867c478bd9Sstevel@tonic-gate 				allfv[allfc++] = errfv[errfc++];
13877c478bd9Sstevel@tonic-gate 				break;
13887c478bd9Sstevel@tonic-gate 			case 'u':
13897c478bd9Sstevel@tonic-gate 				fltfv[fltfc].filt_func = fmd_log_filter_uuid;
13907c478bd9Sstevel@tonic-gate 				fltfv[fltfc].filt_arg = optarg;
13917c478bd9Sstevel@tonic-gate 				allfv[allfc++] = fltfv[fltfc++];
13927c478bd9Sstevel@tonic-gate 				opt_u++;
13937c478bd9Sstevel@tonic-gate 				opt_a++; /* -u implies -a */
13947c478bd9Sstevel@tonic-gate 				break;
13957c478bd9Sstevel@tonic-gate 			case 'v':
13967c478bd9Sstevel@tonic-gate 				opt_v++;
13977c478bd9Sstevel@tonic-gate 				break;
13987c478bd9Sstevel@tonic-gate 			case 'V':
13997c478bd9Sstevel@tonic-gate 				opt_V++;
14007c478bd9Sstevel@tonic-gate 				break;
14017c478bd9Sstevel@tonic-gate 			default:
14027c478bd9Sstevel@tonic-gate 				return (usage(stderr));
14037c478bd9Sstevel@tonic-gate 			}
14047c478bd9Sstevel@tonic-gate 		}
14057c478bd9Sstevel@tonic-gate 
1406f6e214c7SGavin Maltby 		if (opt_A && (opt_e || opt_i || opt_I || opt_m || opt_u))
1407f6e214c7SGavin Maltby 			fmdump_usage("-A excludes all of "
1408f6e214c7SGavin Maltby 			    "-e, -i, -I, -m and -u\n");
1409f6e214c7SGavin Maltby 
14107c478bd9Sstevel@tonic-gate 		if (optind < argc) {
1411f6e214c7SGavin Maltby 			char *dest;
1412f6e214c7SGavin Maltby 
1413f6e214c7SGavin Maltby 			if (ifiles == NULL) {
1414f6e214c7SGavin Maltby 				n_ifiles = argc - optind;
1415f6e214c7SGavin Maltby 				ifiles = calloc(n_ifiles, sizeof (char *));
1416f6e214c7SGavin Maltby 				if (ifiles == NULL) {
1417f6e214c7SGavin Maltby 					fmdump_fatal(
1418f6e214c7SGavin Maltby 					    "failed to allocate memory for "
1419f6e214c7SGavin Maltby 					    "%d input file%s", n_ifiles,
1420f6e214c7SGavin Maltby 					    n_ifiles > 1 ? "s" : "");
1421f6e214c7SGavin Maltby 				}
14227c478bd9Sstevel@tonic-gate 			}
1423f6e214c7SGavin Maltby 
1424f6e214c7SGavin Maltby 			if (ifileidx > 0 && !opt_A)
1425f6e214c7SGavin Maltby 				fmdump_usage("illegal argument -- %s\n",
1426f6e214c7SGavin Maltby 				    argv[optind]);
1427f6e214c7SGavin Maltby 
1428*8efd981eSKeith M Wesolowski 			ASSERT(ifileidx < n_ifiles);
1429*8efd981eSKeith M Wesolowski 
1430f6e214c7SGavin Maltby 			if ((dest = malloc(PATH_MAX)) == NULL)
1431f6e214c7SGavin Maltby 				fmdump_fatal("failed to allocate memory");
1432f6e214c7SGavin Maltby 
1433f6e214c7SGavin Maltby 			(void) strlcpy(dest, argv[optind++], PATH_MAX);
1434f6e214c7SGavin Maltby 			ifiles[ifileidx++] = dest;
1435f6e214c7SGavin Maltby 		}
1436f6e214c7SGavin Maltby 	}
1437f6e214c7SGavin Maltby 
1438*8efd981eSKeith M Wesolowski 	/*
1439*8efd981eSKeith M Wesolowski 	 * It's possible that file arguments were interleaved with options and
1440*8efd981eSKeith M Wesolowski 	 * option arguments, in which case we allocated space for more file
1441*8efd981eSKeith M Wesolowski 	 * arguments that we actually got.  Adjust as required so that we don't
1442*8efd981eSKeith M Wesolowski 	 * reference invalid entries.
1443*8efd981eSKeith M Wesolowski 	 */
1444*8efd981eSKeith M Wesolowski 	n_ifiles = ifileidx;
1445*8efd981eSKeith M Wesolowski 
1446f6e214c7SGavin Maltby 	if (opt_A) {
1447f6e214c7SGavin Maltby 		int rc;
1448f6e214c7SGavin Maltby 
1449f6e214c7SGavin Maltby 		if (!opt_a) {
1450f6e214c7SGavin Maltby 			fltfv[fltfc].filt_func = log_filter_silent;
1451f6e214c7SGavin Maltby 			fltfv[fltfc].filt_arg = (void *)1;
1452f6e214c7SGavin Maltby 			allfv[allfc++] = fltfv[fltfc++];
1453f6e214c7SGavin Maltby 		}
1454f6e214c7SGavin Maltby 
1455f6e214c7SGavin Maltby 		rc = aggregate(ifiles, n_ifiles, opt_f,
1456f6e214c7SGavin Maltby 		    allfv, allfc,
14572db6d663SJoshua M. Clulow 		    opt_v, opt_V, opt_p, opt_j);
1458f6e214c7SGavin Maltby 
1459f6e214c7SGavin Maltby 		cleanup(ifiles, n_ifiles);
1460f6e214c7SGavin Maltby 		return (rc);
1461f6e214c7SGavin Maltby 	} else {
1462f6e214c7SGavin Maltby 		if (ifiles == NULL) {
1463f6e214c7SGavin Maltby 			if ((ifile = calloc(1, PATH_MAX)) == NULL)
1464f6e214c7SGavin Maltby 				fmdump_fatal("failed to allocate memory");
1465f6e214c7SGavin Maltby 		} else {
1466f6e214c7SGavin Maltby 			ifile = ifiles[0];
14677c478bd9Sstevel@tonic-gate 		}
14687c478bd9Sstevel@tonic-gate 	}
14697c478bd9Sstevel@tonic-gate 
1470f6e214c7SGavin Maltby 
14717c478bd9Sstevel@tonic-gate 	if (*ifile == '\0') {
1472f6e214c7SGavin Maltby 		const char *pfx, *sfx;
1473f6e214c7SGavin Maltby 
1474f6e214c7SGavin Maltby 		if (opt_u || (!opt_e && !opt_i && !opt_I)) {
1475f6e214c7SGavin Maltby 			pfx = "flt";
1476f6e214c7SGavin Maltby 			sfx = "";
1477f6e214c7SGavin Maltby 		} else {
1478f6e214c7SGavin Maltby 			if (opt_e) {
1479f6e214c7SGavin Maltby 				pfx = "err";
1480f6e214c7SGavin Maltby 				sfx = "";
1481f6e214c7SGavin Maltby 			} else {
1482f6e214c7SGavin Maltby 				pfx = "info";
1483f6e214c7SGavin Maltby 				sfx = opt_I ? "_hival" : "";
1484f6e214c7SGavin Maltby 			}
1485f6e214c7SGavin Maltby 		}
1486f6e214c7SGavin Maltby 
1487f6e214c7SGavin Maltby 		(void) snprintf(ifile, PATH_MAX, "%s/var/fm/fmd/%slog%s",
1488f6e214c7SGavin Maltby 		    g_root ? g_root : "", pfx, sfx);
1489c93c462eSCheng Sean Ye 		/*
1490c93c462eSCheng Sean Ye 		 * logadm may rotate the logs.  When no input file is specified,
1491c93c462eSCheng Sean Ye 		 * we try to dump all the rotated logs as well in the right
1492c93c462eSCheng Sean Ye 		 * order.
1493c93c462eSCheng Sean Ye 		 */
1494c93c462eSCheng Sean Ye 		if (!opt_H && off == 0)
1495c93c462eSCheng Sean Ye 			rotated_logs = get_rotated_logs(ifile);
14967c478bd9Sstevel@tonic-gate 	} else if (g_root != NULL) {
1497f6e214c7SGavin Maltby 		fmdump_usage("-R option is not appropriate "
1498f6e214c7SGavin Maltby 		    "when file operand is present\n");
14997c478bd9Sstevel@tonic-gate 	}
15007c478bd9Sstevel@tonic-gate 
1501f6e214c7SGavin Maltby 	if ((g_msg = fmd_msg_init(g_root, FMD_MSG_VERSION)) == NULL)
1502f6e214c7SGavin Maltby 		fmdump_fatal("failed to initialize libfmd_msg");
1503b6955755SRobert Johnston 
15047c478bd9Sstevel@tonic-gate 	if ((lp = fmd_log_open(FMD_LOG_VERSION, ifile, &err)) == NULL) {
1505f6e214c7SGavin Maltby 		fmdump_fatal("failed to open %s: %s\n", ifile,
1506f6e214c7SGavin Maltby 		    fmd_log_errmsg(NULL, err));
15077c478bd9Sstevel@tonic-gate 	}
15087c478bd9Sstevel@tonic-gate 
15097c478bd9Sstevel@tonic-gate 	if (opt_H) {
15107c478bd9Sstevel@tonic-gate 		fmd_log_header(lp, &log);
15117c478bd9Sstevel@tonic-gate 
15127c478bd9Sstevel@tonic-gate 		(void) printf("EXD_CREATOR = %s\n", log.log_creator);
15137c478bd9Sstevel@tonic-gate 		(void) printf("EXD_HOSTNAME = %s\n", log.log_hostname);
15147c478bd9Sstevel@tonic-gate 		(void) printf("EXD_FMA_LABEL = %s\n", log.log_label);
15157c478bd9Sstevel@tonic-gate 		(void) printf("EXD_FMA_VERSION = %s\n", log.log_version);
15167c478bd9Sstevel@tonic-gate 		(void) printf("EXD_FMA_OSREL = %s\n", log.log_osrelease);
15177c478bd9Sstevel@tonic-gate 		(void) printf("EXD_FMA_OSVER = %s\n", log.log_osversion);
15187c478bd9Sstevel@tonic-gate 		(void) printf("EXD_FMA_PLAT = %s\n", log.log_platform);
15197ee93e3bSdilpreet 		(void) printf("EXD_FMA_UUID = %s\n", log.log_uuid);
15207c478bd9Sstevel@tonic-gate 
15217c478bd9Sstevel@tonic-gate 		return (FMDUMP_EXIT_SUCCESS);
15227c478bd9Sstevel@tonic-gate 	}
15237c478bd9Sstevel@tonic-gate 
15247c478bd9Sstevel@tonic-gate 	if (off != 0 && fmd_log_seek(lp, off) != 0) {
1525f6e214c7SGavin Maltby 		fmdump_fatal("failed to seek %s: %s\n", ifile,
1526f6e214c7SGavin Maltby 		    fmd_log_errmsg(lp, fmd_log_errno(lp)));
15277c478bd9Sstevel@tonic-gate 	}
15287c478bd9Sstevel@tonic-gate 
15297c478bd9Sstevel@tonic-gate 	if (opt_e && opt_u)
15307c478bd9Sstevel@tonic-gate 		ops = &fmdump_err_ops;
15317c478bd9Sstevel@tonic-gate 	else if (strcmp(fmd_log_label(lp), fmdump_flt_ops.do_label) == 0)
15327c478bd9Sstevel@tonic-gate 		ops = &fmdump_flt_ops;
15337c478bd9Sstevel@tonic-gate 	else if (strcmp(fmd_log_label(lp), fmdump_asru_ops.do_label) == 0)
15347c478bd9Sstevel@tonic-gate 		ops = &fmdump_asru_ops;
1535f6e214c7SGavin Maltby 	else if (strcmp(fmd_log_label(lp), fmdump_info_ops.do_label) == 0)
1536f6e214c7SGavin Maltby 		ops = &fmdump_info_ops;
15377c478bd9Sstevel@tonic-gate 	else
15387c478bd9Sstevel@tonic-gate 		ops = &fmdump_err_ops;
15397c478bd9Sstevel@tonic-gate 
15407c478bd9Sstevel@tonic-gate 	if (!opt_a && ops == &fmdump_flt_ops) {
15417c478bd9Sstevel@tonic-gate 		fltfv[fltfc].filt_func = log_filter_silent;
15427c478bd9Sstevel@tonic-gate 		fltfv[fltfc].filt_arg = NULL;
15437c478bd9Sstevel@tonic-gate 		allfv[allfc++] = fltfv[fltfc++];
15447c478bd9Sstevel@tonic-gate 	}
15457c478bd9Sstevel@tonic-gate 
15467c478bd9Sstevel@tonic-gate 	if (opt_V) {
1547f6e214c7SGavin Maltby 		arg.da_fmt =
15482db6d663SJoshua M. Clulow 		    &ops->do_formats[opt_p ? FMDUMP_PRETTY :
15492db6d663SJoshua M. Clulow 		    opt_j ? FMDUMP_JSON : FMDUMP_VERB2];
15507c478bd9Sstevel@tonic-gate 		iflags |= FMD_LOG_XITER_REFS;
15517c478bd9Sstevel@tonic-gate 	} else if (opt_v) {
15527c478bd9Sstevel@tonic-gate 		arg.da_fmt = &ops->do_formats[FMDUMP_VERB1];
1553b6955755SRobert Johnston 	} else if (opt_m) {
1554b6955755SRobert Johnston 		arg.da_fmt = &ops->do_formats[FMDUMP_MSG];
15557c478bd9Sstevel@tonic-gate 	} else
15567c478bd9Sstevel@tonic-gate 		arg.da_fmt = &ops->do_formats[FMDUMP_SHORT];
15577c478bd9Sstevel@tonic-gate 
1558b6955755SRobert Johnston 	if (opt_m && arg.da_fmt->do_func == NULL) {
1559f6e214c7SGavin Maltby 		fmdump_usage("-m mode is not supported for "
1560f6e214c7SGavin Maltby 		    "log of type %s: %s\n", fmd_log_label(lp), ifile);
1561b6955755SRobert Johnston 	}
1562b6955755SRobert Johnston 
15637c478bd9Sstevel@tonic-gate 	arg.da_fv = errfv;
15647c478bd9Sstevel@tonic-gate 	arg.da_fc = errfc;
15657c478bd9Sstevel@tonic-gate 	arg.da_fp = stdout;
15667c478bd9Sstevel@tonic-gate 
15677c478bd9Sstevel@tonic-gate 	if (iflags & FMD_LOG_XITER_OFFS)
15687c478bd9Sstevel@tonic-gate 		fmdump_printf(arg.da_fp, "%16s ", "OFFSET");
15697c478bd9Sstevel@tonic-gate 
1570540db9a9SStephen Hanson 	if (arg.da_fmt->do_hdr && !(opt_V && ops == &fmdump_flt_ops))
15717c478bd9Sstevel@tonic-gate 		fmdump_printf(arg.da_fp, "%s\n", arg.da_fmt->do_hdr);
15727c478bd9Sstevel@tonic-gate 
15737c478bd9Sstevel@tonic-gate 	if (opt_e && opt_u) {
15747c478bd9Sstevel@tonic-gate 		iflags |= FMD_LOG_XITER_REFS;
15757c478bd9Sstevel@tonic-gate 		func = xref_iter;
15767c478bd9Sstevel@tonic-gate 		farg = &arg;
15777c478bd9Sstevel@tonic-gate 		filtc = fltfc;
15787c478bd9Sstevel@tonic-gate 		filtv = fltfv;
15797c478bd9Sstevel@tonic-gate 	} else {
15807c478bd9Sstevel@tonic-gate 		func = arg.da_fmt->do_func;
15817c478bd9Sstevel@tonic-gate 		farg = arg.da_fp;
15827c478bd9Sstevel@tonic-gate 		filtc = allfc;
15837c478bd9Sstevel@tonic-gate 		filtv = allfv;
15847c478bd9Sstevel@tonic-gate 	}
15857c478bd9Sstevel@tonic-gate 
15867c478bd9Sstevel@tonic-gate 	if (iflags & FMD_LOG_XITER_OFFS) {
15877c478bd9Sstevel@tonic-gate 		lyr.dy_func = func;
15887c478bd9Sstevel@tonic-gate 		lyr.dy_arg = farg;
15897c478bd9Sstevel@tonic-gate 		lyr.dy_fp = arg.da_fp;
15907c478bd9Sstevel@tonic-gate 		func = xoff_iter;
15917c478bd9Sstevel@tonic-gate 		farg = &lyr;
15927c478bd9Sstevel@tonic-gate 	}
15937c478bd9Sstevel@tonic-gate 
1594c93c462eSCheng Sean Ye 	for (llp = rotated_logs; llp != NULL; llp = llp->next) {
1595c93c462eSCheng Sean Ye 		fmd_log_t *rlp;
1596c93c462eSCheng Sean Ye 
1597c93c462eSCheng Sean Ye 		if ((rlp = fmd_log_open(FMD_LOG_VERSION, llp->path, &err))
1598c93c462eSCheng Sean Ye 		    == NULL) {
1599f6e214c7SGavin Maltby 			fmdump_warn("failed to open %s: %s\n",
1600f6e214c7SGavin Maltby 			    llp->path, fmd_log_errmsg(NULL, err));
1601c93c462eSCheng Sean Ye 			g_errs++;
1602c93c462eSCheng Sean Ye 			continue;
1603c93c462eSCheng Sean Ye 		}
1604c93c462eSCheng Sean Ye 
1605c93c462eSCheng Sean Ye 		recs = 0;
1606c93c462eSCheng Sean Ye 		if (fmd_log_xiter(rlp, iflags, filtc, filtv,
1607c93c462eSCheng Sean Ye 		    func, error, farg, &recs) != 0) {
1608f6e214c7SGavin Maltby 			fmdump_warn("failed to dump %s: %s\n", llp->path,
1609c93c462eSCheng Sean Ye 			    fmd_log_errmsg(rlp, fmd_log_errno(rlp)));
1610c93c462eSCheng Sean Ye 			g_errs++;
1611c93c462eSCheng Sean Ye 		}
1612c93c462eSCheng Sean Ye 		g_recs += recs;
1613c93c462eSCheng Sean Ye 
1614c93c462eSCheng Sean Ye 		fmd_log_close(rlp);
1615c93c462eSCheng Sean Ye 	}
1616c93c462eSCheng Sean Ye 
16177c478bd9Sstevel@tonic-gate 	do {
1618c93c462eSCheng Sean Ye 		recs = 0;
16197c478bd9Sstevel@tonic-gate 		if (fmd_log_xiter(lp, iflags, filtc, filtv,
1620c93c462eSCheng Sean Ye 		    func, error, farg, &recs) != 0) {
1621f6e214c7SGavin Maltby 			fmdump_warn("failed to dump %s: %s\n", ifile,
16227c478bd9Sstevel@tonic-gate 			    fmd_log_errmsg(lp, fmd_log_errno(lp)));
16237c478bd9Sstevel@tonic-gate 			g_errs++;
16247c478bd9Sstevel@tonic-gate 		}
1625c93c462eSCheng Sean Ye 		g_recs += recs;
16267c478bd9Sstevel@tonic-gate 
16277c478bd9Sstevel@tonic-gate 		if (opt_f)
16287c478bd9Sstevel@tonic-gate 			(void) sleep(1);
16297c478bd9Sstevel@tonic-gate 
16307c478bd9Sstevel@tonic-gate 	} while (opt_f);
16317c478bd9Sstevel@tonic-gate 
16327c478bd9Sstevel@tonic-gate 	if (!opt_f && g_recs == 0 && isatty(STDOUT_FILENO))
1633f6e214c7SGavin Maltby 		fmdump_warn("%s is empty\n", ifile);
16347c478bd9Sstevel@tonic-gate 
1635b6955755SRobert Johnston 	if (g_thp != NULL)
1636b6955755SRobert Johnston 		topo_close(g_thp);
1637b6955755SRobert Johnston 
16387c478bd9Sstevel@tonic-gate 	fmd_log_close(lp);
1639b6955755SRobert Johnston 	fmd_msg_fini(g_msg);
1640b6955755SRobert Johnston 
1641f6e214c7SGavin Maltby 	if (ifiles == NULL)
1642f6e214c7SGavin Maltby 		free(ifile);
1643f6e214c7SGavin Maltby 	else
1644f6e214c7SGavin Maltby 		cleanup(ifiles, n_ifiles);
1645f6e214c7SGavin Maltby 
16467c478bd9Sstevel@tonic-gate 	return (g_errs ? FMDUMP_EXIT_ERROR : FMDUMP_EXIT_SUCCESS);
16477c478bd9Sstevel@tonic-gate }
1648