GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/savecore/savecore.c Lines: 0 281 0.0 %
Date: 2016-12-06 Branches: 0 206 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: savecore.c,v 1.56 2016/07/04 04:05:29 guenther Exp $	*/
2
/*	$NetBSD: savecore.c,v 1.26 1996/03/18 21:16:05 leo Exp $	*/
3
4
/*-
5
 * Copyright (c) 1986, 1992, 1993
6
 *	The Regents of the University of California.  All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 * 3. Neither the name of the University nor the names of its contributors
17
 *    may be used to endorse or promote products derived from this software
18
 *    without specific prior written permission.
19
 *
20
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30
 * SUCH DAMAGE.
31
 */
32
33
#include <sys/param.h>	/* NODEV DEV_BSIZE */
34
#include <sys/stat.h>
35
#include <sys/mount.h>
36
#include <sys/syslog.h>
37
#include <sys/time.h>
38
#include <sys/resource.h>
39
40
#include <dirent.h>
41
#include <errno.h>
42
#include <fcntl.h>
43
#include <nlist.h>
44
#include <paths.h>
45
#include <stdio.h>
46
#include <stdlib.h>
47
#include <string.h>
48
#include <unistd.h>
49
#include <limits.h>
50
#include <zlib.h>
51
#include <kvm.h>
52
#include <vis.h>
53
54
#define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
55
56
extern FILE *zopen(const char *fname, const char *mode, int bits);
57
58
#define KREAD(kd, addr, p)\
59
	(kvm_read(kd, addr, (char *)(p), sizeof(*(p))) != sizeof(*(p)))
