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