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 55extern 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 60struct 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}; 77int cursyms[] = { X_DUMPDEV, X_DUMPLO, X_VERSION, X_DUMPMAG, -1 }; 78int dumpsyms[] = { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 }; 79 80struct 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. */ 94long dumplo; /* where dump starts on dumpdev (in blocks) */ 95off_t dumpoff; /* where dump starts on dumpdev (in bytes) */ 96u_long dumpmag; /* magic number in dump */ 97int dumppages; /* amount of memory dumped (in pages) */ 98u_long dumpsize; /* amount of memory dumped */ 99 100char *kernel; 101char *dirn; /* directory to save dumps in */ 102char *ddname; /* name of dump device */ 103dev_t dumpdev; /* dump device */ 104int dumpfd; /* read/write descriptor on block dev */ 105kvm_t *kd_dump; /* kvm descriptor on block dev */ 106time_t now; /* current date */ 107char panic_mesg[1024]; 108int panicstr; 109char vers[VERSIONSIZE]; 110 111int clear, zcompress, force, verbose; /* flags */ 112 113void check_kmem(void); 114int check_space(void); 115void clear_dump(void); 116int dump_exists(void); 117char *find_dev(dev_t, int); 118int get_crashtime(void); 119void kmem_setup(void); 120char *rawname(char *s); 121void save_core(void); 122void usage(void); 123 124int 125main(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 213char *dump_sys; 214 215void 216kmem_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 304void 305check_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 350int 351dump_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 377void 378clear_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 391char buf[1024 * 1024]; 392 393void 394save_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)) 412err1: 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)); 494err2: 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 553char * 554find_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 589char * 590rawname(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 607int 608get_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 634int 635check_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 690void 691usage(void) 692{ 693 extern char *__progname; 694 fprintf(stderr, "usage: %s [-cfvz] [-N system] directory\n", 695 __progname); 696 exit(1); 697} 698