1/*- 2 * Copyright (c) 2002 Poul-Henning Kamp 3 * Copyright (c) 2002 Networks Associates Technology, Inc. 4 * All rights reserved. 5 * 6 * This software was developed for the FreeBSD Project by Poul-Henning Kamp 7 * and NAI Labs, the Security Research Division of Network Associates, Inc. 8 * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 9 * DARPA CHATS research program. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. The names of the authors may not be used to endorse or promote 20 * products derived from this software without specific prior written 21 * permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 * Copyright (c) 1986, 1992, 1993 36 * The Regents of the University of California. All rights reserved. 37 * 38 * Redistribution and use in source and binary forms, with or without 39 * modification, are permitted provided that the following conditions 40 * are met: 41 * 1. Redistributions of source code must retain the above copyright 42 * notice, this list of conditions and the following disclaimer. 43 * 2. Redistributions in binary form must reproduce the above copyright 44 * notice, this list of conditions and the following disclaimer in the 45 * documentation and/or other materials provided with the distribution. 46 * 4. Neither the name of the University nor the names of its contributors 47 * may be used to endorse or promote products derived from this software 48 * without specific prior written permission. 49 * 50 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 51 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 52 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 53 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 54 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 55 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 56 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 57 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 58 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 59 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 60 * SUCH DAMAGE. 61 */ 62 63#include <sys/cdefs.h> 64__FBSDID("$FreeBSD: stable/10/sbin/savecore/savecore.c 321212 2017-07-19 16:38:55Z ngie $"); 65 66#include <sys/param.h> 67#include <sys/disk.h> 68#include <sys/kerneldump.h> 69#include <sys/mount.h> 70#include <sys/stat.h> 71#include <ctype.h> 72#include <errno.h> 73#include <fcntl.h> 74#include <fstab.h> 75#include <paths.h> 76#include <signal.h> 77#include <stdarg.h> 78#include <stdio.h> 79#include <stdlib.h> 80#include <string.h> 81#include <syslog.h> 82#include <time.h> 83#include <unistd.h> 84 85/* The size of the buffer used for I/O. */ 86#define BUFFERSIZE (1024*1024) 87 88#define STATUS_BAD 0 89#define STATUS_GOOD 1 90#define STATUS_UNKNOWN 2 91 92static int checkfor, compress, clear, force, keep, verbose; /* flags */ 93static int nfound, nsaved, nerr; /* statistics */ 94static int maxdumps; 95 96extern FILE *zopen(const char *, const char *); 97 98static sig_atomic_t got_siginfo; 99static void infohandler(int); 100 101static void 102printheader(FILE *f, const struct kerneldumpheader *h, const char *device, 103 int bounds, const int status) 104{ 105 uint64_t dumplen; 106 time_t t; 107 const char *stat_str; 108 109 fprintf(f, "Dump header from device %s\n", device); 110 fprintf(f, " Architecture: %s\n", h->architecture); 111 fprintf(f, " Architecture Version: %u\n", 112 dtoh32(h->architectureversion)); 113 dumplen = dtoh64(h->dumplength); 114 fprintf(f, " Dump Length: %lldB (%lld MB)\n", (long long)dumplen, 115 (long long)(dumplen >> 20)); 116 fprintf(f, " Blocksize: %d\n", dtoh32(h->blocksize)); 117 t = dtoh64(h->dumptime); 118 fprintf(f, " Dumptime: %s", ctime(&t)); 119 fprintf(f, " Hostname: %s\n", h->hostname); 120 fprintf(f, " Magic: %s\n", h->magic); 121 fprintf(f, " Version String: %s", h->versionstring); 122 fprintf(f, " Panic String: %s\n", h->panicstring); 123 fprintf(f, " Dump Parity: %u\n", h->parity); 124 fprintf(f, " Bounds: %d\n", bounds); 125 126 switch(status) { 127 case STATUS_BAD: 128 stat_str = "bad"; 129 break; 130 case STATUS_GOOD: 131 stat_str = "good"; 132 break; 133 default: 134 stat_str = "unknown"; 135 } 136 fprintf(f, " Dump Status: %s\n", stat_str); 137 fflush(f); 138} 139 140static int 141getbounds(void) { 142 FILE *fp; 143 char buf[6]; 144 int ret; 145 146 ret = 0; 147 148 if ((fp = fopen("bounds", "r")) == NULL) { 149 if (verbose) 150 printf("unable to open bounds file, using 0\n"); 151 return (ret); 152 } 153 154 if (fgets(buf, sizeof buf, fp) == NULL) { 155 if (feof(fp)) 156 syslog(LOG_WARNING, "bounds file is empty, using 0"); 157 else 158 syslog(LOG_WARNING, "bounds file: %s", strerror(errno)); 159 fclose(fp); 160 return (ret); 161 } 162 163 errno = 0; 164 ret = (int)strtol(buf, NULL, 10); 165 if (ret == 0 && (errno == EINVAL || errno == ERANGE)) 166 syslog(LOG_WARNING, "invalid value found in bounds, using 0"); 167 fclose(fp); 168 return (ret); 169} 170 171static void 172writebounds(int bounds) { 173 FILE *fp; 174 175 if ((fp = fopen("bounds", "w")) == NULL) { 176 syslog(LOG_WARNING, "unable to write to bounds file: %m"); 177 return; 178 } 179 180 if (verbose) 181 printf("bounds number: %d\n", bounds); 182 183 fprintf(fp, "%d\n", bounds); 184 fclose(fp); 185} 186 187static off_t 188file_size(const char *path) 189{ 190 struct stat sb; 191 192 /* Ignore all errors, those file may not exists. */ 193 if (stat(path, &sb) == -1) 194 return (0); 195 return (sb.st_size); 196} 197 198static off_t 199saved_dump_size(int bounds) 200{ 201 static char path[PATH_MAX]; 202 off_t dumpsize; 203 204 dumpsize = 0; 205 206 (void)snprintf(path, sizeof(path), "info.%d", bounds); 207 dumpsize += file_size(path); 208 (void)snprintf(path, sizeof(path), "vmcore.%d", bounds); 209 dumpsize += file_size(path); 210 (void)snprintf(path, sizeof(path), "vmcore.%d.gz", bounds); 211 dumpsize += file_size(path); 212 (void)snprintf(path, sizeof(path), "textdump.tar.%d", bounds); 213 dumpsize += file_size(path); 214 (void)snprintf(path, sizeof(path), "textdump.tar.%d.gz", bounds); 215 dumpsize += file_size(path); 216 217 return (dumpsize); 218} 219 220static void 221saved_dump_remove(int bounds) 222{ 223 static char path[PATH_MAX]; 224 225 (void)snprintf(path, sizeof(path), "info.%d", bounds); 226 (void)unlink(path); 227 (void)snprintf(path, sizeof(path), "vmcore.%d", bounds); 228 (void)unlink(path); 229 (void)snprintf(path, sizeof(path), "vmcore.%d.gz", bounds); 230 (void)unlink(path); 231 (void)snprintf(path, sizeof(path), "textdump.tar.%d", bounds); 232 (void)unlink(path); 233 (void)snprintf(path, sizeof(path), "textdump.tar.%d.gz", bounds); 234 (void)unlink(path); 235} 236 237static void 238symlinks_remove(void) 239{ 240 241 (void)unlink("info.last"); 242 (void)unlink("vmcore.last"); 243 (void)unlink("vmcore.last.gz"); 244 (void)unlink("textdump.tar.last"); 245 (void)unlink("textdump.tar.last.gz"); 246} 247 248/* 249 * Check that sufficient space is available on the disk that holds the 250 * save directory. 251 */ 252static int 253check_space(const char *savedir, off_t dumpsize, int bounds) 254{ 255 FILE *fp; 256 off_t available, minfree, spacefree, totfree, needed; 257 struct statfs fsbuf; 258 char buf[100]; 259 260 if (statfs(".", &fsbuf) < 0) { 261 syslog(LOG_ERR, "%s: %m", savedir); 262 exit(1); 263 } 264 spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024; 265 totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024; 266 267 if ((fp = fopen("minfree", "r")) == NULL) 268 minfree = 0; 269 else { 270 if (fgets(buf, sizeof(buf), fp) == NULL) 271 minfree = 0; 272 else { 273 char *endp; 274 275 errno = 0; 276 minfree = strtoll(buf, &endp, 10); 277 if (minfree == 0 && errno != 0) 278 minfree = -1; 279 else { 280 while (*endp != '\0' && isspace(*endp)) 281 endp++; 282 if (*endp != '\0' || minfree < 0) 283 minfree = -1; 284 } 285 if (minfree < 0) 286 syslog(LOG_WARNING, 287 "`minfree` didn't contain a valid size " 288 "(`%s`). Defaulting to 0", buf); 289 } 290 (void)fclose(fp); 291 } 292 293 available = minfree > 0 ? spacefree - minfree : totfree; 294 needed = dumpsize / 1024 + 2; /* 2 for info file */ 295 needed -= saved_dump_size(bounds); 296 if (available < needed) { 297 syslog(LOG_WARNING, 298 "no dump: not enough free space on device (need at least " 299 "%jdkB for dump; %jdkB available; %jdkB reserved)", 300 (intmax_t)needed, 301 (intmax_t)available + minfree, 302 (intmax_t)minfree); 303 return (0); 304 } 305 if (spacefree - needed < 0) 306 syslog(LOG_WARNING, 307 "dump performed, but free space threshold crossed"); 308 return (1); 309} 310 311#define BLOCKSIZE (1<<12) 312#define BLOCKMASK (~(BLOCKSIZE-1)) 313 314static int 315DoRegularFile(int fd, off_t dumpsize, char *buf, const char *device, 316 const char *filename, FILE *fp) 317{ 318 int he, hs, nr, nw, wl; 319 off_t dmpcnt, origsize; 320 321 dmpcnt = 0; 322 origsize = dumpsize; 323 he = 0; 324 while (dumpsize > 0) { 325 wl = BUFFERSIZE; 326 if (wl > dumpsize) 327 wl = dumpsize; 328 nr = read(fd, buf, wl); 329 if (nr != wl) { 330 if (nr == 0) 331 syslog(LOG_WARNING, 332 "WARNING: EOF on dump device"); 333 else 334 syslog(LOG_ERR, "read error on %s: %m", device); 335 nerr++; 336 return (-1); 337 } 338 if (compress) { 339 nw = fwrite(buf, 1, wl, fp); 340 } else { 341 for (nw = 0; nw < nr; nw = he) { 342 /* find a contiguous block of zeroes */ 343 for (hs = nw; hs < nr; hs += BLOCKSIZE) { 344 for (he = hs; he < nr && buf[he] == 0; 345 ++he) 346 /* nothing */ ; 347 /* is the hole long enough to matter? */ 348 if (he >= hs + BLOCKSIZE) 349 break; 350 } 351 352 /* back down to a block boundary */ 353 he &= BLOCKMASK; 354 355 /* 356 * 1) Don't go beyond the end of the buffer. 357 * 2) If the end of the buffer is less than 358 * BLOCKSIZE bytes away, we're at the end 359 * of the file, so just grab what's left. 360 */ 361 if (hs + BLOCKSIZE > nr) 362 hs = he = nr; 363 364 /* 365 * At this point, we have a partial ordering: 366 * nw <= hs <= he <= nr 367 * If hs > nw, buf[nw..hs] contains non-zero data. 368 * If he > hs, buf[hs..he] is all zeroes. 369 */ 370 if (hs > nw) 371 if (fwrite(buf + nw, hs - nw, 1, fp) 372 != 1) 373 break; 374 if (he > hs) 375 if (fseeko(fp, he - hs, SEEK_CUR) == -1) 376 break; 377 } 378 } 379 if (nw != wl) { 380 syslog(LOG_ERR, 381 "write error on %s file: %m", filename); 382 syslog(LOG_WARNING, 383 "WARNING: vmcore may be incomplete"); 384 nerr++; 385 return (-1); 386 } 387 if (verbose) { 388 dmpcnt += wl; 389 printf("%llu\r", (unsigned long long)dmpcnt); 390 fflush(stdout); 391 } 392 dumpsize -= wl; 393 if (got_siginfo) { 394 printf("%s %.1lf%%\n", filename, (100.0 - (100.0 * 395 (double)dumpsize / (double)origsize))); 396 got_siginfo = 0; 397 } 398 } 399 return (0); 400} 401 402/* 403 * Specialized version of dump-reading logic for use with textdumps, which 404 * are written backwards from the end of the partition, and must be reversed 405 * before being written to the file. Textdumps are small, so do a bit less 406 * work to optimize/sparsify. 407 */ 408static int 409DoTextdumpFile(int fd, off_t dumpsize, off_t lasthd, char *buf, 410 const char *device, const char *filename, FILE *fp) 411{ 412 int nr, nw, wl; 413 off_t dmpcnt, totsize; 414 415 totsize = dumpsize; 416 dmpcnt = 0; 417 wl = 512; 418 if ((dumpsize % wl) != 0) { 419 syslog(LOG_ERR, "textdump uneven multiple of 512 on %s", 420 device); 421 nerr++; 422 return (-1); 423 } 424 while (dumpsize > 0) { 425 nr = pread(fd, buf, wl, lasthd - (totsize - dumpsize) - wl); 426 if (nr != wl) { 427 if (nr == 0) 428 syslog(LOG_WARNING, 429 "WARNING: EOF on dump device"); 430 else 431 syslog(LOG_ERR, "read error on %s: %m", device); 432 nerr++; 433 return (-1); 434 } 435 nw = fwrite(buf, 1, wl, fp); 436 if (nw != wl) { 437 syslog(LOG_ERR, 438 "write error on %s file: %m", filename); 439 syslog(LOG_WARNING, 440 "WARNING: textdump may be incomplete"); 441 nerr++; 442 return (-1); 443 } 444 if (verbose) { 445 dmpcnt += wl; 446 printf("%llu\r", (unsigned long long)dmpcnt); 447 fflush(stdout); 448 } 449 dumpsize -= wl; 450 } 451 return (0); 452} 453 454static void 455DoFile(const char *savedir, const char *device) 456{ 457 static char infoname[PATH_MAX], corename[PATH_MAX], linkname[PATH_MAX]; 458 static char *buf = NULL; 459 struct kerneldumpheader kdhf, kdhl; 460 off_t mediasize, dumpsize, firsthd, lasthd; 461 FILE *info, *fp; 462 mode_t oumask; 463 int fd, fdinfo, error; 464 int bounds, status; 465 u_int sectorsize; 466 int istextdump; 467 468 bounds = getbounds(); 469 mediasize = 0; 470 status = STATUS_UNKNOWN; 471 472 if (maxdumps > 0 && bounds == maxdumps) 473 bounds = 0; 474 475 if (buf == NULL) { 476 buf = malloc(BUFFERSIZE); 477 if (buf == NULL) { 478 syslog(LOG_ERR, "%m"); 479 return; 480 } 481 } 482 483 if (verbose) 484 printf("checking for kernel dump on device %s\n", device); 485 486 fd = open(device, (checkfor || keep) ? O_RDONLY : O_RDWR); 487 if (fd < 0) { 488 syslog(LOG_ERR, "%s: %m", device); 489 return; 490 } 491 492 error = ioctl(fd, DIOCGMEDIASIZE, &mediasize); 493 if (!error) 494 error = ioctl(fd, DIOCGSECTORSIZE, §orsize); 495 if (error) { 496 syslog(LOG_ERR, 497 "couldn't find media and/or sector size of %s: %m", device); 498 goto closefd; 499 } 500 501 if (verbose) { 502 printf("mediasize = %lld bytes\n", (long long)mediasize); 503 printf("sectorsize = %u bytes\n", sectorsize); 504 } 505 506 lasthd = mediasize - sectorsize; 507 lseek(fd, lasthd, SEEK_SET); 508 error = read(fd, &kdhl, sizeof kdhl); 509 if (error != sizeof kdhl) { 510 syslog(LOG_ERR, 511 "error reading last dump header at offset %lld in %s: %m", 512 (long long)lasthd, device); 513 goto closefd; 514 } 515 istextdump = 0; 516 if (strncmp(kdhl.magic, TEXTDUMPMAGIC, sizeof kdhl) == 0) { 517 if (verbose) 518 printf("textdump magic on last dump header on %s\n", 519 device); 520 istextdump = 1; 521 if (dtoh32(kdhl.version) != KERNELDUMP_TEXT_VERSION) { 522 syslog(LOG_ERR, 523 "unknown version (%d) in last dump header on %s", 524 dtoh32(kdhl.version), device); 525 526 status = STATUS_BAD; 527 if (force == 0) 528 goto closefd; 529 } 530 } else if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic) == 531 0) { 532 if (dtoh32(kdhl.version) != KERNELDUMPVERSION) { 533 syslog(LOG_ERR, 534 "unknown version (%d) in last dump header on %s", 535 dtoh32(kdhl.version), device); 536 537 status = STATUS_BAD; 538 if (force == 0) 539 goto closefd; 540 } 541 } else { 542 if (verbose) 543 printf("magic mismatch on last dump header on %s\n", 544 device); 545 546 status = STATUS_BAD; 547 if (force == 0) 548 goto closefd; 549 550 if (memcmp(kdhl.magic, KERNELDUMPMAGIC_CLEARED, 551 sizeof kdhl.magic) == 0) { 552 if (verbose) 553 printf("forcing magic on %s\n", device); 554 memcpy(kdhl.magic, KERNELDUMPMAGIC, 555 sizeof kdhl.magic); 556 } else { 557 syslog(LOG_ERR, "unable to force dump - bad magic"); 558 goto closefd; 559 } 560 if (dtoh32(kdhl.version) != KERNELDUMPVERSION) { 561 syslog(LOG_ERR, 562 "unknown version (%d) in last dump header on %s", 563 dtoh32(kdhl.version), device); 564 565 status = STATUS_BAD; 566 if (force == 0) 567 goto closefd; 568 } 569 } 570 571 nfound++; 572 if (clear) 573 goto nuke; 574 575 if (kerneldump_parity(&kdhl)) { 576 syslog(LOG_ERR, 577 "parity error on last dump header on %s", device); 578 nerr++; 579 status = STATUS_BAD; 580 if (force == 0) 581 goto closefd; 582 } 583 dumpsize = dtoh64(kdhl.dumplength); 584 firsthd = lasthd - dumpsize - sizeof kdhf; 585 lseek(fd, firsthd, SEEK_SET); 586 error = read(fd, &kdhf, sizeof kdhf); 587 if (error != sizeof kdhf) { 588 syslog(LOG_ERR, 589 "error reading first dump header at offset %lld in %s: %m", 590 (long long)firsthd, device); 591 nerr++; 592 goto closefd; 593 } 594 595 if (verbose >= 2) { 596 printf("First dump headers:\n"); 597 printheader(stdout, &kdhf, device, bounds, -1); 598 599 printf("\nLast dump headers:\n"); 600 printheader(stdout, &kdhl, device, bounds, -1); 601 printf("\n"); 602 } 603 604 if (memcmp(&kdhl, &kdhf, sizeof kdhl)) { 605 syslog(LOG_ERR, 606 "first and last dump headers disagree on %s", device); 607 nerr++; 608 status = STATUS_BAD; 609 if (force == 0) 610 goto closefd; 611 } else { 612 status = STATUS_GOOD; 613 } 614 615 if (checkfor) { 616 printf("A dump exists on %s\n", device); 617 close(fd); 618 exit(0); 619 } 620 621 if (kdhl.panicstring[0]) 622 syslog(LOG_ALERT, "reboot after panic: %s", kdhl.panicstring); 623 else 624 syslog(LOG_ALERT, "reboot"); 625 626 if (verbose) 627 printf("Checking for available free space\n"); 628 629 if (!check_space(savedir, dumpsize, bounds)) { 630 nerr++; 631 goto closefd; 632 } 633 634 writebounds(bounds + 1); 635 636 saved_dump_remove(bounds); 637 638 snprintf(infoname, sizeof(infoname), "info.%d", bounds); 639 640 /* 641 * Create or overwrite any existing dump header files. 642 */ 643 fdinfo = open(infoname, O_WRONLY | O_CREAT | O_TRUNC, 0600); 644 if (fdinfo < 0) { 645 syslog(LOG_ERR, "%s: %m", infoname); 646 nerr++; 647 goto closefd; 648 } 649 oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to the core file.*/ 650 if (compress) { 651 snprintf(corename, sizeof(corename), "%s.%d.gz", 652 istextdump ? "textdump.tar" : "vmcore", bounds); 653 fp = zopen(corename, "w"); 654 } else { 655 snprintf(corename, sizeof(corename), "%s.%d", 656 istextdump ? "textdump.tar" : "vmcore", bounds); 657 fp = fopen(corename, "w"); 658 } 659 if (fp == NULL) { 660 syslog(LOG_ERR, "%s: %m", corename); 661 close(fdinfo); 662 nerr++; 663 goto closefd; 664 } 665 (void)umask(oumask); 666 667 info = fdopen(fdinfo, "w"); 668 669 if (info == NULL) { 670 syslog(LOG_ERR, "fdopen failed: %m"); 671 nerr++; 672 goto closefd; 673 } 674 675 if (verbose) 676 printheader(stdout, &kdhl, device, bounds, status); 677 678 printheader(info, &kdhl, device, bounds, status); 679 fclose(info); 680 681 syslog(LOG_NOTICE, "writing %score to %s/%s", 682 compress ? "compressed " : "", savedir, corename); 683 684 if (istextdump) { 685 if (DoTextdumpFile(fd, dumpsize, lasthd, buf, device, 686 corename, fp) < 0) 687 goto closeall; 688 } else { 689 if (DoRegularFile(fd, dumpsize, buf, device, corename, fp) 690 < 0) 691 goto closeall; 692 } 693 if (verbose) 694 printf("\n"); 695 696 if (fclose(fp) < 0) { 697 syslog(LOG_ERR, "error on %s: %m", corename); 698 nerr++; 699 goto closefd; 700 } 701 702 symlinks_remove(); 703 if (symlink(infoname, "info.last") == -1) { 704 syslog(LOG_WARNING, "unable to create symlink %s/%s: %m", 705 savedir, "info.last"); 706 } 707 if (compress) { 708 snprintf(linkname, sizeof(linkname), "%s.last.gz", 709 istextdump ? "textdump.tar" : "vmcore"); 710 } else { 711 snprintf(linkname, sizeof(linkname), "%s.last", 712 istextdump ? "textdump.tar" : "vmcore"); 713 } 714 if (symlink(corename, linkname) == -1) { 715 syslog(LOG_WARNING, "unable to create symlink %s/%s: %m", 716 savedir, linkname); 717 } 718 719 nsaved++; 720 721 if (verbose) 722 printf("dump saved\n"); 723 724nuke: 725 if (!keep) { 726 if (verbose) 727 printf("clearing dump header\n"); 728 memcpy(kdhl.magic, KERNELDUMPMAGIC_CLEARED, sizeof kdhl.magic); 729 lseek(fd, lasthd, SEEK_SET); 730 error = write(fd, &kdhl, sizeof kdhl); 731 if (error != sizeof kdhl) 732 syslog(LOG_ERR, 733 "error while clearing the dump header: %m"); 734 } 735 close(fd); 736 return; 737 738closeall: 739 fclose(fp); 740 741closefd: 742 close(fd); 743} 744 745static void 746usage(void) 747{ 748 fprintf(stderr, "%s\n%s\n%s\n", 749 "usage: savecore -c [-v] [device ...]", 750 " savecore -C [-v] [device ...]", 751 " savecore [-fkvz] [-m maxdumps] [directory [device ...]]"); 752 exit(1); 753} 754 755int 756main(int argc, char **argv) 757{ 758 const char *savedir = "."; 759 struct fstab *fsp; 760 int i, ch, error; 761 762 checkfor = compress = clear = force = keep = verbose = 0; 763 nfound = nsaved = nerr = 0; 764 765 openlog("savecore", LOG_PERROR, LOG_DAEMON); 766 signal(SIGINFO, infohandler); 767 768 while ((ch = getopt(argc, argv, "Ccfkm:vz")) != -1) 769 switch(ch) { 770 case 'C': 771 checkfor = 1; 772 break; 773 case 'c': 774 clear = 1; 775 break; 776 case 'f': 777 force = 1; 778 break; 779 case 'k': 780 keep = 1; 781 break; 782 case 'm': 783 maxdumps = atoi(optarg); 784 if (maxdumps <= 0) { 785 syslog(LOG_ERR, "Invalid maxdump value"); 786 exit(1); 787 } 788 break; 789 case 'v': 790 verbose++; 791 break; 792 case 'z': 793 compress = 1; 794 break; 795 case '?': 796 default: 797 usage(); 798 } 799 if (checkfor && (clear || force || keep)) 800 usage(); 801 if (clear && (compress || keep)) 802 usage(); 803 if (maxdumps > 0 && (checkfor || clear)) 804 usage(); 805 argc -= optind; 806 argv += optind; 807 if (argc >= 1 && !checkfor && !clear) { 808 error = chdir(argv[0]); 809 if (error) { 810 syslog(LOG_ERR, "chdir(%s): %m", argv[0]); 811 exit(1); 812 } 813 savedir = argv[0]; 814 argc--; 815 argv++; 816 } 817 if (argc == 0) { 818 for (;;) { 819 fsp = getfsent(); 820 if (fsp == NULL) 821 break; 822 if (strcmp(fsp->fs_vfstype, "swap") && 823 strcmp(fsp->fs_vfstype, "dump")) 824 continue; 825 DoFile(savedir, fsp->fs_spec); 826 } 827 } else { 828 for (i = 0; i < argc; i++) 829 DoFile(savedir, argv[i]); 830 } 831 832 /* Emit minimal output. */ 833 if (nfound == 0) { 834 if (checkfor) { 835 printf("No dump exists\n"); 836 exit(1); 837 } 838 syslog(LOG_WARNING, "no dumps found"); 839 } 840 else if (nsaved == 0) { 841 if (nerr != 0) 842 syslog(LOG_WARNING, "unsaved dumps found but not saved"); 843 else 844 syslog(LOG_WARNING, "no unsaved dumps found"); 845 } 846 847 return (0); 848} 849 850static void 851infohandler(int sig __unused) 852{ 853 got_siginfo = 1; 854} 855