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