savecore.c revision 97340
1214455Srpaulo/*- 2214455Srpaulo * Copyright (c) 2002 Poul-Henning Kamp 3214455Srpaulo * Copyright (c) 2002 Networks Associates Technology, Inc. 4214455Srpaulo * All rights reserved. 5214455Srpaulo * 6214455Srpaulo * This software was developed for the FreeBSD Project by Poul-Henning Kamp 7214455Srpaulo * and NAI Labs, the Security Research Division of Network Associates, Inc. 8214455Srpaulo * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 9214455Srpaulo * DARPA CHATS research program. 10214455Srpaulo * 11214455Srpaulo * Redistribution and use in source and binary forms, with or without 12214455Srpaulo * modification, are permitted provided that the following conditions 13214455Srpaulo * are met: 14214455Srpaulo * 1. Redistributions of source code must retain the above copyright 15214455Srpaulo * notice, this list of conditions and the following disclaimer. 16214455Srpaulo * 2. Redistributions in binary form must reproduce the above copyright 17214455Srpaulo * notice, this list of conditions and the following disclaimer in the 18214455Srpaulo * documentation and/or other materials provided with the distribution. 19214455Srpaulo * 3. The names of the authors may not be used to endorse or promote 20214455Srpaulo * products derived from this software without specific prior written 21214455Srpaulo * permission. 22214455Srpaulo * 23214455Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 24214455Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25214455Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26214455Srpaulo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 27214455Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28214455Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29214455Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30214455Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31214455Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32214455Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33214455Srpaulo * SUCH DAMAGE. 34214455Srpaulo * 35214455Srpaulo * Copyright (c) 1986, 1992, 1993 36214455Srpaulo * The Regents of the University of California. All rights reserved. 37214455Srpaulo * 38214455Srpaulo * Redistribution and use in source and binary forms, with or without 39214455Srpaulo * modification, are permitted provided that the following conditions 40214455Srpaulo * are met: 41214455Srpaulo * 1. Redistributions of source code must retain the above copyright 42214455Srpaulo * notice, this list of conditions and the following disclaimer. 43214455Srpaulo * 2. Redistributions in binary form must reproduce the above copyright 44214455Srpaulo * notice, this list of conditions and the following disclaimer in the 45214455Srpaulo * documentation and/or other materials provided with the distribution. 46214455Srpaulo * 3. All advertising materials mentioning features or use of this software 47214455Srpaulo * must display the following acknowledgement: 48214455Srpaulo * This product includes software developed by the University of 49214455Srpaulo * California, Berkeley and its contributors. 50214455Srpaulo * 4. Neither the name of the University nor the names of its contributors 51214455Srpaulo * may be used to endorse or promote products derived from this software 52214455Srpaulo * without specific prior written permission. 53214455Srpaulo * 54214455Srpaulo * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 55214455Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 56214455Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 57214455Srpaulo * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 58214455Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 59214455Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 60214455Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 61214455Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 62214455Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 63214455Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 64214455Srpaulo * SUCH DAMAGE. 65214455Srpaulo */ 66214455Srpaulo 67214455Srpaulo#include <sys/cdefs.h> 68214455Srpaulo__FBSDID("$FreeBSD: head/sbin/savecore/savecore.c 97340 2002-05-27 07:54:43Z marcel $"); 69214455Srpaulo 70214455Srpaulo#include <sys/param.h> 71214455Srpaulo#include <sys/disk.h> 72214455Srpaulo#include <sys/kerneldump.h> 73214455Srpaulo#include <sys/param.h> 74214455Srpaulo#include <sys/mount.h> 75251129Sdelphij#include <sys/stat.h> 76251129Sdelphij#include <errno.h> 77251129Sdelphij#include <fcntl.h> 78251129Sdelphij#include <fstab.h> 79251129Sdelphij#include <paths.h> 80251129Sdelphij#include <stdarg.h> 81251129Sdelphij#include <stdio.h> 82251129Sdelphij#include <stdlib.h> 83251129Sdelphij#include <string.h> 84251129Sdelphij#include <syslog.h> 85251129Sdelphij#include <time.h> 86251129Sdelphij#include <unistd.h> 87214455Srpaulo 88251129Sdelphijint compress, clear, force, keep, verbose; /* flags */ 89214455Srpauloint nfound, nsaved, nerr; /* statistics */ 90251129Sdelphij 91251129Sdelphijextern FILE *zopen(const char *, const char *); 92251129Sdelphij 93214455Srpaulostatic void 94214455Srpauloprintheader(FILE *f, const struct kerneldumpheader *h, const char *device, 95251129Sdelphij int bounds) 96251129Sdelphij{ 97251129Sdelphij uint64_t dumplen; 98251129Sdelphij time_t t; 99251129Sdelphij 100251129Sdelphij fprintf(f, "Good dump found on device %s\n", device); 101251129Sdelphij fprintf(f, " Architecture: %s\n", h->architecture); 102251129Sdelphij fprintf(f, " Architecture version: %d\n", 103251129Sdelphij dtoh32(h->architectureversion)); 104251129Sdelphij dumplen = dtoh64(h->dumplength); 105251129Sdelphij fprintf(f, " Dump length: %lldB (%lld MB)\n", (long long)dumplen, 106251129Sdelphij (long long)(dumplen >> 20)); 107251129Sdelphij fprintf(f, " Blocksize: %d\n", dtoh32(h->blocksize)); 108251129Sdelphij t = dtoh64(h->dumptime); 109251129Sdelphij fprintf(f, " Dumptime: %s", ctime(&t)); 110251129Sdelphij fprintf(f, " Hostname: %s\n", h->hostname); 111251129Sdelphij fprintf(f, " Versionstring: %s", h->versionstring); 112251129Sdelphij fprintf(f, " Panicstring: %s\n", h->panicstring); 113251129Sdelphij fprintf(f, " Bounds: %d\n", bounds); 114251129Sdelphij fflush(f); 115251129Sdelphij} 116251129Sdelphij 117251129Sdelphijstatic int 118251129Sdelphijgetbounds(void) { 119251129Sdelphij FILE *fp; 120251129Sdelphij char buf[6]; 121251129Sdelphij int ret; 122251129Sdelphij 123251129Sdelphij ret = 0; 124251129Sdelphij 125251129Sdelphij if ((fp = fopen("bounds", "r")) == NULL) { 126251129Sdelphij syslog(LOG_WARNING, "unable to open bounds file, using 0"); 127214455Srpaulo goto newfile; 128214455Srpaulo } 129214455Srpaulo 130214455Srpaulo if (fgets(buf, sizeof buf, fp) == NULL) { 131214455Srpaulo syslog(LOG_WARNING, "unable to read from bounds, using 0"); 132214455Srpaulo fclose(fp); 133214455Srpaulo goto newfile; 134214455Srpaulo } 135214455Srpaulo 136214455Srpaulo errno = 0; 137214455Srpaulo ret = (int)strtol(buf, NULL, 10); 138214455Srpaulo if (ret == 0 && (errno == EINVAL || errno == ERANGE)) 139214455Srpaulo syslog(LOG_WARNING, "invalid value found in bounds, using 0"); 140214455Srpaulo 141214455Srpaulonewfile: 142214455Srpaulo 143214455Srpaulo if ((fp = fopen("bounds", "w")) == NULL) { 144214455Srpaulo syslog(LOG_WARNING, "unable to write to bounds file: %m"); 145214455Srpaulo goto done; 146214455Srpaulo } 147214455Srpaulo 148214455Srpaulo if (verbose) 149214455Srpaulo printf("bounds number: %d\n", ret); 150214455Srpaulo 151214455Srpaulo fprintf(fp, "%d\n", (ret + 1)); 152214455Srpaulo fclose(fp); 153214455Srpaulo 154214455Srpaulodone: 155214455Srpaulo return (ret); 156214455Srpaulo} 157214455Srpaulo 158214455Srpaulo/* 159214455Srpaulo * Check that sufficient space is available on the disk that holds the 160214455Srpaulo * save directory. 161214455Srpaulo */ 162214455Srpaulostatic int 163214455Srpaulocheck_space(char *savedir, off_t dumpsize) 164214455Srpaulo{ 165214455Srpaulo FILE *fp; 166214455Srpaulo off_t minfree, spacefree, totfree, needed; 167214455Srpaulo struct statfs fsbuf; 168214455Srpaulo char buf[100], path[MAXPATHLEN]; 169214455Srpaulo 170214455Srpaulo if (statfs(savedir, &fsbuf) < 0) { 171214455Srpaulo syslog(LOG_ERR, "%s: %m", savedir); 172214455Srpaulo exit(1); 173214455Srpaulo } 174214455Srpaulo spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024; 175214455Srpaulo totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024; 176214455Srpaulo 177214455Srpaulo (void)snprintf(path, sizeof(path), "%s/minfree", savedir); 178214455Srpaulo if ((fp = fopen(path, "r")) == NULL) 179214455Srpaulo minfree = 0; 180214455Srpaulo else { 181214455Srpaulo if (fgets(buf, sizeof(buf), fp) == NULL) 182214455Srpaulo minfree = 0; 183214455Srpaulo else 184214455Srpaulo minfree = atoi(buf); 185214455Srpaulo (void)fclose(fp); 186214455Srpaulo } 187214455Srpaulo 188214455Srpaulo needed = dumpsize / 1024 + 2; /* 2 for info file */ 189214455Srpaulo if (((minfree > 0) ? spacefree : totfree) - needed < minfree) { 190214455Srpaulo syslog(LOG_WARNING, 191214455Srpaulo "no dump, not enough free space on device (%lld available, need %lld)", 192214455Srpaulo (long long)(minfree > 0 ? spacefree : totfree), 193214455Srpaulo (long long)needed); 194214455Srpaulo return (0); 195214455Srpaulo } 196214455Srpaulo if (spacefree - needed < 0) 197214455Srpaulo syslog(LOG_WARNING, 198214455Srpaulo "dump performed, but free space threshold crossed"); 199214455Srpaulo return (1); 200214455Srpaulo} 201214455Srpaulo 202214455Srpaulo#define BLOCKSIZE (1<<12) 203214455Srpaulo#define BLOCKMASK (~(BLOCKSIZE-1)) 204214455Srpaulo 205214455Srpaulostatic void 206214455SrpauloDoFile(char *savedir, const char *device) 207214455Srpaulo{ 208214455Srpaulo static char *buf = NULL; 209214455Srpaulo struct kerneldumpheader kdhf, kdhl; 210214455Srpaulo off_t mediasize, dumpsize, firsthd, lasthd, dmpcnt; 211214455Srpaulo FILE *info, *fp; 212214455Srpaulo int fd, fdinfo, error, wl; 213214455Srpaulo int nr, nw, hs, he; 214214455Srpaulo int bounds; 215214455Srpaulo u_int sectorsize; 216214455Srpaulo mode_t oumask; 217214455Srpaulo 218214455Srpaulo dmpcnt = 0; 219214455Srpaulo mediasize = 0; 220214455Srpaulo 221214455Srpaulo /* 222214455Srpaulo * XXX On ia64 something breaks when the buffer is put on the 223214455Srpaulo * stack. When the buffer is roughly larger than 128K the read() 224214455Srpaulo * below simply fails with errno=14 (EFAULT). We work around 225214455Srpaulo * this by doing a on-time allocation... 226214455Srpaulo */ 227214455Srpaulo if (buf == NULL) { 228214455Srpaulo buf = malloc(1024 * 1024); 229214455Srpaulo if (buf == NULL) { 230214455Srpaulo syslog(LOG_ERR, "%m"); 231214455Srpaulo return; 232214455Srpaulo } 233214455Srpaulo } 234214455Srpaulo 235214455Srpaulo if (verbose) 236214455Srpaulo printf("checking for kernel dump on device %s\n", device); 237214455Srpaulo 238214455Srpaulo fd = open(device, O_RDWR); 239214455Srpaulo if (fd < 0) { 240214455Srpaulo syslog(LOG_ERR, "%s: %m", device); 241214455Srpaulo return; 242214455Srpaulo } 243214455Srpaulo 244214455Srpaulo error = ioctl(fd, DIOCGMEDIASIZE, &mediasize); 245214455Srpaulo if (!error) 246214455Srpaulo error = ioctl(fd, DIOCGSECTORSIZE, §orsize); 247214455Srpaulo if (error) { 248214455Srpaulo syslog(LOG_ERR, 249214455Srpaulo "couldn't find media and/or sector size of %s: %m", device); 250214455Srpaulo goto closefd; 251214455Srpaulo } 252214455Srpaulo 253214455Srpaulo if (verbose) { 254214455Srpaulo printf("mediasize = %lld\n", (long long)mediasize); 255214455Srpaulo printf("sectorsize = %u\n", sectorsize); 256214455Srpaulo } 257214455Srpaulo 258214455Srpaulo lasthd = mediasize - sectorsize; 259214455Srpaulo lseek(fd, lasthd, SEEK_SET); 260214455Srpaulo error = read(fd, &kdhl, sizeof kdhl); 261214455Srpaulo if (error != sizeof kdhl) { 262214455Srpaulo syslog(LOG_ERR, 263214455Srpaulo "error reading last dump header at offset %lld in %s: %m", 264214455Srpaulo (long long)lasthd, device); 265214455Srpaulo goto closefd; 266214455Srpaulo } 267214455Srpaulo if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic)) { 268214455Srpaulo if (verbose) 269214455Srpaulo printf("magic mismatch on last dump header on %s\n", 270214455Srpaulo device); 271214455Srpaulo 272214455Srpaulo if (force == 0) 273214455Srpaulo goto closefd; 274214455Srpaulo 275214455Srpaulo if (memcmp(kdhl.magic, KERNELDUMPMAGIC_CLEARED, 276214455Srpaulo sizeof kdhl.magic) == 0) { 277214455Srpaulo if (verbose) 278214455Srpaulo printf("forcing magic on %s\n", device); 279214455Srpaulo memcpy(kdhl.magic, KERNELDUMPMAGIC, 280214455Srpaulo sizeof kdhl.magic); 281214455Srpaulo } else { 282214455Srpaulo syslog(LOG_ERR, "unable to force dump - bad magic"); 283214455Srpaulo goto closefd; 284214455Srpaulo } 285214455Srpaulo } 286214455Srpaulo if (dtoh32(kdhl.version) != KERNELDUMPVERSION) { 287214455Srpaulo syslog(LOG_ERR, 288214455Srpaulo "unknown version (%d) in last dump header on %s", 289214455Srpaulo dtoh32(kdhl.version), device); 290214455Srpaulo goto closefd; 291214455Srpaulo } 292214455Srpaulo 293214455Srpaulo nfound++; 294214455Srpaulo if (clear) 295214455Srpaulo goto nuke; 296214455Srpaulo 297214455Srpaulo if (kerneldump_parity(&kdhl)) { 298214455Srpaulo syslog(LOG_ERR, 299214455Srpaulo "parity error on last dump header on %s", device); 300214455Srpaulo nerr++; 301214455Srpaulo goto closefd; 302214455Srpaulo } 303214455Srpaulo dumpsize = dtoh64(kdhl.dumplength); 304214455Srpaulo firsthd = lasthd - dumpsize - sizeof kdhf; 305214455Srpaulo lseek(fd, firsthd, SEEK_SET); 306214455Srpaulo error = read(fd, &kdhf, sizeof kdhf); 307214455Srpaulo if (error != sizeof kdhf) { 308214455Srpaulo syslog(LOG_ERR, 309214455Srpaulo "error reading first dump header at offset %lld in %s: %m", 310214455Srpaulo (long long)firsthd, device); 311214455Srpaulo nerr++; 312 goto closefd; 313 } 314 if (memcmp(&kdhl, &kdhf, sizeof kdhl)) { 315 syslog(LOG_ERR, 316 "first and last dump headers disagree on %s", device); 317 nerr++; 318 goto closefd; 319 } 320 321 if (kdhl.panicstring[0]) 322 syslog(LOG_ALERT, "reboot after panic: %s", kdhl.panicstring); 323 else 324 syslog(LOG_ALERT, "reboot"); 325 326 if (verbose) 327 printf("Checking for available free space\n"); 328 if (!check_space(savedir, dumpsize)) { 329 nerr++; 330 goto closefd; 331 } 332 333 bounds = getbounds(); 334 335 sprintf(buf, "info.%d", bounds); 336 337 /* 338 * Create or overwrite any existing files. 339 */ 340 fdinfo = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600); 341 if (fdinfo < 0) { 342 syslog(LOG_ERR, "%s: %m", buf); 343 nerr++; 344 goto closefd; 345 } 346 oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to the core file.*/ 347 if (compress) { 348 sprintf(buf, "vmcore.%d.gz", bounds); 349 fp = zopen(buf, "w"); 350 } else { 351 sprintf(buf, "vmcore.%d", bounds); 352 fp = fopen(buf, "w"); 353 } 354 if (fp == NULL) { 355 syslog(LOG_ERR, "%s: %m", buf); 356 close(fdinfo); 357 nerr++; 358 goto closefd; 359 } 360 (void)umask(oumask); 361 362 info = fdopen(fdinfo, "w"); 363 364 if (verbose) 365 printheader(stdout, &kdhl, device, bounds); 366 367 printheader(info, &kdhl, device, bounds); 368 fclose(info); 369 370 syslog(LOG_NOTICE, "writing %score to %s", 371 compress ? "compressed " : "", buf); 372 373 while (dumpsize > 0) { 374 wl = sizeof(buf); 375 if (wl > dumpsize) 376 wl = dumpsize; 377 nr = read(fd, buf, wl); 378 if (nr != wl) { 379 if (nr == 0) 380 syslog(LOG_WARNING, 381 "WARNING: EOF on dump device"); 382 else 383 syslog(LOG_ERR, "read error on %s: %m", device); 384 nerr++; 385 goto closeall; 386 } 387 if (compress) { 388 nw = fwrite(buf, 1, wl, fp); 389 } else { 390 for (nw = 0; nw < nr; nw = he) { 391 /* find a contiguous block of zeroes */ 392 for (hs = nw; hs < nr; hs += BLOCKSIZE) { 393 for (he = hs; he < nr && buf[he] == 0; ++he) 394 /* nothing */ ; 395 /* is the hole long enough to matter? */ 396 if (he >= hs + BLOCKSIZE) 397 break; 398 } 399 400 /* back down to a block boundary */ 401 he &= BLOCKMASK; 402 403 /* 404 * 1) Don't go beyond the end of the buffer. 405 * 2) If the end of the buffer is less than 406 * BLOCKSIZE bytes away, we're at the end 407 * of the file, so just grab what's left. 408 */ 409 if (hs + BLOCKSIZE > nr) 410 hs = he = nr; 411 412 /* 413 * At this point, we have a partial ordering: 414 * nw <= hs <= he <= nr 415 * If hs > nw, buf[nw..hs] contains non-zero data. 416 * If he > hs, buf[hs..he] is all zeroes. 417 */ 418 if (hs > nw) 419 if (fwrite(buf + nw, hs - nw, 1, fp) != 1) 420 break; 421 if (he > hs) 422 if (fseek(fp, he - hs, SEEK_CUR) == -1) 423 break; 424 } 425 } 426 if (nw != wl) { 427 syslog(LOG_ERR, 428 "write error on vmcore.%d file: %m", bounds); 429 syslog(LOG_WARNING, 430 "WARNING: vmcore may be incomplete"); 431 nerr++; 432 goto closeall; 433 } 434 if (verbose) { 435 dmpcnt += wl; 436 printf("%llu\r", (unsigned long long)dmpcnt); 437 fflush(stdout); 438 } 439 dumpsize -= wl; 440 } 441 if (verbose) 442 printf("\n"); 443 444 if (fclose(fp) < 0) { 445 syslog(LOG_ERR, "error on vmcore.%d: %m", bounds); 446 nerr++; 447 goto closeall; 448 } 449 nsaved++; 450 451 if (verbose) 452 printf("dump saved\n"); 453 454nuke: 455 if (clear || !keep) { 456 if (verbose) 457 printf("clearing dump header\n"); 458 memcpy(kdhl.magic, KERNELDUMPMAGIC_CLEARED, sizeof kdhl.magic); 459 lseek(fd, lasthd, SEEK_SET); 460 error = write(fd, &kdhl, sizeof kdhl); 461 if (error != sizeof kdhl) 462 syslog(LOG_ERR, 463 "error while clearing the dump header: %m"); 464 } 465 close(fd); 466 return; 467 468closeall: 469 fclose(fp); 470 471closefd: 472 close(fd); 473} 474 475static void 476usage(void) 477{ 478 fprintf(stderr, "usage: savecore [-cfkv] [directory [device...]]\n"); 479 exit (1); 480} 481 482int 483main(int argc, char **argv) 484{ 485 int i, ch, error; 486 struct fstab *fsp; 487 char *savedir; 488 489 openlog("savecore", LOG_PERROR, LOG_DAEMON); 490 491 savedir = strdup("."); 492 if (savedir == NULL) { 493 syslog(LOG_ERR, "Cannot allocate memory"); 494 exit(1); 495 } 496 while ((ch = getopt(argc, argv, "cdfkN:vz")) != -1) 497 switch(ch) { 498 case 'c': 499 clear = 1; 500 break; 501 case 'k': 502 keep = 1; 503 break; 504 case 'v': 505 verbose = 1; 506 break; 507 case 'f': 508 force = 1; 509 break; 510 case 'z': 511 compress = 1; 512 break; 513 case 'd': /* Obsolete */ 514 case 'N': 515 case '?': 516 default: 517 usage(); 518 } 519 argc -= optind; 520 argv += optind; 521 if (argc >= 1) { 522 error = chdir(argv[0]); 523 if (error) { 524 syslog(LOG_ERR, "chdir(%s): %m", argv[0]); 525 exit(1); 526 } 527 savedir = argv[0]; 528 argc--; 529 argv++; 530 } 531 if (argc == 0) { 532 for (;;) { 533 fsp = getfsent(); 534 if (fsp == NULL) 535 break; 536 if (strcmp(fsp->fs_vfstype, "swap") && 537 strcmp(fsp->fs_vfstype, "dump")) 538 continue; 539 DoFile(savedir, fsp->fs_spec); 540 } 541 } else { 542 for (i = 0; i < argc; i++) 543 DoFile(savedir, argv[i]); 544 } 545 546 /* Emit minimal output. */ 547 if (nfound == 0) 548 syslog(LOG_WARNING, "no dumps found"); 549 else if (nsaved == 0) { 550 if (nerr != 0) 551 syslog(LOG_WARNING, "unsaved dumps found but not saved"); 552 else 553 syslog(LOG_WARNING, "no unsaved dumps found"); 554 } 555 556 return (0); 557} 558