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 #define BLOCKSIZE (1<<12) 338 #define BLOCKMASK (~(BLOCKSIZE-1)) 339 340 static int 341 DoRegularFile(int fd, bool isencrypted, off_t dumpsize, char *buf, 342 const char *device, const char *filename, FILE *fp) 343 { 344 int he, hs, nr, nw, wl; 345 off_t dmpcnt, origsize; 346 347 dmpcnt = 0; 348 origsize = dumpsize; 349 he = 0; 350 while (dumpsize > 0) { 351 wl = BUFFERSIZE; 352 if (wl > dumpsize) 353 wl = dumpsize; 354 nr = read(fd, buf, wl); 355 if (nr != wl) { 356 if (nr == 0) 357 syslog(LOG_WARNING, 358 "WARNING: EOF on dump device"); 359 else 360 syslog(LOG_ERR, "read error on %s: %m", device); 361 nerr++; 362 return (-1); 363 } 364 if (compress || isencrypted) { 365 nw = fwrite(buf, 1, wl, fp); 366 } else { 367 for (nw = 0; nw < nr; nw = he) { 368 /* find a contiguous block of zeroes */ 369 for (hs = nw; hs < nr; hs += BLOCKSIZE) { 370 for (he = hs; he < nr && buf[he] == 0; 371 ++he) 372 /* nothing */ ; 373 /* is the hole long enough to matter? */ 374 if (he >= hs + BLOCKSIZE) 375 break; 376 } 377 378 /* back down to a block boundary */ 379 he &= BLOCKMASK; 380 381 /* 382 * 1) Don't go beyond the end of the buffer. 383 * 2) If the end of the buffer is less than 384 * BLOCKSIZE bytes away, we're at the end 385 * of the file, so just grab what's left. 386 */ 387 if (hs + BLOCKSIZE > nr) 388 hs = he = nr; 389 390 /* 391 * At this point, we have a partial ordering: 392 * nw <= hs <= he <= nr 393 * If hs > nw, buf[nw..hs] contains non-zero data. 394 * If he > hs, buf[hs..he] is all zeroes. 395 */ 396 if (hs > nw) 397 if (fwrite(buf + nw, hs - nw, 1, fp) 398 != 1) 399 break; 400 if (he > hs) 401 if (fseeko(fp, he - hs, SEEK_CUR) == -1) 402 break; 403 } 404 } 405 if (nw != wl) { 406 syslog(LOG_ERR, 407 "write error on %s file: %m", filename); 408 syslog(LOG_WARNING, 409 "WARNING: vmcore may be incomplete"); 410 nerr++; 411 return (-1); 412 } 413 if (verbose) { 414 dmpcnt += wl; 415 printf("%llu\r", (unsigned long long)dmpcnt); 416 fflush(stdout); 417 } 418 dumpsize -= wl; 419 if (got_siginfo) { 420 printf("%s %.1lf%%\n", filename, (100.0 - (100.0 * 421 (double)dumpsize / (double)origsize))); 422 got_siginfo = 0; 423 } 424 } 425 return (0); 426 } 427 428 /* 429 * Specialized version of dump-reading logic for use with textdumps, which 430 * are written backwards from the end of the partition, and must be reversed 431 * before being written to the file. Textdumps are small, so do a bit less 432 * work to optimize/sparsify. 433 */ 434 static int 435 DoTextdumpFile(int fd, off_t dumpsize, off_t lasthd, char *buf, 436 const char *device, const char *filename, FILE *fp) 437 { 438 int nr, nw, wl; 439 off_t dmpcnt, totsize; 440 441 totsize = dumpsize; 442 dmpcnt = 0; 443 wl = 512; 444 if ((dumpsize % wl) != 0) { 445 syslog(LOG_ERR, "textdump uneven multiple of 512 on %s", 446 device); 447 nerr++; 448 return (-1); 449 } 450 while (dumpsize > 0) { 451 nr = pread(fd, buf, wl, lasthd - (totsize - dumpsize) - wl); 452 if (nr != wl) { 453 if (nr == 0) 454 syslog(LOG_WARNING, 455 "WARNING: EOF on dump device"); 456 else 457 syslog(LOG_ERR, "read error on %s: %m", device); 458 nerr++; 459 return (-1); 460 } 461 nw = fwrite(buf, 1, wl, fp); 462 if (nw != wl) { 463 syslog(LOG_ERR, 464 "write error on %s file: %m", filename); 465 syslog(LOG_WARNING, 466 "WARNING: textdump may be incomplete"); 467 nerr++; 468 return (-1); 469 } 470 if (verbose) { 471 dmpcnt += wl; 472 printf("%llu\r", (unsigned long long)dmpcnt); 473 fflush(stdout); 474 } 475 dumpsize -= wl; 476 } 477 return (0); 478 } 479 480 static void 481 DoFile(const char *savedir, const char *device) 482 { 483 xo_handle_t *xostdout, *xoinfo; 484 static char infoname[PATH_MAX], corename[PATH_MAX], linkname[PATH_MAX]; 485 static char keyname[PATH_MAX]; 486 static char *buf = NULL; 487 char *temp = NULL; 488 struct kerneldumpheader kdhf, kdhl; 489 uint8_t *dumpkey; 490 off_t mediasize, dumpsize, firsthd, lasthd; 491 FILE *info, *fp; 492 mode_t oumask; 493 int fd, fdinfo, error; 494 int bounds, status; 495 u_int sectorsize, xostyle; 496 int istextdump; 497 uint32_t dumpkeysize; 498 bool isencrypted, ret; 499 500 bounds = getbounds(); 501 dumpkey = NULL; 502 mediasize = 0; 503 status = STATUS_UNKNOWN; 504 505 xostdout = xo_create_to_file(stdout, XO_STYLE_TEXT, 0); 506 if (xostdout == NULL) { 507 syslog(LOG_ERR, "%s: %m", infoname); 508 return; 509 } 510 511 if (maxdumps > 0 && bounds == maxdumps) 512 bounds = 0; 513 514 if (buf == NULL) { 515 buf = malloc(BUFFERSIZE); 516 if (buf == NULL) { 517 syslog(LOG_ERR, "%m"); 518 return; 519 } 520 } 521 522 if (verbose) 523 printf("checking for kernel dump on device %s\n", device); 524 525 fd = open(device, (checkfor || keep) ? O_RDONLY : O_RDWR); 526 if (fd < 0) { 527 syslog(LOG_ERR, "%s: %m", device); 528 return; 529 } 530 531 error = ioctl(fd, DIOCGMEDIASIZE, &mediasize); 532 if (!error) 533 error = ioctl(fd, DIOCGSECTORSIZE, §orsize); 534 if (error) { 535 syslog(LOG_ERR, 536 "couldn't find media and/or sector size of %s: %m", device); 537 goto closefd; 538 } 539 540 if (verbose) { 541 printf("mediasize = %lld bytes\n", (long long)mediasize); 542 printf("sectorsize = %u bytes\n", sectorsize); 543 } 544 545 if (sectorsize < sizeof(kdhl)) { 546 syslog(LOG_ERR, 547 "Sector size is less the kernel dump header %zu", 548 sizeof(kdhl)); 549 goto closefd; 550 } 551 552 lasthd = mediasize - sectorsize; 553 temp = malloc(sectorsize); 554 if (temp == NULL) { 555 syslog(LOG_ERR, "%m"); 556 goto closefd; 557 } 558 if (lseek(fd, lasthd, SEEK_SET) != lasthd || 559 read(fd, temp, sectorsize) != (ssize_t)sectorsize) { 560 syslog(LOG_ERR, 561 "error reading last dump header at offset %lld in %s: %m", 562 (long long)lasthd, device); 563 goto closefd; 564 } 565 memcpy(&kdhl, temp, sizeof(kdhl)); 566 istextdump = 0; 567 if (strncmp(kdhl.magic, TEXTDUMPMAGIC, sizeof kdhl) == 0) { 568 if (verbose) 569 printf("textdump magic on last dump header on %s\n", 570 device); 571 istextdump = 1; 572 if (dtoh32(kdhl.version) != KERNELDUMP_TEXT_VERSION) { 573 syslog(LOG_ERR, 574 "unknown version (%d) in last dump header on %s", 575 dtoh32(kdhl.version), device); 576 577 status = STATUS_BAD; 578 if (force == 0) 579 goto closefd; 580 } 581 } else if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic) == 582 0) { 583 if (dtoh32(kdhl.version) != KERNELDUMPVERSION) { 584 syslog(LOG_ERR, 585 "unknown version (%d) in last dump header on %s", 586 dtoh32(kdhl.version), device); 587 588 status = STATUS_BAD; 589 if (force == 0) 590 goto closefd; 591 } 592 } else { 593 if (verbose) 594 printf("magic mismatch on last dump header on %s\n", 595 device); 596 597 status = STATUS_BAD; 598 if (force == 0) 599 goto closefd; 600 601 if (memcmp(kdhl.magic, KERNELDUMPMAGIC_CLEARED, 602 sizeof kdhl.magic) == 0) { 603 if (verbose) 604 printf("forcing magic on %s\n", device); 605 memcpy(kdhl.magic, KERNELDUMPMAGIC, 606 sizeof kdhl.magic); 607 } else { 608 syslog(LOG_ERR, "unable to force dump - bad magic"); 609 goto closefd; 610 } 611 if (dtoh32(kdhl.version) != KERNELDUMPVERSION) { 612 syslog(LOG_ERR, 613 "unknown version (%d) in last dump header on %s", 614 dtoh32(kdhl.version), device); 615 616 status = STATUS_BAD; 617 if (force == 0) 618 goto closefd; 619 } 620 } 621 622 nfound++; 623 if (clear) 624 goto nuke; 625 626 if (kerneldump_parity(&kdhl)) { 627 syslog(LOG_ERR, 628 "parity error on last dump header on %s", device); 629 nerr++; 630 status = STATUS_BAD; 631 if (force == 0) 632 goto closefd; 633 } 634 dumpsize = dtoh64(kdhl.dumplength); 635 dumpkeysize = dtoh32(kdhl.dumpkeysize); 636 firsthd = lasthd - dumpsize - sectorsize - dumpkeysize; 637 if (lseek(fd, firsthd, SEEK_SET) != firsthd || 638 read(fd, temp, sectorsize) != (ssize_t)sectorsize) { 639 syslog(LOG_ERR, 640 "error reading first dump header at offset %lld in %s: %m", 641 (long long)firsthd, device); 642 nerr++; 643 goto closefd; 644 } 645 memcpy(&kdhf, temp, sizeof(kdhf)); 646 647 if (verbose >= 2) { 648 printf("First dump headers:\n"); 649 printheader(xostdout, &kdhf, device, bounds, -1); 650 651 printf("\nLast dump headers:\n"); 652 printheader(xostdout, &kdhl, device, bounds, -1); 653 printf("\n"); 654 } 655 656 if (memcmp(&kdhl, &kdhf, sizeof(kdhl))) { 657 syslog(LOG_ERR, 658 "first and last dump headers disagree on %s", device); 659 nerr++; 660 status = STATUS_BAD; 661 if (force == 0) 662 goto closefd; 663 } else { 664 status = STATUS_GOOD; 665 } 666 667 if (checkfor) { 668 printf("A dump exists on %s\n", device); 669 close(fd); 670 exit(0); 671 } 672 673 if (kdhl.panicstring[0] != '\0') 674 syslog(LOG_ALERT, "reboot after panic: %.*s", 675 (int)sizeof(kdhl.panicstring), kdhl.panicstring); 676 else 677 syslog(LOG_ALERT, "reboot"); 678 679 if (verbose) 680 printf("Checking for available free space\n"); 681 682 if (!check_space(savedir, dumpsize, bounds)) { 683 nerr++; 684 goto closefd; 685 } 686 687 writebounds(bounds + 1); 688 689 saved_dump_remove(bounds); 690 691 snprintf(infoname, sizeof(infoname), "info.%d", bounds); 692 693 /* 694 * Create or overwrite any existing dump header files. 695 */ 696 fdinfo = open(infoname, O_WRONLY | O_CREAT | O_TRUNC, 0600); 697 if (fdinfo < 0) { 698 syslog(LOG_ERR, "%s: %m", infoname); 699 nerr++; 700 goto closefd; 701 } 702 703 oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to the core file.*/ 704 isencrypted = (dumpkeysize > 0); 705 if (compress) { 706 snprintf(corename, sizeof(corename), "%s.%d.gz", 707 istextdump ? "textdump.tar" : 708 (isencrypted ? "vmcore_encrypted" : "vmcore"), bounds); 709 fp = zopen(corename, "w"); 710 } else { 711 snprintf(corename, sizeof(corename), "%s.%d", 712 istextdump ? "textdump.tar" : 713 (isencrypted ? "vmcore_encrypted" : "vmcore"), bounds); 714 fp = fopen(corename, "w"); 715 } 716 if (fp == NULL) { 717 syslog(LOG_ERR, "%s: %m", corename); 718 close(fdinfo); 719 nerr++; 720 goto closefd; 721 } 722 (void)umask(oumask); 723 724 info = fdopen(fdinfo, "w"); 725 726 if (info == NULL) { 727 syslog(LOG_ERR, "fdopen failed: %m"); 728 nerr++; 729 goto closeall; 730 } 731 732 xostyle = xo_get_style(NULL); 733 xoinfo = xo_create_to_file(info, xostyle, 0); 734 if (xoinfo == NULL) { 735 syslog(LOG_ERR, "%s: %m", infoname); 736 nerr++; 737 goto closeall; 738 } 739 xo_open_container_h(xoinfo, "crashdump"); 740 741 if (verbose) 742 printheader(xostdout, &kdhl, device, bounds, status); 743 744 printheader(xoinfo, &kdhl, device, bounds, status); 745 xo_close_container_h(xoinfo, "crashdump"); 746 xo_flush_h(xoinfo); 747 xo_finish_h(xoinfo); 748 fclose(info); 749 750 if (isencrypted) { 751 dumpkey = calloc(1, dumpkeysize); 752 if (dumpkey == NULL) { 753 syslog(LOG_ERR, "Unable to allocate kernel dump key."); 754 nerr++; 755 goto closeall; 756 } 757 758 if (read(fd, dumpkey, dumpkeysize) != (ssize_t)dumpkeysize) { 759 syslog(LOG_ERR, "Unable to read kernel dump key: %m."); 760 nerr++; 761 goto closeall; 762 } 763 764 snprintf(keyname, sizeof(keyname), "key.%d", bounds); 765 ret = writekey(keyname, dumpkey, dumpkeysize); 766 explicit_bzero(dumpkey, dumpkeysize); 767 if (!ret) { 768 nerr++; 769 goto closeall; 770 } 771 } 772 773 syslog(LOG_NOTICE, "writing %s%score to %s/%s", 774 isencrypted ? "encrypted " : "", compress ? "compressed " : "", 775 savedir, corename); 776 777 if (istextdump) { 778 if (DoTextdumpFile(fd, dumpsize, lasthd, buf, device, 779 corename, fp) < 0) 780 goto closeall; 781 } else { 782 if (DoRegularFile(fd, isencrypted, dumpsize, buf, device, 783 corename, fp) < 0) { 784 goto closeall; 785 } 786 } 787 if (verbose) 788 printf("\n"); 789 790 if (fclose(fp) < 0) { 791 syslog(LOG_ERR, "error on %s: %m", corename); 792 nerr++; 793 goto closefd; 794 } 795 796 symlinks_remove(); 797 if (symlink(infoname, "info.last") == -1) { 798 syslog(LOG_WARNING, "unable to create symlink %s/%s: %m", 799 savedir, "info.last"); 800 } 801 if (isencrypted) { 802 if (symlink(keyname, "key.last") == -1) { 803 syslog(LOG_WARNING, 804 "unable to create symlink %s/%s: %m", savedir, 805 "key.last"); 806 } 807 } 808 if (compress) { 809 snprintf(linkname, sizeof(linkname), "%s.last.gz", 810 istextdump ? "textdump.tar" : 811 (isencrypted ? "vmcore_encrypted" : "vmcore")); 812 } else { 813 snprintf(linkname, sizeof(linkname), "%s.last", 814 istextdump ? "textdump.tar" : 815 (isencrypted ? "vmcore_encrypted" : "vmcore")); 816 } 817 if (symlink(corename, linkname) == -1) { 818 syslog(LOG_WARNING, "unable to create symlink %s/%s: %m", 819 savedir, linkname); 820 } 821 822 nsaved++; 823 824 if (verbose) 825 printf("dump saved\n"); 826 827 nuke: 828 if (!keep) { 829 if (verbose) 830 printf("clearing dump header\n"); 831 memcpy(kdhl.magic, KERNELDUMPMAGIC_CLEARED, sizeof(kdhl.magic)); 832 memcpy(temp, &kdhl, sizeof(kdhl)); 833 if (lseek(fd, lasthd, SEEK_SET) != lasthd || 834 write(fd, temp, sectorsize) != (ssize_t)sectorsize) 835 syslog(LOG_ERR, 836 "error while clearing the dump header: %m"); 837 } 838 xo_close_container_h(xostdout, "crashdump"); 839 xo_finish_h(xostdout); 840 free(dumpkey); 841 free(temp); 842 close(fd); 843 return; 844 845 closeall: 846 fclose(fp); 847 848 closefd: 849 free(dumpkey); 850 free(temp); 851 close(fd); 852 } 853 854 static void 855 usage(void) 856 { 857 xo_error("%s\n%s\n%s\n", 858 "usage: savecore -c [-v] [device ...]", 859 " savecore -C [-v] [device ...]", 860 " savecore [-fkvz] [-m maxdumps] [directory [device ...]]"); 861 exit(1); 862 } 863 864 int 865 main(int argc, char **argv) 866 { 867 const char *savedir = "."; 868 struct fstab *fsp; 869 int i, ch, error; 870 871 checkfor = compress = clear = force = keep = verbose = 0; 872 nfound = nsaved = nerr = 0; 873 874 openlog("savecore", LOG_PERROR, LOG_DAEMON); 875 signal(SIGINFO, infohandler); 876 877 argc = xo_parse_args(argc, argv); 878 if (argc < 0) 879 exit(1); 880 881 while ((ch = getopt(argc, argv, "Ccfkm:vz")) != -1) 882 switch(ch) { 883 case 'C': 884 checkfor = 1; 885 break; 886 case 'c': 887 clear = 1; 888 break; 889 case 'f': 890 force = 1; 891 break; 892 case 'k': 893 keep = 1; 894 break; 895 case 'm': 896 maxdumps = atoi(optarg); 897 if (maxdumps <= 0) { 898 syslog(LOG_ERR, "Invalid maxdump value"); 899 exit(1); 900 } 901 break; 902 case 'v': 903 verbose++; 904 break; 905 case 'z': 906 compress = 1; 907 break; 908 case '?': 909 default: 910 usage(); 911 } 912 if (checkfor && (clear || force || keep)) 913 usage(); 914 if (clear && (compress || keep)) 915 usage(); 916 if (maxdumps > 0 && (checkfor || clear)) 917 usage(); 918 argc -= optind; 919 argv += optind; 920 if (argc >= 1 && !checkfor && !clear) { 921 error = chdir(argv[0]); 922 if (error) { 923 syslog(LOG_ERR, "chdir(%s): %m", argv[0]); 924 exit(1); 925 } 926 savedir = argv[0]; 927 argc--; 928 argv++; 929 } 930 if (argc == 0) { 931 for (;;) { 932 fsp = getfsent(); 933 if (fsp == NULL) 934 break; 935 if (strcmp(fsp->fs_vfstype, "swap") && 936 strcmp(fsp->fs_vfstype, "dump")) 937 continue; 938 DoFile(savedir, fsp->fs_spec); 939 } 940 endfsent(); 941 } else { 942 for (i = 0; i < argc; i++) 943 DoFile(savedir, argv[i]); 944 } 945 946 /* Emit minimal output. */ 947 if (nfound == 0) { 948 if (checkfor) { 949 if (verbose) 950 printf("No dump exists\n"); 951 exit(1); 952 } 953 if (verbose) 954 syslog(LOG_WARNING, "no dumps found"); 955 } else if (nsaved == 0) { 956 if (nerr != 0) { 957 if (verbose) 958 syslog(LOG_WARNING, "unsaved dumps found but not saved"); 959 exit(1); 960 } else if (verbose) 961 syslog(LOG_WARNING, "no unsaved dumps found"); 962 } 963 964 return (0); 965 } 966 967 static void 968 infohandler(int sig __unused) 969 { 970 got_siginfo = 1; 971 } 972