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