GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/savecore/savecore.c Lines: 0 310 0.0 %
Date: 2017-11-07 Branches: 0 187 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: savecore.c,v 1.57 2016/09/01 14:12:07 tedu 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
#define VERSIONSIZE 512
93
94
/* Types match kernel declarations. */
95
long	dumplo;			/* where dump starts on dumpdev (in blocks) */
96
off_t	dumpoff;		/* where dump starts on dumpdev (in bytes) */
97
u_long	dumpmag;		/* magic number in dump */
98
int	dumppages;		/* amount of memory dumped (in pages) */
99
u_long	dumpsize;		/* amount of memory dumped */
100
101
char	*kernel;
102
char	*dirn;			/* directory to save dumps in */
103
char	*ddname;		/* name of dump device */
104
dev_t	dumpdev;		/* dump device */
105
int	dumpfd;			/* read/write descriptor on block dev */
106
kvm_t	*kd_dump;		/* kvm descriptor on block dev	*/
107
time_t	now;			/* current date */
108
char	panic_mesg[1024];
109
int	panicstr;
110
char	vers[VERSIONSIZE];
111
112
int	clear, zcompress, force, verbose;	/* flags */
113
114
void	 check_kmem(void);
115
int	 check_space(void);
116
void	 clear_dump(void);
117
int	 dump_exists(void);
118
char	*find_dev(dev_t, int);
119
int	 get_crashtime(void);
120
void	 kmem_setup(void);
121
char	*rawname(char *s);
122
void	 save_core(void);
123
void	 usage(void);
124
125
int
126
main(int argc, char *argv[])
127
{
128
	struct rlimit rl;
129
	int ch;
130
131
	openlog("savecore", LOG_PERROR, LOG_DAEMON);
132
133
	/* Increase our data size to the max if we can. */
134
	if (getrlimit(RLIMIT_DATA, &rl) == 0) {
135
		rl.rlim_cur = rl.rlim_max;
136
		if (setrlimit(RLIMIT_DATA, &rl) < 0)
137
			syslog(LOG_WARNING, "can't set rlimit data size: %m");
138
	}
139
140
	while ((ch = getopt(argc, argv, "cdfN:vz")) != -1)
141
		switch(ch) {
142
		case 'c':
143
			clear = 1;
144
			break;
145
		case 'd':		/* Not documented. */
146
		case 'v':
147
			verbose = 1;
148
			break;
149
		case 'f':
150
			force = 1;
151
			break;
152
		case 'N':
153
			kernel = optarg;
154
			break;
155
		case 'z':
156
			zcompress = 1;
157
			break;
158
		case '?':
159
		default:
160
			usage();
161
		}
162
	argc -= optind;
163
	argv += optind;
164
165
	if (!clear) {
166
		if (argc != 1)
167
			usage();
168
		dirn = argv[0];
169
	}
170
171
	(void)time(&now);
172
	kmem_setup();
173
174
	if (pledge("stdio rpath wpath cpath flock", NULL) == -1) {
175
		syslog(LOG_ERR, "pledge: %m");
176
		exit(1);
177
	}
178
179
	if (clear) {
180
		clear_dump();
181
		return (0);
182
	}
183
184
	if (!dump_exists() && !force)
185
		return (1);
186
187
	check_kmem();
188
189
	if (panicstr)
190
		syslog(LOG_ALERT, "reboot after panic: %s", panic_mesg);
191
	else
192
		syslog(LOG_ALERT, "reboot");
193
194
	if ((!get_crashtime() || !check_space()) && !force)
195
		return (1);
196
197
	save_core();
198
199
	clear_dump();
200
	return (0);
201
}
202
203
char	*dump_sys;
204
205
void
206
kmem_setup(void)
207
{
208
	kvm_t	*kd_kern;
209
	char	errbuf[_POSIX2_LINE_MAX];
210
	int	i, hdrsz;
211
212
	/*
213
	 * Some names we need for the currently running system, others for
214
	 * the system that was running when the dump was made.  The values
215
	 * obtained from the current system are used to look for things in
216
	 * /dev/kmem that cannot be found in the dump_sys namelist, but are
217
	 * presumed to be the same (since the disk partitions are probably
218
	 * the same!)
219
	 */
220
	kd_kern = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
221
	if (kd_kern == NULL) {
222
		syslog(LOG_ERR, "%s: kvm_openfiles: %s", _PATH_UNIX, errbuf);
223
		exit(1);
224
	}
225
	if (kvm_nlist(kd_kern, current_nl) == -1)
226
		syslog(LOG_ERR, "%s: kvm_nlist: %s", _PATH_UNIX,
227
			kvm_geterr(kd_kern));
228
229
	for (i = 0; cursyms[i] != -1; i++)
230
		if (current_nl[cursyms[i]].n_value == 0) {
231
			syslog(LOG_ERR, "%s: %s not in namelist",
232
			    _PATH_UNIX, current_nl[cursyms[i]].n_name);
233
			exit(1);
234
		}
235
236
	(void)KREAD(kd_kern, current_nl[X_DUMPDEV].n_value, &dumpdev);
237
	if (dumpdev == NODEV) {
238
		syslog(LOG_WARNING, "no core dump (no dumpdev)");
239
		exit(1);
240
	}
241
	(void)KREAD(kd_kern, current_nl[X_DUMPLO].n_value, &dumplo);
242
	dumpoff = (off_t)dumplo * DEV_BSIZE;
243
	if (verbose)
244
		(void)printf("dumpoff = %lld (%ld * %d)\n",
245
		    (long long)dumpoff, dumplo, DEV_BSIZE);
246
	(void) KREAD(kd_kern, current_nl[X_DUMPMAG].n_value, &dumpmag);
247
248
	if (kernel == NULL) {
249
		if (kvm_read(kd_kern, current_nl[X_VERSION].n_value,
250
		    vers, sizeof(vers)) == -1) {
251
			syslog(LOG_ERR, "%s: kvm_read: version misread", _PATH_UNIX);
252
			exit(1);
253
		}
254
		vers[sizeof(vers) - 1] = '\0';
255
	}
256
257
	ddname = find_dev(dumpdev, S_IFBLK);
258
	dumpfd = open(ddname, O_RDWR);
259
	if (dumpfd == -1) {
260
		syslog(LOG_ERR, "%s: %m", ddname);
261
		exit(1);
262
	}
263
264
265
	dump_sys = kernel ? kernel : _PATH_UNIX;
266
	kd_dump = kvm_openfiles(kernel, ddname, NULL, O_RDWR, errbuf);
267
	if (kd_dump == NULL) {
268
		syslog(LOG_ERR, "%s: kvm_openfiles: %s", dump_sys, errbuf);
269
		exit(1);
270
	}
271
272
	if (kvm_nlist(kd_dump, dump_nl) == -1)
273
		syslog(LOG_ERR, "%s: kvm_nlist: %s", dump_sys,
274
			kvm_geterr(kd_dump));
275
276
	for (i = 0; dumpsyms[i] != -1; i++)
277
		if (dump_nl[dumpsyms[i]].n_value == 0) {
278
			syslog(LOG_ERR, "%s: %s not in namelist",
279
			    dump_sys, dump_nl[dumpsyms[i]].n_name);
280
			exit(1);
281
		}
282
	hdrsz = kvm_dump_mkheader(kd_dump, dumpoff);
283
	if (hdrsz == -1) {
284
		if(verbose)
285
			syslog(LOG_ERR, "%s: kvm_dump_mkheader: %s", dump_sys,
286
				kvm_geterr(kd_dump));
287
		syslog(LOG_WARNING, "no core dump");
288
		exit(1);
289
	}
290
	dumpoff += hdrsz;
291
	kvm_close(kd_kern);
292
}
293
294
void
295
check_kmem(void)
296
{
297
	char	*cp;
298
	int	panicloc;
299
	char core_vers[VERSIONSIZE];
300
301
	if (kvm_read(kd_dump, dump_nl[X_VERSION].n_value, core_vers,
302
	    sizeof(core_vers)) != sizeof(core_vers)) {
303
		syslog(LOG_ERR, "%s: kvm_read: version misread", dump_sys);
304
		exit(1);
305
	}
306
	core_vers[sizeof(core_vers) - 1] = '\0';
307
308
	if (strcmp(vers, core_vers) && kernel == 0) {
309
		vers[strcspn(vers, "\n")] = '\0';
310
		core_vers[strcspn(core_vers, "\n")] = '\0';
311
312
		syslog(LOG_WARNING,
313
		    "warning: %s version mismatch:\n\t%s\nand\t%s\n",
314
		    _PATH_UNIX, vers, core_vers);
315
	}
316
317
	(void)KREAD(kd_dump, dump_nl[X_PANICSTR].n_value, &panicstr);
318
	if (panicstr) {
319
		char	c, visout[5];
320
		size_t	vislen;
321
322
		cp       = panic_mesg;
323
		panicloc = panicstr;
324
		for (;;) {
325
			if (KREAD(kd_dump, panicloc, &c) != 0 || c == '\0')
326
				break;
327
			panicloc++;
328
329
			vis(visout, c, VIS_SAFE|VIS_NOSLASH, 0);
330
			vislen = strlen(visout);
331
			if (cp - panic_mesg + vislen >= sizeof(panic_mesg))
332
				break;
333
			strlcat(cp, visout,
334
			    panic_mesg + sizeof panic_mesg - cp);
335
			cp += strlen(cp);
336
		}
337
	}
338
}
339
340
int
341
dump_exists(void)
342
{
343
	u_long newdumpmag;
344
345
	(void)KREAD(kd_dump, dump_nl[X_DUMPMAG].n_value, &newdumpmag);
346
347
	/* Read the dump size. */
348
	(void)KREAD(kd_dump, dump_nl[X_DUMPSIZE].n_value, &dumppages);
349
	dumpsize = (u_long)dumppages * getpagesize();
350
351
	/*
352
	 * Return zero if core dump doesn't seem to be there and note
353
	 * it for syslog.  This check and return happens after the dump size
354
	 * is read, so dumpsize is whether or not the core is valid (for -f).
355
	 */
356
	if (newdumpmag != dumpmag) {
357
		if (verbose)
358
			syslog(LOG_WARNING,
359
			    "magic number mismatch (%lx != %lx)",
360
			    newdumpmag, dumpmag);
361
		syslog(LOG_WARNING, "no core dump");
362
		return (0);
363
	}
364
	return (1);
365
}
366
367
void
368
clear_dump(void)
369
{
370
	if (kvm_dump_inval(kd_dump) == -1)
371
		syslog(LOG_ERR, "%s: kvm_clear_dump: %s", ddname,
372
			kvm_geterr(kd_dump));
373
374
}
375
376
char buf[1024 * 1024];
377
378
void
379
save_core(void)
380
{
381
	FILE *fp;
382
	int bounds, ifd, nr, nw, ofd = -1;
383
	char *rawp, path[PATH_MAX];
384
	mode_t um;
385
386
	um = umask(S_IRWXG|S_IRWXO);
387
388
	/*
389
	 * Get the current number and update the bounds file.  Do the update
390
	 * now, because we may fail later and don't want to overwrite anything.
391
	 */
392
	(void)snprintf(path, sizeof(path), "%s/bounds", dirn);
393
	if ((fp = fopen(path, "r")) == NULL)
394
		goto err1;
395
	if (fgets(buf, sizeof(buf), fp) == NULL) {
396
		if (ferror(fp))
397
err1:			syslog(LOG_WARNING, "%s: %s", path, strerror(errno));
398
		bounds = 0;
399
	} else {
400
		const char *errstr = NULL;
401
		char *p;
402
403
		if ((p = strchr(buf, '\n')) != NULL)
404
			*p = '\0';
405
		bounds = strtonum(buf, 0, INT_MAX, &errstr);
406
		if (errstr)
407
			syslog(LOG_WARNING, "bounds was corrupt: %s", errstr);
408
	}
409
	if (fp != NULL)
410
		(void)fclose(fp);
411
	if ((fp = fopen(path, "w")) == NULL)
412
		syslog(LOG_ERR, "%s: %m", path);
413
	else {
414
		(void)fprintf(fp, "%d\n", bounds + 1);
415
		(void)fclose(fp);
416
	}
417
418
	/* Create the core file. */
419
	(void)snprintf(path, sizeof(path), "%s%s.%d.core%s",
420
	    dirn, _PATH_UNIX, bounds, zcompress ? ".Z" : "");
421
	if (zcompress) {
422
		if ((fp = zopen(path, "w", 0)) == NULL) {
423
			syslog(LOG_ERR, "%s: %s", path, strerror(errno));
424
			exit(1);
425
		}
426
	} else {
427
		ofd = open(path, O_WRONLY | O_CREAT | O_TRUNC,
428
		    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
429
		if (ofd == -1) {
430
			syslog(LOG_ERR, "%s: %m", path);
431
			exit(1);
432
		}
433
434
		fp  = fdopen(ofd, "w");
435
		if (fp == NULL) {
436
			syslog(LOG_ERR, "%s: fdopen: %s", path, strerror(errno));
437
			exit(1);
438
		}
439
	}
440
441
	/* Open the raw device. */
442
	rawp = rawname(ddname);
443
	if ((ifd = open(rawp, O_RDONLY)) == -1) {
444
		syslog(LOG_WARNING, "%s: %m; using block device", rawp);
445
		ifd = dumpfd;
446
	}
447
448
	/* Seek to the start of the core. */
449
	if (lseek(ifd, dumpoff, SEEK_SET) == -1) {
450
		syslog(LOG_ERR, "lseek: %m");
451
		exit(1);
452
	}
453
454
	if (kvm_dump_wrtheader(kd_dump, fp, dumpsize) == -1) {
455
		syslog(LOG_ERR, "kvm_dump_wrtheader: %s : %s", path,
456
			kvm_geterr(kd_dump));
457
		exit(1);
458
	}
459
460
	/* Copy the core file. */
461
	syslog(LOG_NOTICE, "writing %score to %s",
462
	    zcompress ? "compressed " : "", path);
463
	for (; dumpsize != 0; dumpsize -= nr) {
464
		(void)printf("%8luK\r", dumpsize / 1024);
465
		(void)fflush(stdout);
466
		nr = read(ifd, buf, MINIMUM(dumpsize, sizeof(buf)));
467
		if (nr <= 0) {
468
			if (nr == 0)
469
				syslog(LOG_WARNING,
470
				    "WARNING: EOF on dump device");
471
			else
472
				syslog(LOG_ERR, "%s: %m", rawp);
473
			goto err2;
474
		}
475
		nw = fwrite(buf, 1, nr, fp);
476
		if (nw != nr) {
477
			syslog(LOG_ERR, "%s: %s",
478
			    path, strerror(nw == 0 ? EIO : errno));
479
err2:			syslog(LOG_WARNING,
480
			    "WARNING: core may be incomplete");
481
			(void)printf("\n");
482
			exit(1);
483
		}
484
	}
485
	(void)close(ifd);
486
	(void)fclose(fp);
487
488
	/* Copy the kernel. */
489
	ifd = open(kernel ? kernel : _PATH_UNIX, O_RDONLY);
490
	if (ifd == -1) {
491
		syslog(LOG_ERR, "%s: %m", kernel ? kernel : _PATH_UNIX);
492
		exit(1);
493
	}
494
	(void)snprintf(path, sizeof(path), "%s%s.%d%s",
495
	    dirn, _PATH_UNIX, bounds, zcompress ? ".Z" : "");
496
	if (zcompress) {
497
		if ((fp = zopen(path, "w", 0)) == NULL) {
498
			syslog(LOG_ERR, "%s: %s", path, strerror(errno));
499
			exit(1);
500
		}
501
	} else {
502
		ofd = open(path, O_WRONLY | O_CREAT | O_TRUNC,
503
		    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
504
		if (ofd == -1) {
505
			syslog(LOG_ERR, "%s: %m", path);
506
			exit(1);
507
		}
508
	}
509
	syslog(LOG_NOTICE, "writing %skernel to %s",
510
	    zcompress ? "compressed " : "", path);
511
	while ((nr = read(ifd, buf, sizeof(buf))) > 0) {
512
		if (zcompress)
513
			nw = fwrite(buf, 1, nr, fp);
514
		else
515
			nw = write(ofd, buf, nr);
516
		if (nw != nr) {
517
			syslog(LOG_ERR, "%s: %s",
518
			    path, strerror(nw == 0 ? EIO : errno));
519
			syslog(LOG_WARNING,
520
			    "WARNING: kernel may be incomplete");
521
			exit(1);
522
		}
523
	}
524
	if (nr < 0) {
525
		syslog(LOG_ERR, "%s: %s",
526
		    kernel ? kernel : _PATH_UNIX, strerror(errno));
527
		syslog(LOG_WARNING,
528
		    "WARNING: kernel may be incomplete");
529
		exit(1);
530
	}
531
	if (zcompress)
532
		(void)fclose(fp);
533
	else
534
		(void)close(ofd);
535
	(void)umask(um);
536
}
537
538
char *
539
find_dev(dev_t dev, int type)
540
{
541
	DIR *dfd;
542
	struct dirent *dir;
543
	struct stat sb;
544
	char *dp, devname[PATH_MAX];
545
546
	if ((dfd = opendir(_PATH_DEV)) == NULL) {
547
		syslog(LOG_ERR, "%s: %s", _PATH_DEV, strerror(errno));
548
		exit(1);
549
	}
550
	(void)strlcpy(devname, _PATH_DEV, sizeof devname);
551
	while ((dir = readdir(dfd))) {
552
		(void)strlcpy(devname + sizeof(_PATH_DEV) - 1, dir->d_name,
553
		    sizeof devname - (sizeof(_PATH_DEV) - 1));
554
		if (lstat(devname, &sb)) {
555
			syslog(LOG_ERR, "%s: %s", devname, strerror(errno));
556
			continue;
557
		}
558
		if ((sb.st_mode & S_IFMT) != type)
559
			continue;
560
		if (dev == sb.st_rdev) {
561
			closedir(dfd);
562
			if ((dp = strdup(devname)) == NULL) {
563
				syslog(LOG_ERR, "%s", strerror(errno));
564
				exit(1);
565
			}
566
			return (dp);
567
		}
568
	}
569
	closedir(dfd);
570
	syslog(LOG_ERR, "can't find device %d/%d", major(dev), minor(dev));
571
	exit(1);
572
}
573
574
char *
575
rawname(char *s)
576
{
577
	char *sl, name[PATH_MAX];
578
579
	if ((sl = strrchr(s, '/')) == NULL || sl[1] == '0') {
580
		syslog(LOG_ERR,
581
		    "can't make raw dump device name from %s", s);
582
		return (s);
583
	}
584
	(void)snprintf(name, sizeof(name), "%.*s/r%s", (int)(sl - s), s, sl + 1);
585
	if ((sl = strdup(name)) == NULL) {
586
		syslog(LOG_ERR, "%s", strerror(errno));
587
		exit(1);
588
	}
589
	return (sl);
590
}
591
592
int
593
get_crashtime(void)
594
{
595
	time_t dumptime;			/* Time the dump was taken. */
596
597
	(void)KREAD(kd_dump, dump_nl[X_TIME].n_value, &dumptime);
598
	if (dumptime == 0) {
599
		if (verbose)
600
			syslog(LOG_ERR, "dump time is zero");
601
		return (0);
602
	}
603
	(void)printf("savecore: system went down at %s", ctime(&dumptime));
604
#define	SECSPERDAY	(24 * 60 * 60)
605
#define	LEEWAY		(7 * SECSPERDAY)
606
	if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) {
607
		(void)printf("dump time is unreasonable\n");
608
		return (0);
609
	}
610
	return (1);
611
}
612
613
int
614
check_space(void)
615
{
616
	FILE *fp;
617
	char *tkernel;
618
	off_t minfree, spacefree, kernelsize, needed;
619
	struct stat st;
620
	struct statfs fsbuf;
621
	char buf[100], path[PATH_MAX];
622
	int fd;
623
624
	tkernel = kernel ? kernel : _PATH_UNIX;
625
	if (stat(tkernel, &st) < 0) {
626
		syslog(LOG_ERR, "%s: %m", tkernel);
627
		exit(1);
628
	}
629
	kernelsize = st.st_blocks * S_BLKSIZE;
630
	if ((fd = open(dirn, O_RDONLY, 0)) < 0 || fstatfs(fd, &fsbuf) < 0) {
631
		syslog(LOG_ERR, "%s: %m", dirn);
632
		exit(1);
633
	}
634
	close(fd);
635
	spacefree = ((off_t)fsbuf.f_bavail * fsbuf.f_bsize) / 1024;
636
637
	(void)snprintf(path, sizeof(path), "%s/minfree", dirn);
638
	if ((fp = fopen(path, "r")) == NULL)
639
		minfree = 0;
640
	else {
641
		if (fgets(buf, sizeof(buf), fp) == NULL)
642
			minfree = 0;
643
		else {
644
			const char *errstr;
645
			char *p;
646
647
			if ((p = strchr(buf, '\n')) != NULL)
648
				*p = '\0';
649
			minfree = strtonum(buf, 0, LLONG_MAX, &errstr);
650
			if (errstr)
651
				syslog(LOG_WARNING,
652
				    "minfree was corrupt: %s", errstr);
653
		}
654
		(void)fclose(fp);
655
	}
656
657
	needed = (dumpsize + kernelsize) / 1024;
658
	if (minfree > 0 && spacefree - needed < minfree) {
659
		syslog(LOG_WARNING,
660
		    "no dump, not enough free space on device");
661
		return (0);
662
	}
663
	if (spacefree - needed < minfree)
664
		syslog(LOG_WARNING,
665
		    "dump performed, but free space threshold crossed");
666
	return (1);
667
}
668
669
void
670
usage(void)
671
{
672
	extern char *__progname;
673
	fprintf(stderr, "usage: %s [-cfvz] [-N system] directory\n",
674
		__progname);
675
	exit(1);
676
}