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 * 3. 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$"); 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 <stdbool.h> 79 #include <stdio.h> 80 #include <stdlib.h> 81 #include <string.h> 82 #include <syslog.h> 83 #include <time.h> 84 #include <unistd.h> 85 #include <libxo/xo.h> 86 87 /* The size of the buffer used for I/O. */ 88 #define BUFFERSIZE (1024*1024) 89 90 #define STATUS_BAD 0 91 #define STATUS_GOOD 1 92 #define STATUS_UNKNOWN 2 93 94 static int checkfor, compress, clear, force, keep, verbose; /* flags */ 95 static int nfound, nsaved, nerr; /* statistics */ 96 static int maxdumps; 97 98 extern FILE *zopen(const char *, const char *); 99 100 static sig_atomic_t got_siginfo; 101 static void infohandler(int); 102 103 static void 104 printheader(xo_handle_t *xo, const struct kerneldumpheader *h, const char *device, 105 int bounds, const int status) 106 { 107 uint64_t dumplen; 108 time_t t; 109 const char *stat_str; 110 111 xo_flush_h(xo); 112 xo_emit_h(xo, "{Lwc:Dump header from device}{:dump_device/%s}\n", device); 113 xo_emit_h(xo, "{P: }{Lwc:Architecture}{:architecture/%s}\n", h->architecture); 114 xo_emit_h(xo, "{P: }{Lwc:Architecture Version}{:architecture_version/%u}\n", dtoh32(h->architectureversion)); 115 dumplen = dtoh64(h->dumplength); 116 xo_emit_h(xo, "{P: }{Lwc:Dump Length}{:dump_length_bytes/%lld}\n", (long long)dumplen); 117 xo_emit_h(xo, "{P: }{Lwc:Blocksize}{:blocksize/%d}\n", dtoh32(h->blocksize)); 118 t = dtoh64(h->dumptime); 119 xo_emit_h(xo, "{P: }{Lwc:Dumptime}{:dumptime/%s}", ctime(&t)); 120 xo_emit_h(xo, "{P: }{Lwc:Hostname}{:hostname/%s}\n", h->hostname); 121 xo_emit_h(xo, "{P: }{Lwc:Magic}{:magic/%s}\n", h->magic); 122 xo_emit_h(xo, "{P: }{Lwc:Version String}{:version_string/%s}", h->versionstring); 123 xo_emit_h(xo, "{P: }{Lwc:Panic String}{:panic_string/%s}\n", h->panicstring); 124 xo_emit_h(xo, "{P: }{Lwc:Dump Parity}{:dump_parity/%u}\n", h->parity); 125 xo_emit_h(xo, "{P: }{Lwc:Bounds}{:bounds/%d}\n", bounds); 126 127 switch(status) { 128 case STATUS_BAD: 129 stat_str = "bad"; 130 break; 131 case STATUS_GOOD: 132 stat_str = "good"; 133 break; 134 default: 135 stat_str = "unknown"; 136 } 137 xo_emit_h(xo, "{P: }{Lwc:Dump Status}{:dump_status/%s}\n", stat_str); 138 xo_flush_h(xo); 139 } 140 141 static int 142 getbounds(void) { 143 FILE *fp; 144 char buf[6]; 145 int ret; 146 147 ret = 0; 148 149 if ((fp = fopen("bounds", "r")) == NULL) { 150 if (verbose) 151 printf("unable to open bounds file, using 0\n"); 152 return (ret); 153 } 154 155 if (fgets(buf, sizeof buf, fp) == NULL) { 156 if (feof(fp)) 157 syslog(LOG_WARNING, "bounds file is empty, using 0"); 158 else 159 syslog(LOG_WARNING, "bounds file: %s", strerror(errno)); 160 fclose(fp); 161 return (ret); 162 } 163 164 errno = 0; 165 ret = (int)strtol(buf, NULL, 10); 166 if (ret == 0 && (errno == EINVAL || errno == ERANGE)) 167 syslog(LOG_WARNING, "invalid value found in bounds, using 0"); 168 fclose(fp); 169 return (ret); 170 } 171 172 static void 173 writebounds(int bounds) { 174 FILE *fp; 175 176 if ((fp = fopen("bounds", "w")) == NULL) { 177 syslog(LOG_WARNING, "unable to write to bounds file: %m"); 178 return; 179 } 180 181 if (verbose) 182 printf("bounds number: %d\n", bounds); 183 184 fprintf(fp, "%d\n", bounds); 185 fclose(fp); 186 } 187 188 static bool 189 writekey(const char *keyname, uint8_t *dumpkey, uint32_t dumpkeysize) 190 { 191 int fd; 192 193 fd = open(keyname, O_WRONLY | O_CREAT | O_TRUNC, 0600); 194 if (fd == -1) { 195 syslog(LOG_ERR, "Unable to open %s to write the key: %m.", 196 keyname); 197 return (false); 198 } 199 200 if (write(fd, dumpkey, dumpkeysize) != (ssize_t)dumpkeysize) { 201 syslog(LOG_ERR, "Unable to write the key to %s: %m.", keyname); 202 close(fd); 203 return (false); 204 } 205 206 close(fd); 207 return (true); 208 } 209 210 static off_t 211 file_size(const char *path) 212 { 213 struct stat sb; 214 215 /* Ignore all errors, those file may not exists. */ 216 if (stat(path, &sb) == -1) 217 return (0); 218 return (sb.st_size); 219 } 220 221 static off_t 222 saved_dump_size(int bounds) 223 { 224 static char path[PATH_MAX]; 225 off_t dumpsize; 226 227 dumpsize = 0; 228 229 (void)snprintf(path, sizeof(path), "info.%d", bounds); 230 dumpsize += file_size(path); 231 (void)snprintf(path, sizeof(path), "vmcore.%d", bounds); 232 dumpsize += file_size(path); 233 (void)snprintf(path, sizeof(path), "vmcore.%d.gz", bounds); 234 dumpsize += file_size(path); 235 (void)snprintf(path, sizeof(path), "textdump.tar.%d", bounds); 236 dumpsize += file_size(path); 237 (void)snprintf(path, sizeof(path), "textdump.tar.%d.gz", bounds); 238 dumpsize += file_size(path); 239 240 return (dumpsize); 241 } 242 243 static void 244 saved_dump_remove(int bounds) 245 { 246 static char path[PATH_MAX]; 247 248 (void)snprintf(path, sizeof(path), "info.%d", bounds); 249 (void)unlink(path); 250 (void)snprintf(path, sizeof(path), "vmcore.%d", bounds); 251 (void)unlink(path); 252 (void)snprintf(path, sizeof(path), "vmcore.%d.gz", bounds); 253 (void)unlink(path); 254 (void)snprintf(path, sizeof(path), "textdump.tar.%d", bounds); 255 (void)unlink(path); 256 (void)snprintf(path, sizeof(path), "textdump.tar.%d.gz", bounds); 257 (void)unlink(path); 258 } 259 260 static void 261 symlinks_remove(void) 262 { 263 264 (void)unlink("info.last"); 265 (void)unlink("key.last"); 266 (void)unlink("vmcore.last"); 267 (void)unlink("vmcore.last.gz"); 268 (void)unlink("vmcore_encrypted.last"); 269 (void)unlink("vmcore_encrypted.last.gz"); 270 (void)unlink("textdump.tar.last"); 271 (void)unlink("textdump.tar.last.gz"); 272 } 273 274 /* 275 * Check that sufficient space is available on the disk that holds the 276 * save directory. 277 */ 278 static int 279 check_space(const char *savedir, off_t dumpsize, int bounds) 280 { 281 FILE *fp; 282 off_t available, minfree, spacefree, totfree, needed; 283 struct statfs fsbuf; 284 char buf[100]; 285 286 if (statfs(".", &fsbuf) < 0) { 287 syslog(LOG_ERR, "%s: %m", savedir); 288 exit(1); 289 } 290 spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024; 291 totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024; 292 293 if ((fp = fopen("minfree", "r")) == NULL) 294 minfree = 0; 295 else { 296 if (fgets(buf, sizeof(buf), fp) == NULL) 297 minfree = 0; 298 else { 299 char *endp; 300 301 errno = 0; 302 minfree = strtoll(buf, &endp, 10); 303 if (minfree == 0 && errno != 0) 304 minfree = -1; 305 else { 306 while (*endp != '\0' && isspace(*endp)) 307 endp++; 308 if (*endp != '\0' || minfree < 0) 309 minfree = -1; 310 } 311 if (minfree < 0) 312 syslog(LOG_WARNING, 313 "`minfree` didn't contain a valid size " 314 "(`%s`). Defaulting to 0", buf); 315 } 316 (void)fclose(fp); 317 } 318 319 available = minfree > 0 ? spacefree - minfree : totfree; 320 needed = dumpsize / 1024 + 2; /* 2 for info file */ 321 needed -= saved_dump_size(bounds); 322 if (available < needed) { 323 syslog(LOG_WARNING, 324 "no dump: not enough free space on device (need at least " 325 "%jdkB for dump; %jdkB available; %jdkB reserved)", 326 (intmax_t)needed, 327 (intmax_t)available + minfree, 328 (intmax_t)minfree); 329 return (0); 330 } 331 if (spacefree - needed < 0) 332 syslog(LOG_WARNING, 333 "dump performed, but free space threshold crossed"); 334 return (1); 335 } 336 337 static bool 338 compare_magic(const struct kerneldumpheader *kdh, const char *magic) 339 { 340 341 return (strncmp(kdh->magic, magic, sizeof(kdh->magic)) == 0); 342 } 343 344 #define BLOCKSIZE (1<<12) 345 #define BLOCKMASK (~(BLOCKSIZE-1)) 346 347 static int 348 DoRegularFile(int fd, bool isencrypted, off_t dumpsize, char *buf, 349 const char *device, const char *filename, FILE *fp) 350 { 351 int he, hs, nr, nw, wl; 352 off_t dmpcnt, origsize; 353 354 dmpcnt = 0; 355 origsize = dumpsize; 356 he = 0; 357 while (dumpsize > 0) { 358 wl = BUFFERSIZE; 359 if (wl > dumpsize) 360 wl = dumpsize; 361 nr = read(fd, buf, wl); 362 if (nr != wl) { 363 if (nr == 0) 364 syslog(LOG_WARNING, 365 "WARNING: EOF on dump device"); 366 else 367 syslog(LOG_ERR, "read error on %s: %m", device); 368 nerr++; 369 return (-1); 370 } 371 if (compress || isencrypted) { 372 nw = fwrite(buf, 1, wl, fp); 373 } else { 374 for (nw = 0; nw < nr; nw = he) { 375 /* find a contiguous block of zeroes */ 376 for (hs = nw; hs < nr; hs += BLOCKSIZE) { 377 for (he = hs; he < nr && buf[he] == 0; 378 ++he) 379 /* nothing */ ; 380 /* is the hole long enough to matter? */ 381 if (he >= hs + BLOCKSIZE) 382 break; 383 } 384 385 /* back down to a block boundary */ 386 he &= BLOCKMASK; 387 388 /* 389 * 1) Don't go beyond the end of the buffer. 390 * 2) If the end of the buffer is less than 391 * BLOCKSIZE bytes away, we're at the end 392 * of the file, so just grab what's left. 393 */ 394 if (hs + BLOCKSIZE > nr) 395 hs = he = nr; 396 397 /* 398 * At this point, we have a partial ordering: 399 * nw <= hs <= he <= nr 400 * If hs > nw, buf[nw..hs] contains non-zero data. 401 * If he > hs, buf[hs..he] is all zeroes. 402 */ 403 if (hs > nw) 404 if (fwrite(buf + nw, hs - nw, 1, fp) 405 != 1) 406 break; 407 if (he > hs) 408 if (fseeko(fp, he - hs, SEEK_CUR) == -1) 409 break; 410 } 411 } 412 if (nw != wl) { 413 syslog(LOG_ERR, 414 "write error on %s file: %m", filename); 415 syslog(LOG_WARNING, 416 "WARNING: vmcore may be incomplete"); 417 nerr++; 418 return (-1); 419 } 420 if (verbose) { 421 dmpcnt += wl; 422 printf("%llu\r", (unsigned long long)dmpcnt); 423 fflush(stdout); 424 } 425 dumpsize -= wl; 426 if (got_siginfo) { 427 printf("%s %.1lf%%\n", filename, (100.0 - (100.0 * 428 (double)dumpsize / (double)origsize))); 429 got_siginfo = 0; 430 } 431 } 432 return (0); 433 } 434 435 /* 436 * Specialized version of dump-reading logic for use with textdumps, which 437 * are written backwards from the end of the partition, and must be reversed 438 * before being written to the file. Textdumps are small, so do a bit less 439 * work to optimize/sparsify. 440 */ 441 static int 442 DoTextdumpFile(int fd, off_t dumpsize, off_t lasthd, char *buf, 443 const char *device, const char *filename, FILE *fp) 444 { 445 int nr, nw, wl; 446 off_t dmpcnt, totsize; 447 448 totsize = dumpsize; 449 dmpcnt = 0; 450 wl = 512; 451 if ((dumpsize % wl) != 0) { 452 syslog(LOG_ERR, "textdump uneven multiple of 512 on %s", 453 device); 454 nerr++; 455 return (-1); 456 } 457 while (dumpsize > 0) { 458 nr = pread(fd, buf, wl, lasthd - (totsize - dumpsize) - wl); 459 if (nr != wl) { 460 if (nr == 0) 461 syslog(LOG_WARNING, 462 "WARNING: EOF on dump device"); 463 else 464 syslog(LOG_ERR, "read error on %s: %m", device); 465 nerr++; 466 return (-1); 467 } 468 nw = fwrite(buf, 1, wl, fp); 469 if (nw != wl) { 470 syslog(LOG_ERR, 471 "write error on %s file: %m", filename); 472 syslog(LOG_WARNING, 473 "WARNING: textdump may be incomplete"); 474 nerr++; 475 return (-1); 476 } 477 if (verbose) { 478 dmpcnt += wl; 479 printf("%llu\r", (unsigned long long)dmpcnt); 480 fflush(stdout); 481 } 482 dumpsize -= wl; 483 } 484 return (0); 485 } 486 487 static void 488 DoFile(const char *savedir, const char *device) 489 { 490 xo_handle_t *xostdout, *xoinfo; 491 static char infoname[PATH_MAX], corename[PATH_MAX], linkname[PATH_MAX]; 492 static char keyname[PATH_MAX]; 493 static char *buf = NULL; 494 char *temp = NULL; 495 struct kerneldumpheader kdhf, kdhl; 496 uint8_t *dumpkey; 497 off_t mediasize, dumpsize, firsthd, lasthd; 498 FILE *info, *fp; 499 mode_t oumask; 500 int fd, fdinfo, error; 501 int bounds, status; 502 u_int sectorsize, xostyle; 503 int istextdump; 504 uint32_t dumpkeysize; 505 bool isencrypted, ret; 506 507 bounds = getbounds(); 508 dumpkey = NULL; 509 mediasize = 0; 510 status = STATUS_UNKNOWN; 511 512 xostdout = xo_create_to_file(stdout, XO_STYLE_TEXT, 0); 513 if (xostdout == NULL) { 514 syslog(LOG_ERR, "%s: %m", infoname); 515 return; 516 } 517 518 if (maxdumps > 0 && bounds == maxdumps) 519 bounds = 0; 520 521 if (buf == NULL) { 522 buf = malloc(BUFFERSIZE); 523 if (buf == NULL) { 524 syslog(LOG_ERR, "%m"); 525 return; 526 } 527 } 528 529 if (verbose) 530 printf("checking for kernel dump on device %s\n", device); 531 532 fd = open(device, (checkfor || keep) ? O_RDONLY : O_RDWR); 533 if (fd < 0) { 534 syslog(LOG_ERR, "%s: %m", device); 535 return; 536 } 537 538 error = ioctl(fd, DIOCGMEDIASIZE, &mediasize); 539 if (!error) 540 error = ioctl(fd, DIOCGSECTORSIZE, §orsize); 541 if (error) { 542 syslog(LOG_ERR, 543 "couldn't find media and/or sector size of %s: %m", device); 544 goto closefd; 545 } 546 547 if (verbose) { 548 printf("mediasize = %lld bytes\n", (long long)mediasize); 549 printf("sectorsize = %u bytes\n", sectorsize); 550 } 551 552 if (sectorsize < sizeof(kdhl)) { 553 syslog(LOG_ERR, 554 "Sector size is less the kernel dump header %zu", 555 sizeof(kdhl)); 556 goto closefd; 557 } 558 559 lasthd = mediasize - sectorsize; 560 temp = malloc(sectorsize); 561 if (temp == NULL) { 562 syslog(LOG_ERR, "%m"); 563 goto closefd; 564 } 565 if (lseek(fd, lasthd, SEEK_SET) != lasthd || 566 read(fd, temp, sectorsize) != (ssize_t)sectorsize) { 567 syslog(LOG_ERR, 568 "error reading last dump header at offset %lld in %s: %m", 569 (long long)lasthd, device); 570 goto closefd; 571 } 572 memcpy(&kdhl, temp, sizeof(kdhl)); 573 istextdump = 0; 574 if (compare_magic(&kdhl, TEXTDUMPMAGIC)) { 575 if (verbose) 576 printf("textdump magic on last dump header on %s\n", 577 device); 578 istextdump = 1; 579 if (dtoh32(kdhl.version) != KERNELDUMP_TEXT_VERSION) { 580 syslog(LOG_ERR, 581 "unknown version (%d) in last dump header on %s", 582 dtoh32(kdhl.version), device); 583 584 status = STATUS_BAD; 585 if (force == 0) 586 goto closefd; 587 } 588 } else if (compare_magic(&kdhl, KERNELDUMPMAGIC)) { 589 if (dtoh32(kdhl.version) != KERNELDUMPVERSION) { 590 syslog(LOG_ERR, 591 "unknown version (%d) in last dump header on %s", 592 dtoh32(kdhl.version), device); 593 594 status = STATUS_BAD; 595 if (force == 0) 596 goto closefd; 597 } 598 } else { 599 if (verbose) 600 printf("magic mismatch on last dump header on %s\n", 601 device); 602 603 status = STATUS_BAD; 604 if (force == 0) 605 goto closefd; 606 607 if (compare_magic(&kdhl, KERNELDUMPMAGIC_CLEARED)) { 608 if (verbose) 609 printf("forcing magic on %s\n", device); 610 memcpy(kdhl.magic, KERNELDUMPMAGIC, 611 sizeof kdhl.magic); 612 } else { 613 syslog(LOG_ERR, "unable to force dump - bad magic"); 614 goto closefd; 615 } 616 if (dtoh32(kdhl.version) != KERNELDUMPVERSION) { 617 syslog(LOG_ERR, 618 "unknown version (%d) in last dump header on %s", 619 dtoh32(kdhl.version), device); 620 621 status = STATUS_BAD; 622 if (force == 0) 623 goto closefd; 624 } 625 } 626 627 nfound++; 628 if (clear) 629 goto nuke; 630 631 if (kerneldump_parity(&kdhl)) { 632 syslog(LOG_ERR, 633 "parity error on last dump header on %s", device); 634 nerr++; 635 status = STATUS_BAD; 636 if (force == 0) 637 goto closefd; 638 } 639 dumpsize = dtoh64(kdhl.dumplength); 640 dumpkeysize = dtoh32(kdhl.dumpkeysize); 641 firsthd = lasthd - dumpsize - sectorsize - dumpkeysize; 642 if (lseek(fd, firsthd, SEEK_SET) != firsthd || 643 read(fd, temp, sectorsize) != (ssize_t)sectorsize) { 644 syslog(LOG_ERR, 645 "error reading first dump header at offset %lld in %s: %m", 646 (long long)firsthd, device); 647 nerr++; 648 goto closefd; 649 } 650 memcpy(&kdhf, temp, sizeof(kdhf)); 651 652 if (verbose >= 2) { 653 printf("First dump headers:\n"); 654 printheader(xostdout, &kdhf, device, bounds, -1); 655 656 printf("\nLast dump headers:\n"); 657 printheader(xostdout, &kdhl, device, bounds, -1); 658 printf("\n"); 659 } 660 661 if (memcmp(&kdhl, &kdhf, sizeof(kdhl))) { 662 syslog(LOG_ERR, 663 "first and last dump headers disagree on %s", device); 664 nerr++; 665 status = STATUS_BAD; 666 if (force == 0) 667 goto closefd; 668 } else { 669 status = STATUS_GOOD; 670 } 671 672 if (checkfor) { 673 printf("A dump exists on %s\n", device); 674 close(fd); 675 exit(0); 676 } 677 678 if (kdhl.panicstring[0] != '\0') 679 syslog(LOG_ALERT, "reboot after panic: %.*s", 680 (int)sizeof(kdhl.panicstring), kdhl.panicstring); 681 else 682 syslog(LOG_ALERT, "reboot"); 683 684 if (verbose) 685 printf("Checking for available free space\n"); 686 687 if (!check_space(savedir, dumpsize, bounds)) { 688 nerr++; 689 goto closefd; 690 } 691 692 writebounds(bounds + 1); 693 694 saved_dump_remove(bounds); 695 696 snprintf(infoname, sizeof(infoname), "info.%d", bounds); 697 698 /* 699 * Create or overwrite any existing dump header files. 700 */ 701 fdinfo = open(infoname, O_WRONLY | O_CREAT | O_TRUNC, 0600); 702 if (fdinfo < 0) { 703 syslog(LOG_ERR, "%s: %m", infoname); 704 nerr++; 705 goto closefd; 706 } 707 708 oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to the core file.*/ 709 isencrypted = (dumpkeysize > 0); 710 if (compress) { 711 snprintf(corename, sizeof(corename), "%s.%d.gz", 712 istextdump ? "textdump.tar" : 713 (isencrypted ? "vmcore_encrypted" : "vmcore"), bounds); 714 fp = zopen(corename, "w"); 715 } else { 716 snprintf(corename, sizeof(corename), "%s.%d", 717 istextdump ? "textdump.tar" : 718 (isencrypted ? "vmcore_encrypted" : "vmcore"), bounds); 719 fp = fopen(corename, "w"); 720 } 721 if (fp == NULL) { 722 syslog(LOG_ERR, "%s: %m", corename); 723 close(fdinfo); 724 nerr++; 725 goto closefd; 726 } 727 (void)umask(oumask); 728 729 info = fdopen(fdinfo, "w"); 730 731 if (info == NULL) { 732 syslog(LOG_ERR, "fdopen failed: %m"); 733 nerr++; 734 goto closeall; 735 } 736 737 xostyle = xo_get_style(NULL); 738 xoinfo = xo_create_to_file(info, xostyle, 0); 739 if (xoinfo == NULL) { 740 syslog(LOG_ERR, "%s: %m", infoname); 741 nerr++; 742 goto closeall; 743 } 744 xo_open_container_h(xoinfo, "crashdump"); 745 746 if (verbose) 747 printheader(xostdout, &kdhl, device, bounds, status); 748 749 printheader(xoinfo, &kdhl, device, bounds, status); 750 xo_close_container_h(xoinfo, "crashdump"); 751 xo_flush_h(xoinfo); 752 xo_finish_h(xoinfo); 753 fclose(info); 754 755 if (isencrypted) { 756 dumpkey = calloc(1, dumpkeysize); 757 if (dumpkey == NULL) { 758 syslog(LOG_ERR, "Unable to allocate kernel dump key."); 759 nerr++; 760 goto closeall; 761 } 762 763 if (read(fd, dumpkey, dumpkeysize) != (ssize_t)dumpkeysize) { 764 syslog(LOG_ERR, "Unable to read kernel dump key: %m."); 765 nerr++; 766 goto closeall; 767 } 768 769 snprintf(keyname, sizeof(keyname), "key.%d", bounds); 770 ret = writekey(keyname, dumpkey, dumpkeysize); 771 explicit_bzero(dumpkey, dumpkeysize); 772 if (!ret) { 773 nerr++; 774 goto closeall; 775 } 776 } 777 778 syslog(LOG_NOTICE, "writing %s%score to %s/%s", 779 isencrypted ? "encrypted " : "", compress ? "compressed " : "", 780 savedir, corename); 781 782 if (istextdump) { 783 if (DoTextdumpFile(fd, dumpsize, lasthd, buf, device, 784 corename, fp) < 0) 785 goto closeall; 786 } else { 787 if (DoRegularFile(fd, isencrypted, dumpsize, buf, device, 788 corename, fp) < 0) { 789 goto closeall; 790 } 791 } 792 if (verbose) 793 printf("\n"); 794 795 if (fclose(fp) < 0) { 796 syslog(LOG_ERR, "error on %s: %m", corename); 797 nerr++; 798 goto closefd; 799 } 800 801 symlinks_remove(); 802 if (symlink(infoname, "info.last") == -1) { 803 syslog(LOG_WARNING, "unable to create symlink %s/%s: %m", 804 savedir, "info.last"); 805 } 806 if (isencrypted) { 807 if (symlink(keyname, "key.last") == -1) { 808 syslog(LOG_WARNING, 809 "unable to create symlink %s/%s: %m", savedir, 810 "key.last"); 811 } 812 } 813 if (compress) { 814 snprintf(linkname, sizeof(linkname), "%s.last.gz", 815 istextdump ? "textdump.tar" : 816 (isencrypted ? "vmcore_encrypted" : "vmcore")); 817 } else { 818 snprintf(linkname, sizeof(linkname), "%s.last", 819 istextdump ? "textdump.tar" : 820 (isencrypted ? "vmcore_encrypted" : "vmcore")); 821 } 822 if (symlink(corename, linkname) == -1) { 823 syslog(LOG_WARNING, "unable to create symlink %s/%s: %m", 824 savedir, linkname); 825 } 826 827 nsaved++; 828 829 if (verbose) 830 printf("dump saved\n"); 831 832 nuke: 833 if (!keep) { 834 if (verbose) 835 printf("clearing dump header\n"); 836 memcpy(kdhl.magic, KERNELDUMPMAGIC_CLEARED, sizeof(kdhl.magic)); 837 memcpy(temp, &kdhl, sizeof(kdhl)); 838 if (lseek(fd, lasthd, SEEK_SET) != lasthd || 839 write(fd, temp, sectorsize) != (ssize_t)sectorsize) 840 syslog(LOG_ERR, 841 "error while clearing the dump header: %m"); 842 } 843 xo_close_container_h(xostdout, "crashdump"); 844 xo_finish_h(xostdout); 845 free(dumpkey); 846 free(temp); 847 close(fd); 848 return; 849 850 closeall: 851 fclose(fp); 852 853 closefd: 854 free(dumpkey); 855 free(temp); 856 close(fd); 857 } 858 859 static void 860 usage(void) 861 { 862 xo_error("%s\n%s\n%s\n", 863 "usage: savecore -c [-v] [device ...]", 864 " savecore -C [-v] [device ...]", 865 " savecore [-fkvz] [-m maxdumps] [directory [device ...]]"); 866 exit(1); 867 } 868 869 int 870 main(int argc, char **argv) 871 { 872 const char *savedir = "."; 873 struct fstab *fsp; 874 int i, ch, error; 875 876 checkfor = compress = clear = force = keep = verbose = 0; 877 nfound = nsaved = nerr = 0; 878 879 openlog("savecore", LOG_PERROR, LOG_DAEMON); 880 signal(SIGINFO, infohandler); 881 882 argc = xo_parse_args(argc, argv); 883 if (argc < 0) 884 exit(1); 885 886 while ((ch = getopt(argc, argv, "Ccfkm:vz")) != -1) 887 switch(ch) { 888 case 'C': 889 checkfor = 1; 890 break; 891 case 'c': 892 clear = 1; 893 break; 894 case 'f': 895 force = 1; 896 break; 897 case 'k': 898 keep = 1; 899 break; 900 case 'm': 901 maxdumps = atoi(optarg); 902 if (maxdumps <= 0) { 903 syslog(LOG_ERR, "Invalid maxdump value"); 904 exit(1); 905 } 906 break; 907 case 'v': 908 verbose++; 909 break; 910 case 'z': 911 compress = 1; 912 break; 913 case '?': 914 default: 915 usage(); 916 } 917 if (checkfor && (clear || force || keep)) 918 usage(); 919 if (clear && (compress || keep)) 920 usage(); 921 if (maxdumps > 0 && (checkfor || clear)) 922 usage(); 923 argc -= optind; 924 argv += optind; 925 if (argc >= 1 && !checkfor && !clear) { 926 error = chdir(argv[0]); 927 if (error) { 928 syslog(LOG_ERR, "chdir(%s): %m", argv[0]); 929 exit(1); 930 } 931 savedir = argv[0]; 932 argc--; 933 argv++; 934 } 935 if (argc == 0) { 936 for (;;) { 937 fsp = getfsent(); 938 if (fsp == NULL) 939 break; 940 if (strcmp(fsp->fs_vfstype, "swap") && 941 strcmp(fsp->fs_vfstype, "dump")) 942 continue; 943 DoFile(savedir, fsp->fs_spec); 944 } 945 endfsent(); 946 } else { 947 for (i = 0; i < argc; i++) 948 DoFile(savedir, argv[i]); 949 } 950 951 /* Emit minimal output. */ 952 if (nfound == 0) { 953 if (checkfor) { 954 if (verbose) 955 printf("No dump exists\n"); 956 exit(1); 957 } 958 if (verbose) 959 syslog(LOG_WARNING, "no dumps found"); 960 } else if (nsaved == 0) { 961 if (nerr != 0) { 962 if (verbose) 963 syslog(LOG_WARNING, "unsaved dumps found but not saved"); 964 exit(1); 965 } else if (verbose) 966 syslog(LOG_WARNING, "no unsaved dumps found"); 967 } 968 969 return (0); 970 } 971 972 static void 973 infohandler(int sig __unused) 974 { 975 got_siginfo = 1; 976 } 977