xref: /freebsd/sbin/savecore/savecore.c (revision 3203428dc5c8f2237a4396b907bd3d306936315f)
18fae3551SRodney W. Grimes /*-
2d503fad0SPoul-Henning Kamp  * Copyright (c) 2002 Poul-Henning Kamp
3d503fad0SPoul-Henning Kamp  * Copyright (c) 2002 Networks Associates Technology, Inc.
4d503fad0SPoul-Henning Kamp  * All rights reserved.
5d503fad0SPoul-Henning Kamp  *
6d503fad0SPoul-Henning Kamp  * This software was developed for the FreeBSD Project by Poul-Henning Kamp
7d503fad0SPoul-Henning Kamp  * and NAI Labs, the Security Research Division of Network Associates, Inc.
8d503fad0SPoul-Henning Kamp  * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
9d503fad0SPoul-Henning Kamp  * DARPA CHATS research program.
108fae3551SRodney W. Grimes  *
118fae3551SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
128fae3551SRodney W. Grimes  * modification, are permitted provided that the following conditions
138fae3551SRodney W. Grimes  * are met:
148fae3551SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
158fae3551SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
168fae3551SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
178fae3551SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
188fae3551SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
19d503fad0SPoul-Henning Kamp  * 3. The names of the authors may not be used to endorse or promote
20d503fad0SPoul-Henning Kamp  *    products derived from this software without specific prior written
21d503fad0SPoul-Henning Kamp  *    permission.
228fae3551SRodney W. Grimes  *
23d503fad0SPoul-Henning Kamp  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
248fae3551SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
258fae3551SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26d503fad0SPoul-Henning Kamp  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
278fae3551SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
288fae3551SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
298fae3551SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
308fae3551SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
318fae3551SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
328fae3551SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
338fae3551SRodney W. Grimes  * SUCH DAMAGE.
348fae3551SRodney W. Grimes  */
358fae3551SRodney W. Grimes 
364b8b6734SPhilippe Charnier #include <sys/cdefs.h>
374b8b6734SPhilippe Charnier __FBSDID("$FreeBSD$");
384b8b6734SPhilippe Charnier 
395da217f6SMarcel Moolenaar #include <sys/types.h>
402dd527b3SPoul-Henning Kamp #include <sys/disk.h>
41d503fad0SPoul-Henning Kamp #include <sys/kerneldump.h>
423203428dSMaxime Henrion #include <sys/param.h>
433203428dSMaxime Henrion #include <sys/mount.h>
445da217f6SMarcel Moolenaar #include <sys/stat.h>
455da217f6SMarcel Moolenaar #include <err.h>
465da217f6SMarcel Moolenaar #include <errno.h>
475da217f6SMarcel Moolenaar #include <fcntl.h>
485da217f6SMarcel Moolenaar #include <fstab.h>
495da217f6SMarcel Moolenaar #include <md5.h>
503203428dSMaxime Henrion #include <paths.h>
515da217f6SMarcel Moolenaar #include <stdio.h>
525da217f6SMarcel Moolenaar #include <stdlib.h>
535da217f6SMarcel Moolenaar #include <string.h>
545da217f6SMarcel Moolenaar #include <time.h>
555da217f6SMarcel Moolenaar #include <unistd.h>
565da217f6SMarcel Moolenaar 
575da217f6SMarcel Moolenaar int clear, force, keep, verbose;	/* flags */
583203428dSMaxime Henrion int nfound, nsaved, nerr;		/* statistics */
59dff462c3SKris Kennaway 
60d503fad0SPoul-Henning Kamp static void
615da217f6SMarcel Moolenaar printheader(FILE *f, const struct kerneldumpheader *h, const char *device,
625cb87b0cSMarcel Moolenaar     const char *md5)
63d503fad0SPoul-Henning Kamp {
645cb87b0cSMarcel Moolenaar 	uint64_t dumplen;
65d503fad0SPoul-Henning Kamp 	time_t t;
668fae3551SRodney W. Grimes 
675da217f6SMarcel Moolenaar 	fprintf(f, "Good dump found on device %s\n", device);
68d503fad0SPoul-Henning Kamp 	fprintf(f, "  Architecture: %s\n", h->architecture);
695cb87b0cSMarcel Moolenaar 	fprintf(f, "  Architecture version: %d\n",
705cb87b0cSMarcel Moolenaar 	    dtoh32(h->architectureversion));
715cb87b0cSMarcel Moolenaar 	dumplen = dtoh64(h->dumplength);
725cb87b0cSMarcel Moolenaar 	fprintf(f, "  Dump length: %lldB (%lld MB)\n", (long long)dumplen,
735cb87b0cSMarcel Moolenaar 	    (long long)(dumplen >> 20));
745cb87b0cSMarcel Moolenaar 	fprintf(f, "  Blocksize: %d\n", dtoh32(h->blocksize));
755cb87b0cSMarcel Moolenaar 	t = dtoh64(h->dumptime);
76d503fad0SPoul-Henning Kamp 	fprintf(f, "  Dumptime: %s", ctime(&t));
77d503fad0SPoul-Henning Kamp 	fprintf(f, "  Hostname: %s\n", h->hostname);
78d503fad0SPoul-Henning Kamp 	fprintf(f, "  Versionstring: %s", h->versionstring);
79d503fad0SPoul-Henning Kamp 	fprintf(f, "  Panicstring: %s\n", h->panicstring);
80d503fad0SPoul-Henning Kamp 	fprintf(f, "  MD5: %s\n", md5);
81dce9aaabSPoul-Henning Kamp 	fflush(f);
82d503fad0SPoul-Henning Kamp }
838fae3551SRodney W. Grimes 
843203428dSMaxime Henrion /*
853203428dSMaxime Henrion  * Check that sufficient space is available on the disk that holds the
863203428dSMaxime Henrion  * save directory.
873203428dSMaxime Henrion  */
883203428dSMaxime Henrion static int
893203428dSMaxime Henrion check_space(char *savedir, off_t dumpsize)
903203428dSMaxime Henrion {
913203428dSMaxime Henrion 	FILE *fp;
923203428dSMaxime Henrion 	const char *tkernel;
933203428dSMaxime Henrion 	off_t minfree, spacefree, totfree, kernelsize, needed;
943203428dSMaxime Henrion 	struct stat st;
953203428dSMaxime Henrion 	struct statfs fsbuf;
963203428dSMaxime Henrion 	char buf[100], path[MAXPATHLEN];
973203428dSMaxime Henrion 
983203428dSMaxime Henrion 	tkernel = getbootfile();
993203428dSMaxime Henrion 	if (stat(tkernel, &st) < 0)
1003203428dSMaxime Henrion 		err(1, "%s", tkernel);
1013203428dSMaxime Henrion 	kernelsize = st.st_blocks * S_BLKSIZE;
1023203428dSMaxime Henrion 
1033203428dSMaxime Henrion 	if (statfs(savedir, &fsbuf) < 0)
1043203428dSMaxime Henrion 		err(1, "%s", savedir);
1053203428dSMaxime Henrion  	spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024;
1063203428dSMaxime Henrion 	totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024;
1073203428dSMaxime Henrion 
1083203428dSMaxime Henrion 	(void)snprintf(path, sizeof(path), "%s/minfree", savedir);
1093203428dSMaxime Henrion 	if ((fp = fopen(path, "r")) == NULL)
1103203428dSMaxime Henrion 		minfree = 0;
1113203428dSMaxime Henrion 	else {
1123203428dSMaxime Henrion 		if (fgets(buf, sizeof(buf), fp) == NULL)
1133203428dSMaxime Henrion 			minfree = 0;
1143203428dSMaxime Henrion 		else
1153203428dSMaxime Henrion 			minfree = atoi(buf);
1163203428dSMaxime Henrion 		(void)fclose(fp);
1173203428dSMaxime Henrion 	}
1183203428dSMaxime Henrion 
1193203428dSMaxime Henrion 	needed = (dumpsize + kernelsize) / 1024;
1203203428dSMaxime Henrion  	if (((minfree > 0) ? spacefree : totfree) - needed < minfree) {
1213203428dSMaxime Henrion 		warnx("no dump, not enough free space on device"
1223203428dSMaxime Henrion 		    " (%lld available, need %lld)",
1233203428dSMaxime Henrion 		    (long long)(minfree > 0 ? spacefree : totfree),
1243203428dSMaxime Henrion 		    (long long)needed);
1253203428dSMaxime Henrion 		return (0);
1263203428dSMaxime Henrion 	}
1273203428dSMaxime Henrion 	if (spacefree - needed < 0)
1283203428dSMaxime Henrion 		warnx("dump performed, but free space threshold crossed");
1293203428dSMaxime Henrion 	return (1);
1303203428dSMaxime Henrion }
1313203428dSMaxime Henrion 
1323203428dSMaxime Henrion 
1338fae3551SRodney W. Grimes 
134d503fad0SPoul-Henning Kamp static void
1353203428dSMaxime Henrion DoFile(char *savedir, const char *device)
136d503fad0SPoul-Henning Kamp {
137d503fad0SPoul-Henning Kamp 	struct kerneldumpheader kdhf, kdhl;
1385da217f6SMarcel Moolenaar 	char buf[BUFSIZ];
1395da217f6SMarcel Moolenaar 	struct stat sb;
1405da217f6SMarcel Moolenaar 	off_t mediasize, dumpsize, firsthd, lasthd;
141d503fad0SPoul-Henning Kamp 	char *md5;
142d503fad0SPoul-Henning Kamp 	FILE *info;
1435da217f6SMarcel Moolenaar 	int fd, fdcore, fdinfo, error, wl;
1445da217f6SMarcel Moolenaar 	u_int sectorsize;
1455da217f6SMarcel Moolenaar 
1465da217f6SMarcel Moolenaar 	if (verbose)
1475da217f6SMarcel Moolenaar 		printf("Checking for kernel dump on device %s\n", device);
1488fae3551SRodney W. Grimes 
149d503fad0SPoul-Henning Kamp 	mediasize = 0;
1505da217f6SMarcel Moolenaar 	fd = open(device, O_RDWR);
151d503fad0SPoul-Henning Kamp 	if (fd < 0) {
1525da217f6SMarcel Moolenaar 		warn("%s", device);
153d503fad0SPoul-Henning Kamp 		return;
154d503fad0SPoul-Henning Kamp 	}
155d503fad0SPoul-Henning Kamp 	error = ioctl(fd, DIOCGMEDIASIZE, &mediasize);
156d503fad0SPoul-Henning Kamp 	if (!error)
157d503fad0SPoul-Henning Kamp 		error = ioctl(fd, DIOCGSECTORSIZE, &sectorsize);
158d503fad0SPoul-Henning Kamp 	if (error) {
1594b8b6734SPhilippe Charnier 		warn("couldn't find media and/or sector size of %s", device);
1605da217f6SMarcel Moolenaar 		goto closefd;
161d503fad0SPoul-Henning Kamp 	}
1625da217f6SMarcel Moolenaar 
1635da217f6SMarcel Moolenaar 	if (verbose) {
1643837db64SJohn Baldwin 		printf("Mediasize = %lld\n", (long long)mediasize);
165d503fad0SPoul-Henning Kamp 		printf("Sectorsize = %u\n", sectorsize);
1665da217f6SMarcel Moolenaar 	}
1675da217f6SMarcel Moolenaar 
168d503fad0SPoul-Henning Kamp 	lasthd = mediasize - sectorsize;
169d503fad0SPoul-Henning Kamp 	lseek(fd, lasthd, SEEK_SET);
170d503fad0SPoul-Henning Kamp 	error = read(fd, &kdhl, sizeof kdhl);
171d503fad0SPoul-Henning Kamp 	if (error != sizeof kdhl) {
1724b8b6734SPhilippe Charnier 		warn("error reading last dump header at offset %lld in %s",
1735da217f6SMarcel Moolenaar 		    (long long)lasthd, device);
1745da217f6SMarcel Moolenaar 		goto closefd;
175d503fad0SPoul-Henning Kamp 	}
176d503fad0SPoul-Henning Kamp 	if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic)) {
1775da217f6SMarcel Moolenaar 		if (verbose)
1784b8b6734SPhilippe Charnier 			warnx("magic mismatch on last dump header on %s",
1795da217f6SMarcel Moolenaar 			    device);
1805da217f6SMarcel Moolenaar 		goto closefd;
181d503fad0SPoul-Henning Kamp 	}
1825cb87b0cSMarcel Moolenaar 	if (dtoh32(kdhl.version) != KERNELDUMPVERSION) {
1834b8b6734SPhilippe Charnier 		warnx("unknown version (%d) in last dump header on %s",
1845da217f6SMarcel Moolenaar 		    dtoh32(kdhl.version), device);
1855da217f6SMarcel Moolenaar 		goto closefd;
1865da217f6SMarcel Moolenaar 	}
1875da217f6SMarcel Moolenaar 
1885da217f6SMarcel Moolenaar 	nfound++;
1895da217f6SMarcel Moolenaar 	if (clear)
1905da217f6SMarcel Moolenaar 		goto nuke;
1915da217f6SMarcel Moolenaar 
1925da217f6SMarcel Moolenaar 	if (kerneldump_parity(&kdhl)) {
1934b8b6734SPhilippe Charnier 		warnx("parity error on last dump header on %s", device);
1943203428dSMaxime Henrion 		nerr++;
1955da217f6SMarcel Moolenaar 		goto closefd;
196d503fad0SPoul-Henning Kamp 	}
1975cb87b0cSMarcel Moolenaar 	dumpsize = dtoh64(kdhl.dumplength);
1985cb87b0cSMarcel Moolenaar 	firsthd = lasthd - dumpsize - sizeof kdhf;
199d503fad0SPoul-Henning Kamp 	lseek(fd, firsthd, SEEK_SET);
200d503fad0SPoul-Henning Kamp 	error = read(fd, &kdhf, sizeof kdhf);
201d503fad0SPoul-Henning Kamp 	if (error != sizeof kdhf) {
2024b8b6734SPhilippe Charnier 		warn("error reading first dump header at offset %lld in %s",
2035da217f6SMarcel Moolenaar 		    (long long)firsthd, device);
2043203428dSMaxime Henrion 		nerr++;
2055da217f6SMarcel Moolenaar 		goto closefd;
206d503fad0SPoul-Henning Kamp 	}
207d503fad0SPoul-Henning Kamp 	if (memcmp(&kdhl, &kdhf, sizeof kdhl)) {
2084b8b6734SPhilippe Charnier 		warn("first and last dump headers disagree on %s", device);
2093203428dSMaxime Henrion 		nerr++;
2105da217f6SMarcel Moolenaar 		goto closefd;
211d503fad0SPoul-Henning Kamp 	}
212d503fad0SPoul-Henning Kamp 	md5 = MD5Data((unsigned char *)&kdhl, sizeof kdhl, NULL);
213d503fad0SPoul-Henning Kamp 	sprintf(buf, "%s.info", md5);
2145da217f6SMarcel Moolenaar 
2155da217f6SMarcel Moolenaar 	/*
2165da217f6SMarcel Moolenaar 	 * See if the dump has been saved already. Don't save the dump
2175da217f6SMarcel Moolenaar 	 * again, unless 'force' is in effect.
2185da217f6SMarcel Moolenaar 	 */
2195da217f6SMarcel Moolenaar 	if (stat(buf, &sb) == 0) {
2205da217f6SMarcel Moolenaar 		if (!force) {
2215da217f6SMarcel Moolenaar 			if (verbose)
2225da217f6SMarcel Moolenaar 				printf("Dump on device %s already saved\n",
2235da217f6SMarcel Moolenaar 				    device);
2245da217f6SMarcel Moolenaar 			goto closefd;
225d503fad0SPoul-Henning Kamp 		}
2265da217f6SMarcel Moolenaar 	} else if (errno != ENOENT) {
2274b8b6734SPhilippe Charnier 		warn("error while checking for pre-saved core file");
2283203428dSMaxime Henrion 		nerr++;
2295da217f6SMarcel Moolenaar 		goto closefd;
2305da217f6SMarcel Moolenaar 	}
2315da217f6SMarcel Moolenaar 
2323203428dSMaxime Henrion 	if (!check_space(savedir, dumpsize)) {
2333203428dSMaxime Henrion 		nerr++;
2343203428dSMaxime Henrion 		goto closefd;
2353203428dSMaxime Henrion 	}
2365da217f6SMarcel Moolenaar 	/*
2375da217f6SMarcel Moolenaar 	 * Create or overwrite any existing files.
2385da217f6SMarcel Moolenaar 	 */
2395da217f6SMarcel Moolenaar 	fdinfo = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600);
240d503fad0SPoul-Henning Kamp 	if (fdinfo < 0) {
241d503fad0SPoul-Henning Kamp 		warn("%s", buf);
2423203428dSMaxime Henrion 		nerr++;
2435da217f6SMarcel Moolenaar 		goto closefd;
244d503fad0SPoul-Henning Kamp 	}
245d503fad0SPoul-Henning Kamp 	sprintf(buf, "%s.core", md5);
2465da217f6SMarcel Moolenaar 	fdcore = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600);
247d503fad0SPoul-Henning Kamp 	if (fdcore < 0) {
248d503fad0SPoul-Henning Kamp 		warn("%s", buf);
2495da217f6SMarcel Moolenaar 		close(fdinfo);
2503203428dSMaxime Henrion 		nerr++;
2515da217f6SMarcel Moolenaar 		goto closefd;
252d503fad0SPoul-Henning Kamp 	}
253d503fad0SPoul-Henning Kamp 	info = fdopen(fdinfo, "w");
2545da217f6SMarcel Moolenaar 
2555da217f6SMarcel Moolenaar 	if (verbose)
2565da217f6SMarcel Moolenaar 		printheader(stdout, &kdhl, device, md5);
2575da217f6SMarcel Moolenaar 
2585da217f6SMarcel Moolenaar 	printf("Saving dump to file %s\n", buf);
2595da217f6SMarcel Moolenaar 
2605da217f6SMarcel Moolenaar 	printheader(info, &kdhl, device, md5);
2615da217f6SMarcel Moolenaar 
262d503fad0SPoul-Henning Kamp 	while (dumpsize > 0) {
263d503fad0SPoul-Henning Kamp 		wl = sizeof(buf);
264d503fad0SPoul-Henning Kamp 		if (wl > dumpsize)
265d503fad0SPoul-Henning Kamp 			wl = dumpsize;
266d503fad0SPoul-Henning Kamp 		error = read(fd, buf, wl);
267d503fad0SPoul-Henning Kamp 		if (error != wl) {
2684b8b6734SPhilippe Charnier 			warn("read error on %s", device);
2693203428dSMaxime Henrion 			nerr++;
2705da217f6SMarcel Moolenaar 			goto closeall;
271d503fad0SPoul-Henning Kamp 		}
272d503fad0SPoul-Henning Kamp 		error = write(fdcore, buf, wl);
273d503fad0SPoul-Henning Kamp 		if (error != wl) {
2744b8b6734SPhilippe Charnier 			warn("write error on %s.core file", md5);
2753203428dSMaxime Henrion 			nerr++;
2765da217f6SMarcel Moolenaar 			goto closeall;
277d503fad0SPoul-Henning Kamp 		}
278d503fad0SPoul-Henning Kamp 		dumpsize -= wl;
279d503fad0SPoul-Henning Kamp 	}
2803203428dSMaxime Henrion 	nsaved++;
281d503fad0SPoul-Henning Kamp 	close(fdinfo);
282d503fad0SPoul-Henning Kamp 	close(fdcore);
2835da217f6SMarcel Moolenaar 
2845da217f6SMarcel Moolenaar 	if (verbose)
285d503fad0SPoul-Henning Kamp 		printf("Dump saved\n");
2865da217f6SMarcel Moolenaar 
2875da217f6SMarcel Moolenaar  nuke:
2885da217f6SMarcel Moolenaar 	if (clear || !keep) {
2895da217f6SMarcel Moolenaar 		if (verbose)
2905da217f6SMarcel Moolenaar 			printf("Clearing dump header\n");
2915da217f6SMarcel Moolenaar 		memset(&kdhl, 0, sizeof kdhl);
2925da217f6SMarcel Moolenaar 		lseek(fd, lasthd, SEEK_SET);
2935da217f6SMarcel Moolenaar 		error = write(fd, &kdhl, sizeof kdhl);
2945da217f6SMarcel Moolenaar 		if (error != sizeof kdhl)
2954b8b6734SPhilippe Charnier 			warn("error while clearing the dump header");
2965da217f6SMarcel Moolenaar 	}
2975da217f6SMarcel Moolenaar 	close(fd);
2985da217f6SMarcel Moolenaar 	return;
2995da217f6SMarcel Moolenaar 
3005da217f6SMarcel Moolenaar  closeall:
3015da217f6SMarcel Moolenaar 	close(fdinfo);
3025da217f6SMarcel Moolenaar 	close(fdcore);
3035da217f6SMarcel Moolenaar 
3045da217f6SMarcel Moolenaar  closefd:
3055da217f6SMarcel Moolenaar 	close(fd);
306d503fad0SPoul-Henning Kamp }
3079b0a8ba3SPeter Wemm 
308d503fad0SPoul-Henning Kamp static void
309d503fad0SPoul-Henning Kamp usage(void)
310d503fad0SPoul-Henning Kamp {
3114b8b6734SPhilippe Charnier 	fprintf(stderr, "usage: savecore [-cfkv] [directory [device...]]\n");
312d503fad0SPoul-Henning Kamp 	exit (1);
313d503fad0SPoul-Henning Kamp }
3148fae3551SRodney W. Grimes 
3158fae3551SRodney W. Grimes int
316d503fad0SPoul-Henning Kamp main(int argc, char **argv)
3178fae3551SRodney W. Grimes {
318d503fad0SPoul-Henning Kamp 	int i, ch, error;
319d503fad0SPoul-Henning Kamp 	struct fstab *fsp;
3203203428dSMaxime Henrion 	char *savedir;
3218fae3551SRodney W. Grimes 
3223203428dSMaxime Henrion 	savedir = strdup(".");
3233203428dSMaxime Henrion 	if (savedir == NULL)
3243203428dSMaxime Henrion 		errx(1, "Cannot allocate memory");
325867dd038SDag-Erling Smørgrav 	while ((ch = getopt(argc, argv, "cdfkN:vz")) != -1)
3268fae3551SRodney W. Grimes 		switch(ch) {
3278fae3551SRodney W. Grimes 		case 'c':
3285da217f6SMarcel Moolenaar 			clear = 1;
3295da217f6SMarcel Moolenaar 			break;
330532c1901SDag-Erling Smørgrav 		case 'k':
3315da217f6SMarcel Moolenaar 			keep = 1;
3325da217f6SMarcel Moolenaar 			break;
3335da217f6SMarcel Moolenaar 		case 'v':
3345da217f6SMarcel Moolenaar 			verbose = 1;
3355da217f6SMarcel Moolenaar 			break;
3365da217f6SMarcel Moolenaar 		case 'f':
3375da217f6SMarcel Moolenaar 			force = 1;
3385da217f6SMarcel Moolenaar 			break;
3395da217f6SMarcel Moolenaar 		case 'd':	/* Obsolete */
3408fae3551SRodney W. Grimes 		case 'N':
3418fae3551SRodney W. Grimes 		case 'z':
3428fae3551SRodney W. Grimes 		case '?':
3438fae3551SRodney W. Grimes 		default:
3448fae3551SRodney W. Grimes 			usage();
3458fae3551SRodney W. Grimes 		}
3468fae3551SRodney W. Grimes 	argc -= optind;
3478fae3551SRodney W. Grimes 	argv += optind;
348d503fad0SPoul-Henning Kamp 	if (argc >= 1) {
349d503fad0SPoul-Henning Kamp 		error = chdir(argv[0]);
350d503fad0SPoul-Henning Kamp 		if (error)
351d503fad0SPoul-Henning Kamp 			err(1, "chdir(%s)", argv[0]);
3523203428dSMaxime Henrion 		savedir = argv[0];
353d503fad0SPoul-Henning Kamp 		argc--;
354d503fad0SPoul-Henning Kamp 		argv++;
3558fae3551SRodney W. Grimes 	}
356d503fad0SPoul-Henning Kamp 	if (argc == 0) {
357d503fad0SPoul-Henning Kamp 		for (;;) {
358d503fad0SPoul-Henning Kamp 			fsp = getfsent();
359d503fad0SPoul-Henning Kamp 			if (fsp == NULL)
360d503fad0SPoul-Henning Kamp 				break;
361d503fad0SPoul-Henning Kamp 			if (strcmp(fsp->fs_vfstype, "swap") &&
362d503fad0SPoul-Henning Kamp 			    strcmp(fsp->fs_vfstype, "dump"))
363d503fad0SPoul-Henning Kamp 				continue;
3643203428dSMaxime Henrion 			DoFile(savedir, fsp->fs_spec);
3658fae3551SRodney W. Grimes 		}
366dff462c3SKris Kennaway 	} else {
367d503fad0SPoul-Henning Kamp 		for (i = 0; i < argc; i++)
3683203428dSMaxime Henrion 			DoFile(savedir, argv[i]);
369532c1901SDag-Erling Smørgrav 	}
3705da217f6SMarcel Moolenaar 
3715da217f6SMarcel Moolenaar 	/* Emit minimal output. */
3725da217f6SMarcel Moolenaar 	if (nfound == 0)
3735da217f6SMarcel Moolenaar 		printf("No dumps found\n");
3743203428dSMaxime Henrion 	else if (nsaved == 0) {
3753203428dSMaxime Henrion 		if (nerr != 0)
3763203428dSMaxime Henrion 			printf("Unsaved dumps found but not saved\n");
3773203428dSMaxime Henrion 		else
3785da217f6SMarcel Moolenaar 			printf("No unsaved dumps found\n");
3793203428dSMaxime Henrion 	}
3805da217f6SMarcel Moolenaar 
381c74e16f9SDag-Erling Smørgrav 	return (0);
382c74e16f9SDag-Erling Smørgrav }
383