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