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