xref: /illumos-gate/usr/src/cmd/savecore/savecore.c (revision bbf21555)
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
57950274eSeschrock  * Common Development and Distribution License (the "License").
67950274eSeschrock  * 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  */
217c478bd9Sstevel@tonic-gate /*
22f6e214c7SGavin Maltby  * Copyright (c) 1983, 2010, Oracle and/or its affiliates. All rights reserved.
2346648336SHans Rosenfeld  * Copyright 2019 Joyent, Inc.
247c478bd9Sstevel@tonic-gate  */
255ada8a07SAlexander Eremin /*
26222d1ab6SAlexander Eremin  * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
275ada8a07SAlexander Eremin  */
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate #include <stdio.h>
307c478bd9Sstevel@tonic-gate #include <stdlib.h>
317c478bd9Sstevel@tonic-gate #include <stdarg.h>
327c478bd9Sstevel@tonic-gate #include <unistd.h>
337c478bd9Sstevel@tonic-gate #include <fcntl.h>
347c478bd9Sstevel@tonic-gate #include <errno.h>
357c478bd9Sstevel@tonic-gate #include <string.h>
367c478bd9Sstevel@tonic-gate #include <deflt.h>
377c478bd9Sstevel@tonic-gate #include <time.h>
387c478bd9Sstevel@tonic-gate #include <syslog.h>
397c478bd9Sstevel@tonic-gate #include <stropts.h>
40ca3e8d88SDave Plauger #include <pthread.h>
41ca3e8d88SDave Plauger #include <limits.h>
42ca3e8d88SDave Plauger #include <atomic.h>
43f6e214c7SGavin Maltby #include <libnvpair.h>
44f6e214c7SGavin Maltby #include <libintl.h>
457c478bd9Sstevel@tonic-gate #include <sys/mem.h>
467c478bd9Sstevel@tonic-gate #include <sys/statvfs.h>
477c478bd9Sstevel@tonic-gate #include <sys/dumphdr.h>
487c478bd9Sstevel@tonic-gate #include <sys/dumpadm.h>
497c478bd9Sstevel@tonic-gate #include <sys/compress.h>
50f6e214c7SGavin Maltby #include <sys/panic.h>
517c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
52ca3e8d88SDave Plauger #include <sys/stat.h>
53ca3e8d88SDave Plauger #include <sys/resource.h>
54ca3e8d88SDave Plauger #include <bzip2/bzlib.h>
55f6e214c7SGavin Maltby #include <sys/fm/util.h>
56f6e214c7SGavin Maltby #include <fm/libfmevent.h>
57f6e214c7SGavin Maltby #include <sys/int_fmtio.h>
58f6e214c7SGavin Maltby 
59ca3e8d88SDave Plauger 
60ca3e8d88SDave Plauger /* fread/fwrite buffer size */
61ca3e8d88SDave Plauger #define	FBUFSIZE		(1ULL << 20)
62ca3e8d88SDave Plauger 
63ca3e8d88SDave Plauger /* minimum size for output buffering */
64ca3e8d88SDave Plauger #define	MINCOREBLKSIZE		(1ULL << 17)
65ca3e8d88SDave Plauger 
66ca3e8d88SDave Plauger /* create this file if metrics collection is enabled in the kernel */
67ca3e8d88SDave Plauger #define	METRICSFILE "METRICS.csv"
687c478bd9Sstevel@tonic-gate 
69f6e214c7SGavin Maltby static char	progname[9] = "savecore";
707c478bd9Sstevel@tonic-gate static char	*savedir;		/* savecore directory */
717c478bd9Sstevel@tonic-gate static char	*dumpfile;		/* source of raw crash dump */
72f6e214c7SGavin Maltby static long	bounds = -1;		/* numeric suffix */
737c478bd9Sstevel@tonic-gate static long	pagesize;		/* dump pagesize */
747c478bd9Sstevel@tonic-gate static int	dumpfd = -1;		/* dumpfile descriptor */
75222d1ab6SAlexander Eremin static boolean_t have_dumpfile = B_TRUE;	/* dumpfile existence */
767c478bd9Sstevel@tonic-gate static dumphdr_t corehdr, dumphdr;	/* initial and terminal dumphdrs */
77f6e214c7SGavin Maltby static boolean_t dump_incomplete;	/* dumphdr indicates incomplete */
78f6e214c7SGavin Maltby static boolean_t fm_panic;		/* dump is the result of fm_panic */
797c478bd9Sstevel@tonic-gate static offset_t	endoff;			/* offset of end-of-dump header */
807c478bd9Sstevel@tonic-gate static int	verbose;		/* chatty mode */
817c478bd9Sstevel@tonic-gate static int	disregard_valid_flag;	/* disregard valid flag */
827c478bd9Sstevel@tonic-gate static int	livedump;		/* dump the current running system */
83ca3e8d88SDave Plauger static int	interactive;		/* user invoked; no syslog */
84ca3e8d88SDave Plauger static int	csave;			/* save dump compressed */
85ca3e8d88SDave Plauger static int	filemode;		/* processing file, not dump device */
86ca3e8d88SDave Plauger static int	percent_done;		/* progress indicator */
872fb0949cSJoshua M. Clulow static int	sec_done;		/* progress last report time */
88ca3e8d88SDave Plauger static hrtime_t	startts;		/* timestamp at start */
89ca3e8d88SDave Plauger static volatile uint64_t saved;		/* count of pages written */
90ca3e8d88SDave Plauger static volatile uint64_t zpages;	/* count of zero pages not written */
91ca3e8d88SDave Plauger static dumpdatahdr_t datahdr;		/* compression info */
92ca3e8d88SDave Plauger static long	coreblksize;		/* preferred write size (st_blksize) */
93f6e214c7SGavin Maltby static int	cflag;			/* run as savecore -c */
94f6e214c7SGavin Maltby static int	mflag;			/* run as savecore -m */
9546648336SHans Rosenfeld static int	rflag;			/* run as savecore -r */
96f6e214c7SGavin Maltby 
97f6e214c7SGavin Maltby /*
98f6e214c7SGavin Maltby  * Payload information for the events we raise.  These are used
99f6e214c7SGavin Maltby  * in raise_event to determine what payload to include.
100f6e214c7SGavin Maltby  */
101f6e214c7SGavin Maltby #define	SC_PAYLOAD_SAVEDIR	0x0001	/* Include savedir in event */
102f6e214c7SGavin Maltby #define	SC_PAYLOAD_INSTANCE	0x0002	/* Include bounds instance number */
103f6e214c7SGavin Maltby #define	SC_PAYLOAD_IMAGEUUID	0x0004	/* Include dump OS instance uuid */
104f6e214c7SGavin Maltby #define	SC_PAYLOAD_CRASHTIME	0x0008	/* Include epoch crashtime */
105f6e214c7SGavin Maltby #define	SC_PAYLOAD_PANICSTR	0x0010	/* Include panic string */
106f6e214c7SGavin Maltby #define	SC_PAYLOAD_PANICSTACK	0x0020	/* Include panic string */
107f6e214c7SGavin Maltby #define	SC_PAYLOAD_FAILREASON	0x0040	/* Include failure reason */
108f6e214c7SGavin Maltby #define	SC_PAYLOAD_DUMPCOMPLETE	0x0080	/* Include completeness indicator */
109f6e214c7SGavin Maltby #define	SC_PAYLOAD_ISCOMPRESSED	0x0100	/* Dump is in vmdump.N form */
110f6e214c7SGavin Maltby #define	SC_PAYLOAD_DUMPADM_EN	0x0200	/* Is dumpadm enabled or not? */
111f6e214c7SGavin Maltby #define	SC_PAYLOAD_FM_PANIC	0x0400	/* Panic initiated by FMA */
112f6e214c7SGavin Maltby #define	SC_PAYLOAD_JUSTCHECKING	0x0800	/* Run with -c flag? */
113f6e214c7SGavin Maltby 
114f6e214c7SGavin Maltby enum sc_event_type {
115f6e214c7SGavin Maltby 	SC_EVENT_DUMP_PENDING,
116f6e214c7SGavin Maltby 	SC_EVENT_SAVECORE_FAILURE,
117f6e214c7SGavin Maltby 	SC_EVENT_DUMP_AVAILABLE
118f6e214c7SGavin Maltby };
119f6e214c7SGavin Maltby 
120f6e214c7SGavin Maltby /*
121f6e214c7SGavin Maltby  * Common payload
122f6e214c7SGavin Maltby  */
123f6e214c7SGavin Maltby #define	_SC_PAYLOAD_CMN \
124f6e214c7SGavin Maltby     SC_PAYLOAD_IMAGEUUID | \
125f6e214c7SGavin Maltby     SC_PAYLOAD_CRASHTIME | \
126f6e214c7SGavin Maltby     SC_PAYLOAD_PANICSTR | \
127f6e214c7SGavin Maltby     SC_PAYLOAD_PANICSTACK | \
128f6e214c7SGavin Maltby     SC_PAYLOAD_DUMPCOMPLETE | \
129f6e214c7SGavin Maltby     SC_PAYLOAD_FM_PANIC | \
130f6e214c7SGavin Maltby     SC_PAYLOAD_SAVEDIR
131f6e214c7SGavin Maltby 
132f6e214c7SGavin Maltby static const struct {
133f6e214c7SGavin Maltby 	const char *sce_subclass;
134f6e214c7SGavin Maltby 	uint32_t sce_payload;
135f6e214c7SGavin Maltby } sc_event[] = {
136f6e214c7SGavin Maltby 	/*
137f6e214c7SGavin Maltby 	 * SC_EVENT_DUMP_PENDING
138f6e214c7SGavin Maltby 	 */
139f6e214c7SGavin Maltby 	{
140f6e214c7SGavin Maltby 		"dump_pending_on_device",
141f6e214c7SGavin Maltby 		_SC_PAYLOAD_CMN | SC_PAYLOAD_DUMPADM_EN |
142f6e214c7SGavin Maltby 		    SC_PAYLOAD_JUSTCHECKING
143f6e214c7SGavin Maltby 	},
144f6e214c7SGavin Maltby 
145f6e214c7SGavin Maltby 	/*
146f6e214c7SGavin Maltby 	 * SC_EVENT_SAVECORE_FAILURE
147f6e214c7SGavin Maltby 	 */
148f6e214c7SGavin Maltby 	{
149f6e214c7SGavin Maltby 		"savecore_failure",
150f6e214c7SGavin Maltby 		_SC_PAYLOAD_CMN | SC_PAYLOAD_INSTANCE | SC_PAYLOAD_FAILREASON
151f6e214c7SGavin Maltby 	},
152f6e214c7SGavin Maltby 
153f6e214c7SGavin Maltby 	/*
154f6e214c7SGavin Maltby 	 * SC_EVENT_DUMP_AVAILABLE
155f6e214c7SGavin Maltby 	 */
156f6e214c7SGavin Maltby 	{
157f6e214c7SGavin Maltby 		"dump_available",
158f6e214c7SGavin Maltby 		_SC_PAYLOAD_CMN | SC_PAYLOAD_INSTANCE | SC_PAYLOAD_ISCOMPRESSED
159f6e214c7SGavin Maltby 	},
160f6e214c7SGavin Maltby };
161f6e214c7SGavin Maltby 
162f6e214c7SGavin Maltby static void raise_event(enum sc_event_type, char *);
1637c478bd9Sstevel@tonic-gate 
1647c478bd9Sstevel@tonic-gate static void
usage(void)1657c478bd9Sstevel@tonic-gate usage(void)
1667c478bd9Sstevel@tonic-gate {
1677c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr,
16846648336SHans Rosenfeld 	    "usage: %s [-L | -r] [-vd] [-f dumpfile] [dirname]\n", progname);
1697c478bd9Sstevel@tonic-gate 	exit(1);
1707c478bd9Sstevel@tonic-gate }
1717c478bd9Sstevel@tonic-gate 
172f6e214c7SGavin Maltby #define	SC_SL_NONE	0x0001	/* no syslog */
173f6e214c7SGavin Maltby #define	SC_SL_ERR	0x0002	/* syslog if !interactive, LOG_ERR */
174f6e214c7SGavin Maltby #define	SC_SL_WARN	0x0004	/* syslog if !interactive, LOG_WARNING */
175f6e214c7SGavin Maltby #define	SC_IF_VERBOSE	0x0008	/* message only if -v */
176f6e214c7SGavin Maltby #define	SC_IF_ISATTY	0x0010	/* message only if interactive */
177f6e214c7SGavin Maltby #define	SC_EXIT_OK	0x0020	/* exit(0) */
178f6e214c7SGavin Maltby #define	SC_EXIT_ERR	0x0040	/* exit(1) */
179f6e214c7SGavin Maltby #define	SC_EXIT_PEND	0x0080	/* exit(2) */
180f6e214c7SGavin Maltby #define	SC_EXIT_FM	0x0100	/* exit(3) */
181f6e214c7SGavin Maltby 
182f6e214c7SGavin Maltby #define	_SC_ALLEXIT	(SC_EXIT_OK | SC_EXIT_ERR | SC_EXIT_PEND | SC_EXIT_FM)
183f6e214c7SGavin Maltby 
1847c478bd9Sstevel@tonic-gate static void
logprint(uint32_t flags,char * message,...)185f6e214c7SGavin Maltby logprint(uint32_t flags, char *message, ...)
1867c478bd9Sstevel@tonic-gate {
1877c478bd9Sstevel@tonic-gate 	va_list args;
1887c478bd9Sstevel@tonic-gate 	char buf[1024];
189f6e214c7SGavin Maltby 	int do_always = ((flags & (SC_IF_VERBOSE | SC_IF_ISATTY)) == 0);
190f6e214c7SGavin Maltby 	int do_ifverb = (flags & SC_IF_VERBOSE) && verbose;
191f6e214c7SGavin Maltby 	int do_ifisatty = (flags & SC_IF_ISATTY) && interactive;
192f6e214c7SGavin Maltby 	int code;
193f6e214c7SGavin Maltby 	static int logprint_raised = 0;
1947c478bd9Sstevel@tonic-gate 
195f6e214c7SGavin Maltby 	if (do_always || do_ifverb || do_ifisatty) {
1967c478bd9Sstevel@tonic-gate 		va_start(args, message);
197f6e214c7SGavin Maltby 		/*LINTED: E_SEC_PRINTF_VAR_FMT*/
198ca3e8d88SDave Plauger 		(void) vsnprintf(buf, sizeof (buf), message, args);
1997c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "%s: %s\n", progname, buf);
200f6e214c7SGavin Maltby 		if (!interactive) {
201f6e214c7SGavin Maltby 			switch (flags & (SC_SL_NONE | SC_SL_ERR | SC_SL_WARN)) {
202f6e214c7SGavin Maltby 			case SC_SL_ERR:
203f6e214c7SGavin Maltby 				/*LINTED: E_SEC_PRINTF_VAR_FMT*/
204f6e214c7SGavin Maltby 				syslog(LOG_ERR, buf);
205f6e214c7SGavin Maltby 				break;
206f6e214c7SGavin Maltby 
207f6e214c7SGavin Maltby 			case SC_SL_WARN:
208f6e214c7SGavin Maltby 				/*LINTED: E_SEC_PRINTF_VAR_FMT*/
209f6e214c7SGavin Maltby 				syslog(LOG_WARNING, buf);
210f6e214c7SGavin Maltby 				break;
211f6e214c7SGavin Maltby 
212f6e214c7SGavin Maltby 			default:
213f6e214c7SGavin Maltby 				break;
214f6e214c7SGavin Maltby 			}
215f6e214c7SGavin Maltby 		}
2167c478bd9Sstevel@tonic-gate 		va_end(args);
2177c478bd9Sstevel@tonic-gate 	}
218f6e214c7SGavin Maltby 
219f6e214c7SGavin Maltby 	switch (flags & _SC_ALLEXIT) {
220f6e214c7SGavin Maltby 	case 0:
221f6e214c7SGavin Maltby 		return;
222f6e214c7SGavin Maltby 
223f6e214c7SGavin Maltby 	case SC_EXIT_OK:
224f6e214c7SGavin Maltby 		code = 0;
225f6e214c7SGavin Maltby 		break;
226f6e214c7SGavin Maltby 
227f6e214c7SGavin Maltby 	case SC_EXIT_PEND:
2285ada8a07SAlexander Eremin 		/*
2295ada8a07SAlexander Eremin 		 * Raise an ireport saying why we are exiting.  Do not
2305ada8a07SAlexander Eremin 		 * raise if run as savecore -m.  If something in the
2315ada8a07SAlexander Eremin 		 * raise_event codepath calls logprint avoid recursion.
2325ada8a07SAlexander Eremin 		 */
23346648336SHans Rosenfeld 		if (!mflag && !rflag && logprint_raised++ == 0)
2345ada8a07SAlexander Eremin 			raise_event(SC_EVENT_SAVECORE_FAILURE, buf);
235f6e214c7SGavin Maltby 		code = 2;
236f6e214c7SGavin Maltby 		break;
237f6e214c7SGavin Maltby 
238f6e214c7SGavin Maltby 	case SC_EXIT_FM:
239f6e214c7SGavin Maltby 		code = 3;
240f6e214c7SGavin Maltby 		break;
241f6e214c7SGavin Maltby 
242f6e214c7SGavin Maltby 	case SC_EXIT_ERR:
243f6e214c7SGavin Maltby 	default:
24446648336SHans Rosenfeld 		if (!mflag && !rflag && logprint_raised++ == 0 && have_dumpfile)
245f6e214c7SGavin Maltby 			raise_event(SC_EVENT_SAVECORE_FAILURE, buf);
246f6e214c7SGavin Maltby 		code = 1;
247f6e214c7SGavin Maltby 		break;
248f6e214c7SGavin Maltby 	}
249f6e214c7SGavin Maltby 
250f6e214c7SGavin Maltby 	exit(code);
2517c478bd9Sstevel@tonic-gate }
2527c478bd9Sstevel@tonic-gate 
2537c478bd9Sstevel@tonic-gate /*
2547c478bd9Sstevel@tonic-gate  * System call / libc wrappers that exit on error.
2557c478bd9Sstevel@tonic-gate  */
2567c478bd9Sstevel@tonic-gate static int
Open(const char * name,int oflags,mode_t mode)2577c478bd9Sstevel@tonic-gate Open(const char *name, int oflags, mode_t mode)
2587c478bd9Sstevel@tonic-gate {
2597c478bd9Sstevel@tonic-gate 	int fd;
2607c478bd9Sstevel@tonic-gate 
2617c478bd9Sstevel@tonic-gate 	if ((fd = open64(name, oflags, mode)) == -1)
262f6e214c7SGavin Maltby 		logprint(SC_SL_ERR | SC_EXIT_ERR, "open(\"%s\"): %s",
2637c478bd9Sstevel@tonic-gate 		    name, strerror(errno));
2647c478bd9Sstevel@tonic-gate 	return (fd);
2657c478bd9Sstevel@tonic-gate }
2667c478bd9Sstevel@tonic-gate 
267ca3e8d88SDave Plauger static void
Fread(void * buf,size_t size,FILE * f)268ca3e8d88SDave Plauger Fread(void *buf, size_t size, FILE *f)
269ca3e8d88SDave Plauger {
270ca3e8d88SDave Plauger 	if (fread(buf, size, 1, f) != 1)
2710e06cf66SAlexander Eremin 		logprint(SC_SL_ERR | SC_EXIT_ERR, "fread: %s",
2720e06cf66SAlexander Eremin 		    strerror(errno));
273ca3e8d88SDave Plauger }
274ca3e8d88SDave Plauger 
275ca3e8d88SDave Plauger static void
Fwrite(void * buf,size_t size,FILE * f)276ca3e8d88SDave Plauger Fwrite(void *buf, size_t size, FILE *f)
277ca3e8d88SDave Plauger {
278ca3e8d88SDave Plauger 	if (fwrite(buf, size, 1, f) != 1)
279f6e214c7SGavin Maltby 		logprint(SC_SL_ERR | SC_EXIT_ERR, "fwrite: %s",
280f6e214c7SGavin Maltby 		    strerror(errno));
281ca3e8d88SDave Plauger }
282ca3e8d88SDave Plauger 
283ca3e8d88SDave Plauger static void
Fseek(offset_t off,FILE * f)284ca3e8d88SDave Plauger Fseek(offset_t off, FILE *f)
285ca3e8d88SDave Plauger {
286ca3e8d88SDave Plauger 	if (fseeko64(f, off, SEEK_SET) != 0)
287f6e214c7SGavin Maltby 		logprint(SC_SL_ERR | SC_EXIT_ERR, "fseeko64: %s",
288f6e214c7SGavin Maltby 		    strerror(errno));
289ca3e8d88SDave Plauger }
290ca3e8d88SDave Plauger 
291ca3e8d88SDave Plauger typedef struct stat64 Stat_t;
292ca3e8d88SDave Plauger 
293ca3e8d88SDave Plauger static void
Fstat(int fd,Stat_t * sb,const char * fname)294ca3e8d88SDave Plauger Fstat(int fd, Stat_t *sb, const char *fname)
295ca3e8d88SDave Plauger {
296ca3e8d88SDave Plauger 	if (fstat64(fd, sb) != 0)
297f6e214c7SGavin Maltby 		logprint(SC_SL_ERR | SC_EXIT_ERR, "fstat(\"%s\"): %s", fname,
298ca3e8d88SDave Plauger 		    strerror(errno));
299ca3e8d88SDave Plauger }
300ca3e8d88SDave Plauger 
301ca3e8d88SDave Plauger static void
Stat(const char * fname,Stat_t * sb)302ca3e8d88SDave Plauger Stat(const char *fname, Stat_t *sb)
303ca3e8d88SDave Plauger {
304222d1ab6SAlexander Eremin 	if (stat64(fname, sb) != 0) {
305222d1ab6SAlexander Eremin 		have_dumpfile = B_FALSE;
306222d1ab6SAlexander Eremin 		logprint(SC_SL_ERR | SC_EXIT_ERR, "failed to get status "
307222d1ab6SAlexander Eremin 		    "of file %s", fname);
308222d1ab6SAlexander Eremin 	}
309ca3e8d88SDave Plauger }
310ca3e8d88SDave Plauger 
3117c478bd9Sstevel@tonic-gate static void
Pread(int fd,void * buf,size_t size,offset_t off)3127c478bd9Sstevel@tonic-gate Pread(int fd, void *buf, size_t size, offset_t off)
3137c478bd9Sstevel@tonic-gate {
314ca3e8d88SDave Plauger 	ssize_t sz = pread64(fd, buf, size, off);
315ca3e8d88SDave Plauger 
316ca3e8d88SDave Plauger 	if (sz < 0)
317f6e214c7SGavin Maltby 		logprint(SC_SL_ERR | SC_EXIT_ERR,
318ca3e8d88SDave Plauger 		    "pread: %s", strerror(errno));
319ca3e8d88SDave Plauger 	else if (sz != size)
320f6e214c7SGavin Maltby 		logprint(SC_SL_ERR | SC_EXIT_ERR,
321ca3e8d88SDave Plauger 		    "pread: size %ld != %ld", sz, size);
3227c478bd9Sstevel@tonic-gate }
3237c478bd9Sstevel@tonic-gate 
3247c478bd9Sstevel@tonic-gate static void
Pwrite(int fd,void * buf,size_t size,off64_t off)325ca3e8d88SDave Plauger Pwrite(int fd, void *buf, size_t size, off64_t off)
3267c478bd9Sstevel@tonic-gate {
3277c478bd9Sstevel@tonic-gate 	if (pwrite64(fd, buf, size, off) != size)
328f6e214c7SGavin Maltby 		logprint(SC_SL_ERR | SC_EXIT_ERR, "pwrite: %s",
329f6e214c7SGavin Maltby 		    strerror(errno));
3307c478bd9Sstevel@tonic-gate }
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate static void *
Zalloc(size_t size)3337c478bd9Sstevel@tonic-gate Zalloc(size_t size)
3347c478bd9Sstevel@tonic-gate {
3357c478bd9Sstevel@tonic-gate 	void *buf;
3367c478bd9Sstevel@tonic-gate 
3377c478bd9Sstevel@tonic-gate 	if ((buf = calloc(size, 1)) == NULL)
338f6e214c7SGavin Maltby 		logprint(SC_SL_ERR | SC_EXIT_ERR, "calloc: %s",
339f6e214c7SGavin Maltby 		    strerror(errno));
3407c478bd9Sstevel@tonic-gate 	return (buf);
3417c478bd9Sstevel@tonic-gate }
3427c478bd9Sstevel@tonic-gate 
3437c478bd9Sstevel@tonic-gate static long
read_number_from_file(const char * filename,long default_value)3447c478bd9Sstevel@tonic-gate read_number_from_file(const char *filename, long default_value)
3457c478bd9Sstevel@tonic-gate {
3467c478bd9Sstevel@tonic-gate 	long file_value = -1;
3477c478bd9Sstevel@tonic-gate 	FILE *fp;
3487c478bd9Sstevel@tonic-gate 
3497c478bd9Sstevel@tonic-gate 	if ((fp = fopen(filename, "r")) != NULL) {
3507c478bd9Sstevel@tonic-gate 		(void) fscanf(fp, "%ld", &file_value);
3517c478bd9Sstevel@tonic-gate 		(void) fclose(fp);
3527c478bd9Sstevel@tonic-gate 	}
3537c478bd9Sstevel@tonic-gate 	return (file_value < 0 ? default_value : file_value);
3547c478bd9Sstevel@tonic-gate }
3557c478bd9Sstevel@tonic-gate 
3567c478bd9Sstevel@tonic-gate static void
read_dumphdr(void)3577c478bd9Sstevel@tonic-gate read_dumphdr(void)
3587c478bd9Sstevel@tonic-gate {
35946648336SHans Rosenfeld 	if (filemode || rflag)
360ca3e8d88SDave Plauger 		dumpfd = Open(dumpfile, O_RDONLY, 0644);
361ca3e8d88SDave Plauger 	else
362ca3e8d88SDave Plauger 		dumpfd = Open(dumpfile, O_RDWR | O_DSYNC, 0644);
3637c478bd9Sstevel@tonic-gate 	endoff = llseek(dumpfd, -DUMP_OFFSET, SEEK_END) & -DUMP_OFFSET;
3647c478bd9Sstevel@tonic-gate 	Pread(dumpfd, &dumphdr, sizeof (dumphdr), endoff);
365ca3e8d88SDave Plauger 	Pread(dumpfd, &datahdr, sizeof (datahdr), endoff + sizeof (dumphdr));
3667c478bd9Sstevel@tonic-gate 
3677c478bd9Sstevel@tonic-gate 	pagesize = dumphdr.dump_pagesize;
3687c478bd9Sstevel@tonic-gate 
3697c478bd9Sstevel@tonic-gate 	if (dumphdr.dump_magic != DUMP_MAGIC)
3705ada8a07SAlexander Eremin 		logprint(SC_SL_NONE | SC_EXIT_PEND, "bad magic number %x",
3717c478bd9Sstevel@tonic-gate 		    dumphdr.dump_magic);
3727c478bd9Sstevel@tonic-gate 
373ca3e8d88SDave Plauger 	if ((dumphdr.dump_flags & DF_VALID) == 0 && !disregard_valid_flag)
374f6e214c7SGavin Maltby 		logprint(SC_SL_NONE | SC_IF_VERBOSE | SC_EXIT_OK,
375f6e214c7SGavin Maltby 		    "dump already processed");
376ca3e8d88SDave Plauger 
3777c478bd9Sstevel@tonic-gate 	if (dumphdr.dump_version != DUMP_VERSION)
3785ada8a07SAlexander Eremin 		logprint(SC_SL_NONE | SC_IF_VERBOSE | SC_EXIT_PEND,
3797c478bd9Sstevel@tonic-gate 		    "dump version (%d) != %s version (%d)",
3807c478bd9Sstevel@tonic-gate 		    dumphdr.dump_version, progname, DUMP_VERSION);
3817c478bd9Sstevel@tonic-gate 
3827c478bd9Sstevel@tonic-gate 	if (dumphdr.dump_wordsize != DUMP_WORDSIZE)
3835ada8a07SAlexander Eremin 		logprint(SC_SL_NONE | SC_EXIT_PEND,
3847c478bd9Sstevel@tonic-gate 		    "dump is from %u-bit kernel - cannot save on %u-bit kernel",
3857c478bd9Sstevel@tonic-gate 		    dumphdr.dump_wordsize, DUMP_WORDSIZE);
386ca3e8d88SDave Plauger 
387ca3e8d88SDave Plauger 	if (datahdr.dump_datahdr_magic == DUMP_DATAHDR_MAGIC) {
388ca3e8d88SDave Plauger 		if (datahdr.dump_datahdr_version != DUMP_DATAHDR_VERSION)
3895ada8a07SAlexander Eremin 			logprint(SC_SL_NONE | SC_IF_VERBOSE | SC_EXIT_PEND,
390ca3e8d88SDave Plauger 			    "dump data version (%d) != %s data version (%d)",
391ca3e8d88SDave Plauger 			    datahdr.dump_datahdr_version, progname,
392ca3e8d88SDave Plauger 			    DUMP_DATAHDR_VERSION);
393ca3e8d88SDave Plauger 	} else {
394f6e214c7SGavin Maltby 		(void) memset(&datahdr, 0, sizeof (datahdr));
395ca3e8d88SDave Plauger 		datahdr.dump_maxcsize = pagesize;
396ca3e8d88SDave Plauger 	}
397ca3e8d88SDave Plauger 
3987c478bd9Sstevel@tonic-gate 	/*
3997c478bd9Sstevel@tonic-gate 	 * Read the initial header, clear the valid bits, and compare headers.
4007c478bd9Sstevel@tonic-gate 	 * The main header may have been overwritten by swapping if we're
4017c478bd9Sstevel@tonic-gate 	 * using a swap partition as the dump device, in which case we bail.
4027c478bd9Sstevel@tonic-gate 	 */
4037c478bd9Sstevel@tonic-gate 	Pread(dumpfd, &corehdr, sizeof (dumphdr_t), dumphdr.dump_start);
4047c478bd9Sstevel@tonic-gate 
4057c478bd9Sstevel@tonic-gate 	corehdr.dump_flags &= ~DF_VALID;
4067c478bd9Sstevel@tonic-gate 	dumphdr.dump_flags &= ~DF_VALID;
4077c478bd9Sstevel@tonic-gate 
4087c478bd9Sstevel@tonic-gate 	if (memcmp(&corehdr, &dumphdr, sizeof (dumphdr_t)) != 0) {
4097c478bd9Sstevel@tonic-gate 		/*
4107c478bd9Sstevel@tonic-gate 		 * Clear valid bit so we don't complain on every invocation.
4117c478bd9Sstevel@tonic-gate 		 */
41246648336SHans Rosenfeld 		if (!filemode && !rflag)
413ca3e8d88SDave Plauger 			Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff);
414f6e214c7SGavin Maltby 		logprint(SC_SL_ERR | SC_EXIT_ERR,
415f6e214c7SGavin Maltby 		    "initial dump header corrupt");
4167c478bd9Sstevel@tonic-gate 	}
4177c478bd9Sstevel@tonic-gate }
4187c478bd9Sstevel@tonic-gate 
4197c478bd9Sstevel@tonic-gate static void
check_space(int csave)420ca3e8d88SDave Plauger check_space(int csave)
4217c478bd9Sstevel@tonic-gate {
4227c478bd9Sstevel@tonic-gate 	struct statvfs fsb;
423ca3e8d88SDave Plauger 	int64_t spacefree, dumpsize, minfree, datasize;
4247c478bd9Sstevel@tonic-gate 
4257c478bd9Sstevel@tonic-gate 	if (statvfs(".", &fsb) < 0)
426f6e214c7SGavin Maltby 		logprint(SC_SL_ERR | SC_EXIT_ERR, "statvfs: %s",
427f6e214c7SGavin Maltby 		    strerror(errno));
4287c478bd9Sstevel@tonic-gate 
429ca3e8d88SDave Plauger 	dumpsize = dumphdr.dump_data - dumphdr.dump_start;
430ca3e8d88SDave Plauger 	datasize = dumphdr.dump_npages * pagesize;
431ca3e8d88SDave Plauger 	if (!csave)
432ca3e8d88SDave Plauger 		dumpsize += datasize;
433ca3e8d88SDave Plauger 	else
434ca3e8d88SDave Plauger 		dumpsize += datahdr.dump_data_csize;
435ca3e8d88SDave Plauger 
4367c478bd9Sstevel@tonic-gate 	spacefree = (int64_t)fsb.f_bavail * fsb.f_frsize;
4377c478bd9Sstevel@tonic-gate 	minfree = 1024LL * read_number_from_file("minfree", 1024);
438f6e214c7SGavin Maltby 	if (spacefree < minfree + dumpsize) {
439f6e214c7SGavin Maltby 		logprint(SC_SL_ERR | SC_EXIT_ERR,
4407c478bd9Sstevel@tonic-gate 		    "not enough space in %s (%lld MB avail, %lld MB needed)",
4417c478bd9Sstevel@tonic-gate 		    savedir, spacefree >> 20, (minfree + dumpsize) >> 20);
442f6e214c7SGavin Maltby 	}
4437c478bd9Sstevel@tonic-gate }
4447c478bd9Sstevel@tonic-gate 
4457c478bd9Sstevel@tonic-gate static void
build_dump_map(int corefd,const pfn_t * pfn_table)4467c478bd9Sstevel@tonic-gate build_dump_map(int corefd, const pfn_t *pfn_table)
4477c478bd9Sstevel@tonic-gate {
4487c478bd9Sstevel@tonic-gate 	long i;
4497c478bd9Sstevel@tonic-gate 	static long misses = 0;
4507c478bd9Sstevel@tonic-gate 	size_t dump_mapsize = (corehdr.dump_hashmask + 1) * sizeof (dump_map_t);
4517c478bd9Sstevel@tonic-gate 	mem_vtop_t vtop;
4527c478bd9Sstevel@tonic-gate 	dump_map_t *dmp = Zalloc(dump_mapsize);
453ca3e8d88SDave Plauger 	char *inbuf = Zalloc(FBUFSIZE);
454ca3e8d88SDave Plauger 	FILE *in = fdopen(dup(dumpfd), "rb");
455ca3e8d88SDave Plauger 
456f6e214c7SGavin Maltby 	(void) setvbuf(in, inbuf, _IOFBF, FBUFSIZE);
457ca3e8d88SDave Plauger 	Fseek(dumphdr.dump_map, in);
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate 	corehdr.dump_data = corehdr.dump_map + roundup(dump_mapsize, pagesize);
4607c478bd9Sstevel@tonic-gate 
4617c478bd9Sstevel@tonic-gate 	for (i = 0; i < corehdr.dump_nvtop; i++) {
4627c478bd9Sstevel@tonic-gate 		long first = 0;
4637c478bd9Sstevel@tonic-gate 		long last = corehdr.dump_npages - 1;
4646d89ca53SIgor Kozhukhov 		long middle = 0;
4656d89ca53SIgor Kozhukhov 		pfn_t pfn = 0;
4667c478bd9Sstevel@tonic-gate 		uintptr_t h;
4677c478bd9Sstevel@tonic-gate 
468ca3e8d88SDave Plauger 		Fread(&vtop, sizeof (mem_vtop_t), in);
4697c478bd9Sstevel@tonic-gate 		while (last >= first) {
4707c478bd9Sstevel@tonic-gate 			middle = (first + last) / 2;
4717c478bd9Sstevel@tonic-gate 			pfn = pfn_table[middle];
4727c478bd9Sstevel@tonic-gate 			if (pfn == vtop.m_pfn)
4737c478bd9Sstevel@tonic-gate 				break;
4747c478bd9Sstevel@tonic-gate 			if (pfn < vtop.m_pfn)
4757c478bd9Sstevel@tonic-gate 				first = middle + 1;
4767c478bd9Sstevel@tonic-gate 			else
4777c478bd9Sstevel@tonic-gate 				last = middle - 1;
4787c478bd9Sstevel@tonic-gate 		}
4797c478bd9Sstevel@tonic-gate 		if (pfn != vtop.m_pfn) {
4807c478bd9Sstevel@tonic-gate 			if (++misses <= 10)
4817c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
4827c478bd9Sstevel@tonic-gate 				    "pfn %ld not found for as=%p, va=%p\n",
4837c478bd9Sstevel@tonic-gate 				    vtop.m_pfn, (void *)vtop.m_as, vtop.m_va);
4847c478bd9Sstevel@tonic-gate 			continue;
4857c478bd9Sstevel@tonic-gate 		}
4867c478bd9Sstevel@tonic-gate 
4877c478bd9Sstevel@tonic-gate 		dmp[i].dm_as = vtop.m_as;
4887c478bd9Sstevel@tonic-gate 		dmp[i].dm_va = (uintptr_t)vtop.m_va;
4897c478bd9Sstevel@tonic-gate 		dmp[i].dm_data = corehdr.dump_data +
4907c478bd9Sstevel@tonic-gate 		    ((uint64_t)middle << corehdr.dump_pageshift);
4917c478bd9Sstevel@tonic-gate 
4927c478bd9Sstevel@tonic-gate 		h = DUMP_HASH(&corehdr, dmp[i].dm_as, dmp[i].dm_va);
4937c478bd9Sstevel@tonic-gate 		dmp[i].dm_next = dmp[h].dm_first;
4947c478bd9Sstevel@tonic-gate 		dmp[h].dm_first = corehdr.dump_map + i * sizeof (dump_map_t);
4957c478bd9Sstevel@tonic-gate 	}
4967c478bd9Sstevel@tonic-gate 
4977c478bd9Sstevel@tonic-gate 	Pwrite(corefd, dmp, dump_mapsize, corehdr.dump_map);
4987c478bd9Sstevel@tonic-gate 	free(dmp);
499f6e214c7SGavin Maltby 	(void) fclose(in);
500ca3e8d88SDave Plauger 	free(inbuf);
501ca3e8d88SDave Plauger }
502ca3e8d88SDave Plauger 
503ca3e8d88SDave Plauger /*
504ca3e8d88SDave Plauger  * Copy whole sections of the dump device to the file.
505ca3e8d88SDave Plauger  */
506ca3e8d88SDave Plauger static void
Copy(offset_t dumpoff,len_t nb,offset_t * offp,int fd,char * buf,size_t sz)507ca3e8d88SDave Plauger Copy(offset_t dumpoff, len_t nb, offset_t *offp, int fd, char *buf,
508ca3e8d88SDave Plauger     size_t sz)
509ca3e8d88SDave Plauger {
510ca3e8d88SDave Plauger 	size_t nr;
511ca3e8d88SDave Plauger 	offset_t off = *offp;
512ca3e8d88SDave Plauger 
513ca3e8d88SDave Plauger 	while (nb > 0) {
514ca3e8d88SDave Plauger 		nr = sz < nb ? sz : (size_t)nb;
515ca3e8d88SDave Plauger 		Pread(dumpfd, buf, nr, dumpoff);
516ca3e8d88SDave Plauger 		Pwrite(fd, buf, nr, off);
517ca3e8d88SDave Plauger 		off += nr;
518ca3e8d88SDave Plauger 		dumpoff += nr;
519ca3e8d88SDave Plauger 		nb -= nr;
520ca3e8d88SDave Plauger 	}
521ca3e8d88SDave Plauger 	*offp = off;
522ca3e8d88SDave Plauger }
523ca3e8d88SDave Plauger 
524ca3e8d88SDave Plauger /*
525ca3e8d88SDave Plauger  * Copy pages when the dump data header is missing.
526ca3e8d88SDave Plauger  * This supports older kernels with latest savecore.
527ca3e8d88SDave Plauger  */
528ca3e8d88SDave Plauger static void
CopyPages(offset_t * offp,int fd,char * buf,size_t sz)529f6e214c7SGavin Maltby CopyPages(offset_t *offp, int fd, char *buf, size_t sz)
530ca3e8d88SDave Plauger {
531ca3e8d88SDave Plauger 	uint32_t csize;
532ca3e8d88SDave Plauger 	FILE *in = fdopen(dup(dumpfd), "rb");
533ca3e8d88SDave Plauger 	FILE *out = fdopen(dup(fd), "wb");
534ca3e8d88SDave Plauger 	char *cbuf = Zalloc(pagesize);
535ca3e8d88SDave Plauger 	char *outbuf = Zalloc(FBUFSIZE);
536ca3e8d88SDave Plauger 	pgcnt_t np = dumphdr.dump_npages;
537ca3e8d88SDave Plauger 
538f6e214c7SGavin Maltby 	(void) setvbuf(out, outbuf, _IOFBF, FBUFSIZE);
539f6e214c7SGavin Maltby 	(void) setvbuf(in, buf, _IOFBF, sz);
540ca3e8d88SDave Plauger 	Fseek(dumphdr.dump_data, in);
541ca3e8d88SDave Plauger 
542ca3e8d88SDave Plauger 	Fseek(*offp, out);
543ca3e8d88SDave Plauger 	while (np > 0) {
544ca3e8d88SDave Plauger 		Fread(&csize, sizeof (uint32_t), in);
545ca3e8d88SDave Plauger 		Fwrite(&csize, sizeof (uint32_t), out);
546ca3e8d88SDave Plauger 		*offp += sizeof (uint32_t);
547ca3e8d88SDave Plauger 		if (csize > pagesize || csize == 0) {
548f6e214c7SGavin Maltby 			logprint(SC_SL_ERR,
549ca3e8d88SDave Plauger 			    "CopyPages: page %lu csize %d (0x%x) pagesize %d",
550ca3e8d88SDave Plauger 			    dumphdr.dump_npages - np, csize, csize,
551ca3e8d88SDave Plauger 			    pagesize);
552ca3e8d88SDave Plauger 			break;
553ca3e8d88SDave Plauger 		}
554ca3e8d88SDave Plauger 		Fread(cbuf, csize, in);
555ca3e8d88SDave Plauger 		Fwrite(cbuf, csize, out);
556ca3e8d88SDave Plauger 		*offp += csize;
557ca3e8d88SDave Plauger 		np--;
558ca3e8d88SDave Plauger 	}
559f6e214c7SGavin Maltby 	(void) fclose(in);
560f6e214c7SGavin Maltby 	(void) fclose(out);
561ca3e8d88SDave Plauger 	free(outbuf);
562ca3e8d88SDave Plauger 	free(buf);
563ca3e8d88SDave Plauger }
564ca3e8d88SDave Plauger 
565ca3e8d88SDave Plauger /*
566ca3e8d88SDave Plauger  * Concatenate dump contents into a new file.
567ca3e8d88SDave Plauger  * Update corehdr with new offsets.
568ca3e8d88SDave Plauger  */
569ca3e8d88SDave Plauger static void
copy_crashfile(const char * corefile)570ca3e8d88SDave Plauger copy_crashfile(const char *corefile)
571ca3e8d88SDave Plauger {
572ca3e8d88SDave Plauger 	int corefd = Open(corefile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
573ca3e8d88SDave Plauger 	size_t bufsz = FBUFSIZE;
574ca3e8d88SDave Plauger 	char *inbuf = Zalloc(bufsz);
575ca3e8d88SDave Plauger 	offset_t coreoff;
576ca3e8d88SDave Plauger 	size_t nb;
577ca3e8d88SDave Plauger 
578f6e214c7SGavin Maltby 	logprint(SC_SL_ERR | SC_IF_VERBOSE,
579ca3e8d88SDave Plauger 	    "Copying %s to %s/%s\n", dumpfile, savedir, corefile);
580ca3e8d88SDave Plauger 
581ca3e8d88SDave Plauger 	/*
582ca3e8d88SDave Plauger 	 * This dump file is still compressed
583ca3e8d88SDave Plauger 	 */
584ca3e8d88SDave Plauger 	corehdr.dump_flags |= DF_COMPRESSED | DF_VALID;
585ca3e8d88SDave Plauger 
586ca3e8d88SDave Plauger 	/*
587ca3e8d88SDave Plauger 	 * Leave room for corehdr, it is updated and written last
588ca3e8d88SDave Plauger 	 */
589ca3e8d88SDave Plauger 	corehdr.dump_start = 0;
590ca3e8d88SDave Plauger 	coreoff = sizeof (corehdr);
591ca3e8d88SDave Plauger 
592ca3e8d88SDave Plauger 	/*
593ca3e8d88SDave Plauger 	 * Read in the compressed symbol table, copy it to corefile.
594ca3e8d88SDave Plauger 	 */
595ca3e8d88SDave Plauger 	coreoff = roundup(coreoff, pagesize);
596ca3e8d88SDave Plauger 	corehdr.dump_ksyms = coreoff;
597ca3e8d88SDave Plauger 	Copy(dumphdr.dump_ksyms, dumphdr.dump_ksyms_csize, &coreoff, corefd,
598ca3e8d88SDave Plauger 	    inbuf, bufsz);
599ca3e8d88SDave Plauger 
600ca3e8d88SDave Plauger 	/*
601ca3e8d88SDave Plauger 	 * Save the pfn table.
602ca3e8d88SDave Plauger 	 */
603ca3e8d88SDave Plauger 	coreoff = roundup(coreoff, pagesize);
604ca3e8d88SDave Plauger 	corehdr.dump_pfn = coreoff;
605ca3e8d88SDave Plauger 	Copy(dumphdr.dump_pfn, dumphdr.dump_npages * sizeof (pfn_t), &coreoff,
606ca3e8d88SDave Plauger 	    corefd, inbuf, bufsz);
607ca3e8d88SDave Plauger 
608ca3e8d88SDave Plauger 	/*
609ca3e8d88SDave Plauger 	 * Save the dump map.
610ca3e8d88SDave Plauger 	 */
611ca3e8d88SDave Plauger 	coreoff = roundup(coreoff, pagesize);
612ca3e8d88SDave Plauger 	corehdr.dump_map = coreoff;
613ca3e8d88SDave Plauger 	Copy(dumphdr.dump_map, dumphdr.dump_nvtop * sizeof (mem_vtop_t),
614ca3e8d88SDave Plauger 	    &coreoff, corefd, inbuf, bufsz);
615ca3e8d88SDave Plauger 
616ca3e8d88SDave Plauger 	/*
617ca3e8d88SDave Plauger 	 * Save the data pages.
618ca3e8d88SDave Plauger 	 */
619ca3e8d88SDave Plauger 	coreoff = roundup(coreoff, pagesize);
620ca3e8d88SDave Plauger 	corehdr.dump_data = coreoff;
621ca3e8d88SDave Plauger 	if (datahdr.dump_data_csize != 0)
622ca3e8d88SDave Plauger 		Copy(dumphdr.dump_data, datahdr.dump_data_csize, &coreoff,
623ca3e8d88SDave Plauger 		    corefd, inbuf, bufsz);
624ca3e8d88SDave Plauger 	else
625f6e214c7SGavin Maltby 		CopyPages(&coreoff, corefd, inbuf, bufsz);
626ca3e8d88SDave Plauger 
627ca3e8d88SDave Plauger 	/*
628ca3e8d88SDave Plauger 	 * Now write the modified dump header to front and end of the copy.
629ca3e8d88SDave Plauger 	 * Make it look like a valid dump device.
630ca3e8d88SDave Plauger 	 *
631ca3e8d88SDave Plauger 	 * From dumphdr.h: Two headers are written out: one at the
632ca3e8d88SDave Plauger 	 * beginning of the dump, and the other at the very end of the
633ca3e8d88SDave Plauger 	 * dump device. The terminal header is at a known location
634ca3e8d88SDave Plauger 	 * (end of device) so we can always find it.
635ca3e8d88SDave Plauger 	 *
636ca3e8d88SDave Plauger 	 * Pad with zeros to each DUMP_OFFSET boundary.
637ca3e8d88SDave Plauger 	 */
638f6e214c7SGavin Maltby 	(void) memset(inbuf, 0, DUMP_OFFSET);
639ca3e8d88SDave Plauger 
640ca3e8d88SDave Plauger 	nb = DUMP_OFFSET - (coreoff & (DUMP_OFFSET - 1));
641ca3e8d88SDave Plauger 	if (nb > 0) {
642ca3e8d88SDave Plauger 		Pwrite(corefd, inbuf, nb, coreoff);
643ca3e8d88SDave Plauger 		coreoff += nb;
644ca3e8d88SDave Plauger 	}
645ca3e8d88SDave Plauger 
646ca3e8d88SDave Plauger 	Pwrite(corefd, &corehdr, sizeof (corehdr), coreoff);
647ca3e8d88SDave Plauger 	coreoff += sizeof (corehdr);
648ca3e8d88SDave Plauger 
649ca3e8d88SDave Plauger 	Pwrite(corefd, &datahdr, sizeof (datahdr), coreoff);
650ca3e8d88SDave Plauger 	coreoff += sizeof (datahdr);
651ca3e8d88SDave Plauger 
652ca3e8d88SDave Plauger 	nb = DUMP_OFFSET - (coreoff & (DUMP_OFFSET - 1));
653ca3e8d88SDave Plauger 	if (nb > 0) {
654ca3e8d88SDave Plauger 		Pwrite(corefd, inbuf, nb, coreoff);
655ca3e8d88SDave Plauger 	}
656ca3e8d88SDave Plauger 
657ca3e8d88SDave Plauger 	free(inbuf);
658ca3e8d88SDave Plauger 	Pwrite(corefd, &corehdr, sizeof (corehdr), corehdr.dump_start);
659ca3e8d88SDave Plauger 
660ca3e8d88SDave Plauger 	/*
661ca3e8d88SDave Plauger 	 * Write out the modified dump header to the dump device.
662ca3e8d88SDave Plauger 	 * The dump device has been processed, so DF_VALID is clear.
663ca3e8d88SDave Plauger 	 */
66446648336SHans Rosenfeld 	if (!filemode && !rflag)
665ca3e8d88SDave Plauger 		Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff);
666ca3e8d88SDave Plauger 
667ca3e8d88SDave Plauger 	(void) close(corefd);
668ca3e8d88SDave Plauger }
669ca3e8d88SDave Plauger 
670ca3e8d88SDave Plauger /*
671ca3e8d88SDave Plauger  * compressed streams
672ca3e8d88SDave Plauger  */
673ca3e8d88SDave Plauger typedef struct blockhdr blockhdr_t;
674ca3e8d88SDave Plauger typedef struct block block_t;
675ca3e8d88SDave Plauger 
676ca3e8d88SDave Plauger struct blockhdr {
677ca3e8d88SDave Plauger 	block_t *head;
678ca3e8d88SDave Plauger 	block_t *tail;
679ca3e8d88SDave Plauger };
680ca3e8d88SDave Plauger 
681ca3e8d88SDave Plauger struct block {
682ca3e8d88SDave Plauger 	block_t *next;
683ca3e8d88SDave Plauger 	char *block;
684ca3e8d88SDave Plauger 	int size;
685ca3e8d88SDave Plauger };
686ca3e8d88SDave Plauger 
687ca3e8d88SDave Plauger typedef enum streamstate {
688ca3e8d88SDave Plauger 	STREAMSTART,
689ca3e8d88SDave Plauger 	STREAMPAGES
690ca3e8d88SDave Plauger } streamstate_t;
691ca3e8d88SDave Plauger 
692ca3e8d88SDave Plauger typedef struct stream {
693ca3e8d88SDave Plauger 	streamstate_t state;
694ca3e8d88SDave Plauger 	int init;
695ca3e8d88SDave Plauger 	int tag;
696ca3e8d88SDave Plauger 	int bound;
697ca3e8d88SDave Plauger 	int nout;
698ca3e8d88SDave Plauger 	char *blkbuf;
699ca3e8d88SDave Plauger 	blockhdr_t blocks;
700ca3e8d88SDave Plauger 	pgcnt_t pagenum;
701ca3e8d88SDave Plauger 	pgcnt_t curpage;
702ca3e8d88SDave Plauger 	pgcnt_t npages;
703ca3e8d88SDave Plauger 	pgcnt_t done;
704ca3e8d88SDave Plauger 	bz_stream strm;
705ca3e8d88SDave Plauger 	dumpcsize_t sc;
706ca3e8d88SDave Plauger 	dumpstreamhdr_t sh;
707ca3e8d88SDave Plauger } stream_t;
708ca3e8d88SDave Plauger 
709ca3e8d88SDave Plauger static stream_t *streams;
710ca3e8d88SDave Plauger static stream_t *endstreams;
711ca3e8d88SDave Plauger 
712ca3e8d88SDave Plauger const int cs = sizeof (dumpcsize_t);
713ca3e8d88SDave Plauger 
714ca3e8d88SDave Plauger typedef struct tinfo {
715ca3e8d88SDave Plauger 	pthread_t tid;
716ca3e8d88SDave Plauger 	int corefd;
717ca3e8d88SDave Plauger } tinfo_t;
718ca3e8d88SDave Plauger 
719ca3e8d88SDave Plauger static int threads_stop;
720ca3e8d88SDave Plauger static int threads_active;
721ca3e8d88SDave Plauger static tinfo_t *tinfo;
722ca3e8d88SDave Plauger static tinfo_t *endtinfo;
723ca3e8d88SDave Plauger 
724ca3e8d88SDave Plauger static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
725ca3e8d88SDave Plauger static pthread_cond_t cvfree = PTHREAD_COND_INITIALIZER;
726ca3e8d88SDave Plauger static pthread_cond_t cvwork = PTHREAD_COND_INITIALIZER;
727ca3e8d88SDave Plauger static pthread_cond_t cvbarrier = PTHREAD_COND_INITIALIZER;
728ca3e8d88SDave Plauger 
729ca3e8d88SDave Plauger static blockhdr_t freeblocks;
730ca3e8d88SDave Plauger 
731ca3e8d88SDave Plauger static void
enqt(blockhdr_t * h,block_t * b)732ca3e8d88SDave Plauger enqt(blockhdr_t *h, block_t *b)
733ca3e8d88SDave Plauger {
734ca3e8d88SDave Plauger 	b->next = NULL;
735ca3e8d88SDave Plauger 	if (h->tail == NULL)
736ca3e8d88SDave Plauger 		h->head = b;
737ca3e8d88SDave Plauger 	else
738ca3e8d88SDave Plauger 		h->tail->next = b;
739ca3e8d88SDave Plauger 	h->tail = b;
740ca3e8d88SDave Plauger }
741ca3e8d88SDave Plauger 
742ca3e8d88SDave Plauger static block_t *
deqh(blockhdr_t * h)743ca3e8d88SDave Plauger deqh(blockhdr_t *h)
744ca3e8d88SDave Plauger {
745ca3e8d88SDave Plauger 	block_t *b = h->head;
746ca3e8d88SDave Plauger 
747ca3e8d88SDave Plauger 	if (b != NULL) {
748ca3e8d88SDave Plauger 		h->head = b->next;
749ca3e8d88SDave Plauger 		if (h->head == NULL)
750ca3e8d88SDave Plauger 			h->tail = NULL;
751ca3e8d88SDave Plauger 	}
752ca3e8d88SDave Plauger 	return (b);
753ca3e8d88SDave Plauger }
754ca3e8d88SDave Plauger 
755ca3e8d88SDave Plauger static void *runstreams(void *arg);
756ca3e8d88SDave Plauger 
757ca3e8d88SDave Plauger static void
initstreams(int corefd,int nstreams,int maxcsize)758ca3e8d88SDave Plauger initstreams(int corefd, int nstreams, int maxcsize)
759ca3e8d88SDave Plauger {
760ca3e8d88SDave Plauger 	int nthreads;
761ca3e8d88SDave Plauger 	int nblocks;
762ca3e8d88SDave Plauger 	int i;
763ca3e8d88SDave Plauger 	block_t *b;
764ca3e8d88SDave Plauger 	tinfo_t *t;
765ca3e8d88SDave Plauger 
766ca3e8d88SDave Plauger 	nthreads = sysconf(_SC_NPROCESSORS_ONLN);
767ca3e8d88SDave Plauger 	if (nstreams < nthreads)
768ca3e8d88SDave Plauger 		nthreads = nstreams;
769ca3e8d88SDave Plauger 	if (nthreads < 1)
770ca3e8d88SDave Plauger 		nthreads = 1;
771ca3e8d88SDave Plauger 	nblocks = nthreads * 2;
772ca3e8d88SDave Plauger 
773ca3e8d88SDave Plauger 	tinfo = Zalloc(nthreads * sizeof (tinfo_t));
774ca3e8d88SDave Plauger 	endtinfo = &tinfo[nthreads];
775ca3e8d88SDave Plauger 
776ca3e8d88SDave Plauger 	/* init streams */
777ca3e8d88SDave Plauger 	streams = Zalloc(nstreams * sizeof (stream_t));
778ca3e8d88SDave Plauger 	endstreams = &streams[nstreams];
779ca3e8d88SDave Plauger 
780ca3e8d88SDave Plauger 	/* init stream block buffers */
781ca3e8d88SDave Plauger 	for (i = 0; i < nblocks; i++) {
782ca3e8d88SDave Plauger 		b = Zalloc(sizeof (block_t));
783ca3e8d88SDave Plauger 		b->block = Zalloc(maxcsize);
784ca3e8d88SDave Plauger 		enqt(&freeblocks, b);
785ca3e8d88SDave Plauger 	}
786ca3e8d88SDave Plauger 
787ca3e8d88SDave Plauger 	/* init worker threads */
788f6e214c7SGavin Maltby 	(void) pthread_mutex_lock(&lock);
789ca3e8d88SDave Plauger 	threads_active = 1;
790ca3e8d88SDave Plauger 	threads_stop = 0;
791ca3e8d88SDave Plauger 	for (t = tinfo; t != endtinfo; t++) {
792ca3e8d88SDave Plauger 		t->corefd = dup(corefd);
793ca3e8d88SDave Plauger 		if (t->corefd < 0) {
794ca3e8d88SDave Plauger 			nthreads = t - tinfo;
795ca3e8d88SDave Plauger 			endtinfo = t;
796ca3e8d88SDave Plauger 			break;
797ca3e8d88SDave Plauger 		}
798ca3e8d88SDave Plauger 		if (pthread_create(&t->tid, NULL, runstreams, t) != 0)
799f6e214c7SGavin Maltby 			logprint(SC_SL_ERR | SC_EXIT_ERR, "pthread_create: %s",
800ca3e8d88SDave Plauger 			    strerror(errno));
801ca3e8d88SDave Plauger 	}
802f6e214c7SGavin Maltby 	(void) pthread_mutex_unlock(&lock);
803ca3e8d88SDave Plauger }
804ca3e8d88SDave Plauger 
805ca3e8d88SDave Plauger static void
sbarrier()806ca3e8d88SDave Plauger sbarrier()
807ca3e8d88SDave Plauger {
808ca3e8d88SDave Plauger 	stream_t *s;
809ca3e8d88SDave Plauger 
810f6e214c7SGavin Maltby 	(void) pthread_mutex_lock(&lock);
811ca3e8d88SDave Plauger 	for (s = streams; s != endstreams; s++) {
812ca3e8d88SDave Plauger 		while (s->bound || s->blocks.head != NULL)
813f6e214c7SGavin Maltby 			(void) pthread_cond_wait(&cvbarrier, &lock);
814ca3e8d88SDave Plauger 	}
815f6e214c7SGavin Maltby 	(void) pthread_mutex_unlock(&lock);
816ca3e8d88SDave Plauger }
817ca3e8d88SDave Plauger 
818ca3e8d88SDave Plauger static void
stopstreams()819ca3e8d88SDave Plauger stopstreams()
820ca3e8d88SDave Plauger {
821ca3e8d88SDave Plauger 	tinfo_t *t;
822ca3e8d88SDave Plauger 
823ca3e8d88SDave Plauger 	if (threads_active) {
824ca3e8d88SDave Plauger 		sbarrier();
825f6e214c7SGavin Maltby 		(void) pthread_mutex_lock(&lock);
826ca3e8d88SDave Plauger 		threads_stop = 1;
827f6e214c7SGavin Maltby 		(void) pthread_cond_signal(&cvwork);
828f6e214c7SGavin Maltby 		(void) pthread_mutex_unlock(&lock);
829ca3e8d88SDave Plauger 		for (t = tinfo; t != endtinfo; t++)
830f6e214c7SGavin Maltby 			(void) pthread_join(t->tid, NULL);
831ca3e8d88SDave Plauger 		free(tinfo);
832ca3e8d88SDave Plauger 		tinfo = NULL;
833ca3e8d88SDave Plauger 		threads_active = 0;
834ca3e8d88SDave Plauger 	}
835ca3e8d88SDave Plauger }
836ca3e8d88SDave Plauger 
837ca3e8d88SDave Plauger static block_t *
getfreeblock()838ca3e8d88SDave Plauger getfreeblock()
839ca3e8d88SDave Plauger {
840ca3e8d88SDave Plauger 	block_t *b;
841ca3e8d88SDave Plauger 
842f6e214c7SGavin Maltby 	(void) pthread_mutex_lock(&lock);
843ca3e8d88SDave Plauger 	while ((b = deqh(&freeblocks)) == NULL)
844f6e214c7SGavin Maltby 		(void) pthread_cond_wait(&cvfree, &lock);
845f6e214c7SGavin Maltby 	(void) pthread_mutex_unlock(&lock);
846ca3e8d88SDave Plauger 	return (b);
847ca3e8d88SDave Plauger }
848ca3e8d88SDave Plauger 
849ca3e8d88SDave Plauger /* data page offset from page number */
850ca3e8d88SDave Plauger #define	BTOP(b)		((b) >> dumphdr.dump_pageshift)
851ca3e8d88SDave Plauger #define	PTOB(p)		((p) << dumphdr.dump_pageshift)
852ca3e8d88SDave Plauger #define	DATAOFF(p)	(corehdr.dump_data + PTOB(p))
853ca3e8d88SDave Plauger 
854ca3e8d88SDave Plauger /* check for coreblksize boundary */
855ca3e8d88SDave Plauger static int
isblkbnd(pgcnt_t pgnum)856ca3e8d88SDave Plauger isblkbnd(pgcnt_t pgnum)
857ca3e8d88SDave Plauger {
858ca3e8d88SDave Plauger 	return (P2PHASE(DATAOFF(pgnum), coreblksize) == 0);
859ca3e8d88SDave Plauger }
860ca3e8d88SDave Plauger 
861ca3e8d88SDave Plauger static int
iszpage(char * buf)862ca3e8d88SDave Plauger iszpage(char *buf)
863ca3e8d88SDave Plauger {
864ca3e8d88SDave Plauger 	size_t sz;
865ca3e8d88SDave Plauger 	uint64_t *pl;
866ca3e8d88SDave Plauger 
867f6e214c7SGavin Maltby 	/*LINTED:E_BAD_PTR_CAST_ALIGN*/
868ca3e8d88SDave Plauger 	pl = (uint64_t *)(buf);
869ca3e8d88SDave Plauger 	for (sz = 0; sz < pagesize; sz += sizeof (*pl))
870ca3e8d88SDave Plauger 		if (*pl++ != 0)
871ca3e8d88SDave Plauger 			return (0);
872ca3e8d88SDave Plauger 	return (1);
873ca3e8d88SDave Plauger }
874ca3e8d88SDave Plauger 
875ca3e8d88SDave Plauger volatile uint_t *hist;
876ca3e8d88SDave Plauger 
877ca3e8d88SDave Plauger /* write pages to the core file */
878ca3e8d88SDave Plauger static void
putpage(int corefd,char * buf,pgcnt_t pgnum,pgcnt_t np)879ca3e8d88SDave Plauger putpage(int corefd, char *buf, pgcnt_t pgnum, pgcnt_t np)
880ca3e8d88SDave Plauger {
881ca3e8d88SDave Plauger 	atomic_inc_uint(&hist[np]);
882ca3e8d88SDave Plauger 	if (np > 0)
883ca3e8d88SDave Plauger 		Pwrite(corefd, buf, PTOB(np), DATAOFF(pgnum));
884ca3e8d88SDave Plauger }
885ca3e8d88SDave Plauger 
886ca3e8d88SDave Plauger /*
887ca3e8d88SDave Plauger  * Process one lzjb block.
888ca3e8d88SDave Plauger  * No object (stream header or page) will be split over a block boundary.
889ca3e8d88SDave Plauger  */
890ca3e8d88SDave Plauger static void
lzjbblock(int corefd,stream_t * s,char * block,size_t blocksz)891ca3e8d88SDave Plauger lzjbblock(int corefd, stream_t *s, char *block, size_t blocksz)
892ca3e8d88SDave Plauger {
893ca3e8d88SDave Plauger 	int in = 0;
894ca3e8d88SDave Plauger 	int csize;
895ca3e8d88SDave Plauger 	int doflush;
896ca3e8d88SDave Plauger 	char *out;
897ca3e8d88SDave Plauger 	size_t dsize;
898ca3e8d88SDave Plauger 	dumpcsize_t sc;
899ca3e8d88SDave Plauger 	dumpstreamhdr_t sh;
900ca3e8d88SDave Plauger 
901ca3e8d88SDave Plauger 	if (!s->init) {
902ca3e8d88SDave Plauger 		s->init = 1;
903ca3e8d88SDave Plauger 		if (s->blkbuf == NULL)
904ca3e8d88SDave Plauger 			s->blkbuf = Zalloc(coreblksize);
905ca3e8d88SDave Plauger 		s->state = STREAMSTART;
906ca3e8d88SDave Plauger 	}
907ca3e8d88SDave Plauger 	while (in < blocksz) {
908ca3e8d88SDave Plauger 		switch (s->state) {
909ca3e8d88SDave Plauger 		case STREAMSTART:
910f6e214c7SGavin Maltby 			(void) memcpy(&sh, block + in, sizeof (sh));
911ca3e8d88SDave Plauger 			in += sizeof (sh);
912ca3e8d88SDave Plauger 			if (strcmp(DUMP_STREAM_MAGIC, sh.stream_magic) != 0)
913f6e214c7SGavin Maltby 				logprint(SC_SL_ERR | SC_EXIT_ERR,
914ca3e8d88SDave Plauger 				    "LZJB STREAMSTART: bad stream header");
915ca3e8d88SDave Plauger 			if (sh.stream_npages > datahdr.dump_maxrange)
916f6e214c7SGavin Maltby 				logprint(SC_SL_ERR | SC_EXIT_ERR,
917ca3e8d88SDave Plauger 				    "LZJB STREAMSTART: bad range: %d > %d",
918ca3e8d88SDave Plauger 				    sh.stream_npages, datahdr.dump_maxrange);
919ca3e8d88SDave Plauger 			s->pagenum = sh.stream_pagenum;
920ca3e8d88SDave Plauger 			s->npages = sh.stream_npages;
921ca3e8d88SDave Plauger 			s->curpage = s->pagenum;
922ca3e8d88SDave Plauger 			s->nout = 0;
923ca3e8d88SDave Plauger 			s->done = 0;
924ca3e8d88SDave Plauger 			s->state = STREAMPAGES;
925ca3e8d88SDave Plauger 			break;
926ca3e8d88SDave Plauger 		case STREAMPAGES:
927f6e214c7SGavin Maltby 			(void) memcpy(&sc, block + in, cs);
928ca3e8d88SDave Plauger 			in += cs;
929ca3e8d88SDave Plauger 			csize = DUMP_GET_CSIZE(sc);
930ca3e8d88SDave Plauger 			if (csize > pagesize)
931f6e214c7SGavin Maltby 				logprint(SC_SL_ERR | SC_EXIT_ERR,
932ca3e8d88SDave Plauger 				    "LZJB STREAMPAGES: bad csize=%d", csize);
933ca3e8d88SDave Plauger 
934ca3e8d88SDave Plauger 			out =  s->blkbuf + PTOB(s->nout);
935ca3e8d88SDave Plauger 			dsize = decompress(block + in, out, csize, pagesize);
936ca3e8d88SDave Plauger 
937ca3e8d88SDave Plauger 			if (dsize != pagesize)
938f6e214c7SGavin Maltby 				logprint(SC_SL_ERR | SC_EXIT_ERR,
939ca3e8d88SDave Plauger 				    "LZJB STREAMPAGES: dsize %d != pagesize %d",
940ca3e8d88SDave Plauger 				    dsize, pagesize);
941ca3e8d88SDave Plauger 
942ca3e8d88SDave Plauger 			in += csize;
943ca3e8d88SDave Plauger 			atomic_inc_64(&saved);
944ca3e8d88SDave Plauger 
945ca3e8d88SDave Plauger 			doflush = 0;
946ca3e8d88SDave Plauger 			if (s->nout == 0 && iszpage(out)) {
947ca3e8d88SDave Plauger 				doflush = 1;
948ca3e8d88SDave Plauger 				atomic_inc_64(&zpages);
949ca3e8d88SDave Plauger 			} else if (++s->nout >= BTOP(coreblksize) ||
950ca3e8d88SDave Plauger 			    isblkbnd(s->curpage + s->nout)) {
951ca3e8d88SDave Plauger 				doflush = 1;
952ca3e8d88SDave Plauger 			}
953ca3e8d88SDave Plauger 			if (++s->done >= s->npages) {
954ca3e8d88SDave Plauger 				s->state = STREAMSTART;
955ca3e8d88SDave Plauger 				doflush = 1;
956ca3e8d88SDave Plauger 			}
957ca3e8d88SDave Plauger 			if (doflush) {
958ca3e8d88SDave Plauger 				putpage(corefd, s->blkbuf, s->curpage, s->nout);
959ca3e8d88SDave Plauger 				s->nout = 0;
960ca3e8d88SDave Plauger 				s->curpage = s->pagenum + s->done;
961ca3e8d88SDave Plauger 			}
962ca3e8d88SDave Plauger 			break;
963ca3e8d88SDave Plauger 		}
964ca3e8d88SDave Plauger 	}
965ca3e8d88SDave Plauger }
966ca3e8d88SDave Plauger 
967ca3e8d88SDave Plauger /* bzlib library reports errors with this callback */
968ca3e8d88SDave Plauger void
bz_internal_error(int errcode)969ca3e8d88SDave Plauger bz_internal_error(int errcode)
970ca3e8d88SDave Plauger {
971f6e214c7SGavin Maltby 	logprint(SC_SL_ERR | SC_EXIT_ERR, "bz_internal_error: err %s\n",
972ca3e8d88SDave Plauger 	    BZ2_bzErrorString(errcode));
973ca3e8d88SDave Plauger }
974ca3e8d88SDave Plauger 
975ca3e8d88SDave Plauger /*
976ca3e8d88SDave Plauger  * Return one object in the stream.
977ca3e8d88SDave Plauger  *
978ca3e8d88SDave Plauger  * An object (stream header or page) will likely span an input block
979ca3e8d88SDave Plauger  * of compression data. Return non-zero when an entire object has been
980ca3e8d88SDave Plauger  * retrieved from the stream.
981ca3e8d88SDave Plauger  */
982ca3e8d88SDave Plauger static int
bz2decompress(stream_t * s,void * buf,size_t size)983ca3e8d88SDave Plauger bz2decompress(stream_t *s, void *buf, size_t size)
984ca3e8d88SDave Plauger {
985ca3e8d88SDave Plauger 	int rc;
986ca3e8d88SDave Plauger 
987ca3e8d88SDave Plauger 	if (s->strm.avail_out == 0) {
988ca3e8d88SDave Plauger 		s->strm.next_out = buf;
989ca3e8d88SDave Plauger 		s->strm.avail_out = size;
990ca3e8d88SDave Plauger 	}
991ca3e8d88SDave Plauger 	while (s->strm.avail_in > 0) {
992ca3e8d88SDave Plauger 		rc = BZ2_bzDecompress(&s->strm);
993ca3e8d88SDave Plauger 		if (rc == BZ_STREAM_END) {
994ca3e8d88SDave Plauger 			rc = BZ2_bzDecompressReset(&s->strm);
995ca3e8d88SDave Plauger 			if (rc != BZ_OK)
996f6e214c7SGavin Maltby 				logprint(SC_SL_ERR | SC_EXIT_ERR,
997ca3e8d88SDave Plauger 				    "BZ2_bzDecompressReset: %s",
998ca3e8d88SDave Plauger 				    BZ2_bzErrorString(rc));
999ca3e8d88SDave Plauger 			continue;
1000ca3e8d88SDave Plauger 		}
1001ca3e8d88SDave Plauger 
1002ca3e8d88SDave Plauger 		if (s->strm.avail_out == 0)
1003ca3e8d88SDave Plauger 			break;
1004ca3e8d88SDave Plauger 	}
1005ca3e8d88SDave Plauger 	return (s->strm.avail_out == 0);
1006ca3e8d88SDave Plauger }
1007ca3e8d88SDave Plauger 
1008ca3e8d88SDave Plauger /*
1009ca3e8d88SDave Plauger  * Process one bzip2 block.
1010ca3e8d88SDave Plauger  * The interface is documented here:
1011ca3e8d88SDave Plauger  * http://www.bzip.org/1.0.5/bzip2-manual-1.0.5.html
1012ca3e8d88SDave Plauger  */
1013ca3e8d88SDave Plauger static void
bz2block(int corefd,stream_t * s,char * block,size_t blocksz)1014ca3e8d88SDave Plauger bz2block(int corefd, stream_t *s, char *block, size_t blocksz)
1015ca3e8d88SDave Plauger {
1016ca3e8d88SDave Plauger 	int rc = 0;
1017ca3e8d88SDave Plauger 	int doflush;
1018ca3e8d88SDave Plauger 	char *out;
1019ca3e8d88SDave Plauger 
1020ca3e8d88SDave Plauger 	if (!s->init) {
1021ca3e8d88SDave Plauger 		s->init = 1;
1022ca3e8d88SDave Plauger 		rc = BZ2_bzDecompressInit(&s->strm, 0, 0);
1023ca3e8d88SDave Plauger 		if (rc != BZ_OK)
1024f6e214c7SGavin Maltby 			logprint(SC_SL_ERR | SC_EXIT_ERR,
1025ca3e8d88SDave Plauger 			    "BZ2_bzDecompressInit: %s", BZ2_bzErrorString(rc));
1026ca3e8d88SDave Plauger 		if (s->blkbuf == NULL)
1027ca3e8d88SDave Plauger 			s->blkbuf = Zalloc(coreblksize);
1028ca3e8d88SDave Plauger 		s->strm.avail_out = 0;
1029ca3e8d88SDave Plauger 		s->state = STREAMSTART;
1030ca3e8d88SDave Plauger 	}
1031ca3e8d88SDave Plauger 	s->strm.next_in = block;
1032ca3e8d88SDave Plauger 	s->strm.avail_in = blocksz;
1033ca3e8d88SDave Plauger 
1034ca3e8d88SDave Plauger 	while (s->strm.avail_in > 0) {
1035ca3e8d88SDave Plauger 		switch (s->state) {
1036ca3e8d88SDave Plauger 		case STREAMSTART:
1037ca3e8d88SDave Plauger 			if (!bz2decompress(s, &s->sh, sizeof (s->sh)))
1038ca3e8d88SDave Plauger 				return;
1039ca3e8d88SDave Plauger 			if (strcmp(DUMP_STREAM_MAGIC, s->sh.stream_magic) != 0)
1040f6e214c7SGavin Maltby 				logprint(SC_SL_ERR | SC_EXIT_ERR,
1041ca3e8d88SDave Plauger 				    "BZ2 STREAMSTART: bad stream header");
1042ca3e8d88SDave Plauger 			if (s->sh.stream_npages > datahdr.dump_maxrange)
1043f6e214c7SGavin Maltby 				logprint(SC_SL_ERR | SC_EXIT_ERR,
1044ca3e8d88SDave Plauger 				    "BZ2 STREAMSTART: bad range: %d > %d",
1045ca3e8d88SDave Plauger 				    s->sh.stream_npages, datahdr.dump_maxrange);
1046ca3e8d88SDave Plauger 			s->pagenum = s->sh.stream_pagenum;
1047ca3e8d88SDave Plauger 			s->npages = s->sh.stream_npages;
1048ca3e8d88SDave Plauger 			s->curpage = s->pagenum;
1049ca3e8d88SDave Plauger 			s->nout = 0;
1050ca3e8d88SDave Plauger 			s->done = 0;
1051ca3e8d88SDave Plauger 			s->state = STREAMPAGES;
1052ca3e8d88SDave Plauger 			break;
1053ca3e8d88SDave Plauger 		case STREAMPAGES:
1054ca3e8d88SDave Plauger 			out = s->blkbuf + PTOB(s->nout);
1055ca3e8d88SDave Plauger 			if (!bz2decompress(s, out, pagesize))
1056ca3e8d88SDave Plauger 				return;
1057ca3e8d88SDave Plauger 
1058ca3e8d88SDave Plauger 			atomic_inc_64(&saved);
1059ca3e8d88SDave Plauger 
1060ca3e8d88SDave Plauger 			doflush = 0;
1061ca3e8d88SDave Plauger 			if (s->nout == 0 && iszpage(out)) {
1062ca3e8d88SDave Plauger 				doflush = 1;
1063ca3e8d88SDave Plauger 				atomic_inc_64(&zpages);
1064ca3e8d88SDave Plauger 			} else if (++s->nout >= BTOP(coreblksize) ||
1065ca3e8d88SDave Plauger 			    isblkbnd(s->curpage + s->nout)) {
1066ca3e8d88SDave Plauger 				doflush = 1;
1067ca3e8d88SDave Plauger 			}
1068ca3e8d88SDave Plauger 			if (++s->done >= s->npages) {
1069ca3e8d88SDave Plauger 				s->state = STREAMSTART;
1070ca3e8d88SDave Plauger 				doflush = 1;
1071ca3e8d88SDave Plauger 			}
1072ca3e8d88SDave Plauger 			if (doflush) {
1073ca3e8d88SDave Plauger 				putpage(corefd, s->blkbuf, s->curpage, s->nout);
1074ca3e8d88SDave Plauger 				s->nout = 0;
1075ca3e8d88SDave Plauger 				s->curpage = s->pagenum + s->done;
1076ca3e8d88SDave Plauger 			}
1077ca3e8d88SDave Plauger 			break;
1078ca3e8d88SDave Plauger 		}
1079ca3e8d88SDave Plauger 	}
1080ca3e8d88SDave Plauger }
1081ca3e8d88SDave Plauger 
1082ca3e8d88SDave Plauger /* report progress */
1083ca3e8d88SDave Plauger static void
report_progress()1084ca3e8d88SDave Plauger report_progress()
1085ca3e8d88SDave Plauger {
1086ca3e8d88SDave Plauger 	int sec, percent;
1087ca3e8d88SDave Plauger 
1088ca3e8d88SDave Plauger 	if (!interactive)
1089ca3e8d88SDave Plauger 		return;
1090ca3e8d88SDave Plauger 
1091ca3e8d88SDave Plauger 	percent = saved * 100LL / corehdr.dump_npages;
10922fb0949cSJoshua M. Clulow 	sec = (gethrtime() - startts) / NANOSEC;
10932fb0949cSJoshua M. Clulow 	if (percent > percent_done || sec > sec_done) {
1094ca3e8d88SDave Plauger 		(void) printf("\r%2d:%02d %3d%% done", sec / 60, sec % 60,
1095ca3e8d88SDave Plauger 		    percent);
1096ca3e8d88SDave Plauger 		(void) fflush(stdout);
10972fb0949cSJoshua M. Clulow 		sec_done = sec;
1098ca3e8d88SDave Plauger 		percent_done = percent;
1099ca3e8d88SDave Plauger 	}
1100ca3e8d88SDave Plauger }
1101ca3e8d88SDave Plauger 
1102ca3e8d88SDave Plauger /* thread body */
1103ca3e8d88SDave Plauger static void *
runstreams(void * arg)1104ca3e8d88SDave Plauger runstreams(void *arg)
1105ca3e8d88SDave Plauger {
1106ca3e8d88SDave Plauger 	tinfo_t *t = arg;
1107ca3e8d88SDave Plauger 	stream_t *s;
1108ca3e8d88SDave Plauger 	block_t *b;
1109ca3e8d88SDave Plauger 	int bound;
1110ca3e8d88SDave Plauger 
1111f6e214c7SGavin Maltby 	(void) pthread_mutex_lock(&lock);
1112ca3e8d88SDave Plauger 	while (!threads_stop) {
1113ca3e8d88SDave Plauger 		bound = 0;
1114ca3e8d88SDave Plauger 		for (s = streams; s != endstreams; s++) {
1115ca3e8d88SDave Plauger 			if (s->bound || s->blocks.head == NULL)
1116ca3e8d88SDave Plauger 				continue;
1117ca3e8d88SDave Plauger 			s->bound = 1;
1118ca3e8d88SDave Plauger 			bound = 1;
1119f6e214c7SGavin Maltby 			(void) pthread_cond_signal(&cvwork);
1120ca3e8d88SDave Plauger 			while (s->blocks.head != NULL) {
1121ca3e8d88SDave Plauger 				b = deqh(&s->blocks);
1122f6e214c7SGavin Maltby 				(void) pthread_mutex_unlock(&lock);
1123ca3e8d88SDave Plauger 
1124ca3e8d88SDave Plauger 				if (datahdr.dump_clevel < DUMP_CLEVEL_BZIP2)
1125ca3e8d88SDave Plauger 					lzjbblock(t->corefd, s, b->block,
1126ca3e8d88SDave Plauger 					    b->size);
1127ca3e8d88SDave Plauger 				else
1128ca3e8d88SDave Plauger 					bz2block(t->corefd, s, b->block,
1129ca3e8d88SDave Plauger 					    b->size);
1130ca3e8d88SDave Plauger 
1131f6e214c7SGavin Maltby 				(void) pthread_mutex_lock(&lock);
1132ca3e8d88SDave Plauger 				enqt(&freeblocks, b);
1133f6e214c7SGavin Maltby 				(void) pthread_cond_signal(&cvfree);
1134ca3e8d88SDave Plauger 
1135ca3e8d88SDave Plauger 				report_progress();
1136ca3e8d88SDave Plauger 			}
1137ca3e8d88SDave Plauger 			s->bound = 0;
1138f6e214c7SGavin Maltby 			(void) pthread_cond_signal(&cvbarrier);
1139ca3e8d88SDave Plauger 		}
1140ca3e8d88SDave Plauger 		if (!bound && !threads_stop)
1141f6e214c7SGavin Maltby 			(void) pthread_cond_wait(&cvwork, &lock);
1142ca3e8d88SDave Plauger 	}
1143f6e214c7SGavin Maltby 	(void) close(t->corefd);
1144f6e214c7SGavin Maltby 	(void) pthread_cond_signal(&cvwork);
1145f6e214c7SGavin Maltby 	(void) pthread_mutex_unlock(&lock);
1146ca3e8d88SDave Plauger 	return (arg);
1147ca3e8d88SDave Plauger }
1148ca3e8d88SDave Plauger 
1149ca3e8d88SDave Plauger /*
1150ca3e8d88SDave Plauger  * Process compressed pages.
1151ca3e8d88SDave Plauger  *
1152ca3e8d88SDave Plauger  * The old format, now called single-threaded lzjb, is a 32-bit size
1153ca3e8d88SDave Plauger  * word followed by 'size' bytes of lzjb compression data for one
1154ca3e8d88SDave Plauger  * page. The new format extends this by storing a 12-bit "tag" in the
1155ca3e8d88SDave Plauger  * upper bits of the size word. When the size word is pagesize or
1156ca3e8d88SDave Plauger  * less, it is assumed to be one lzjb page. When the size word is
1157ca3e8d88SDave Plauger  * greater than pagesize, it is assumed to be a "stream block",
1158ca3e8d88SDave Plauger  * belonging to up to 4095 streams. In practice, the number of streams
1159ca3e8d88SDave Plauger  * is set to one less than the number of CPUs running at crash
1160ca3e8d88SDave Plauger  * time. One CPU processes the crash dump, the remaining CPUs
1161ca3e8d88SDave Plauger  * separately process groups of data pages.
1162ca3e8d88SDave Plauger  *
1163ca3e8d88SDave Plauger  * savecore creates a thread per stream, but never more threads than
1164ca3e8d88SDave Plauger  * the number of CPUs running savecore. This is because savecore can
1165ca3e8d88SDave Plauger  * be processing a crash file from a remote machine, which may have
1166ca3e8d88SDave Plauger  * more CPUs.
1167ca3e8d88SDave Plauger  *
1168ca3e8d88SDave Plauger  * When the kernel uses parallel lzjb or parallel bzip2, we expect a
1169ca3e8d88SDave Plauger  * series of 128KB blocks of compression data. In this case, each
1170ca3e8d88SDave Plauger  * block has a "tag", in the range 1-4095. Each block is handed off to
1171ca3e8d88SDave Plauger  * to the threads running "runstreams". The dump format is either lzjb
1172ca3e8d88SDave Plauger  * or bzip2, never a mixture. These threads, in turn, process the
1173ca3e8d88SDave Plauger  * compression data for groups of pages. Groups of pages are delimited
1174ca3e8d88SDave Plauger  * by a "stream header", which indicates a starting pfn and number of
1175ca3e8d88SDave Plauger  * pages. When a stream block has been read, the condition variable
1176ca3e8d88SDave Plauger  * "cvwork" is signalled, which causes one of the avaiable threads to
1177ca3e8d88SDave Plauger  * wake up and process the stream.
1178ca3e8d88SDave Plauger  *
1179ca3e8d88SDave Plauger  * In the parallel case there will be streams blocks encoding all data
1180ca3e8d88SDave Plauger  * pages. The stream of blocks is terminated by a zero size
1181ca3e8d88SDave Plauger  * word. There can be a few lzjb pages tacked on the end, depending on
1182ca3e8d88SDave Plauger  * the architecture. The sbarrier function ensures that all stream
1183ca3e8d88SDave Plauger  * blocks have been processed so that the page number for the few
1184ca3e8d88SDave Plauger  * single pages at the end can be known.
1185ca3e8d88SDave Plauger  */
1186ca3e8d88SDave Plauger static void
decompress_pages(int corefd)1187ca3e8d88SDave Plauger decompress_pages(int corefd)
1188ca3e8d88SDave Plauger {
1189ca3e8d88SDave Plauger 	char *cpage = NULL;
1190ca3e8d88SDave Plauger 	char *dpage = NULL;
1191ca3e8d88SDave Plauger 	char *out;
11926d89ca53SIgor Kozhukhov 	pgcnt_t curpage = 0;
1193ca3e8d88SDave Plauger 	block_t *b;
1194ca3e8d88SDave Plauger 	FILE *dumpf;
1195ca3e8d88SDave Plauger 	FILE *tracef = NULL;
1196ca3e8d88SDave Plauger 	stream_t *s;
1197ca3e8d88SDave Plauger 	size_t dsize;
1198ca3e8d88SDave Plauger 	size_t insz = FBUFSIZE;
1199ca3e8d88SDave Plauger 	char *inbuf = Zalloc(insz);
1200ca3e8d88SDave Plauger 	uint32_t csize;
1201ca3e8d88SDave Plauger 	dumpcsize_t dcsize;
1202ca3e8d88SDave Plauger 	int nstreams = datahdr.dump_nstreams;
1203ca3e8d88SDave Plauger 	int maxcsize = datahdr.dump_maxcsize;
12046d89ca53SIgor Kozhukhov 	int nout = 0, tag, doflush;
1205ca3e8d88SDave Plauger 
1206ca3e8d88SDave Plauger 	dumpf = fdopen(dup(dumpfd), "rb");
1207ca3e8d88SDave Plauger 	if (dumpf == NULL)
1208f6e214c7SGavin Maltby 		logprint(SC_SL_ERR | SC_EXIT_ERR, "fdopen: %s",
1209f6e214c7SGavin Maltby 		    strerror(errno));
1210ca3e8d88SDave Plauger 
1211f6e214c7SGavin Maltby 	(void) setvbuf(dumpf, inbuf, _IOFBF, insz);
1212ca3e8d88SDave Plauger 	Fseek(dumphdr.dump_data, dumpf);
1213ca3e8d88SDave Plauger 
1214f6e214c7SGavin Maltby 	/*LINTED: E_CONSTANT_CONDITION*/
1215ca3e8d88SDave Plauger 	while (1) {
1216ca3e8d88SDave Plauger 
1217ca3e8d88SDave Plauger 		/*
1218ca3e8d88SDave Plauger 		 * The csize word delimits stream blocks.
1219ca3e8d88SDave Plauger 		 * See dumphdr.h for a description.
1220ca3e8d88SDave Plauger 		 */
1221ca3e8d88SDave Plauger 		Fread(&dcsize, sizeof (dcsize), dumpf);
1222ca3e8d88SDave Plauger 
1223ca3e8d88SDave Plauger 		tag = DUMP_GET_TAG(dcsize);
1224ca3e8d88SDave Plauger 		csize = DUMP_GET_CSIZE(dcsize);
1225ca3e8d88SDave Plauger 
1226ca3e8d88SDave Plauger 		if (tag != 0) {		/* a stream block */
1227ca3e8d88SDave Plauger 
1228ca3e8d88SDave Plauger 			if (nstreams == 0)
1229f6e214c7SGavin Maltby 				logprint(SC_SL_ERR | SC_EXIT_ERR,
1230ca3e8d88SDave Plauger 				    "starting data header is missing");
1231ca3e8d88SDave Plauger 
1232ca3e8d88SDave Plauger 			if (tag > nstreams)
1233f6e214c7SGavin Maltby 				logprint(SC_SL_ERR | SC_EXIT_ERR,
1234ca3e8d88SDave Plauger 				    "stream tag %d not in range 1..%d",
1235ca3e8d88SDave Plauger 				    tag, nstreams);
1236ca3e8d88SDave Plauger 
1237ca3e8d88SDave Plauger 			if (csize > maxcsize)
1238f6e214c7SGavin Maltby 				logprint(SC_SL_ERR | SC_EXIT_ERR,
1239ca3e8d88SDave Plauger 				    "block size 0x%x > max csize 0x%x",
1240ca3e8d88SDave Plauger 				    csize, maxcsize);
1241ca3e8d88SDave Plauger 
1242ca3e8d88SDave Plauger 			if (streams == NULL)
1243ca3e8d88SDave Plauger 				initstreams(corefd, nstreams, maxcsize);
1244ca3e8d88SDave Plauger 			s = &streams[tag - 1];
1245ca3e8d88SDave Plauger 			s->tag = tag;
1246ca3e8d88SDave Plauger 
1247ca3e8d88SDave Plauger 			b = getfreeblock();
1248ca3e8d88SDave Plauger 			b->size = csize;
1249ca3e8d88SDave Plauger 			Fread(b->block, csize, dumpf);
1250ca3e8d88SDave Plauger 
1251f6e214c7SGavin Maltby 			(void) pthread_mutex_lock(&lock);
1252ca3e8d88SDave Plauger 			enqt(&s->blocks, b);
1253ca3e8d88SDave Plauger 			if (!s->bound)
1254f6e214c7SGavin Maltby 				(void) pthread_cond_signal(&cvwork);
1255f6e214c7SGavin Maltby 			(void) pthread_mutex_unlock(&lock);
1256ca3e8d88SDave Plauger 
1257ca3e8d88SDave Plauger 		} else if (csize > 0) {		/* one lzjb page */
1258ca3e8d88SDave Plauger 
1259ca3e8d88SDave Plauger 			if (csize > pagesize)
1260f6e214c7SGavin Maltby 				logprint(SC_SL_ERR | SC_EXIT_ERR,
1261ca3e8d88SDave Plauger 				    "csize 0x%x > pagesize 0x%x",
1262ca3e8d88SDave Plauger 				    csize, pagesize);
1263ca3e8d88SDave Plauger 
1264ca3e8d88SDave Plauger 			if (cpage == NULL)
1265ca3e8d88SDave Plauger 				cpage = Zalloc(pagesize);
1266ca3e8d88SDave Plauger 			if (dpage == NULL) {
1267ca3e8d88SDave Plauger 				dpage = Zalloc(coreblksize);
1268ca3e8d88SDave Plauger 				nout = 0;
1269ca3e8d88SDave Plauger 			}
1270ca3e8d88SDave Plauger 
1271ca3e8d88SDave Plauger 			Fread(cpage, csize, dumpf);
1272ca3e8d88SDave Plauger 
1273ca3e8d88SDave Plauger 			out = dpage + PTOB(nout);
1274ca3e8d88SDave Plauger 			dsize = decompress(cpage, out, csize, pagesize);
1275ca3e8d88SDave Plauger 
1276ca3e8d88SDave Plauger 			if (dsize != pagesize)
1277f6e214c7SGavin Maltby 				logprint(SC_SL_ERR | SC_EXIT_ERR,
1278ca3e8d88SDave Plauger 				    "dsize 0x%x != pagesize 0x%x",
1279ca3e8d88SDave Plauger 				    dsize, pagesize);
1280ca3e8d88SDave Plauger 
1281ca3e8d88SDave Plauger 			/*
1282ca3e8d88SDave Plauger 			 * wait for streams to flush so that 'saved' is correct
1283ca3e8d88SDave Plauger 			 */
1284ca3e8d88SDave Plauger 			if (threads_active)
1285ca3e8d88SDave Plauger 				sbarrier();
1286ca3e8d88SDave Plauger 
1287ca3e8d88SDave Plauger 			doflush = 0;
1288ca3e8d88SDave Plauger 			if (nout == 0)
1289ca3e8d88SDave Plauger 				curpage = saved;
1290ca3e8d88SDave Plauger 
1291ca3e8d88SDave Plauger 			atomic_inc_64(&saved);
1292ca3e8d88SDave Plauger 
1293ca3e8d88SDave Plauger 			if (nout == 0 && iszpage(dpage)) {
1294ca3e8d88SDave Plauger 				doflush = 1;
1295ca3e8d88SDave Plauger 				atomic_inc_64(&zpages);
1296ca3e8d88SDave Plauger 			} else if (++nout >= BTOP(coreblksize) ||
1297ca3e8d88SDave Plauger 			    isblkbnd(curpage + nout) ||
1298ca3e8d88SDave Plauger 			    saved >= dumphdr.dump_npages) {
1299ca3e8d88SDave Plauger 				doflush = 1;
1300ca3e8d88SDave Plauger 			}
1301ca3e8d88SDave Plauger 
1302ca3e8d88SDave Plauger 			if (doflush) {
1303ca3e8d88SDave Plauger 				putpage(corefd, dpage, curpage, nout);
1304ca3e8d88SDave Plauger 				nout = 0;
1305ca3e8d88SDave Plauger 			}
1306ca3e8d88SDave Plauger 
1307ca3e8d88SDave Plauger 			report_progress();
1308ca3e8d88SDave Plauger 
1309ca3e8d88SDave Plauger 			/*
1310ca3e8d88SDave Plauger 			 * Non-streams lzjb does not use blocks.  Stop
1311ca3e8d88SDave Plauger 			 * here if all the pages have been decompressed.
1312ca3e8d88SDave Plauger 			 */
1313ca3e8d88SDave Plauger 			if (saved >= dumphdr.dump_npages)
1314ca3e8d88SDave Plauger 				break;
1315ca3e8d88SDave Plauger 
1316ca3e8d88SDave Plauger 		} else {
1317ca3e8d88SDave Plauger 			break;			/* end of data */
1318ca3e8d88SDave Plauger 		}
1319ca3e8d88SDave Plauger 	}
1320ca3e8d88SDave Plauger 
1321ca3e8d88SDave Plauger 	stopstreams();
1322ca3e8d88SDave Plauger 	if (tracef != NULL)
1323f6e214c7SGavin Maltby 		(void) fclose(tracef);
1324f6e214c7SGavin Maltby 	(void) fclose(dumpf);
1325ca3e8d88SDave Plauger 	if (inbuf)
1326ca3e8d88SDave Plauger 		free(inbuf);
1327ca3e8d88SDave Plauger 	if (cpage)
1328ca3e8d88SDave Plauger 		free(cpage);
1329ca3e8d88SDave Plauger 	if (dpage)
1330ca3e8d88SDave Plauger 		free(dpage);
1331ca3e8d88SDave Plauger 	if (streams)
1332ca3e8d88SDave Plauger 		free(streams);
13337c478bd9Sstevel@tonic-gate }
13347c478bd9Sstevel@tonic-gate 
13357c478bd9Sstevel@tonic-gate static void
build_corefile(const char * namelist,const char * corefile)13367c478bd9Sstevel@tonic-gate build_corefile(const char *namelist, const char *corefile)
13377c478bd9Sstevel@tonic-gate {
13387c478bd9Sstevel@tonic-gate 	size_t pfn_table_size = dumphdr.dump_npages * sizeof (pfn_t);
13397c478bd9Sstevel@tonic-gate 	size_t ksyms_size = dumphdr.dump_ksyms_size;
13407c478bd9Sstevel@tonic-gate 	size_t ksyms_csize = dumphdr.dump_ksyms_csize;
1341ca3e8d88SDave Plauger 	pfn_t *pfn_table;
13427c478bd9Sstevel@tonic-gate 	char *ksyms_base = Zalloc(ksyms_size);
13437c478bd9Sstevel@tonic-gate 	char *ksyms_cbase = Zalloc(ksyms_csize);
1344ca3e8d88SDave Plauger 	size_t ksyms_dsize;
1345ca3e8d88SDave Plauger 	Stat_t st;
13467c478bd9Sstevel@tonic-gate 	int corefd = Open(corefile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
13477c478bd9Sstevel@tonic-gate 	int namefd = Open(namelist, O_WRONLY | O_CREAT | O_TRUNC, 0644);
13487c478bd9Sstevel@tonic-gate 
13497c478bd9Sstevel@tonic-gate 	(void) printf("Constructing namelist %s/%s\n", savedir, namelist);
13507c478bd9Sstevel@tonic-gate 
1351ca3e8d88SDave Plauger 	/*
1352ca3e8d88SDave Plauger 	 * Determine the optimum write size for the core file
1353ca3e8d88SDave Plauger 	 */
1354ca3e8d88SDave Plauger 	Fstat(corefd, &st, corefile);
1355ca3e8d88SDave Plauger 
1356ca3e8d88SDave Plauger 	if (verbose > 1)
1357f6e214c7SGavin Maltby 		(void) printf("%s: %ld block size\n", corefile,
1358f6e214c7SGavin Maltby 		    (long)st.st_blksize);
1359ca3e8d88SDave Plauger 	coreblksize = st.st_blksize;
1360ca3e8d88SDave Plauger 	if (coreblksize < MINCOREBLKSIZE || !ISP2(coreblksize))
1361ca3e8d88SDave Plauger 		coreblksize = MINCOREBLKSIZE;
1362ca3e8d88SDave Plauger 
1363ca3e8d88SDave Plauger 	hist = Zalloc((sizeof (uint64_t) * BTOP(coreblksize)) + 1);
1364ca3e8d88SDave Plauger 
1365ca3e8d88SDave Plauger 	/*
1366ca3e8d88SDave Plauger 	 * This dump file is now uncompressed
1367ca3e8d88SDave Plauger 	 */
1368ca3e8d88SDave Plauger 	corehdr.dump_flags &= ~DF_COMPRESSED;
1369ca3e8d88SDave Plauger 
13707c478bd9Sstevel@tonic-gate 	/*
13717c478bd9Sstevel@tonic-gate 	 * Read in the compressed symbol table, copy it to corefile,
13727c478bd9Sstevel@tonic-gate 	 * decompress it, and write the result to namelist.
13737c478bd9Sstevel@tonic-gate 	 */
13747c478bd9Sstevel@tonic-gate 	corehdr.dump_ksyms = pagesize;
13757c478bd9Sstevel@tonic-gate 	Pread(dumpfd, ksyms_cbase, ksyms_csize, dumphdr.dump_ksyms);
13767c478bd9Sstevel@tonic-gate 	Pwrite(corefd, ksyms_cbase, ksyms_csize, corehdr.dump_ksyms);
13777c478bd9Sstevel@tonic-gate 
1378ca3e8d88SDave Plauger 	ksyms_dsize = decompress(ksyms_cbase, ksyms_base, ksyms_csize,
1379ca3e8d88SDave Plauger 	    ksyms_size);
1380ca3e8d88SDave Plauger 	if (ksyms_dsize != ksyms_size)
1381f6e214c7SGavin Maltby 		logprint(SC_SL_WARN,
1382ca3e8d88SDave Plauger 		    "bad data in symbol table, %lu of %lu bytes saved",
1383ca3e8d88SDave Plauger 		    ksyms_dsize, ksyms_size);
13847c478bd9Sstevel@tonic-gate 
13857c478bd9Sstevel@tonic-gate 	Pwrite(namefd, ksyms_base, ksyms_size, 0);
13867c478bd9Sstevel@tonic-gate 	(void) close(namefd);
13877c478bd9Sstevel@tonic-gate 	free(ksyms_cbase);
13887c478bd9Sstevel@tonic-gate 	free(ksyms_base);
13897c478bd9Sstevel@tonic-gate 
13907c478bd9Sstevel@tonic-gate 	(void) printf("Constructing corefile %s/%s\n", savedir, corefile);
13917c478bd9Sstevel@tonic-gate 
13927c478bd9Sstevel@tonic-gate 	/*
13937c478bd9Sstevel@tonic-gate 	 * Read in and write out the pfn table.
13947c478bd9Sstevel@tonic-gate 	 */
1395ca3e8d88SDave Plauger 	pfn_table = Zalloc(pfn_table_size);
13967c478bd9Sstevel@tonic-gate 	corehdr.dump_pfn = corehdr.dump_ksyms + roundup(ksyms_size, pagesize);
13977c478bd9Sstevel@tonic-gate 	Pread(dumpfd, pfn_table, pfn_table_size, dumphdr.dump_pfn);
13987c478bd9Sstevel@tonic-gate 	Pwrite(corefd, pfn_table, pfn_table_size, corehdr.dump_pfn);
13997c478bd9Sstevel@tonic-gate 
14007c478bd9Sstevel@tonic-gate 	/*
14017c478bd9Sstevel@tonic-gate 	 * Convert the raw translation data into a hashed dump map.
14027c478bd9Sstevel@tonic-gate 	 */
14037c478bd9Sstevel@tonic-gate 	corehdr.dump_map = corehdr.dump_pfn + roundup(pfn_table_size, pagesize);
14047c478bd9Sstevel@tonic-gate 	build_dump_map(corefd, pfn_table);
1405ca3e8d88SDave Plauger 	free(pfn_table);
14067c478bd9Sstevel@tonic-gate 
14077c478bd9Sstevel@tonic-gate 	/*
1408ca3e8d88SDave Plauger 	 * Decompress the pages
14097c478bd9Sstevel@tonic-gate 	 */
1410ca3e8d88SDave Plauger 	decompress_pages(corefd);
1411ca3e8d88SDave Plauger 	(void) printf(": %ld of %ld pages saved\n", (pgcnt_t)saved,
1412ca3e8d88SDave Plauger 	    dumphdr.dump_npages);
14137c478bd9Sstevel@tonic-gate 
1414ca3e8d88SDave Plauger 	if (verbose)
1415ca3e8d88SDave Plauger 		(void) printf("%ld (%ld%%) zero pages were not written\n",
1416ca3e8d88SDave Plauger 		    (pgcnt_t)zpages, (pgcnt_t)zpages * 100 /
1417ca3e8d88SDave Plauger 		    dumphdr.dump_npages);
14187c478bd9Sstevel@tonic-gate 
14197c478bd9Sstevel@tonic-gate 	if (saved != dumphdr.dump_npages)
1420f6e214c7SGavin Maltby 		logprint(SC_SL_WARN, "bad data after page %ld", saved);
14217c478bd9Sstevel@tonic-gate 
14227c478bd9Sstevel@tonic-gate 	/*
14237c478bd9Sstevel@tonic-gate 	 * Write out the modified dump headers.
14247c478bd9Sstevel@tonic-gate 	 */
14257c478bd9Sstevel@tonic-gate 	Pwrite(corefd, &corehdr, sizeof (corehdr), 0);
142646648336SHans Rosenfeld 	if (!filemode && !rflag)
1427ca3e8d88SDave Plauger 		Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff);
14287c478bd9Sstevel@tonic-gate 
14297c478bd9Sstevel@tonic-gate 	(void) close(corefd);
14307c478bd9Sstevel@tonic-gate }
14317c478bd9Sstevel@tonic-gate 
14327c478bd9Sstevel@tonic-gate /*
14337c478bd9Sstevel@tonic-gate  * When the system panics, the kernel saves all undelivered messages (messages
1434*bbf21555SRichard Lowe  * that never made it out to syslogd(8)) in the dump.  At a mimimum, the
14357c478bd9Sstevel@tonic-gate  * panic message itself will always fall into this category.  Upon reboot,
14367c478bd9Sstevel@tonic-gate  * the syslog startup script runs savecore -m to recover these messages.
14377c478bd9Sstevel@tonic-gate  *
14387c478bd9Sstevel@tonic-gate  * To do this, we read the unsent messages from the dump and send them to
14397c478bd9Sstevel@tonic-gate  * /dev/conslog on priority band 1.  This has the effect of prepending them
14407c478bd9Sstevel@tonic-gate  * to any already-accumulated messages in the console backlog, thus preserving
14417c478bd9Sstevel@tonic-gate  * temporal ordering across the reboot.
14427c478bd9Sstevel@tonic-gate  *
14437c478bd9Sstevel@tonic-gate  * Note: since savecore -m is used *only* for this purpose, it does *not*
14447c478bd9Sstevel@tonic-gate  * attempt to save the crash dump.  The dump will be saved later, after
1445*bbf21555SRichard Lowe  * syslogd(8) starts, by the savecore startup script.
14467c478bd9Sstevel@tonic-gate  */
14477c478bd9Sstevel@tonic-gate static int
message_save(void)14487c478bd9Sstevel@tonic-gate message_save(void)
14497c478bd9Sstevel@tonic-gate {
14507c478bd9Sstevel@tonic-gate 	offset_t dumpoff = -(DUMP_OFFSET + DUMP_LOGSIZE);
14517c478bd9Sstevel@tonic-gate 	offset_t ldoff;
14527c478bd9Sstevel@tonic-gate 	log_dump_t ld;
14537c478bd9Sstevel@tonic-gate 	log_ctl_t lc;
14547c478bd9Sstevel@tonic-gate 	struct strbuf ctl, dat;
14557c478bd9Sstevel@tonic-gate 	int logfd;
14567c478bd9Sstevel@tonic-gate 
14577c478bd9Sstevel@tonic-gate 	logfd = Open("/dev/conslog", O_WRONLY, 0644);
14587c478bd9Sstevel@tonic-gate 	dumpfd = Open(dumpfile, O_RDWR | O_DSYNC, 0644);
14597c478bd9Sstevel@tonic-gate 	dumpoff = llseek(dumpfd, dumpoff, SEEK_END) & -DUMP_OFFSET;
14607c478bd9Sstevel@tonic-gate 
14617c478bd9Sstevel@tonic-gate 	ctl.buf = (void *)&lc;
14627c478bd9Sstevel@tonic-gate 	ctl.len = sizeof (log_ctl_t);
14637c478bd9Sstevel@tonic-gate 
14647c478bd9Sstevel@tonic-gate 	dat.buf = Zalloc(DUMP_LOGSIZE);
14657c478bd9Sstevel@tonic-gate 
14667c478bd9Sstevel@tonic-gate 	for (;;) {
14677c478bd9Sstevel@tonic-gate 		ldoff = dumpoff;
14687c478bd9Sstevel@tonic-gate 
14697c478bd9Sstevel@tonic-gate 		Pread(dumpfd, &ld, sizeof (log_dump_t), dumpoff);
14707c478bd9Sstevel@tonic-gate 		dumpoff += sizeof (log_dump_t);
14717c478bd9Sstevel@tonic-gate 		dat.len = ld.ld_msgsize;
14727c478bd9Sstevel@tonic-gate 
14737c478bd9Sstevel@tonic-gate 		if (ld.ld_magic == 0)
14747c478bd9Sstevel@tonic-gate 			break;
14757c478bd9Sstevel@tonic-gate 
14767c478bd9Sstevel@tonic-gate 		if (ld.ld_magic != LOG_MAGIC)
1477f6e214c7SGavin Maltby 			logprint(SC_SL_ERR | SC_IF_VERBOSE | SC_EXIT_ERR,
1478f6e214c7SGavin Maltby 			    "bad magic %x", ld.ld_magic);
14797c478bd9Sstevel@tonic-gate 
14807c478bd9Sstevel@tonic-gate 		if (dat.len >= DUMP_LOGSIZE)
1481f6e214c7SGavin Maltby 			logprint(SC_SL_ERR | SC_IF_VERBOSE | SC_EXIT_ERR,
1482f6e214c7SGavin Maltby 			    "bad size %d", ld.ld_msgsize);
14837c478bd9Sstevel@tonic-gate 
14847c478bd9Sstevel@tonic-gate 		Pread(dumpfd, ctl.buf, ctl.len, dumpoff);
14857c478bd9Sstevel@tonic-gate 		dumpoff += ctl.len;
14867c478bd9Sstevel@tonic-gate 
14877c478bd9Sstevel@tonic-gate 		if (ld.ld_csum != checksum32(ctl.buf, ctl.len))
1488f6e214c7SGavin Maltby 			logprint(SC_SL_ERR | SC_IF_VERBOSE | SC_EXIT_OK,
1489f6e214c7SGavin Maltby 			    "bad log_ctl checksum");
14907c478bd9Sstevel@tonic-gate 
14917c478bd9Sstevel@tonic-gate 		lc.flags |= SL_LOGONLY;
14927c478bd9Sstevel@tonic-gate 
14937c478bd9Sstevel@tonic-gate 		Pread(dumpfd, dat.buf, dat.len, dumpoff);
14947c478bd9Sstevel@tonic-gate 		dumpoff += dat.len;
14957c478bd9Sstevel@tonic-gate 
14967c478bd9Sstevel@tonic-gate 		if (ld.ld_msum != checksum32(dat.buf, dat.len))
1497f6e214c7SGavin Maltby 			logprint(SC_SL_ERR | SC_IF_VERBOSE | SC_EXIT_OK,
1498f6e214c7SGavin Maltby 			    "bad message checksum");
14997c478bd9Sstevel@tonic-gate 
15007c478bd9Sstevel@tonic-gate 		if (putpmsg(logfd, &ctl, &dat, 1, MSG_BAND) == -1)
1501f6e214c7SGavin Maltby 			logprint(SC_SL_ERR | SC_EXIT_ERR, "putpmsg: %s",
1502f6e214c7SGavin Maltby 			    strerror(errno));
15037c478bd9Sstevel@tonic-gate 
15047c478bd9Sstevel@tonic-gate 		ld.ld_magic = 0;	/* clear magic so we never save twice */
15057c478bd9Sstevel@tonic-gate 		Pwrite(dumpfd, &ld, sizeof (log_dump_t), ldoff);
15067c478bd9Sstevel@tonic-gate 	}
15077c478bd9Sstevel@tonic-gate 	return (0);
15087c478bd9Sstevel@tonic-gate }
15097c478bd9Sstevel@tonic-gate 
1510ca3e8d88SDave Plauger static long
getbounds(const char * f)1511ca3e8d88SDave Plauger getbounds(const char *f)
1512ca3e8d88SDave Plauger {
1513ca3e8d88SDave Plauger 	long b = -1;
1514ca3e8d88SDave Plauger 	const char *p = strrchr(f, '/');
1515ca3e8d88SDave Plauger 
1516ae449904SBill Pijewski 	if (p == NULL || strncmp(p, "vmdump", 6) != 0)
1517ae449904SBill Pijewski 		p = strstr(f, "vmdump");
1518ae449904SBill Pijewski 
1519ae449904SBill Pijewski 	if (p != NULL && *p == '/')
1520ae449904SBill Pijewski 		p++;
1521ae449904SBill Pijewski 
1522ae449904SBill Pijewski 	(void) sscanf(p ? p : f, "vmdump.%ld", &b);
1523ae449904SBill Pijewski 
1524ca3e8d88SDave Plauger 	return (b);
1525ca3e8d88SDave Plauger }
1526ca3e8d88SDave Plauger 
1527f6e214c7SGavin Maltby static void
stack_retrieve(char * stack)1528f6e214c7SGavin Maltby stack_retrieve(char *stack)
1529f6e214c7SGavin Maltby {
1530f6e214c7SGavin Maltby 	summary_dump_t sd;
1531f6e214c7SGavin Maltby 	offset_t dumpoff = -(DUMP_OFFSET + DUMP_LOGSIZE +
1532f6e214c7SGavin Maltby 	    DUMP_ERPTSIZE);
1533f6e214c7SGavin Maltby 	dumpoff -= DUMP_SUMMARYSIZE;
1534f6e214c7SGavin Maltby 
153546648336SHans Rosenfeld 	if (rflag)
153646648336SHans Rosenfeld 		dumpfd = Open(dumpfile, O_RDONLY, 0644);
153746648336SHans Rosenfeld 	else
153846648336SHans Rosenfeld 		dumpfd = Open(dumpfile, O_RDWR | O_DSYNC, 0644);
1539f6e214c7SGavin Maltby 	dumpoff = llseek(dumpfd, dumpoff, SEEK_END) & -DUMP_OFFSET;
1540f6e214c7SGavin Maltby 
1541f6e214c7SGavin Maltby 	Pread(dumpfd, &sd, sizeof (summary_dump_t), dumpoff);
1542f6e214c7SGavin Maltby 	dumpoff += sizeof (summary_dump_t);
1543f6e214c7SGavin Maltby 
1544f6e214c7SGavin Maltby 	if (sd.sd_magic == 0) {
1545f6e214c7SGavin Maltby 		*stack = '\0';
1546f6e214c7SGavin Maltby 		return;
1547f6e214c7SGavin Maltby 	}
1548f6e214c7SGavin Maltby 
1549f6e214c7SGavin Maltby 	if (sd.sd_magic != SUMMARY_MAGIC) {
1550f6e214c7SGavin Maltby 		*stack = '\0';
1551f6e214c7SGavin Maltby 		logprint(SC_SL_NONE | SC_IF_VERBOSE,
1552f6e214c7SGavin Maltby 		    "bad summary magic %x", sd.sd_magic);
1553f6e214c7SGavin Maltby 		return;
1554f6e214c7SGavin Maltby 	}
1555f6e214c7SGavin Maltby 	Pread(dumpfd, stack, STACK_BUF_SIZE, dumpoff);
1556f6e214c7SGavin Maltby 	if (sd.sd_ssum != checksum32(stack, STACK_BUF_SIZE))
1557f6e214c7SGavin Maltby 		logprint(SC_SL_NONE | SC_IF_VERBOSE, "bad stack checksum");
1558f6e214c7SGavin Maltby }
1559f6e214c7SGavin Maltby 
1560f6e214c7SGavin Maltby static void
raise_event(enum sc_event_type evidx,char * warn_string)1561f6e214c7SGavin Maltby raise_event(enum sc_event_type evidx, char *warn_string)
1562f6e214c7SGavin Maltby {
1563f6e214c7SGavin Maltby 	uint32_t pl = sc_event[evidx].sce_payload;
1564f6e214c7SGavin Maltby 	char panic_stack[STACK_BUF_SIZE];
1565f6e214c7SGavin Maltby 	nvlist_t *attr = NULL;
1566f6e214c7SGavin Maltby 	char uuidbuf[36 + 1];
1567f6e214c7SGavin Maltby 	int err = 0;
1568f6e214c7SGavin Maltby 
1569f6e214c7SGavin Maltby 	if (nvlist_alloc(&attr, NV_UNIQUE_NAME, 0) != 0)
1570f6e214c7SGavin Maltby 		goto publish;	/* try to send payload-free event */
1571f6e214c7SGavin Maltby 
1572f6e214c7SGavin Maltby 	if (pl & SC_PAYLOAD_SAVEDIR && savedir != NULL)
1573f6e214c7SGavin Maltby 		err |= nvlist_add_string(attr, "dumpdir", savedir);
1574f6e214c7SGavin Maltby 
1575f6e214c7SGavin Maltby 	if (pl & SC_PAYLOAD_INSTANCE && bounds != -1)
1576f6e214c7SGavin Maltby 		err |= nvlist_add_int64(attr, "instance", bounds);
1577f6e214c7SGavin Maltby 
1578f6e214c7SGavin Maltby 	if (pl & SC_PAYLOAD_ISCOMPRESSED) {
1579f6e214c7SGavin Maltby 		err |= nvlist_add_boolean_value(attr, "compressed",
1580f6e214c7SGavin Maltby 		    csave ? B_TRUE : B_FALSE);
1581f6e214c7SGavin Maltby 	}
1582f6e214c7SGavin Maltby 
1583f6e214c7SGavin Maltby 	if (pl & SC_PAYLOAD_DUMPADM_EN) {
1584f6e214c7SGavin Maltby 		char *disabled = defread("DUMPADM_ENABLE=no");
1585f6e214c7SGavin Maltby 
1586f6e214c7SGavin Maltby 		err |= nvlist_add_boolean_value(attr, "savecore-enabled",
1587f6e214c7SGavin Maltby 		    disabled ? B_FALSE : B_TRUE);
1588f6e214c7SGavin Maltby 	}
1589f6e214c7SGavin Maltby 
1590f6e214c7SGavin Maltby 	if (pl & SC_PAYLOAD_IMAGEUUID) {
1591f6e214c7SGavin Maltby 		(void) strncpy(uuidbuf, corehdr.dump_uuid, 36);
1592f6e214c7SGavin Maltby 		uuidbuf[36] = '\0';
1593f6e214c7SGavin Maltby 		err |= nvlist_add_string(attr, "os-instance-uuid", uuidbuf);
1594f6e214c7SGavin Maltby 	}
1595f6e214c7SGavin Maltby 
1596f6e214c7SGavin Maltby 	if (pl & SC_PAYLOAD_CRASHTIME) {
1597f6e214c7SGavin Maltby 		err |= nvlist_add_int64(attr, "crashtime",
1598f6e214c7SGavin Maltby 		    (int64_t)corehdr.dump_crashtime);
1599f6e214c7SGavin Maltby 	}
1600f6e214c7SGavin Maltby 
1601f6e214c7SGavin Maltby 	if (pl & SC_PAYLOAD_PANICSTR && corehdr.dump_panicstring[0] != '\0') {
1602f6e214c7SGavin Maltby 		err |= nvlist_add_string(attr, "panicstr",
1603f6e214c7SGavin Maltby 		    corehdr.dump_panicstring);
1604f6e214c7SGavin Maltby 	}
1605f6e214c7SGavin Maltby 
1606f6e214c7SGavin Maltby 	if (pl & SC_PAYLOAD_PANICSTACK) {
1607f6e214c7SGavin Maltby 		stack_retrieve(panic_stack);
1608f6e214c7SGavin Maltby 
1609f6e214c7SGavin Maltby 		if (panic_stack[0] != '\0') {
1610f6e214c7SGavin Maltby 			/*
1611f6e214c7SGavin Maltby 			 * The summary page may not be present if the dump
1612f6e214c7SGavin Maltby 			 * was previously recorded compressed.
1613f6e214c7SGavin Maltby 			 */
1614f6e214c7SGavin Maltby 			(void) nvlist_add_string(attr, "panicstack",
1615f6e214c7SGavin Maltby 			    panic_stack);
1616f6e214c7SGavin Maltby 		}
1617f6e214c7SGavin Maltby 	}
1618f6e214c7SGavin Maltby 
1619f6e214c7SGavin Maltby 	/* add warning string if this is an ireport for dump failure */
1620f6e214c7SGavin Maltby 	if (pl & SC_PAYLOAD_FAILREASON && warn_string != NULL)
1621f6e214c7SGavin Maltby 		(void) nvlist_add_string(attr, "failure-reason", warn_string);
1622f6e214c7SGavin Maltby 
1623f6e214c7SGavin Maltby 	if (pl & SC_PAYLOAD_DUMPCOMPLETE)
1624f6e214c7SGavin Maltby 		err |= nvlist_add_boolean_value(attr, "dump-incomplete",
1625f6e214c7SGavin Maltby 		    dump_incomplete ? B_TRUE : B_FALSE);
1626f6e214c7SGavin Maltby 
1627f6e214c7SGavin Maltby 	if (pl & SC_PAYLOAD_FM_PANIC) {
1628f6e214c7SGavin Maltby 		err |= nvlist_add_boolean_value(attr, "fm-panic",
1629f6e214c7SGavin Maltby 		    fm_panic ? B_TRUE : B_FALSE);
1630f6e214c7SGavin Maltby 	}
1631f6e214c7SGavin Maltby 
1632f6e214c7SGavin Maltby 	if (pl & SC_PAYLOAD_JUSTCHECKING) {
1633f6e214c7SGavin Maltby 		err |= nvlist_add_boolean_value(attr, "will-attempt-savecore",
1634f6e214c7SGavin Maltby 		    cflag ? B_FALSE : B_TRUE);
1635f6e214c7SGavin Maltby 	}
1636f6e214c7SGavin Maltby 
1637f6e214c7SGavin Maltby 	if (err)
1638f6e214c7SGavin Maltby 		logprint(SC_SL_WARN, "Errors while constructing '%s' "
1639f6e214c7SGavin Maltby 		    "event payload; will try to publish anyway.");
1640f6e214c7SGavin Maltby publish:
1641f6e214c7SGavin Maltby 	if (fmev_rspublish_nvl(FMEV_RULESET_ON_SUNOS,
1642f6e214c7SGavin Maltby 	    "panic", sc_event[evidx].sce_subclass, FMEV_HIPRI,
1643f6e214c7SGavin Maltby 	    attr) != FMEV_SUCCESS) {
1644f6e214c7SGavin Maltby 		logprint(SC_SL_ERR, "failed to publish '%s' event: %s",
1645f6e214c7SGavin Maltby 		    sc_event[evidx].sce_subclass, fmev_strerror(fmev_errno));
1646f6e214c7SGavin Maltby 		nvlist_free(attr);
1647f6e214c7SGavin Maltby 	}
1648f6e214c7SGavin Maltby 
1649f6e214c7SGavin Maltby }
1650f6e214c7SGavin Maltby 
1651f6e214c7SGavin Maltby 
16527c478bd9Sstevel@tonic-gate int
main(int argc,char * argv[])16537c478bd9Sstevel@tonic-gate main(int argc, char *argv[])
16547c478bd9Sstevel@tonic-gate {
1655f6e214c7SGavin Maltby 	int i, c, bfd;
1656ca3e8d88SDave Plauger 	Stat_t st;
1657ca3e8d88SDave Plauger 	struct rlimit rl;
1658ca3e8d88SDave Plauger 	long filebounds = -1;
165907d8ed1aSDave Plauger 	char namelist[30], corefile[30], boundstr[30];
1660ae449904SBill Pijewski 	dumpfile = NULL;
1661ca3e8d88SDave Plauger 
1662ca3e8d88SDave Plauger 	startts = gethrtime();
1663ca3e8d88SDave Plauger 
1664f6e214c7SGavin Maltby 	(void) getrlimit(RLIMIT_NOFILE, &rl);
1665ca3e8d88SDave Plauger 	rl.rlim_cur = rl.rlim_max;
1666f6e214c7SGavin Maltby 	(void) setrlimit(RLIMIT_NOFILE, &rl);
16677c478bd9Sstevel@tonic-gate 
16687c478bd9Sstevel@tonic-gate 	openlog(progname, LOG_ODELAY, LOG_AUTH);
1669ca3e8d88SDave Plauger 
16707c478bd9Sstevel@tonic-gate 	(void) defopen("/etc/dumpadm.conf");
167107d8ed1aSDave Plauger 	savedir = defread("DUMPADM_SAVDIR=");
167207d8ed1aSDave Plauger 	if (savedir != NULL)
167307d8ed1aSDave Plauger 		savedir = strdup(savedir);
16747c478bd9Sstevel@tonic-gate 
167546648336SHans Rosenfeld 	while ((c = getopt(argc, argv, "Lvcdmf:r")) != EOF) {
16767c478bd9Sstevel@tonic-gate 		switch (c) {
16777c478bd9Sstevel@tonic-gate 		case 'L':
16787c478bd9Sstevel@tonic-gate 			livedump++;
16797c478bd9Sstevel@tonic-gate 			break;
16807c478bd9Sstevel@tonic-gate 		case 'v':
16817c478bd9Sstevel@tonic-gate 			verbose++;
16827c478bd9Sstevel@tonic-gate 			break;
1683f6e214c7SGavin Maltby 		case 'c':
1684f6e214c7SGavin Maltby 			cflag++;
1685f6e214c7SGavin Maltby 			break;
16867c478bd9Sstevel@tonic-gate 		case 'd':
16877c478bd9Sstevel@tonic-gate 			disregard_valid_flag++;
16887c478bd9Sstevel@tonic-gate 			break;
16897c478bd9Sstevel@tonic-gate 		case 'm':
16907c478bd9Sstevel@tonic-gate 			mflag++;
16917c478bd9Sstevel@tonic-gate 			break;
169246648336SHans Rosenfeld 		case 'r':
169346648336SHans Rosenfeld 			rflag++;
169446648336SHans Rosenfeld 			break;
16957c478bd9Sstevel@tonic-gate 		case 'f':
16967c478bd9Sstevel@tonic-gate 			dumpfile = optarg;
1697ca3e8d88SDave Plauger 			filebounds = getbounds(dumpfile);
16987c478bd9Sstevel@tonic-gate 			break;
16997c478bd9Sstevel@tonic-gate 		case '?':
17007c478bd9Sstevel@tonic-gate 			usage();
17017c478bd9Sstevel@tonic-gate 		}
17027c478bd9Sstevel@tonic-gate 	}
17037c478bd9Sstevel@tonic-gate 
1704ae449904SBill Pijewski 	/*
1705ae449904SBill Pijewski 	 * If doing something other than extracting an existing dump (i.e.
1706ae449904SBill Pijewski 	 * dumpfile has been provided as an option), the user must be root.
1707ae449904SBill Pijewski 	 */
1708ae449904SBill Pijewski 	if (geteuid() != 0 && dumpfile == NULL) {
1709bfbff323SChristopher William Beal 		(void) fprintf(stderr, "%s: %s %s\n", progname,
1710bfbff323SChristopher William Beal 		    gettext("you must be root to use"), progname);
1711bfbff323SChristopher William Beal 		exit(1);
1712bfbff323SChristopher William Beal 	}
1713bfbff323SChristopher William Beal 
1714ca3e8d88SDave Plauger 	interactive = isatty(STDOUT_FILENO);
1715ca3e8d88SDave Plauger 
1716f6e214c7SGavin Maltby 	if (cflag && livedump)
1717f6e214c7SGavin Maltby 		usage();
1718f6e214c7SGavin Maltby 
171946648336SHans Rosenfeld 	if (rflag && (cflag || mflag || livedump))
172046648336SHans Rosenfeld 		usage();
172146648336SHans Rosenfeld 
17227c478bd9Sstevel@tonic-gate 	if (dumpfile == NULL || livedump)
17237c478bd9Sstevel@tonic-gate 		dumpfd = Open("/dev/dump", O_RDONLY, 0444);
17247c478bd9Sstevel@tonic-gate 
17257c478bd9Sstevel@tonic-gate 	if (dumpfile == NULL) {
17267c478bd9Sstevel@tonic-gate 		dumpfile = Zalloc(MAXPATHLEN);
1727222d1ab6SAlexander Eremin 		if (ioctl(dumpfd, DIOCGETDEV, dumpfile) == -1) {
1728222d1ab6SAlexander Eremin 			have_dumpfile = B_FALSE;
1729f6e214c7SGavin Maltby 			logprint(SC_SL_NONE | SC_IF_ISATTY | SC_EXIT_ERR,
1730ca3e8d88SDave Plauger 			    "no dump device configured");
1731222d1ab6SAlexander Eremin 		}
17327c478bd9Sstevel@tonic-gate 	}
17337c478bd9Sstevel@tonic-gate 
17347c478bd9Sstevel@tonic-gate 	if (mflag)
17357c478bd9Sstevel@tonic-gate 		return (message_save());
17367c478bd9Sstevel@tonic-gate 
17377c478bd9Sstevel@tonic-gate 	if (optind == argc - 1)
17387c478bd9Sstevel@tonic-gate 		savedir = argv[optind];
1739ca3e8d88SDave Plauger 
17407c478bd9Sstevel@tonic-gate 	if (savedir == NULL || optind < argc - 1)
17417c478bd9Sstevel@tonic-gate 		usage();
17427c478bd9Sstevel@tonic-gate 
17437c478bd9Sstevel@tonic-gate 	if (livedump && ioctl(dumpfd, DIOCDUMP, NULL) == -1)
1744f6e214c7SGavin Maltby 		logprint(SC_SL_NONE | SC_EXIT_ERR,
1745f6e214c7SGavin Maltby 		    "dedicated dump device required");
17467c478bd9Sstevel@tonic-gate 
17477c478bd9Sstevel@tonic-gate 	(void) close(dumpfd);
1748ca3e8d88SDave Plauger 	dumpfd = -1;
1749ca3e8d88SDave Plauger 
1750ca3e8d88SDave Plauger 	Stat(dumpfile, &st);
1751ca3e8d88SDave Plauger 
1752ca3e8d88SDave Plauger 	filemode = S_ISREG(st.st_mode);
1753ca3e8d88SDave Plauger 
1754ca3e8d88SDave Plauger 	if (!filemode && defread("DUMPADM_CSAVE=off") == NULL)
1755ca3e8d88SDave Plauger 		csave = 1;
17567c478bd9Sstevel@tonic-gate 
17577c478bd9Sstevel@tonic-gate 	read_dumphdr();
17587c478bd9Sstevel@tonic-gate 
17597c478bd9Sstevel@tonic-gate 	/*
17607c478bd9Sstevel@tonic-gate 	 * We want this message to go to the log file, but not the console.
17617c478bd9Sstevel@tonic-gate 	 * There's no good way to do that with the existing syslog facility.
17627c478bd9Sstevel@tonic-gate 	 * We could extend it to handle this, but there doesn't seem to be
17637c478bd9Sstevel@tonic-gate 	 * a general need for it, so we isolate the complexity here instead.
17647c478bd9Sstevel@tonic-gate 	 */
176546648336SHans Rosenfeld 	if (dumphdr.dump_panicstring[0] != '\0' && !rflag) {
17667c478bd9Sstevel@tonic-gate 		int logfd = Open("/dev/conslog", O_WRONLY, 0644);
17677c478bd9Sstevel@tonic-gate 		log_ctl_t lc;
17687c478bd9Sstevel@tonic-gate 		struct strbuf ctl, dat;
17697c478bd9Sstevel@tonic-gate 		char msg[DUMP_PANICSIZE + 100];
17707c478bd9Sstevel@tonic-gate 		char fmt[] = "reboot after panic: %s";
17717c478bd9Sstevel@tonic-gate 		uint32_t msgid;
17727c478bd9Sstevel@tonic-gate 
17737c478bd9Sstevel@tonic-gate 		STRLOG_MAKE_MSGID(fmt, msgid);
17747c478bd9Sstevel@tonic-gate 
1775f6e214c7SGavin Maltby 		/* LINTED: E_SEC_SPRINTF_UNBOUNDED_COPY */
17767c478bd9Sstevel@tonic-gate 		(void) sprintf(msg, "%s: [ID %u FACILITY_AND_PRIORITY] ",
17777c478bd9Sstevel@tonic-gate 		    progname, msgid);
1778f6e214c7SGavin Maltby 		/* LINTED: E_SEC_PRINTF_VAR_FMT */
17797c478bd9Sstevel@tonic-gate 		(void) sprintf(msg + strlen(msg), fmt,
17807c478bd9Sstevel@tonic-gate 		    dumphdr.dump_panicstring);
17817c478bd9Sstevel@tonic-gate 
17827c478bd9Sstevel@tonic-gate 		lc.pri = LOG_AUTH | LOG_ERR;
17837c478bd9Sstevel@tonic-gate 		lc.flags = SL_CONSOLE | SL_LOGONLY;
17847c478bd9Sstevel@tonic-gate 		lc.level = 0;
17857c478bd9Sstevel@tonic-gate 
17867c478bd9Sstevel@tonic-gate 		ctl.buf = (void *)&lc;
17877c478bd9Sstevel@tonic-gate 		ctl.len = sizeof (log_ctl_t);
17887c478bd9Sstevel@tonic-gate 
17897c478bd9Sstevel@tonic-gate 		dat.buf = (void *)msg;
17907c478bd9Sstevel@tonic-gate 		dat.len = strlen(msg) + 1;
17917c478bd9Sstevel@tonic-gate 
17927c478bd9Sstevel@tonic-gate 		(void) putmsg(logfd, &ctl, &dat, 0);
17937c478bd9Sstevel@tonic-gate 		(void) close(logfd);
17947c478bd9Sstevel@tonic-gate 	}
17957c478bd9Sstevel@tonic-gate 
1796f6e214c7SGavin Maltby 	if ((dumphdr.dump_flags & DF_COMPLETE) == 0) {
1797f6e214c7SGavin Maltby 		logprint(SC_SL_WARN, "incomplete dump on dump device");
1798f6e214c7SGavin Maltby 		dump_incomplete = B_TRUE;
1799f6e214c7SGavin Maltby 	}
18007c478bd9Sstevel@tonic-gate 
1801f6e214c7SGavin Maltby 	if (dumphdr.dump_fm_panic)
1802f6e214c7SGavin Maltby 		fm_panic = B_TRUE;
18037c478bd9Sstevel@tonic-gate 
1804f6e214c7SGavin Maltby 	/*
1805f6e214c7SGavin Maltby 	 * We have a valid dump on a dump device and know as much about
1806f6e214c7SGavin Maltby 	 * it as we're going to at this stage.  Raise an event for
1807f6e214c7SGavin Maltby 	 * logging and so that FMA can open a case for this panic.
1808f6e214c7SGavin Maltby 	 * Avoid this step for FMA-initiated panics - FMA will replay
1809f6e214c7SGavin Maltby 	 * ereports off the dump device independently of savecore and
1810f6e214c7SGavin Maltby 	 * will make a diagnosis, so we don't want to open two cases
1811f6e214c7SGavin Maltby 	 * for the same event.  Also avoid raising an event for a
1812f6e214c7SGavin Maltby 	 * livedump, or when we inflating a compressed dump.
1813f6e214c7SGavin Maltby 	 */
181446648336SHans Rosenfeld 	if (!fm_panic && !livedump && !filemode && !rflag)
1815f6e214c7SGavin Maltby 		raise_event(SC_EVENT_DUMP_PENDING, NULL);
1816f6e214c7SGavin Maltby 
1817f6e214c7SGavin Maltby 	logprint(SC_SL_WARN, "System dump time: %s",
1818ca3e8d88SDave Plauger 	    ctime(&dumphdr.dump_crashtime));
1819ca3e8d88SDave Plauger 
1820f6e214c7SGavin Maltby 	/*
1821f6e214c7SGavin Maltby 	 * Option -c is designed for use from svc-dumpadm where we know
1822f6e214c7SGavin Maltby 	 * that dumpadm -n is in effect but run savecore -c just to
1823f6e214c7SGavin Maltby 	 * get the above dump_pending_on_device event raised.  If it is run
1824f6e214c7SGavin Maltby 	 * interactively then just print further panic details.
1825f6e214c7SGavin Maltby 	 */
1826f6e214c7SGavin Maltby 	if (cflag) {
1827f6e214c7SGavin Maltby 		char *disabled = defread("DUMPADM_ENABLE=no");
1828f6e214c7SGavin Maltby 		int lvl = interactive ? SC_SL_WARN : SC_SL_ERR;
1829f6e214c7SGavin Maltby 		int ec = fm_panic ? SC_EXIT_FM : SC_EXIT_PEND;
1830f6e214c7SGavin Maltby 
1831f6e214c7SGavin Maltby 		logprint(lvl | ec,
1832f6e214c7SGavin Maltby 		    "Panic crashdump pending on dump device%s "
1833*bbf21555SRichard Lowe 		    "run savecore(8) manually to extract. "
1834f6e214c7SGavin Maltby 		    "Image UUID %s%s.",
1835f6e214c7SGavin Maltby 		    disabled ? " but dumpadm -n in effect;" : ";",
1836f6e214c7SGavin Maltby 		    corehdr.dump_uuid,
1837f6e214c7SGavin Maltby 		    fm_panic ?  "(fault-management initiated)" : "");
1838f6e214c7SGavin Maltby 		/*NOTREACHED*/
1839f6e214c7SGavin Maltby 	}
1840f6e214c7SGavin Maltby 
1841f6e214c7SGavin Maltby 	if (chdir(savedir) == -1)
1842f6e214c7SGavin Maltby 		logprint(SC_SL_ERR | SC_EXIT_ERR, "chdir(\"%s\"): %s",
1843f6e214c7SGavin Maltby 		    savedir, strerror(errno));
1844f6e214c7SGavin Maltby 
1845ca3e8d88SDave Plauger 	check_space(csave);
1846ca3e8d88SDave Plauger 
1847ca3e8d88SDave Plauger 	if (filebounds < 0)
1848ca3e8d88SDave Plauger 		bounds = read_number_from_file("bounds", 0);
1849ca3e8d88SDave Plauger 	else
1850ca3e8d88SDave Plauger 		bounds = filebounds;
1851ca3e8d88SDave Plauger 
1852ca3e8d88SDave Plauger 	if (csave) {
1853ca3e8d88SDave Plauger 		size_t metrics_size = datahdr.dump_metrics;
1854ca3e8d88SDave Plauger 
1855ca3e8d88SDave Plauger 		(void) sprintf(corefile, "vmdump.%ld", bounds);
1856ca3e8d88SDave Plauger 
1857ca3e8d88SDave Plauger 		datahdr.dump_metrics = 0;
1858ca3e8d88SDave Plauger 
1859f6e214c7SGavin Maltby 		logprint(SC_SL_ERR,
1860ca3e8d88SDave Plauger 		    "Saving compressed system crash dump in %s/%s",
1861ca3e8d88SDave Plauger 		    savedir, corefile);
1862ca3e8d88SDave Plauger 
1863ca3e8d88SDave Plauger 		copy_crashfile(corefile);
1864ca3e8d88SDave Plauger 
1865f6e214c7SGavin Maltby 		/*
1866f6e214c7SGavin Maltby 		 * Raise a fault management event that indicates the system
1867f6e214c7SGavin Maltby 		 * has panicked. We know a reasonable amount about the
1868f6e214c7SGavin Maltby 		 * condition at this time, but the dump is still compressed.
1869f6e214c7SGavin Maltby 		 */
187046648336SHans Rosenfeld 		if (!livedump && !fm_panic && !rflag)
1871f6e214c7SGavin Maltby 			raise_event(SC_EVENT_DUMP_AVAILABLE, NULL);
1872f6e214c7SGavin Maltby 
1873ca3e8d88SDave Plauger 		if (metrics_size > 0) {
1874ca3e8d88SDave Plauger 			int sec = (gethrtime() - startts) / 1000 / 1000 / 1000;
187507d8ed1aSDave Plauger 			FILE *mfile = fopen(METRICSFILE, "a");
1876ca3e8d88SDave Plauger 			char *metrics = Zalloc(metrics_size + 1);
1877ca3e8d88SDave Plauger 
1878ca3e8d88SDave Plauger 			Pread(dumpfd, metrics, metrics_size, endoff +
1879ca3e8d88SDave Plauger 			    sizeof (dumphdr) + sizeof (datahdr));
1880ca3e8d88SDave Plauger 
18810b8c024eSDave Plauger 			if (sec < 1)
18820b8c024eSDave Plauger 				sec = 1;
18830b8c024eSDave Plauger 
1884ca3e8d88SDave Plauger 			if (mfile == NULL) {
1885f6e214c7SGavin Maltby 				logprint(SC_SL_WARN,
1886ca3e8d88SDave Plauger 				    "Can't create %s:\n%s",
188707d8ed1aSDave Plauger 				    METRICSFILE, metrics);
1888ca3e8d88SDave Plauger 			} else {
1889f6e214c7SGavin Maltby 				(void) fprintf(mfile, "[[[[,,,");
1890ca3e8d88SDave Plauger 				for (i = 0; i < argc; i++)
1891f6e214c7SGavin Maltby 					(void) fprintf(mfile, "%s ", argv[i]);
1892f6e214c7SGavin Maltby 				(void) fprintf(mfile, "\n");
1893f6e214c7SGavin Maltby 				(void) fprintf(mfile, ",,,%s %s %s %s %s\n",
1894ca3e8d88SDave Plauger 				    dumphdr.dump_utsname.sysname,
1895ca3e8d88SDave Plauger 				    dumphdr.dump_utsname.nodename,
1896ca3e8d88SDave Plauger 				    dumphdr.dump_utsname.release,
1897ca3e8d88SDave Plauger 				    dumphdr.dump_utsname.version,
1898ca3e8d88SDave Plauger 				    dumphdr.dump_utsname.machine);
1899f6e214c7SGavin Maltby 				(void) fprintf(mfile, ",,,%s dump time %s\n",
1900ca3e8d88SDave Plauger 				    dumphdr.dump_flags & DF_LIVE ? "Live" :
1901ca3e8d88SDave Plauger 				    "Crash", ctime(&dumphdr.dump_crashtime));
1902f6e214c7SGavin Maltby 				(void) fprintf(mfile, ",,,%s/%s\n", savedir,
1903f6e214c7SGavin Maltby 				    corefile);
1904f6e214c7SGavin Maltby 				(void) fprintf(mfile, "Metrics:\n%s\n",
1905f6e214c7SGavin Maltby 				    metrics);
1906f6e214c7SGavin Maltby 				(void) fprintf(mfile, "Copy pages,%ld\n",
1907f6e214c7SGavin Maltby 				    dumphdr.  dump_npages);
1908f6e214c7SGavin Maltby 				(void) fprintf(mfile, "Copy time,%d\n", sec);
1909f6e214c7SGavin Maltby 				(void) fprintf(mfile, "Copy pages/sec,%ld\n",
1910ca3e8d88SDave Plauger 				    dumphdr.dump_npages / sec);
1911f6e214c7SGavin Maltby 				(void) fprintf(mfile, "]]]]\n");
1912f6e214c7SGavin Maltby 				(void) fclose(mfile);
1913ca3e8d88SDave Plauger 			}
1914ca3e8d88SDave Plauger 			free(metrics);
1915ca3e8d88SDave Plauger 		}
19167c478bd9Sstevel@tonic-gate 
1917f6e214c7SGavin Maltby 		logprint(SC_SL_ERR,
1918ca3e8d88SDave Plauger 		    "Decompress the crash dump with "
1919ca3e8d88SDave Plauger 		    "\n'savecore -vf %s/%s'",
1920ca3e8d88SDave Plauger 		    savedir, corefile);
1921ca3e8d88SDave Plauger 
1922ca3e8d88SDave Plauger 	} else {
1923ca3e8d88SDave Plauger 		(void) sprintf(namelist, "unix.%ld", bounds);
1924ca3e8d88SDave Plauger 		(void) sprintf(corefile, "vmcore.%ld", bounds);
1925ca3e8d88SDave Plauger 
1926ca3e8d88SDave Plauger 		if (interactive && filebounds >= 0 && access(corefile, F_OK)
1927ca3e8d88SDave Plauger 		    == 0)
1928f6e214c7SGavin Maltby 			logprint(SC_SL_NONE | SC_EXIT_ERR,
1929ca3e8d88SDave Plauger 			    "%s already exists: remove with "
1930ca3e8d88SDave Plauger 			    "'rm -f %s/{unix,vmcore}.%ld'",
1931ca3e8d88SDave Plauger 			    corefile, savedir, bounds);
1932ca3e8d88SDave Plauger 
1933f6e214c7SGavin Maltby 		logprint(SC_SL_ERR,
1934ca3e8d88SDave Plauger 		    "saving system crash dump in %s/{unix,vmcore}.%ld",
1935ca3e8d88SDave Plauger 		    savedir, bounds);
1936ca3e8d88SDave Plauger 
1937ca3e8d88SDave Plauger 		build_corefile(namelist, corefile);
1938ca3e8d88SDave Plauger 
193946648336SHans Rosenfeld 		if (!livedump && !filemode && !fm_panic && !rflag)
1940f6e214c7SGavin Maltby 			raise_event(SC_EVENT_DUMP_AVAILABLE, NULL);
1941f6e214c7SGavin Maltby 
194207d8ed1aSDave Plauger 		if (access(METRICSFILE, F_OK) == 0) {
1943ca3e8d88SDave Plauger 			int sec = (gethrtime() - startts) / 1000 / 1000 / 1000;
194407d8ed1aSDave Plauger 			FILE *mfile = fopen(METRICSFILE, "a");
1945ca3e8d88SDave Plauger 
19460b8c024eSDave Plauger 			if (sec < 1)
19470b8c024eSDave Plauger 				sec = 1;
19480b8c024eSDave Plauger 
19498d222d81SRobert Mustacchi 			if (mfile == NULL) {
19508d222d81SRobert Mustacchi 				logprint(SC_SL_WARN,
19518d222d81SRobert Mustacchi 				    "Can't create %s: %s",
19528d222d81SRobert Mustacchi 				    METRICSFILE, strerror(errno));
19538d222d81SRobert Mustacchi 			} else {
19548d222d81SRobert Mustacchi 				(void) fprintf(mfile, "[[[[,,,");
19558d222d81SRobert Mustacchi 				for (i = 0; i < argc; i++)
19568d222d81SRobert Mustacchi 					(void) fprintf(mfile, "%s ", argv[i]);
19578d222d81SRobert Mustacchi 				(void) fprintf(mfile, "\n");
19588d222d81SRobert Mustacchi 				(void) fprintf(mfile, ",,,%s/%s\n", savedir,
19598d222d81SRobert Mustacchi 				    corefile);
19608d222d81SRobert Mustacchi 				(void) fprintf(mfile, ",,,%s %s %s %s %s\n",
19618d222d81SRobert Mustacchi 				    dumphdr.dump_utsname.sysname,
19628d222d81SRobert Mustacchi 				    dumphdr.dump_utsname.nodename,
19638d222d81SRobert Mustacchi 				    dumphdr.dump_utsname.release,
19648d222d81SRobert Mustacchi 				    dumphdr.dump_utsname.version,
19658d222d81SRobert Mustacchi 				    dumphdr.dump_utsname.machine);
19668d222d81SRobert Mustacchi 				(void) fprintf(mfile,
19678d222d81SRobert Mustacchi 				    "Uncompress pages,%"PRIu64"\n", saved);
19688d222d81SRobert Mustacchi 				(void) fprintf(mfile, "Uncompress time,%d\n",
19698d222d81SRobert Mustacchi 				    sec);
19708d222d81SRobert Mustacchi 				(void) fprintf(mfile, "Uncompress pages/sec,%"
19718d222d81SRobert Mustacchi 				    PRIu64"\n", saved / sec);
19728d222d81SRobert Mustacchi 				(void) fprintf(mfile, "]]]]\n");
19738d222d81SRobert Mustacchi 				(void) fclose(mfile);
19748d222d81SRobert Mustacchi 			}
1975ca3e8d88SDave Plauger 		}
1976ca3e8d88SDave Plauger 	}
19777c478bd9Sstevel@tonic-gate 
1978ca3e8d88SDave Plauger 	if (filebounds < 0) {
1979ca3e8d88SDave Plauger 		(void) sprintf(boundstr, "%ld\n", bounds + 1);
1980ca3e8d88SDave Plauger 		bfd = Open("bounds", O_WRONLY | O_CREAT | O_TRUNC, 0644);
1981ca3e8d88SDave Plauger 		Pwrite(bfd, boundstr, strlen(boundstr), 0);
1982ca3e8d88SDave Plauger 		(void) close(bfd);
1983ca3e8d88SDave Plauger 	}
19847c478bd9Sstevel@tonic-gate 
1985ca3e8d88SDave Plauger 	if (verbose) {
1986ca3e8d88SDave Plauger 		int sec = (gethrtime() - startts) / 1000 / 1000 / 1000;
19877c478bd9Sstevel@tonic-gate 
1988f6e214c7SGavin Maltby 		(void) printf("%d:%02d dump %s is done\n",
1989ca3e8d88SDave Plauger 		    sec / 60, sec % 60,
1990ca3e8d88SDave Plauger 		    csave ? "copy" : "decompress");
1991ca3e8d88SDave Plauger 	}
19927c478bd9Sstevel@tonic-gate 
1993ca3e8d88SDave Plauger 	if (verbose > 1 && hist != NULL) {
1994ca3e8d88SDave Plauger 		int i, nw;
1995ca3e8d88SDave Plauger 
1996ca3e8d88SDave Plauger 		for (i = 1, nw = 0; i <= BTOP(coreblksize); ++i)
1997ca3e8d88SDave Plauger 			nw += hist[i] * i;
1998f6e214c7SGavin Maltby 		(void) printf("pages count     %%\n");
1999ca3e8d88SDave Plauger 		for (i = 0; i <= BTOP(coreblksize); ++i) {
2000ca3e8d88SDave Plauger 			if (hist[i] == 0)
2001ca3e8d88SDave Plauger 				continue;
2002f6e214c7SGavin Maltby 			(void) printf("%3d   %5u  %6.2f\n",
2003ca3e8d88SDave Plauger 			    i, hist[i], 100.0 * hist[i] * i / nw);
2004ca3e8d88SDave Plauger 		}
2005ca3e8d88SDave Plauger 	}
20067c478bd9Sstevel@tonic-gate 
2007ca3e8d88SDave Plauger 	(void) close(dumpfd);
2008ca3e8d88SDave Plauger 	dumpfd = -1;
20097c478bd9Sstevel@tonic-gate 
20107c478bd9Sstevel@tonic-gate 	return (0);
20117c478bd9Sstevel@tonic-gate }
2012