savecore.c revision 142533
1181624Skmacy/*-
2181624Skmacy * Copyright (c) 2002 Poul-Henning Kamp
3181624Skmacy * Copyright (c) 2002 Networks Associates Technology, Inc.
4181624Skmacy * All rights reserved.
5181624Skmacy *
6181624Skmacy * This software was developed for the FreeBSD Project by Poul-Henning Kamp
7181624Skmacy * and NAI Labs, the Security Research Division of Network Associates, Inc.
8181624Skmacy * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
9181624Skmacy * DARPA CHATS research program.
10181624Skmacy *
11181624Skmacy * Redistribution and use in source and binary forms, with or without
12181624Skmacy * modification, are permitted provided that the following conditions
13181624Skmacy * are met:
14181624Skmacy * 1. Redistributions of source code must retain the above copyright
15181624Skmacy *    notice, this list of conditions and the following disclaimer.
16181624Skmacy * 2. Redistributions in binary form must reproduce the above copyright
17181624Skmacy *    notice, this list of conditions and the following disclaimer in the
18181624Skmacy *    documentation and/or other materials provided with the distribution.
19181624Skmacy * 3. The names of the authors may not be used to endorse or promote
20181624Skmacy *    products derived from this software without specific prior written
21181624Skmacy *    permission.
22181624Skmacy *
23181624Skmacy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24181624Skmacy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25181624Skmacy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26181624Skmacy * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27181624Skmacy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28181624Skmacy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29181624Skmacy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30181624Skmacy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31181624Skmacy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32181624Skmacy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33181624Skmacy * SUCH DAMAGE.
34181624Skmacy *
35181624Skmacy * Copyright (c) 1986, 1992, 1993
36181624Skmacy *	The Regents of the University of California.  All rights reserved.
37181624Skmacy *
38181624Skmacy * Redistribution and use in source and binary forms, with or without
39181624Skmacy * modification, are permitted provided that the following conditions
40181624Skmacy * are met:
41181624Skmacy * 1. Redistributions of source code must retain the above copyright
42181624Skmacy *    notice, this list of conditions and the following disclaimer.
43181624Skmacy * 2. Redistributions in binary form must reproduce the above copyright
44181624Skmacy *    notice, this list of conditions and the following disclaimer in the
45181624Skmacy *    documentation and/or other materials provided with the distribution.
46181624Skmacy * 3. All advertising materials mentioning features or use of this software
47181624Skmacy *    must display the following acknowledgement:
48181624Skmacy *	This product includes software developed by the University of
49181624Skmacy *	California, Berkeley and its contributors.
50181624Skmacy * 4. Neither the name of the University nor the names of its contributors
51181624Skmacy *    may be used to endorse or promote products derived from this software
52181624Skmacy *    without specific prior written permission.
53181624Skmacy *
54181624Skmacy * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
55181624Skmacy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
56181624Skmacy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
57181624Skmacy * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
58181624Skmacy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
59181624Skmacy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
60181624Skmacy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
61181624Skmacy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
62181624Skmacy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
63181624Skmacy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
64181624Skmacy * SUCH DAMAGE.
65181624Skmacy */
66181624Skmacy
67181624Skmacy#include <sys/cdefs.h>
68181624Skmacy__FBSDID("$FreeBSD: head/sbin/savecore/savecore.c 142533 2005-02-26 01:19:21Z obrien $");
69181624Skmacy
70181624Skmacy#include <sys/param.h>
71181624Skmacy#include <sys/disk.h>
72181624Skmacy#include <sys/kerneldump.h>
73181624Skmacy#include <sys/param.h>
74181624Skmacy#include <sys/mount.h>
75181624Skmacy#include <sys/stat.h>
76181624Skmacy#include <errno.h>
77181624Skmacy#include <fcntl.h>
78181624Skmacy#include <fstab.h>
79181624Skmacy#include <paths.h>
80181624Skmacy#include <stdarg.h>
81181624Skmacy#include <stdio.h>
82181624Skmacy#include <stdlib.h>
83181624Skmacy#include <string.h>
84181624Skmacy#include <syslog.h>
85181624Skmacy#include <time.h>
86181624Skmacy#include <unistd.h>
87181624Skmacy
88181624Skmacy/* The size of the buffer used for I/O. */
89181624Skmacy#define	BUFFERSIZE	(1024*1024)
90181624Skmacy
91181624Skmacy#define	STATUS_BAD	0
92181624Skmacy#define	STATUS_GOOD	1
93181624Skmacy#define	STATUS_UNKNOWN	2
94181624Skmacy
95181624Skmacystatic int checkfor, compress, clear, force, keep, verbose;	/* flags */
96181624Skmacystatic int nfound, nsaved, nerr;			/* statistics */
97181624Skmacy
98181624Skmacyextern FILE *zopen(const char *, const char *);
99181624Skmacy
100181624Skmacystatic void
101181624Skmacyprintheader(FILE *f, const struct kerneldumpheader *h, const char *device,
102181624Skmacy    int bounds, const int status)
103181624Skmacy{
104181624Skmacy	uint64_t dumplen;
105181624Skmacy	time_t t;
106181624Skmacy	const char *stat_str;
107181624Skmacy
108181624Skmacy	fprintf(f, "Dump header from device %s\n", device);
109181624Skmacy	fprintf(f, "  Architecture: %s\n", h->architecture);
110181624Skmacy	fprintf(f, "  Architecture Version: %u\n", h->architectureversion);
111181624Skmacy	dumplen = dtoh64(h->dumplength);
112181624Skmacy	fprintf(f, "  Dump Length: %lldB (%lld MB)\n", (long long)dumplen,
113181624Skmacy	    (long long)(dumplen >> 20));
114181624Skmacy	fprintf(f, "  Blocksize: %d\n", dtoh32(h->blocksize));
115181624Skmacy	t = dtoh64(h->dumptime);
116181624Skmacy	fprintf(f, "  Dumptime: %s", ctime(&t));
117181624Skmacy	fprintf(f, "  Hostname: %s\n", h->hostname);
118181624Skmacy	fprintf(f, "  Magic: %s\n", h->magic);
119181624Skmacy	fprintf(f, "  Version String: %s", h->versionstring);
120181624Skmacy	fprintf(f, "  Panic String: %s\n", h->panicstring);
121181624Skmacy	fprintf(f, "  Dump Parity: %u\n", h->parity);
122181624Skmacy	fprintf(f, "  Bounds: %d\n", bounds);
123181624Skmacy
124181624Skmacy	switch(status) {
125181624Skmacy	case STATUS_BAD:
126181624Skmacy		stat_str = "bad";
127181624Skmacy		break;
128181624Skmacy	case STATUS_GOOD:
129181624Skmacy		stat_str = "good";
130181624Skmacy		break;
131181624Skmacy	default:
132181624Skmacy		stat_str = "unknown";
133181624Skmacy	}
134181624Skmacy	fprintf(f, "  Dump Status: %s\n", stat_str);
135181624Skmacy	fflush(f);
136181624Skmacy}
137181624Skmacy
138181624Skmacystatic int
139181624Skmacygetbounds(void) {
140181624Skmacy	FILE *fp;
141181624Skmacy	char buf[6];
142181624Skmacy	int ret;
143181624Skmacy
144181624Skmacy	ret = 0;
145181624Skmacy
146181624Skmacy	if ((fp = fopen("bounds", "r")) == NULL) {
147181624Skmacy		syslog(LOG_WARNING, "unable to open bounds file, using 0");
148181624Skmacy		goto newfile;
149181624Skmacy	}
150181624Skmacy
151181624Skmacy	if (fgets(buf, sizeof buf, fp) == NULL) {
152181624Skmacy		syslog(LOG_WARNING, "unable to read from bounds, using 0");
153181624Skmacy		fclose(fp);
154181624Skmacy		goto newfile;
155181624Skmacy	}
156181624Skmacy
157181624Skmacy	errno = 0;
158181624Skmacy	ret = (int)strtol(buf, NULL, 10);
159181624Skmacy	if (ret == 0 && (errno == EINVAL || errno == ERANGE))
160181624Skmacy		syslog(LOG_WARNING, "invalid value found in bounds, using 0");
161181624Skmacy
162181624Skmacynewfile:
163181624Skmacy
164181624Skmacy	if ((fp = fopen("bounds", "w")) == NULL) {
165181624Skmacy		syslog(LOG_WARNING, "unable to write to bounds file: %m");
166181624Skmacy		goto done;
167181624Skmacy	}
168181624Skmacy
169181624Skmacy	if (verbose)
170181624Skmacy		printf("bounds number: %d\n", ret);
171181624Skmacy
172181624Skmacy	fprintf(fp, "%d\n", (ret + 1));
173181624Skmacy	fclose(fp);
174181624Skmacy
175181624Skmacydone:
176181624Skmacy	return (ret);
177181624Skmacy}
178181624Skmacy
179181624Skmacy/*
180181624Skmacy * Check that sufficient space is available on the disk that holds the
181181624Skmacy * save directory.
182181624Skmacy */
183181624Skmacystatic int
184181624Skmacycheck_space(char *savedir, off_t dumpsize)
185181624Skmacy{
186181624Skmacy	FILE *fp;
187181624Skmacy	off_t minfree, spacefree, totfree, needed;
188181624Skmacy	struct statfs fsbuf;
189181624Skmacy	char buf[100], path[MAXPATHLEN];
190181624Skmacy
191181624Skmacy	if (statfs(savedir, &fsbuf) < 0) {
192181624Skmacy		syslog(LOG_ERR, "%s: %m", savedir);
193181624Skmacy		exit(1);
194181624Skmacy	}
195181624Skmacy 	spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024;
196181624Skmacy	totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024;
197181624Skmacy
198181624Skmacy	(void)snprintf(path, sizeof(path), "%s/minfree", savedir);
199181624Skmacy	if ((fp = fopen(path, "r")) == NULL)
200181624Skmacy		minfree = 0;
201181624Skmacy	else {
202181624Skmacy		if (fgets(buf, sizeof(buf), fp) == NULL)
203181624Skmacy			minfree = 0;
204181624Skmacy		else
205181624Skmacy			minfree = atoi(buf);
206181624Skmacy		(void)fclose(fp);
207181624Skmacy	}
208181624Skmacy
209181624Skmacy	needed = dumpsize / 1024 + 2;	/* 2 for info file */
210181624Skmacy 	if (((minfree > 0) ? spacefree : totfree) - needed < minfree) {
211181624Skmacy		syslog(LOG_WARNING,
212181624Skmacy	"no dump, not enough free space on device (%lld available, need %lld)",
213181624Skmacy		    (long long)(minfree > 0 ? spacefree : totfree),
214181624Skmacy		    (long long)needed);
215181624Skmacy		return (0);
216181624Skmacy	}
217181624Skmacy	if (spacefree - needed < 0)
218181624Skmacy		syslog(LOG_WARNING,
219181624Skmacy		    "dump performed, but free space threshold crossed");
220181624Skmacy	return (1);
221181624Skmacy}
222181624Skmacy
223181624Skmacy#define BLOCKSIZE (1<<12)
224181624Skmacy#define BLOCKMASK (~(BLOCKSIZE-1))
225181624Skmacy
226181624Skmacystatic void
227181624SkmacyDoFile(char *savedir, const char *device)
228181624Skmacy{
229181624Skmacy	static char *buf = NULL;
230181624Skmacy	struct kerneldumpheader kdhf, kdhl;
231181624Skmacy	off_t mediasize, dumpsize, firsthd, lasthd, dmpcnt;
232181624Skmacy	FILE *info, *fp;
233181624Skmacy	mode_t oumask;
234181624Skmacy	int fd, fdinfo, error, wl;
235181624Skmacy	int nr, nw, hs, he = 0;
236181624Skmacy	int bounds, status;
237181624Skmacy	u_int sectorsize;
238181624Skmacy
239181624Skmacy	bounds = getbounds();
240181624Skmacy	dmpcnt = 0;
241181624Skmacy	mediasize = 0;
242181624Skmacy	status = STATUS_UNKNOWN;
243181624Skmacy
244181624Skmacy	if (buf == NULL) {
245181624Skmacy		buf = malloc(BUFFERSIZE);
246181624Skmacy		if (buf == NULL) {
247181624Skmacy			syslog(LOG_ERR, "%m");
248181624Skmacy			return;
249181624Skmacy		}
250181624Skmacy	}
251181624Skmacy
252181624Skmacy	if (verbose)
253181624Skmacy		printf("checking for kernel dump on device %s\n", device);
254181624Skmacy
255181624Skmacy	fd = open(device, O_RDWR);
256181624Skmacy	if (fd < 0) {
257181624Skmacy		syslog(LOG_ERR, "%s: %m", device);
258181624Skmacy		return;
259181624Skmacy	}
260181624Skmacy
261181624Skmacy	error = ioctl(fd, DIOCGMEDIASIZE, &mediasize);
262181624Skmacy	if (!error)
263181624Skmacy		error = ioctl(fd, DIOCGSECTORSIZE, &sectorsize);
264181624Skmacy	if (error) {
265181624Skmacy		syslog(LOG_ERR,
266181624Skmacy		    "couldn't find media and/or sector size of %s: %m", device);
267181624Skmacy		goto closefd;
268181624Skmacy	}
269181624Skmacy
270181624Skmacy	if (verbose) {
271181624Skmacy		printf("mediasize = %lld\n", (long long)mediasize);
272181624Skmacy		printf("sectorsize = %u\n", sectorsize);
273181624Skmacy	}
274181624Skmacy
275181624Skmacy	lasthd = mediasize - sectorsize;
276181624Skmacy	lseek(fd, lasthd, SEEK_SET);
277181624Skmacy	error = read(fd, &kdhl, sizeof kdhl);
278181624Skmacy	if (error != sizeof kdhl) {
279181624Skmacy		syslog(LOG_ERR,
280181624Skmacy		    "error reading last dump header at offset %lld in %s: %m",
281181624Skmacy		    (long long)lasthd, device);
282		goto closefd;
283	}
284	if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic)) {
285		if (verbose)
286			printf("magic mismatch on last dump header on %s\n",
287			    device);
288
289		status = STATUS_BAD;
290		if (force == 0)
291			goto closefd;
292
293		if (memcmp(kdhl.magic, KERNELDUMPMAGIC_CLEARED,
294			    sizeof kdhl.magic) == 0) {
295			if (verbose)
296				printf("forcing magic on %s\n", device);
297			memcpy(kdhl.magic, KERNELDUMPMAGIC,
298			    sizeof kdhl.magic);
299		} else {
300			syslog(LOG_ERR, "unable to force dump - bad magic");
301			goto closefd;
302		}
303	}
304	if (dtoh32(kdhl.version) != KERNELDUMPVERSION) {
305		syslog(LOG_ERR,
306		    "unknown version (%d) in last dump header on %s",
307		    dtoh32(kdhl.version), device);
308
309		status = STATUS_BAD;
310		if (force == 0)
311			goto closefd;
312	}
313
314	nfound++;
315	if (clear)
316		goto nuke;
317
318	if (kerneldump_parity(&kdhl)) {
319		syslog(LOG_ERR,
320		    "parity error on last dump header on %s", device);
321		nerr++;
322		status = STATUS_BAD;
323		if (force == 0)
324			goto closefd;
325	}
326	dumpsize = dtoh64(kdhl.dumplength);
327	firsthd = lasthd - dumpsize - sizeof kdhf;
328	lseek(fd, firsthd, SEEK_SET);
329	error = read(fd, &kdhf, sizeof kdhf);
330	if (error != sizeof kdhf) {
331		syslog(LOG_ERR,
332		    "error reading first dump header at offset %lld in %s: %m",
333		    (long long)firsthd, device);
334		nerr++;
335		goto closefd;
336	}
337
338	if (verbose >= 2) {
339		printf("First dump headers:\n");
340		printheader(stdout, &kdhf, device, bounds, -1);
341
342		printf("\nLast dump headers:\n");
343		printheader(stdout, &kdhl, device, bounds, -1);
344		printf("\n");
345	}
346
347	if (memcmp(&kdhl, &kdhf, sizeof kdhl)) {
348		syslog(LOG_ERR,
349		    "first and last dump headers disagree on %s", device);
350		nerr++;
351		status = STATUS_BAD;
352		if (force == 0)
353			goto closefd;
354	} else {
355		status = STATUS_GOOD;
356	}
357
358	if (checkfor) {
359		printf("A dump exists on %s\n", device);
360		close(fd);
361		exit(0);
362	}
363
364	if (kdhl.panicstring[0])
365		syslog(LOG_ALERT, "reboot after panic: %s", kdhl.panicstring);
366	else
367		syslog(LOG_ALERT, "reboot");
368
369	if (verbose)
370		printf("Checking for available free space\n");
371	if (!check_space(savedir, dumpsize)) {
372		nerr++;
373		goto closefd;
374	}
375
376	sprintf(buf, "info.%d", bounds);
377
378	/*
379	 * Create or overwrite any existing dump header files.
380	 */
381	fdinfo = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600);
382	if (fdinfo < 0) {
383		syslog(LOG_ERR, "%s: %m", buf);
384		nerr++;
385		goto closefd;
386	}
387	oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to the core file.*/
388	if (compress) {
389		sprintf(buf, "vmcore.%d.gz", bounds);
390		fp = zopen(buf, "w");
391	} else {
392		sprintf(buf, "vmcore.%d", bounds);
393		fp = fopen(buf, "w");
394	}
395	if (fp == NULL) {
396		syslog(LOG_ERR, "%s: %m", buf);
397		close(fdinfo);
398		nerr++;
399		goto closefd;
400	}
401	(void)umask(oumask);
402
403	info = fdopen(fdinfo, "w");
404
405	if (verbose)
406		printheader(stdout, &kdhl, device, bounds, status);
407
408	printheader(info, &kdhl, device, bounds, status);
409	fclose(info);
410
411	syslog(LOG_NOTICE, "writing %score to %s",
412	    compress ? "compressed " : "", buf);
413
414	while (dumpsize > 0) {
415		wl = BUFFERSIZE;
416		if (wl > dumpsize)
417			wl = dumpsize;
418		nr = read(fd, buf, wl);
419		if (nr != wl) {
420			if (nr == 0)
421				syslog(LOG_WARNING,
422				    "WARNING: EOF on dump device");
423			else
424				syslog(LOG_ERR, "read error on %s: %m", device);
425			nerr++;
426			goto closeall;
427		}
428		if (compress) {
429			nw = fwrite(buf, 1, wl, fp);
430		} else {
431			for (nw = 0; nw < nr; nw = he) {
432				/* find a contiguous block of zeroes */
433				for (hs = nw; hs < nr; hs += BLOCKSIZE) {
434					for (he = hs; he < nr && buf[he] == 0;
435					    ++he)
436						/* nothing */ ;
437					/* is the hole long enough to matter? */
438					if (he >= hs + BLOCKSIZE)
439						break;
440				}
441
442				/* back down to a block boundary */
443				he &= BLOCKMASK;
444
445				/*
446				 * 1) Don't go beyond the end of the buffer.
447				 * 2) If the end of the buffer is less than
448				 *    BLOCKSIZE bytes away, we're at the end
449				 *    of the file, so just grab what's left.
450				 */
451				if (hs + BLOCKSIZE > nr)
452					hs = he = nr;
453
454				/*
455				 * At this point, we have a partial ordering:
456				 *     nw <= hs <= he <= nr
457				 * If hs > nw, buf[nw..hs] contains non-zero data.
458				 * If he > hs, buf[hs..he] is all zeroes.
459				 */
460				if (hs > nw)
461					if (fwrite(buf + nw, hs - nw, 1, fp)
462					    != 1)
463					break;
464				if (he > hs)
465					if (fseeko(fp, he - hs, SEEK_CUR) == -1)
466						break;
467			}
468		}
469		if (nw != wl) {
470			syslog(LOG_ERR,
471			    "write error on vmcore.%d file: %m", bounds);
472			syslog(LOG_WARNING,
473			    "WARNING: vmcore may be incomplete");
474			nerr++;
475			goto closeall;
476		}
477		if (verbose) {
478			dmpcnt += wl;
479			printf("%llu\r", (unsigned long long)dmpcnt);
480			fflush(stdout);
481		}
482		dumpsize -= wl;
483	}
484	if (verbose)
485		printf("\n");
486
487	if (fclose(fp) < 0) {
488		syslog(LOG_ERR, "error on vmcore.%d: %m", bounds);
489		nerr++;
490		goto closeall;
491	}
492	nsaved++;
493
494	if (verbose)
495		printf("dump saved\n");
496
497nuke:
498	if (clear || !keep) {
499		if (verbose)
500			printf("clearing dump header\n");
501		memcpy(kdhl.magic, KERNELDUMPMAGIC_CLEARED, sizeof kdhl.magic);
502		lseek(fd, lasthd, SEEK_SET);
503		error = write(fd, &kdhl, sizeof kdhl);
504		if (error != sizeof kdhl)
505			syslog(LOG_ERR,
506			    "error while clearing the dump header: %m");
507	}
508	close(fd);
509	return;
510
511closeall:
512	fclose(fp);
513
514closefd:
515	close(fd);
516}
517
518static void
519usage(void)
520{
521	fprintf(stderr, "%s\n%s\n%s\n",
522	    "usage: savecore -c",
523	    "       savecore -C [-v] [directory device]",
524	    "       savecore [-fkvz] [directory [device ...]]");
525	exit (1);
526}
527
528int
529main(int argc, char **argv)
530{
531	char *savedir;
532	struct fstab *fsp;
533	int i, ch, error;
534
535	checkfor = compress = clear = force = keep = verbose = 0;
536	nfound = nsaved = nerr = 0;
537
538	openlog("savecore", LOG_PERROR, LOG_DAEMON);
539
540	savedir = strdup(".");
541	if (savedir == NULL) {
542		syslog(LOG_ERR, "Cannot allocate memory");
543		exit(1);
544	}
545	while ((ch = getopt(argc, argv, "Ccfkvz")) != -1)
546		switch(ch) {
547		case 'C':
548			checkfor = 1;
549			break;
550		case 'c':
551			clear = 1;
552			break;
553		case 'k':
554			keep = 1;
555			break;
556		case 'v':
557			verbose++;
558			break;
559		case 'f':
560			force = 1;
561			break;
562		case 'z':
563			compress = 1;
564			break;
565		case '?':
566		default:
567			usage();
568		}
569	if (checkfor && (clear || force || keep))
570		usage();
571	argc -= optind;
572	argv += optind;
573	if (argc >= 1) {
574		error = chdir(argv[0]);
575		if (error) {
576			syslog(LOG_ERR, "chdir(%s): %m", argv[0]);
577			exit(1);
578		}
579		savedir = argv[0];
580		argc--;
581		argv++;
582	}
583	if (argc == 0) {
584		for (;;) {
585			fsp = getfsent();
586			if (fsp == NULL)
587				break;
588			if (strcmp(fsp->fs_vfstype, "swap") &&
589			    strcmp(fsp->fs_vfstype, "dump"))
590				continue;
591			DoFile(savedir, fsp->fs_spec);
592		}
593	} else {
594		for (i = 0; i < argc; i++)
595			DoFile(savedir, argv[i]);
596	}
597
598	/* Emit minimal output. */
599	if (nfound == 0) {
600		if (checkfor) {
601			printf("No dump exists\n");
602			exit(1);
603		}
604		syslog(LOG_WARNING, "no dumps found");
605	}
606	else if (nsaved == 0) {
607		if (nerr != 0)
608			syslog(LOG_WARNING, "unsaved dumps found but not saved");
609		else
610			syslog(LOG_WARNING, "no unsaved dumps found");
611	}
612
613	return (0);
614}
615