60
61
struct nlist current_nl[] = {	/* Namelist for currently running system. */
62
#define X_DUMPDEV	0
63
	{ "_dumpdev" },
64
#define X_DUMPLO	1
65
	{ "_dumplo" },
66
#define X_TIME		2
67
	{ "_time_second" },
68
#define	X_DUMPSIZE	3
69
	{ "_dumpsize" },
70
#define X_VERSION	4
71
	{ "_version" },
72
#define X_PANICSTR	5
73
	{ "_panicstr" },
74
#define	X_DUMPMAG	6
75
	{ "_dumpmag" },
76
	{ NULL },
77
};
78
int cursyms[] = { X_DUMPDEV, X_DUMPLO, X_VERSION, X_DUMPMAG, -1 };
79
int dumpsyms[] = { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 };
80
81
struct nlist dump_nl[] = {	/* Name list for dumped system. */
82
	{ "_dumpdev" },		/* Entries MUST be the same as */
83
	{ "_dumplo" },		/*	those in current_nl[].  */
84
	{ "_time_second" },
85
	{ "_dumpsize" },
86
	{ "_version" },
87
	{ "_panicstr" },
88
	{ "_dumpmag" },
89
	{ NULL },
90
};
91
92
/* Types match kernel declarations. */
93
long	dumplo;			/* where dump starts on dumpdev (in blocks) */
94
off_t	dumpoff;		/* where dump starts on dumpdev (in bytes) */
95
u_long	dumpmag;		/* magic number in dump */
96
int	dumppages;		/* amount of memory dumped (in pages) */
97
u_long	dumpsize;		/* amount of memory dumped */
98
99
char	*kernel;
100
char	*dirn;			/* directory to save dumps in */
101
char	*ddname;		/* name of dump device */
102
dev_t	dumpdev;		/* dump device */
103
int	dumpfd;			/* read/write descriptor on block dev */
104
kvm_t	*kd_dump;		/* kvm descriptor on block dev	*/
105
time_t	now;			/* current date */
106
char	panic_mesg[1024];
107
int	panicstr;
108
char	vers[1024];
109
110
int	clear, zcompress, force, verbose;	/* flags */
111
112
void	 check_kmem(void);
113
int	 check_space(void);
114
void	 clear_dump(void);
115
int	 dump_exists(void);
116
char	*find_dev(dev_t, int);
117
int	 get_crashtime(void);
118
void	 kmem_setup(void);
119
char	*rawname(char *s);
120
void	 save_core(void);
121
void	 usage(void);
122
123
int
124
main(int argc, char *argv[])
125
{
126
	struct rlimit rl;
127
	int ch;
128
129
	openlog("savecore", LOG_PERROR, LOG_DAEMON);
130
131
	/* Increase our data size to the max if we can. */
132
	if (getrlimit(RLIMIT_DATA, &rl) == 0) {
133
		rl.rlim_cur = rl.rlim_max;
134
		if (setrlimit(RLIMIT_DATA, &rl) < 0)
135
			syslog(LOG_WARNING, "can't set rlimit data size: %m");
136
	}
137
138
	while ((ch = getopt(argc, argv, "cdfN:vz")) != -1)
139
		switch(ch) {
140
		case 'c':
141
			clear = 1;
142
			break;
143
		case 'd':		/* Not documented. */
144
		case 'v':
145
			verbose = 1;
146
			break;
147
		case 'f':
148
			force = 1;
149
			break;
150
		case 'N':
151
			kernel = optarg;
152
			break;
153
		case 'z':
154
			zcompress = 1;
155
			break;
156
		case '?':
157
		default:
158
			usage();
159
		}
160
	argc -= optind;
161
	argv += optind;
162
163
	if (!clear) {
164
		if (argc != 1)
165
			usage();
166
		dirn = argv[0];
167
	}
168
169
	(void)time(&now);
170
	kmem_setup();
171
172
	if (pledge("stdio rpath wpath cpath", NULL) == -1) {
173
		syslog(LOG_ERR, "pledge: %m");
174
		exit(1);
175
	}
176
177
	if (clear) {
178
		clear_dump();
179
		return (0);
180
	}
181
182
	if (!dump_exists() && !force)
183
		return (1);
184
185
	check_kmem();
186
187
	if (panicstr)
188
		syslog(LOG_ALERT, "reboot after panic: %s", panic_mesg);
189
	else
190
		syslog(LOG_ALERT, "reboot");
191
192
	if ((!get_crashtime() || !check_space()) && !force)
193
		return (1);
194
195
	save_core();
196
197
	clear_dump();
198
	return (0);
199
}
200
201
char	*dump_sys;
202
203
void
204
kmem_setup(void)
205
{
206
	kvm_t	*kd_kern;
207
	char	errbuf[_POSIX2_LINE_MAX];
208
	int	i, hdrsz;
209
210
	/*
211
	 * Some names we need for the currently running system, others for
212
	 * the system that was running when the dump was made.  The values
213
	 * obtained from the current system are used to look for things in
214
	 * /dev/kmem that cannot be found in the dump_sys namelist, but are
215
	 * presumed to be the same (since the disk partitions are probably
216
	 * the same!)
217
	 */
218
	kd_kern = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
219
	if (kd_kern == NULL) {
220
		syslog(LOG_ERR, "%s: kvm_openfiles: %s", _PATH_UNIX, errbuf);
221
		exit(1);
222
	}
223
	if (kvm_nlist(kd_kern, current_nl) == -1)
224
		syslog(LOG_ERR, "%s: kvm_nlist: %s", _PATH_UNIX,
225
			kvm_geterr(kd_kern));
226
227
	for (i = 0; cursyms[i] != -1; i++)
228
		if (current_nl[cursyms[i]].n_value == 0) {
229
			syslog(LOG_ERR, "%s: %s not in namelist",
230
			    _PATH_UNIX, current_nl[cursyms[i]].n_name);
231
			exit(1);
232
		}
233
234
	(void)KREAD(kd_kern, current_nl[X_DUMPDEV].n_value, &dumpdev);
235
	if (dumpdev == NODEV) {
236
		syslog(LOG_WARNING, "no core dump (no dumpdev)");
237
		exit(1);
238
	}
239
	(void)KREAD(kd_kern, current_nl[X_DUMPLO].n_value, &dumplo);
240
	dumpoff = (off_t)dumplo * DEV_BSIZE;
241
	if (verbose)
242
		(void)printf("dumpoff = %lld (%ld * %d)\n",
243
		    (long long)dumpoff, dumplo, DEV_BSIZE);
244
	(void) KREAD(kd_kern, current_nl[X_DUMPMAG].n_value, &dumpmag);
245
246
	if (kernel == NULL) {
247
		if (kvm_read(kd_kern, current_nl[X_VERSION].n_value,
248
		    vers, sizeof(vers)) == -1) {
249
			syslog(LOG_ERR, "%s: kvm_read: version misread", _PATH_UNIX);
250
			exit(1);
251
		}
252
		vers[sizeof(vers) - 1] = '\0';
253
	}
254
255
	ddname = find_dev(dumpdev, S_IFBLK);
256
	dumpfd = open(ddname, O_RDWR);
257
	if (dumpfd == -1) {
258
		syslog(LOG_ERR, "%s: %m", ddname);
259
		exit(1);
260
	}
261
262
263
	dump_sys = kernel ? kernel : _PATH_UNIX;
264
	kd_dump = kvm_openfiles(kernel, ddname, NULL, O_RDWR, errbuf);
265
	if (kd_dump == NULL) {
266
		syslog(LOG_ERR, "%s: kvm_openfiles: %s", dump_sys, errbuf);
267
		exit(1);
268
	}
269
270
	if (kvm_nlist(kd_dump, dump_nl) == -1)
271
		syslog(LOG_ERR, "%s: kvm_nlist: %s", dump_sys,
272
			kvm_geterr(kd_dump));
273
274
	for (i = 0; dumpsyms[i] != -1; i++)
275
		if (dump_nl[dumpsyms[i]].n_value == 0) {
276
			syslog(LOG_ERR, "%s: %s not in namelist",
277
			    dump_sys, dump_nl[dumpsyms[i]].n_name);
278
			exit(1);
279
		}
280
	hdrsz = kvm_dump_mkheader(kd_dump, dumpoff);
281
	if (hdrsz == -1) {
282
		if(verbose)
283
			syslog(LOG_ERR, "%s: kvm_dump_mkheader: %s", dump_sys,
284
				kvm_geterr(kd_dump));
285
		syslog(LOG_WARNING, "no core dump");
286
		exit(1);
287
	}
288
	dumpoff += hdrsz;
289
	kvm_close(kd_kern);
290
}
291
292
void
293
check_kmem(void)
294
{
295
	char	*cp;
296
	int	panicloc;
297
	char core_vers[1024];
298
299
	if (kvm_read(kd_dump, dump_nl[X_VERSION].n_value, core_vers,
300
	    sizeof(core_vers)) != sizeof(core_vers)) {
301
		syslog(LOG_ERR, "%s: kvm_read: version misread", dump_sys);
302
		exit(1);
303
	}
304
	core_vers[sizeof(core_vers) - 1] = '\0';
305
306
	if (strcmp(vers, core_vers) && kernel == 0) {
307
		vers[strcspn(vers, "\n")] = '\0';
308
		core_vers[strcspn(core_vers, "\n")] = '\0';
309
310
		syslog(LOG_WARNING,
311
		    "warning: %s version mismatch:\n\t%s\nand\t%s\n",
312
		    _PATH_UNIX, vers, core_vers);
313
	}
314
315
	(void)KREAD(kd_dump, dump_nl[X_PANICSTR].n_value, &panicstr);
316
	if (panicstr) {
317
		char	c, visout[5];
318
		size_t	vislen;
319
320
		cp       = panic_mesg;
321
		panicloc = panicstr;
322
		for (;;) {
323
			if (KREAD(kd_dump, panicloc, &c) != 0 || c == '\0')
324
				break;
325
			panicloc++;
326
327
			vis(visout, c, VIS_SAFE|VIS_NOSLASH, 0);
328
			vislen = strlen(visout);
329
			if (cp - panic_mesg + vislen >= sizeof(panic_mesg))
330
				break;
331
			strlcat(cp, visout,
332
			    panic_mesg + sizeof panic_mesg - cp);
333
			cp += strlen(cp);
334
		}
335
	}
336
}
337
338
int
339
dump_exists(void)
340
{
341
	u_long newdumpmag;
342
343
	(void)KREAD(kd_dump, dump_nl[X_DUMPMAG].n_value, &newdumpmag);
344
345
	/* Read the dump size. */
346
	(void)KREAD(kd_dump, dump_nl[X_DUMPSIZE].n_value, &dumppages);
347
	dumpsize = (u_long)dumppages * getpagesize();
348
349
	/*
350
	 * Return zero if core dump doesn't seem to be there and note
351
	 * it for syslog.  This check and return happens after the dump size
352
	 * is read, so dumpsize is whether or not the core is valid (for -f).
353
	 */
354
	if (newdumpmag != dumpmag) {
355
		if (verbose)
356
			syslog(LOG_WARNING,
357
			    "magic number mismatch (%lx != %lx)",
358
			    newdumpmag, dumpmag);
359
		syslog(LOG_WARNING, "no core dump");
360
		return (0);
361
	}
362
	return (1);
363
}
364
365
void
366
clear_dump(void)
367
{
368
	if (kvm_dump_inval(kd_dump) == -1)
369
		syslog(LOG_ERR, "%s: kvm_clear_dump: %s", ddname,
370
			kvm_geterr(kd_dump));
371
372
}
373
374
char buf[1024 * 1024];
375
376
void
377
save_core(void)
378
{
379
	FILE *fp;
380
	int bounds, ifd, nr, nw, ofd = -1;
381
	char *rawp, path[PATH_MAX];
382
	mode_t um;
383
384
	um = umask(S_IRWXG|S_IRWXO);
385
386
	/*
387
	 * Get the current number and update the bounds file.  Do the update
388
	 * now, because we may fail later and don't want to overwrite anything.
389
	 */
390
	(void)snprintf(path, sizeof(path), "%s/bounds", dirn);
391
	if ((fp = fopen(path, "r")) == NULL)
392
		goto err1;
393
	if (fgets(buf, sizeof(buf), fp) == NULL) {
394
		if (ferror(fp))
395
err1:			syslog(LOG_WARNING, "%s: %s", path, strerror(errno));
396
		bounds = 0;
397
	} else {
398
		const char *errstr = NULL;
399
		char *p;
400
401
		if ((p = strchr(buf, '\n')) != NULL)
402
			*p = '\0';
403
		bounds = strtonum(buf, 0, INT_MAX, &errstr);
404
		if (errstr)
405
			syslog(LOG_WARNING, "bounds was corrupt: %s", errstr);
406
	}
407
	if (fp != NULL)
408
		(void)fclose(fp);
409
	if ((fp = fopen(path, "w")) == NULL)
410
		syslog(LOG_ERR, "%s: %m", path);
411
	else {
412
		(void)fprintf(fp, "%d\n", bounds + 1);
413
		(void)fclose(fp);
414
	}
415
416
	/* Create the core file. */
417
	(void)snprintf(path, sizeof(path), "%s%s.%d.core%s",
418
	    dirn, _PATH_UNIX, bounds, zcompress ? ".Z" : "");
419
	if (zcompress) {
420
		if ((fp = zopen(path, "w", 0)) == NULL) {
421
			syslog(LOG_ERR, "%s: %s", path, strerror(errno));
422
			exit(1);
423
		}
424
	} else {
425
		ofd = open(path, O_WRONLY | O_CREAT | O_TRUNC,
426
		    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
427
		if (ofd == -1) {
428
			syslog(LOG_ERR, "%s: %m", path);
429
			exit(1);
430
		}
431
432
		fp  = fdopen(ofd, "w");
433
		if (fp == NULL) {
434
			syslog(LOG_ERR, "%s: fdopen: %s", path, strerror(errno));
435
			exit(1);
436
		}
437
	}
438
439
	/* Open the raw device. */
440
	rawp = rawname(ddname);
441
	if ((ifd = open(rawp, O_RDONLY)) == -1) {
442
		syslog(LOG_WARNING, "%s: %m; using block device", rawp);
443
		ifd = dumpfd;
444
	}
445
446
	/* Seek to the start of the core. */
447
	if (lseek(ifd, dumpoff, SEEK_SET) == -1) {
448
		syslog(LOG_ERR, "lseek: %m");
449
		exit(1);
450
	}
451
452
	if (kvm_dump_wrtheader(kd_dump, fp, dumpsize) == -1) {
453
		syslog(LOG_ERR, "kvm_dump_wrtheader: %s : %s", path,
454
			kvm_geterr(kd_dump));
455
		exit(1);
456
	}
457
458
	/* Copy the core file. */
459
	syslog(LOG_NOTICE, "writing %score to %s",
460
	    zcompress ? "compressed " : "", path);
461
	for (; dumpsize != 0; dumpsize -= nr) {
462
		(void)printf("%8luK\r", dumpsize / 1024);
463
		(void)fflush(stdout);
464
		nr = read(ifd, buf, MINIMUM(dumpsize, sizeof(buf)));
465
		if (nr <= 0) {
466
			if (nr == 0)
467
				syslog(LOG_WARNING,
468
				    "WARNING: EOF on dump device");
469
			else
470
				syslog(LOG_ERR, "%s: %m", rawp);
471
			goto err2;
472
		}
473
		nw = fwrite(buf, 1, nr, fp);
474
		if (nw != nr) {
475
			syslog(LOG_ERR, "%s: %s",
476
			    path, strerror(nw == 0 ? EIO : errno));
477
err2:			syslog(LOG_WARNING,
478
			    "WARNING: core may be incomplete");
479
			(void)printf("\n");
480
			exit(1);
481
		}
482
	}
483
	(void)close(ifd);
484
	(void)fclose(fp);
485
486
	/* Copy the kernel. */
487
	ifd = open(kernel ? kernel : _PATH_UNIX, O_RDONLY);
488
	if (ifd == -1) {
489
		syslog(LOG_ERR, "%s: %m", kernel ? kernel : _PATH_UNIX);
490
		exit(1);
491
	}
492
	(void)snprintf(path, sizeof(path), "%s%s.%d%s",
493
	    dirn, _PATH_UNIX, bounds, zcompress ? ".Z" : "");
494
	if (zcompress) {
495
		if ((fp = zopen(path, "w", 0)) == NULL) {
496
			syslog(LOG_ERR, "%s: %s", path, strerror(errno));
497
			exit(1);
498
		}
499
	} else {
500
		ofd = open(path, O_WRONLY | O_CREAT | O_TRUNC,
501
		    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
502
		if (ofd == -1) {
503
			syslog(LOG_ERR, "%s: %m", path);
504
			exit(1);
505
		}
506
	}
507
	syslog(LOG_NOTICE, "writing %skernel to %s",
508
	    zcompress ? "compressed " : "", path);
509
	while ((nr = read(ifd, buf, sizeof(buf))) > 0) {
510
		if (zcompress)
511
			nw = fwrite(buf, 1, nr, fp);
512
		else
513
			nw = write(ofd, buf, nr);
514
		if (nw != nr) {
515
			syslog(LOG_ERR, "%s: %s",
516
			    path, strerror(nw == 0 ? EIO : errno));
517
			syslog(LOG_WARNING,
518
			    "WARNING: kernel may be incomplete");
519
			exit(1);
520
		}
521
	}
522
	if (nr < 0) {
523
		syslog(LOG_ERR, "%s: %s",
524
		    kernel ? kernel : _PATH_UNIX, strerror(errno));
525
		syslog(LOG_WARNING,
526
		    "WARNING: kernel may be incomplete");
527
		exit(1);
528
	}
529
	if (zcompress)
530
		(void)fclose(fp);
531
	else
532
		(void)close(ofd);
533
	(void)umask(um);
534
}
535
536
char *
537
find_dev(dev_t dev, int type)
538
{
539
	DIR *dfd;
540
	struct dirent *dir;
541
	struct stat sb;
542
	char *dp, devname[PATH_MAX];
543
544
	if ((dfd = opendir(_PATH_DEV)) == NULL) {
545
		syslog(LOG_ERR, "%s: %s", _PATH_DEV, strerror(errno));
546
		exit(1);
547
	}
548
	(void)strlcpy(devname, _PATH_DEV, sizeof devname);
549
	while ((dir = readdir(dfd))) {
550
		(void)strlcpy(devname + sizeof(_PATH_DEV) - 1, dir->d_name,
551
		    sizeof devname - (sizeof(_PATH_DEV) - 1));
552
		if (lstat(devname, &sb)) {
553
			syslog(LOG_ERR, "%s: %s", devname, strerror(errno));
554
			continue;
555
		}
556
		if ((sb.st_mode & S_IFMT) != type)
557
			continue;
558
		if (dev == sb.st_rdev) {
559
			closedir(dfd);
560
			if ((dp = strdup(devname)) == NULL) {
561
				syslog(LOG_ERR, "%s", strerror(errno));
562
				exit(1);
563
			}
564
			return (dp);
565
		}
566
	}
567
	closedir(dfd);
568
	syslog(LOG_ERR, "can't find device %d/%d", major(dev), minor(dev));
569
	exit(1);
570
}
571
572
char *
573
rawname(char *s)
574
{
575
	char *sl, name[PATH_MAX];
576
577
	if ((sl = strrchr(s, '/')) == NULL || sl[1] == '0') {
578
		syslog(LOG_ERR,
579
		    "can't make raw dump device name from %s", s);
580
		return (s);
581
	}
582
	(void)snprintf(name, sizeof(name), "%.*s/r%s", (int)(sl - s), s, sl + 1);
583
	if ((sl = strdup(name)) == NULL) {
584
		syslog(LOG_ERR, "%s", strerror(errno));
585
		exit(1);
586
	}
587
	return (sl);
588
}
589
590
int
591
get_crashtime(void)
592
{
593
	time_t dumptime;			/* Time the dump was taken. */
594
595
	(void)KREAD(kd_dump, dump_nl[X_TIME].n_value, &dumptime);
596
	if (dumptime == 0) {
597
		if (verbose)
598
			syslog(LOG_ERR, "dump time is zero");
599
		return (0);
600
	}
601
	(void)printf("savecore: system went down at %s", ctime(&dumptime));
602
#define	SECSPERDAY	(24 * 60 * 60)
603
#define	LEEWAY		(7 * SECSPERDAY)
604
	if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) {
605
		(void)printf("dump time is unreasonable\n");
606
		return (0);
607
	}
608
	return (1);
609
}
610
611
int
612
check_space(void)
613
{
614
	FILE *fp;
615
	char *tkernel;
616
	off_t minfree, spacefree, kernelsize, needed;
617
	struct stat st;
618
	struct statfs fsbuf;
619
	char buf[100], path[PATH_MAX];
620
	int fd;
621
622
	tkernel = kernel ? kernel : _PATH_UNIX;
623
	if (stat(tkernel, &st) < 0) {
624
		syslog(LOG_ERR, "%s: %m", tkernel);
625
		exit(1);
626
	}
627
	kernelsize = st.st_blocks * S_BLKSIZE;
628
	if ((fd = open(dirn, O_RDONLY, 0)) < 0 || fstatfs(fd, &fsbuf) < 0) {
629
		syslog(LOG_ERR, "%s: %m", dirn);
630
		exit(1);
631
	}
632
	close(fd);
633
	spacefree = ((off_t)fsbuf.f_bavail * fsbuf.f_bsize) / 1024;
634
635
	(void)snprintf(path, sizeof(path), "%s/minfree", dirn);
636
	if ((fp = fopen(path, "r")) == NULL)
637
		minfree = 0;
638
	else {
639
		if (fgets(buf, sizeof(buf), fp) == NULL)
640
			minfree = 0;
641
		else {
642
			const char *errstr;
643
			char *p;
644
645
			if ((p = strchr(buf, '\n')) != NULL)
646
				*p = '\0';
647
			minfree = strtonum(buf, 0, LLONG_MAX, &errstr);
648
			if (errstr)
649
				syslog(LOG_WARNING,
650
				    "minfree was corrupt: %s", errstr);
651
		}
652
		(void)fclose(fp);
653
	}
654
655
	needed = (dumpsize + kernelsize) / 1024;
656
	if (minfree > 0 && spacefree - needed < minfree) {
657
		syslog(LOG_WARNING,
658
		    "no dump, not enough free space on device");
659
		return (0);
660
	}
661
	if (spacefree - needed < minfree)
662
		syslog(LOG_WARNING,
663
		    "dump performed, but free space threshold crossed");
664
	return (1);
665
}
666
667
void
668
usage(void)
669
{
670
	extern char *__progname;
671
	fprintf(stderr, "usage: %s [-cfvz] [-N system] directory\n",
672
		__progname);
673
	exit(1);
674
}