1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 2002 Poul-Henning Kamp 5 * Copyright (c) 2002 Networks Associates Technology, Inc. 6 * All rights reserved. 7 * 8 * This software was developed for the FreeBSD Project by Poul-Henning Kamp 9 * and NAI Labs, the Security Research Division of Network Associates, Inc. 10 * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 11 * DARPA CHATS research program. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. The names of the authors may not be used to endorse or promote 22 * products derived from this software without specific prior written 23 * permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 * 37 * Copyright (c) 1986, 1992, 1993 38 * The Regents of the University of California. All rights reserved. 39 * 40 * Redistribution and use in source and binary forms, with or without 41 * modification, are permitted provided that the following conditions 42 * are met: 43 * 1. Redistributions of source code must retain the above copyright 44 * notice, this list of conditions and the following disclaimer. 45 * 2. Redistributions in binary form must reproduce the above copyright 46 * notice, this list of conditions and the following disclaimer in the 47 * documentation and/or other materials provided with the distribution. 48 * 3. Neither the name of the University nor the names of its contributors 49 * may be used to endorse or promote products derived from this software 50 * without specific prior written permission. 51 * 52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 55 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 62 * SUCH DAMAGE. 63 */ 64 65 #include <sys/param.h> 66 #include <sys/disk.h> 67 #include <sys/kerneldump.h> 68 #include <sys/memrange.h> 69 #include <sys/mount.h> 70 #include <sys/stat.h> 71 72 #include <capsicum_helpers.h> 73 #include <ctype.h> 74 #include <errno.h> 75 #include <fcntl.h> 76 #include <fstab.h> 77 #include <paths.h> 78 #include <signal.h> 79 #include <stdarg.h> 80 #include <stdbool.h> 81 #include <stdio.h> 82 #include <stdlib.h> 83 #include <string.h> 84 #include <syslog.h> 85 #include <time.h> 86 #include <unistd.h> 87 #define Z_SOLO 88 #include <zlib.h> 89 #include <zstd.h> 90 91 #include <libcasper.h> 92 #include <casper/cap_fileargs.h> 93 #include <casper/cap_syslog.h> 94 95 #include <libxo/xo.h> 96 97 /* The size of the buffer used for I/O. */ 98 #define BUFFERSIZE (1024*1024) 99 100 #define STATUS_BAD 0 101 #define STATUS_GOOD 1 102 #define STATUS_UNKNOWN 2 103 104 static cap_channel_t *capsyslog; 105 static fileargs_t *capfa; 106 static bool checkfor, compress, uncompress, clear, force, keep; /* flags */ 107 static bool livecore; /* flags cont. */ 108 static int verbose; 109 static int nfound, nsaved, nerr; /* statistics */ 110 static int maxdumps; 111 static uint8_t comp_desired; 112 113 extern FILE *zdopen(int, const char *); 114 115 static sig_atomic_t got_siginfo; 116 static void infohandler(int); 117 118 static void 119 logmsg(int pri, const char *fmt, ...) 120 { 121 va_list ap; 122 123 va_start(ap, fmt); 124 if (capsyslog != NULL) 125 cap_vsyslog(capsyslog, pri, fmt, ap); 126 else 127 vsyslog(pri, fmt, ap); 128 va_end(ap); 129 } 130 131 static FILE * 132 xfopenat(int dirfd, const char *path, int flags, const char *modestr, ...) 133 { 134 va_list ap; 135 FILE *fp; 136 mode_t mode; 137 int error, fd; 138 139 if ((flags & O_CREAT) == O_CREAT) { 140 va_start(ap, modestr); 141 mode = (mode_t)va_arg(ap, int); 142 va_end(ap); 143 } else 144 mode = 0; 145 146 fd = openat(dirfd, path, flags, mode); 147 if (fd < 0) 148 return (NULL); 149 fp = fdopen(fd, modestr); 150 if (fp == NULL) { 151 error = errno; 152 (void)close(fd); 153 errno = error; 154 } 155 return (fp); 156 } 157 158 static void 159 printheader(xo_handle_t *xo, const struct kerneldumpheader *h, 160 const char *device, int bounds, const int status) 161 { 162 uint64_t dumplen; 163 time_t t; 164 struct tm tm; 165 char time_str[64]; 166 const char *stat_str; 167 const char *comp_str; 168 169 xo_flush_h(xo); 170 xo_emit_h(xo, "{Lwc:Dump header from device}{:dump_device/%s}\n", 171 device); 172 xo_emit_h(xo, "{P: }{Lwc:Architecture}{:architecture/%s}\n", 173 h->architecture); 174 xo_emit_h(xo, 175 "{P: }{Lwc:Architecture Version}{:architecture_version/%u}\n", 176 dtoh32(h->architectureversion)); 177 dumplen = dtoh64(h->dumplength); 178 xo_emit_h(xo, "{P: }{Lwc:Dump Length}{:dump_length_bytes/%lld}\n", 179 (long long)dumplen); 180 xo_emit_h(xo, "{P: }{Lwc:Blocksize}{:blocksize/%d}\n", 181 dtoh32(h->blocksize)); 182 switch (h->compression) { 183 case KERNELDUMP_COMP_NONE: 184 comp_str = "none"; 185 break; 186 case KERNELDUMP_COMP_GZIP: 187 comp_str = "gzip"; 188 break; 189 case KERNELDUMP_COMP_ZSTD: 190 comp_str = "zstd"; 191 break; 192 default: 193 comp_str = "???"; 194 break; 195 } 196 xo_emit_h(xo, "{P: }{Lwc:Compression}{:compression/%s}\n", comp_str); 197 t = dtoh64(h->dumptime); 198 localtime_r(&t, &tm); 199 if (strftime(time_str, sizeof(time_str), "%F %T %z", &tm) == 0) 200 time_str[0] = '\0'; 201 xo_emit_h(xo, "{P: }{Lwc:Dumptime}{:dumptime/%s}\n", time_str); 202 xo_emit_h(xo, "{P: }{Lwc:Hostname}{:hostname/%s}\n", h->hostname); 203 xo_emit_h(xo, "{P: }{Lwc:Magic}{:magic/%s}\n", h->magic); 204 xo_emit_h(xo, "{P: }{Lwc:Version String}{:version_string/%s}", 205 h->versionstring); 206 xo_emit_h(xo, "{P: }{Lwc:Panic String}{:panic_string/%s}\n", 207 h->panicstring); 208 xo_emit_h(xo, "{P: }{Lwc:Dump Parity}{:dump_parity/%u}\n", h->parity); 209 xo_emit_h(xo, "{P: }{Lwc:Bounds}{:bounds/%d}\n", bounds); 210 211 switch (status) { 212 case STATUS_BAD: 213 stat_str = "bad"; 214 break; 215 case STATUS_GOOD: 216 stat_str = "good"; 217 break; 218 default: 219 stat_str = "unknown"; 220 break; 221 } 222 xo_emit_h(xo, "{P: }{Lwc:Dump Status}{:dump_status/%s}\n", stat_str); 223 xo_flush_h(xo); 224 } 225 226 static int 227 getbounds(int savedirfd) 228 { 229 FILE *fp; 230 char buf[6]; 231 int ret; 232 233 /* 234 * If we are just checking, then we haven't done a chdir to the dump 235 * directory and we should not try to read a bounds file. 236 */ 237 if (checkfor) 238 return (0); 239 240 ret = 0; 241 242 if ((fp = xfopenat(savedirfd, "bounds", O_RDONLY, "r")) == NULL) { 243 if (verbose) 244 printf("unable to open bounds file, using 0\n"); 245 return (ret); 246 } 247 if (fgets(buf, sizeof(buf), fp) == NULL) { 248 if (feof(fp)) 249 logmsg(LOG_WARNING, "bounds file is empty, using 0"); 250 else 251 logmsg(LOG_WARNING, "bounds file: %s", strerror(errno)); 252 fclose(fp); 253 return (ret); 254 } 255 256 errno = 0; 257 ret = (int)strtol(buf, NULL, 10); 258 if (ret == 0 && (errno == EINVAL || errno == ERANGE)) 259 logmsg(LOG_WARNING, "invalid value found in bounds, using 0"); 260 if (maxdumps > 0 && ret == maxdumps) 261 ret = 0; 262 fclose(fp); 263 return (ret); 264 } 265 266 static void 267 writebounds(int savedirfd, int bounds) 268 { 269 FILE *fp; 270 271 if ((fp = xfopenat(savedirfd, "bounds", O_WRONLY | O_CREAT | O_TRUNC, 272 "w", 0644)) == NULL) { 273 logmsg(LOG_WARNING, "unable to write to bounds file: %m"); 274 return; 275 } 276 277 if (verbose) 278 printf("bounds number: %d\n", bounds); 279 280 fprintf(fp, "%d\n", bounds); 281 fclose(fp); 282 } 283 284 static bool 285 writekey(int savedirfd, const char *keyname, uint8_t *dumpkey, 286 uint32_t dumpkeysize) 287 { 288 int fd; 289 290 fd = openat(savedirfd, keyname, O_WRONLY | O_CREAT | O_TRUNC, 0600); 291 if (fd == -1) { 292 logmsg(LOG_ERR, "Unable to open %s to write the key: %m.", 293 keyname); 294 return (false); 295 } 296 297 if (write(fd, dumpkey, dumpkeysize) != (ssize_t)dumpkeysize) { 298 logmsg(LOG_ERR, "Unable to write the key to %s: %m.", keyname); 299 close(fd); 300 return (false); 301 } 302 303 close(fd); 304 return (true); 305 } 306 307 static int 308 write_header_info(xo_handle_t *xostdout, const struct kerneldumpheader *kdh, 309 int savedirfd, const char *infoname, const char *device, int bounds, 310 int status) 311 { 312 xo_handle_t *xoinfo; 313 FILE *info; 314 315 /* 316 * Create or overwrite any existing dump header files. 317 */ 318 if ((info = xfopenat(savedirfd, infoname, 319 O_WRONLY | O_CREAT | O_TRUNC, "w", 0600)) == NULL) { 320 logmsg(LOG_ERR, "open(%s): %m", infoname); 321 return (-1); 322 } 323 324 xoinfo = xo_create_to_file(info, xo_get_style(NULL), 0); 325 if (xoinfo == NULL) { 326 logmsg(LOG_ERR, "%s: %m", infoname); 327 fclose(info); 328 return (-1); 329 } 330 xo_open_container_h(xoinfo, "crashdump"); 331 332 if (verbose) 333 printheader(xostdout, kdh, device, bounds, status); 334 335 printheader(xoinfo, kdh, device, bounds, status); 336 xo_close_container_h(xoinfo, "crashdump"); 337 xo_flush_h(xoinfo); 338 if (xo_finish_h(xoinfo) < 0) 339 xo_err(EXIT_FAILURE, "stdout"); 340 fclose(info); 341 342 return (0); 343 } 344 345 static off_t 346 file_size(int savedirfd, const char *path) 347 { 348 struct stat sb; 349 350 /* Ignore all errors, this file may not exist. */ 351 if (fstatat(savedirfd, path, &sb, 0) == -1) 352 return (0); 353 return (sb.st_size); 354 } 355 356 static off_t 357 saved_dump_size(int savedirfd, int bounds) 358 { 359 char path[32]; 360 off_t dumpsize; 361 362 dumpsize = 0; 363 364 (void)snprintf(path, sizeof(path), "info.%d", bounds); 365 dumpsize += file_size(savedirfd, path); 366 (void)snprintf(path, sizeof(path), "vmcore.%d", bounds); 367 dumpsize += file_size(savedirfd, path); 368 (void)snprintf(path, sizeof(path), "vmcore.%d.gz", bounds); 369 dumpsize += file_size(savedirfd, path); 370 (void)snprintf(path, sizeof(path), "vmcore.%d.zst", bounds); 371 dumpsize += file_size(savedirfd, path); 372 (void)snprintf(path, sizeof(path), "textdump.tar.%d", bounds); 373 dumpsize += file_size(savedirfd, path); 374 (void)snprintf(path, sizeof(path), "textdump.tar.%d.gz", bounds); 375 dumpsize += file_size(savedirfd, path); 376 377 return (dumpsize); 378 } 379 380 static void 381 saved_dump_remove(int savedirfd, int bounds) 382 { 383 char path[32]; 384 385 (void)snprintf(path, sizeof(path), "info.%d", bounds); 386 (void)unlinkat(savedirfd, path, 0); 387 (void)snprintf(path, sizeof(path), "vmcore.%d", bounds); 388 (void)unlinkat(savedirfd, path, 0); 389 (void)snprintf(path, sizeof(path), "vmcore.%d.gz", bounds); 390 (void)unlinkat(savedirfd, path, 0); 391 (void)snprintf(path, sizeof(path), "vmcore.%d.zst", bounds); 392 (void)unlinkat(savedirfd, path, 0); 393 (void)snprintf(path, sizeof(path), "textdump.tar.%d", bounds); 394 (void)unlinkat(savedirfd, path, 0); 395 (void)snprintf(path, sizeof(path), "textdump.tar.%d.gz", bounds); 396 (void)unlinkat(savedirfd, path, 0); 397 (void)snprintf(path, sizeof(path), "livecore.%d", bounds); 398 (void)unlinkat(savedirfd, path, 0); 399 (void)snprintf(path, sizeof(path), "livecore.%d.gz", bounds); 400 (void)unlinkat(savedirfd, path, 0); 401 (void)snprintf(path, sizeof(path), "livecore.%d.zst", bounds); 402 (void)unlinkat(savedirfd, path, 0); 403 } 404 405 static void 406 symlinks_remove(int savedirfd) 407 { 408 409 (void)unlinkat(savedirfd, "info.last", 0); 410 (void)unlinkat(savedirfd, "key.last", 0); 411 (void)unlinkat(savedirfd, "vmcore.last", 0); 412 (void)unlinkat(savedirfd, "vmcore.last.gz", 0); 413 (void)unlinkat(savedirfd, "vmcore.last.zst", 0); 414 (void)unlinkat(savedirfd, "vmcore_encrypted.last", 0); 415 (void)unlinkat(savedirfd, "vmcore_encrypted.last.gz", 0); 416 (void)unlinkat(savedirfd, "textdump.tar.last", 0); 417 (void)unlinkat(savedirfd, "textdump.tar.last.gz", 0); 418 (void)unlinkat(savedirfd, "livecore.last", 0); 419 (void)unlinkat(savedirfd, "livecore.last.gz", 0); 420 (void)unlinkat(savedirfd, "livecore.last.zst", 0); 421 } 422 423 /* 424 * Check that sufficient space is available on the disk that holds the 425 * save directory. 426 */ 427 static int 428 check_space(const char *savedir, int savedirfd, off_t dumpsize, int bounds) 429 { 430 char buf[100]; 431 struct statfs fsbuf; 432 FILE *fp; 433 off_t available, minfree, spacefree, totfree, needed; 434 435 if (fstatfs(savedirfd, &fsbuf) < 0) { 436 logmsg(LOG_ERR, "%s: %m", savedir); 437 exit(EXIT_FAILURE); 438 } 439 spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024; 440 totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024; 441 442 if ((fp = xfopenat(savedirfd, "minfree", O_RDONLY, "r")) == NULL) 443 minfree = 0; 444 else { 445 if (fgets(buf, sizeof(buf), fp) == NULL) 446 minfree = 0; 447 else { 448 char *endp; 449 450 errno = 0; 451 minfree = strtoll(buf, &endp, 10); 452 if (minfree == 0 && errno != 0) 453 minfree = -1; 454 else { 455 while (*endp != '\0' && isspace(*endp)) 456 endp++; 457 if (*endp != '\0' || minfree < 0) 458 minfree = -1; 459 } 460 if (minfree < 0) 461 logmsg(LOG_WARNING, 462 "`minfree` didn't contain a valid size " 463 "(`%s`). Defaulting to 0", buf); 464 } 465 (void)fclose(fp); 466 } 467 468 available = minfree > 0 ? spacefree - minfree : totfree; 469 needed = dumpsize / 1024 + 2; /* 2 for info file */ 470 needed -= saved_dump_size(savedirfd, bounds); 471 if (available < needed) { 472 logmsg(LOG_WARNING, 473 "no dump: not enough free space on device (need at least " 474 "%jdkB for dump; %jdkB available; %jdkB reserved)", 475 (intmax_t)needed, 476 (intmax_t)available + minfree, 477 (intmax_t)minfree); 478 return (0); 479 } 480 if (spacefree - needed < 0) 481 logmsg(LOG_WARNING, 482 "dump performed, but free space threshold crossed"); 483 return (1); 484 } 485 486 static bool 487 compare_magic(const struct kerneldumpheader *kdh, const char *magic) 488 { 489 490 return (strncmp(kdh->magic, magic, sizeof(kdh->magic)) == 0); 491 } 492 493 #define BLOCKSIZE (1<<12) 494 #define BLOCKMASK (~(BLOCKSIZE-1)) 495 496 static size_t 497 sparsefwrite(const char *buf, size_t nr, FILE *fp) 498 { 499 size_t nw, he, hs; 500 501 for (nw = 0; nw < nr; nw = he) { 502 /* find a contiguous block of zeroes */ 503 for (hs = nw; hs < nr; hs += BLOCKSIZE) { 504 for (he = hs; he < nr && buf[he] == 0; ++he) 505 /* nothing */ ; 506 /* is the hole long enough to matter? */ 507 if (he >= hs + BLOCKSIZE) 508 break; 509 } 510 511 /* back down to a block boundary */ 512 he &= BLOCKMASK; 513 514 /* 515 * 1) Don't go beyond the end of the buffer. 516 * 2) If the end of the buffer is less than 517 * BLOCKSIZE bytes away, we're at the end 518 * of the file, so just grab what's left. 519 */ 520 if (hs + BLOCKSIZE > nr) 521 hs = he = nr; 522 523 /* 524 * At this point, we have a partial ordering: 525 * nw <= hs <= he <= nr 526 * If hs > nw, buf[nw..hs] contains non-zero 527 * data. If he > hs, buf[hs..he] is all zeroes. 528 */ 529 if (hs > nw) 530 if (fwrite(buf + nw, hs - nw, 1, fp) != 1) 531 break; 532 if (he > hs) 533 if (fseeko(fp, he - hs, SEEK_CUR) == -1) 534 break; 535 } 536 537 return (nw); 538 } 539 540 static char *zbuf; 541 static size_t zbufsize; 542 543 static ssize_t 544 GunzipWrite(z_stream *z, char *in, size_t insize, FILE *fp) 545 { 546 static bool firstblock = true; /* XXX not re-entrable/usable */ 547 const size_t hdrlen = 10; 548 size_t nw = 0, w; 549 int rv; 550 551 z->next_in = in; 552 z->avail_in = insize; 553 /* 554 * Since contrib/zlib for some reason is compiled 555 * without GUNZIP define, we need to skip the gzip 556 * header manually. Kernel puts minimal 10 byte 557 * header, see sys/kern/subr_compressor.c:gz_reset(). 558 */ 559 if (firstblock) { 560 z->next_in += hdrlen; 561 z->avail_in -= hdrlen; 562 firstblock = false; 563 } 564 do { 565 z->next_out = zbuf; 566 z->avail_out = zbufsize; 567 rv = inflate(z, Z_NO_FLUSH); 568 if (rv != Z_OK && rv != Z_STREAM_END) { 569 logmsg(LOG_ERR, "decompression failed: %s", z->msg); 570 return (-1); 571 } 572 w = sparsefwrite(zbuf, zbufsize - z->avail_out, fp); 573 if (w < zbufsize - z->avail_out) 574 return (-1); 575 nw += w; 576 } while (z->avail_in > 0 && rv != Z_STREAM_END); 577 578 return (nw); 579 } 580 581 static ssize_t 582 ZstdWrite(ZSTD_DCtx *Zctx, char *in, size_t insize, FILE *fp) 583 { 584 ZSTD_inBuffer Zin; 585 ZSTD_outBuffer Zout; 586 size_t nw = 0, w; 587 int rv; 588 589 Zin.src = in; 590 Zin.size = insize; 591 Zin.pos = 0; 592 do { 593 Zout.dst = zbuf; 594 Zout.size = zbufsize; 595 Zout.pos = 0; 596 rv = ZSTD_decompressStream(Zctx, &Zout, &Zin); 597 if (ZSTD_isError(rv)) { 598 logmsg(LOG_ERR, "decompression failed: %s", 599 ZSTD_getErrorName(rv)); 600 return (-1); 601 } 602 w = sparsefwrite(zbuf, Zout.pos, fp); 603 if (w < Zout.pos) 604 return (-1); 605 nw += w; 606 } while (Zin.pos < Zin.size && rv != 0); 607 608 return (nw); 609 } 610 611 static int 612 DoRegularFile(int fd, off_t dumpsize, u_int sectorsize, bool sparse, 613 uint8_t compression, char *buf, const char *device, 614 const char *filename, FILE *fp) 615 { 616 size_t nr, wl; 617 ssize_t nw; 618 off_t dmpcnt, origsize; 619 z_stream z; /* gzip */ 620 ZSTD_DCtx *Zctx; /* zstd */ 621 622 dmpcnt = 0; 623 origsize = dumpsize; 624 if (compression == KERNELDUMP_COMP_GZIP) { 625 memset(&z, 0, sizeof(z)); 626 z.zalloc = Z_NULL; 627 z.zfree = Z_NULL; 628 if (inflateInit2(&z, -MAX_WBITS) != Z_OK) { 629 logmsg(LOG_ERR, "failed to initialize zlib: %s", z.msg); 630 return (-1); 631 } 632 zbufsize = BUFFERSIZE; 633 } else if (compression == KERNELDUMP_COMP_ZSTD) { 634 if ((Zctx = ZSTD_createDCtx()) == NULL) { 635 logmsg(LOG_ERR, "failed to initialize zstd"); 636 return (-1); 637 } 638 zbufsize = ZSTD_DStreamOutSize(); 639 } 640 if (zbufsize > 0) 641 if ((zbuf = malloc(zbufsize)) == NULL) { 642 logmsg(LOG_ERR, "failed to alloc decompression buffer"); 643 return (-1); 644 } 645 646 while (dumpsize > 0) { 647 wl = BUFFERSIZE; 648 if (wl > (size_t)dumpsize) 649 wl = dumpsize; 650 nr = read(fd, buf, roundup(wl, sectorsize)); 651 if (nr != roundup(wl, sectorsize)) { 652 if (nr == 0) 653 logmsg(LOG_WARNING, 654 "WARNING: EOF on dump device"); 655 else 656 logmsg(LOG_ERR, "read error on %s: %m", device); 657 nerr++; 658 return (-1); 659 } 660 if (compression == KERNELDUMP_COMP_GZIP) 661 nw = GunzipWrite(&z, buf, nr, fp); 662 else if (compression == KERNELDUMP_COMP_ZSTD) 663 nw = ZstdWrite(Zctx, buf, nr, fp); 664 else if (!sparse) 665 nw = fwrite(buf, 1, wl, fp); 666 else 667 nw = sparsefwrite(buf, wl, fp); 668 if (nw < 0 || (compression == KERNELDUMP_COMP_NONE && 669 (size_t)nw != wl)) { 670 logmsg(LOG_ERR, 671 "write error on %s file: %m", filename); 672 logmsg(LOG_WARNING, 673 "WARNING: vmcore may be incomplete"); 674 nerr++; 675 return (-1); 676 } 677 if (verbose) { 678 dmpcnt += wl; 679 printf("%llu\r", (unsigned long long)dmpcnt); 680 fflush(stdout); 681 } 682 dumpsize -= wl; 683 if (got_siginfo) { 684 printf("%s %.1lf%%\n", filename, (100.0 - (100.0 * 685 (double)dumpsize / (double)origsize))); 686 got_siginfo = 0; 687 } 688 } 689 return (0); 690 } 691 692 /* 693 * Specialized version of dump-reading logic for use with textdumps, which 694 * are written backwards from the end of the partition, and must be reversed 695 * before being written to the file. Textdumps are small, so do a bit less 696 * work to optimize/sparsify. 697 */ 698 static int 699 DoTextdumpFile(int fd, off_t dumpsize, off_t lasthd, char *buf, 700 const char *device, const char *filename, FILE *fp) 701 { 702 int nr, nw, wl; 703 off_t dmpcnt, totsize; 704 705 totsize = dumpsize; 706 dmpcnt = 0; 707 wl = 512; 708 if ((dumpsize % wl) != 0) { 709 logmsg(LOG_ERR, "textdump uneven multiple of 512 on %s", 710 device); 711 nerr++; 712 return (-1); 713 } 714 while (dumpsize > 0) { 715 nr = pread(fd, buf, wl, lasthd - (totsize - dumpsize) - wl); 716 if (nr != wl) { 717 if (nr == 0) 718 logmsg(LOG_WARNING, 719 "WARNING: EOF on dump device"); 720 else 721 logmsg(LOG_ERR, "read error on %s: %m", device); 722 nerr++; 723 return (-1); 724 } 725 nw = fwrite(buf, 1, wl, fp); 726 if (nw != wl) { 727 logmsg(LOG_ERR, 728 "write error on %s file: %m", filename); 729 logmsg(LOG_WARNING, 730 "WARNING: textdump may be incomplete"); 731 nerr++; 732 return (-1); 733 } 734 if (verbose) { 735 dmpcnt += wl; 736 printf("%llu\r", (unsigned long long)dmpcnt); 737 fflush(stdout); 738 } 739 dumpsize -= wl; 740 } 741 return (0); 742 } 743 744 static void 745 DoLiveFile(const char *savedir, int savedirfd, const char *device) 746 { 747 char infoname[32], corename[32], linkname[32], tmpname[32]; 748 struct mem_livedump_arg marg; 749 struct kerneldumpheader kdhl; 750 xo_handle_t *xostdout; 751 off_t dumplength; 752 uint32_t version; 753 int fddev, fdcore; 754 int bounds; 755 int error, status; 756 757 bounds = getbounds(savedirfd); 758 status = STATUS_UNKNOWN; 759 760 xostdout = xo_create_to_file(stdout, XO_STYLE_TEXT, 0); 761 if (xostdout == NULL) { 762 logmsg(LOG_ERR, "xo_create_to_file() failed: %m"); 763 return; 764 } 765 766 /* 767 * Create a temporary file. We will invoke the live dump and its 768 * contents will be written to this fd. After validating and removing 769 * the kernel dump header from the tail-end of this file, it will be 770 * renamed to its definitive filename (e.g. livecore.2.gz). 771 * 772 * If any errors are encountered before the rename, the temporary file 773 * is unlinked. 774 */ 775 strcpy(tmpname, "livecore.tmp.XXXXXX"); 776 fdcore = mkostempsat(savedirfd, tmpname, 0, 0); 777 if (fdcore < 0) { 778 logmsg(LOG_ERR, "error opening temp file: %m"); 779 return; 780 } 781 782 fddev = fileargs_open(capfa, device); 783 if (fddev < 0) { 784 logmsg(LOG_ERR, "%s: %m", device); 785 goto unlinkexit; 786 } 787 788 bzero(&marg, sizeof(marg)); 789 marg.fd = fdcore; 790 marg.compression = comp_desired; 791 if (ioctl(fddev, MEM_KERNELDUMP, &marg) == -1) { 792 logmsg(LOG_ERR, 793 "failed to invoke live-dump on system: %m"); 794 close(fddev); 795 goto unlinkexit; 796 } 797 798 /* Close /dev/mem fd, we are finished with it. */ 799 close(fddev); 800 801 /* Seek to the end of the file, minus the size of the header. */ 802 if (lseek(fdcore, -(off_t)sizeof(kdhl), SEEK_END) == -1) { 803 logmsg(LOG_ERR, "failed to lseek: %m"); 804 goto unlinkexit; 805 } 806 807 if (read(fdcore, &kdhl, sizeof(kdhl)) != sizeof(kdhl)) { 808 logmsg(LOG_ERR, "failed to read kernel dump header: %m"); 809 goto unlinkexit; 810 } 811 /* Reset cursor */ 812 (void)lseek(fdcore, 0, SEEK_SET); 813 814 /* Validate the dump header. */ 815 version = dtoh32(kdhl.version); 816 if (compare_magic(&kdhl, KERNELDUMPMAGIC)) { 817 if (version != KERNELDUMPVERSION) { 818 logmsg(LOG_ERR, 819 "unknown version (%d) in dump header on %s", 820 version, device); 821 goto unlinkexit; 822 } else if (kdhl.compression != comp_desired) { 823 /* This should be impossible. */ 824 logmsg(LOG_ERR, 825 "dump compression (%u) doesn't match request (%u)", 826 kdhl.compression, comp_desired); 827 if (!force) 828 goto unlinkexit; 829 } 830 } else { 831 logmsg(LOG_ERR, "magic mismatch on live dump header"); 832 goto unlinkexit; 833 } 834 if (kerneldump_parity(&kdhl)) { 835 logmsg(LOG_ERR, 836 "parity error on last dump header on %s", device); 837 nerr++; 838 status = STATUS_BAD; 839 if (!force) 840 goto unlinkexit; 841 } else { 842 status = STATUS_GOOD; 843 } 844 845 nfound++; 846 dumplength = dtoh64(kdhl.dumplength); 847 if (dtoh32(kdhl.dumpkeysize) != 0) { 848 logmsg(LOG_ERR, 849 "dump header unexpectedly reported keysize > 0"); 850 goto unlinkexit; 851 } 852 853 /* Remove the vestigial kernel dump header. */ 854 error = ftruncate(fdcore, dumplength); 855 if (error != 0) { 856 logmsg(LOG_ERR, "failed to truncate the core file: %m"); 857 goto unlinkexit; 858 } 859 860 if (verbose >= 2) { 861 printf("\nDump header:\n"); 862 printheader(xostdout, &kdhl, device, bounds, -1); 863 printf("\n"); 864 } 865 logmsg(LOG_ALERT, "livedump"); 866 867 writebounds(savedirfd, bounds + 1); 868 saved_dump_remove(savedirfd, bounds); 869 870 snprintf(corename, sizeof(corename), "livecore.%d", bounds); 871 if (compress) 872 strcat(corename, kdhl.compression == KERNELDUMP_COMP_ZSTD ? 873 ".zst" : ".gz"); 874 875 if (verbose) 876 printf("renaming %s to %s\n", tmpname, corename); 877 if (renameat(savedirfd, tmpname, savedirfd, corename) != 0) { 878 logmsg(LOG_ERR, "renameat failed: %m"); 879 goto unlinkexit; 880 } 881 882 snprintf(infoname, sizeof(infoname), "info.%d", bounds); 883 if (write_header_info(xostdout, &kdhl, savedirfd, infoname, device, 884 bounds, status) != 0) { 885 nerr++; 886 return; 887 } 888 889 logmsg(LOG_NOTICE, "writing %score to %s/%s", 890 compress ? "compressed " : "", savedir, corename); 891 892 if (verbose) 893 printf("\n"); 894 895 symlinks_remove(savedirfd); 896 if (symlinkat(infoname, savedirfd, "info.last") == -1) { 897 logmsg(LOG_WARNING, "unable to create symlink %s/%s: %m", 898 savedir, "info.last"); 899 } 900 901 snprintf(linkname, sizeof(linkname), "livecore.last"); 902 if (compress) 903 strcat(linkname, kdhl.compression == KERNELDUMP_COMP_ZSTD ? 904 ".zst" : ".gz"); 905 if (symlinkat(corename, savedirfd, linkname) == -1) { 906 logmsg(LOG_WARNING, "unable to create symlink %s/%s: %m", 907 savedir, linkname); 908 } 909 910 nsaved++; 911 if (verbose) 912 printf("dump saved\n"); 913 914 close(fdcore); 915 return; 916 unlinkexit: 917 funlinkat(savedirfd, tmpname, fdcore, 0); 918 close(fdcore); 919 } 920 921 static void 922 DoFile(const char *savedir, int savedirfd, const char *device) 923 { 924 static char *buf = NULL; 925 xo_handle_t *xostdout; 926 char infoname[32], corename[32], linkname[32], keyname[32]; 927 char *temp = NULL; 928 struct kerneldumpheader kdhf, kdhl; 929 uint8_t *dumpkey; 930 off_t mediasize, dumpextent, dumplength, firsthd, lasthd; 931 FILE *core; 932 int fdcore, fddev, error; 933 int bounds, status; 934 u_int sectorsize; 935 uint32_t dumpkeysize; 936 bool iscompressed, isencrypted, istextdump, ret; 937 938 /* Live kernel dumps are handled separately. */ 939 if (livecore) { 940 DoLiveFile(savedir, savedirfd, device); 941 return; 942 } 943 944 bounds = getbounds(savedirfd); 945 dumpkey = NULL; 946 mediasize = 0; 947 status = STATUS_UNKNOWN; 948 949 xostdout = xo_create_to_file(stdout, XO_STYLE_TEXT, 0); 950 if (xostdout == NULL) { 951 logmsg(LOG_ERR, "xo_create_to_file() failed: %m"); 952 return; 953 } 954 955 if (buf == NULL) { 956 buf = malloc(BUFFERSIZE); 957 if (buf == NULL) { 958 logmsg(LOG_ERR, "%m"); 959 return; 960 } 961 } 962 963 if (verbose) 964 printf("checking for kernel dump on device %s\n", device); 965 966 fddev = fileargs_open(capfa, device); 967 if (fddev < 0) { 968 logmsg(LOG_ERR, "%s: %m", device); 969 return; 970 } 971 972 error = ioctl(fddev, DIOCGMEDIASIZE, &mediasize); 973 if (!error) 974 error = ioctl(fddev, DIOCGSECTORSIZE, §orsize); 975 if (error) { 976 logmsg(LOG_ERR, 977 "couldn't find media and/or sector size of %s: %m", device); 978 goto closefd; 979 } 980 981 if (verbose) { 982 printf("mediasize = %lld bytes\n", (long long)mediasize); 983 printf("sectorsize = %u bytes\n", sectorsize); 984 } 985 986 if (sectorsize < sizeof(kdhl)) { 987 logmsg(LOG_ERR, 988 "Sector size is less the kernel dump header %zu", 989 sizeof(kdhl)); 990 goto closefd; 991 } 992 993 lasthd = mediasize - sectorsize; 994 temp = malloc(sectorsize); 995 if (temp == NULL) { 996 logmsg(LOG_ERR, "%m"); 997 goto closefd; 998 } 999 if (lseek(fddev, lasthd, SEEK_SET) != lasthd || 1000 read(fddev, temp, sectorsize) != (ssize_t)sectorsize) { 1001 logmsg(LOG_ERR, 1002 "error reading last dump header at offset %lld in %s: %m", 1003 (long long)lasthd, device); 1004 goto closefd; 1005 } 1006 memcpy(&kdhl, temp, sizeof(kdhl)); 1007 iscompressed = istextdump = false; 1008 if (compare_magic(&kdhl, TEXTDUMPMAGIC)) { 1009 if (verbose) 1010 printf("textdump magic on last dump header on %s\n", 1011 device); 1012 istextdump = true; 1013 if (dtoh32(kdhl.version) != KERNELDUMP_TEXT_VERSION) { 1014 logmsg(LOG_ERR, 1015 "unknown version (%d) in last dump header on %s", 1016 dtoh32(kdhl.version), device); 1017 1018 status = STATUS_BAD; 1019 if (!force) 1020 goto closefd; 1021 } 1022 } else if (compare_magic(&kdhl, KERNELDUMPMAGIC)) { 1023 if (dtoh32(kdhl.version) != KERNELDUMPVERSION) { 1024 logmsg(LOG_ERR, 1025 "unknown version (%d) in last dump header on %s", 1026 dtoh32(kdhl.version), device); 1027 1028 status = STATUS_BAD; 1029 if (!force) 1030 goto closefd; 1031 } 1032 switch (kdhl.compression) { 1033 case KERNELDUMP_COMP_NONE: 1034 uncompress = false; 1035 break; 1036 case KERNELDUMP_COMP_GZIP: 1037 case KERNELDUMP_COMP_ZSTD: 1038 if (compress && verbose) 1039 printf("dump is already compressed\n"); 1040 if (uncompress && verbose) 1041 printf("dump to be uncompressed\n"); 1042 compress = false; 1043 iscompressed = true; 1044 break; 1045 default: 1046 logmsg(LOG_ERR, "unknown compression type %d on %s", 1047 kdhl.compression, device); 1048 break; 1049 } 1050 } else { 1051 if (verbose) 1052 printf("magic mismatch on last dump header on %s\n", 1053 device); 1054 1055 status = STATUS_BAD; 1056 if (!force) 1057 goto closefd; 1058 1059 if (compare_magic(&kdhl, KERNELDUMPMAGIC_CLEARED)) { 1060 if (verbose) 1061 printf("forcing magic on %s\n", device); 1062 memcpy(kdhl.magic, KERNELDUMPMAGIC, sizeof(kdhl.magic)); 1063 } else { 1064 logmsg(LOG_ERR, "unable to force dump - bad magic"); 1065 goto closefd; 1066 } 1067 if (dtoh32(kdhl.version) != KERNELDUMPVERSION) { 1068 logmsg(LOG_ERR, 1069 "unknown version (%d) in last dump header on %s", 1070 dtoh32(kdhl.version), device); 1071 1072 status = STATUS_BAD; 1073 if (!force) 1074 goto closefd; 1075 } 1076 } 1077 1078 nfound++; 1079 if (clear) 1080 goto nuke; 1081 1082 if (kerneldump_parity(&kdhl)) { 1083 logmsg(LOG_ERR, 1084 "parity error on last dump header on %s", device); 1085 nerr++; 1086 status = STATUS_BAD; 1087 if (!force) 1088 goto closefd; 1089 } 1090 dumpextent = dtoh64(kdhl.dumpextent); 1091 dumplength = dtoh64(kdhl.dumplength); 1092 dumpkeysize = dtoh32(kdhl.dumpkeysize); 1093 firsthd = lasthd - dumpextent - sectorsize - dumpkeysize; 1094 if (lseek(fddev, firsthd, SEEK_SET) != firsthd || 1095 read(fddev, temp, sectorsize) != (ssize_t)sectorsize) { 1096 logmsg(LOG_ERR, 1097 "error reading first dump header at offset %lld in %s: %m", 1098 (long long)firsthd, device); 1099 nerr++; 1100 goto closefd; 1101 } 1102 memcpy(&kdhf, temp, sizeof(kdhf)); 1103 1104 if (verbose >= 2) { 1105 printf("First dump headers:\n"); 1106 printheader(xostdout, &kdhf, device, bounds, -1); 1107 1108 printf("\nLast dump headers:\n"); 1109 printheader(xostdout, &kdhl, device, bounds, -1); 1110 printf("\n"); 1111 } 1112 1113 if (memcmp(&kdhl, &kdhf, sizeof(kdhl))) { 1114 logmsg(LOG_ERR, 1115 "first and last dump headers disagree on %s", device); 1116 nerr++; 1117 status = STATUS_BAD; 1118 if (!force) 1119 goto closefd; 1120 } else { 1121 status = STATUS_GOOD; 1122 } 1123 1124 if (checkfor) { 1125 printf("A dump exists on %s\n", device); 1126 close(fddev); 1127 exit(EXIT_SUCCESS); 1128 } 1129 1130 if (kdhl.panicstring[0] != '\0') 1131 logmsg(LOG_ALERT, "reboot after panic: %.*s", 1132 (int)sizeof(kdhl.panicstring), kdhl.panicstring); 1133 else 1134 logmsg(LOG_ALERT, "reboot"); 1135 1136 if (verbose) 1137 printf("Checking for available free space\n"); 1138 1139 if (!check_space(savedir, savedirfd, dumplength, bounds)) { 1140 nerr++; 1141 goto closefd; 1142 } 1143 1144 writebounds(savedirfd, bounds + 1); 1145 1146 saved_dump_remove(savedirfd, bounds); 1147 1148 isencrypted = (dumpkeysize > 0); 1149 if (compress) 1150 snprintf(corename, sizeof(corename), "%s.%d.gz", 1151 istextdump ? "textdump.tar" : 1152 (isencrypted ? "vmcore_encrypted" : "vmcore"), bounds); 1153 else if (iscompressed && !isencrypted && !uncompress) 1154 snprintf(corename, sizeof(corename), "vmcore.%d.%s", bounds, 1155 (kdhl.compression == KERNELDUMP_COMP_GZIP) ? "gz" : "zst"); 1156 else 1157 snprintf(corename, sizeof(corename), "%s.%d", 1158 istextdump ? "textdump.tar" : 1159 (isencrypted ? "vmcore_encrypted" : "vmcore"), bounds); 1160 fdcore = openat(savedirfd, corename, O_WRONLY | O_CREAT | O_TRUNC, 1161 0600); 1162 if (fdcore < 0) { 1163 logmsg(LOG_ERR, "open(%s): %m", corename); 1164 nerr++; 1165 goto closefd; 1166 } 1167 1168 if (compress) 1169 core = zdopen(fdcore, "w"); 1170 else 1171 core = fdopen(fdcore, "w"); 1172 if (core == NULL) { 1173 logmsg(LOG_ERR, "%s: %m", corename); 1174 (void)close(fdcore); 1175 nerr++; 1176 goto closefd; 1177 } 1178 fdcore = -1; 1179 1180 snprintf(infoname, sizeof(infoname), "info.%d", bounds); 1181 if (write_header_info(xostdout, &kdhl, savedirfd, infoname, device, 1182 bounds, status) != 0) { 1183 nerr++; 1184 goto closeall; 1185 } 1186 1187 if (isencrypted) { 1188 dumpkey = calloc(1, dumpkeysize); 1189 if (dumpkey == NULL) { 1190 logmsg(LOG_ERR, "Unable to allocate kernel dump key."); 1191 nerr++; 1192 goto closeall; 1193 } 1194 1195 if (read(fddev, dumpkey, dumpkeysize) != (ssize_t)dumpkeysize) { 1196 logmsg(LOG_ERR, "Unable to read kernel dump key: %m."); 1197 nerr++; 1198 goto closeall; 1199 } 1200 1201 snprintf(keyname, sizeof(keyname), "key.%d", bounds); 1202 ret = writekey(savedirfd, keyname, dumpkey, dumpkeysize); 1203 explicit_bzero(dumpkey, dumpkeysize); 1204 if (!ret) { 1205 nerr++; 1206 goto closeall; 1207 } 1208 } 1209 1210 logmsg(LOG_NOTICE, "writing %s%score to %s/%s", 1211 isencrypted ? "encrypted " : "", compress ? "compressed " : "", 1212 savedir, corename); 1213 1214 if (istextdump) { 1215 if (DoTextdumpFile(fddev, dumplength, lasthd, buf, device, 1216 corename, core) < 0) 1217 goto closeall; 1218 } else { 1219 if (DoRegularFile(fddev, dumplength, sectorsize, 1220 !(compress || iscompressed || isencrypted), 1221 uncompress ? kdhl.compression : KERNELDUMP_COMP_NONE, 1222 buf, device, corename, core) < 0) { 1223 goto closeall; 1224 } 1225 } 1226 if (verbose) 1227 printf("\n"); 1228 1229 if (fclose(core) < 0) { 1230 logmsg(LOG_ERR, "error on %s: %m", corename); 1231 nerr++; 1232 goto closefd; 1233 } 1234 1235 symlinks_remove(savedirfd); 1236 if (symlinkat(infoname, savedirfd, "info.last") == -1) { 1237 logmsg(LOG_WARNING, "unable to create symlink %s/%s: %m", 1238 savedir, "info.last"); 1239 } 1240 if (isencrypted) { 1241 if (symlinkat(keyname, savedirfd, "key.last") == -1) { 1242 logmsg(LOG_WARNING, 1243 "unable to create symlink %s/%s: %m", savedir, 1244 "key.last"); 1245 } 1246 } 1247 if ((iscompressed && !uncompress) || compress) { 1248 snprintf(linkname, sizeof(linkname), "%s.last.%s", 1249 istextdump ? "textdump.tar" : 1250 (isencrypted ? "vmcore_encrypted" : "vmcore"), 1251 (kdhl.compression == KERNELDUMP_COMP_ZSTD) ? "zst" : "gz"); 1252 } else { 1253 snprintf(linkname, sizeof(linkname), "%s.last", 1254 istextdump ? "textdump.tar" : 1255 (isencrypted ? "vmcore_encrypted" : "vmcore")); 1256 } 1257 if (symlinkat(corename, savedirfd, linkname) == -1) { 1258 logmsg(LOG_WARNING, "unable to create symlink %s/%s: %m", 1259 savedir, linkname); 1260 } 1261 1262 nsaved++; 1263 1264 if (verbose) 1265 printf("dump saved\n"); 1266 1267 nuke: 1268 if (!keep) { 1269 if (verbose) 1270 printf("clearing dump header\n"); 1271 memcpy(kdhl.magic, KERNELDUMPMAGIC_CLEARED, sizeof(kdhl.magic)); 1272 memcpy(temp, &kdhl, sizeof(kdhl)); 1273 if (lseek(fddev, lasthd, SEEK_SET) != lasthd || 1274 write(fddev, temp, sectorsize) != (ssize_t)sectorsize) 1275 logmsg(LOG_ERR, 1276 "error while clearing the dump header: %m"); 1277 } 1278 xo_close_container_h(xostdout, "crashdump"); 1279 if (xo_finish_h(xostdout) < 0) 1280 xo_err(EXIT_FAILURE, "stdout"); 1281 free(dumpkey); 1282 free(temp); 1283 close(fddev); 1284 return; 1285 1286 closeall: 1287 fclose(core); 1288 1289 closefd: 1290 free(dumpkey); 1291 free(temp); 1292 close(fddev); 1293 } 1294 1295 /* Prepend "/dev/" to any arguments that don't already have it */ 1296 static char ** 1297 devify(int argc, char **argv) 1298 { 1299 char **devs; 1300 int i, l; 1301 1302 devs = malloc(argc * sizeof(*argv)); 1303 if (devs == NULL) { 1304 logmsg(LOG_ERR, "malloc(): %m"); 1305 exit(EXIT_FAILURE); 1306 } 1307 for (i = 0; i < argc; i++) { 1308 if (strncmp(argv[i], _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) 1309 devs[i] = strdup(argv[i]); 1310 else { 1311 char *fullpath; 1312 1313 fullpath = malloc(PATH_MAX); 1314 if (fullpath == NULL) { 1315 logmsg(LOG_ERR, "malloc(): %m"); 1316 exit(EXIT_FAILURE); 1317 } 1318 l = snprintf(fullpath, PATH_MAX, "%s%s", _PATH_DEV, 1319 argv[i]); 1320 if (l < 0) { 1321 logmsg(LOG_ERR, "snprintf(): %m"); 1322 exit(EXIT_FAILURE); 1323 } else if (l >= PATH_MAX) { 1324 logmsg(LOG_ERR, "device name too long"); 1325 exit(EXIT_FAILURE); 1326 } 1327 devs[i] = fullpath; 1328 } 1329 } 1330 return (devs); 1331 } 1332 1333 static char ** 1334 enum_dumpdevs(int *argcp) 1335 { 1336 struct fstab *fsp; 1337 char **argv; 1338 int argc, n; 1339 1340 /* 1341 * We cannot use getfsent(3) in capability mode, so we must 1342 * scan /etc/fstab and build up a list of candidate devices 1343 * before proceeding. 1344 */ 1345 argc = 0; 1346 n = 8; 1347 argv = malloc(n * sizeof(*argv)); 1348 if (argv == NULL) { 1349 logmsg(LOG_ERR, "malloc(): %m"); 1350 exit(EXIT_FAILURE); 1351 } 1352 for (;;) { 1353 fsp = getfsent(); 1354 if (fsp == NULL) 1355 break; 1356 if (strcmp(fsp->fs_vfstype, "swap") != 0 && 1357 strcmp(fsp->fs_vfstype, "dump") != 0) 1358 continue; 1359 if (argc >= n) { 1360 n *= 2; 1361 argv = realloc(argv, n * sizeof(*argv)); 1362 if (argv == NULL) { 1363 logmsg(LOG_ERR, "realloc(): %m"); 1364 exit(EXIT_FAILURE); 1365 } 1366 } 1367 argv[argc] = strdup(fsp->fs_spec); 1368 if (argv[argc] == NULL) { 1369 logmsg(LOG_ERR, "strdup(): %m"); 1370 exit(EXIT_FAILURE); 1371 } 1372 argc++; 1373 } 1374 *argcp = argc; 1375 return (argv); 1376 } 1377 1378 static void 1379 init_caps(int argc, char **argv) 1380 { 1381 cap_rights_t rights; 1382 cap_channel_t *capcas; 1383 1384 capcas = cap_init(); 1385 if (capcas == NULL) { 1386 logmsg(LOG_ERR, "cap_init(): %m"); 1387 exit(EXIT_FAILURE); 1388 } 1389 /* 1390 * The fileargs capability does not currently provide a way to limit 1391 * ioctls. 1392 */ 1393 (void)cap_rights_init(&rights, CAP_PREAD, CAP_WRITE, CAP_IOCTL); 1394 capfa = fileargs_init(argc, argv, checkfor || keep ? O_RDONLY : O_RDWR, 1395 0, &rights, FA_OPEN); 1396 if (capfa == NULL) { 1397 logmsg(LOG_ERR, "fileargs_init(): %m"); 1398 exit(EXIT_FAILURE); 1399 } 1400 caph_cache_catpages(); 1401 caph_cache_tzdata(); 1402 if (caph_enter_casper() != 0) { 1403 logmsg(LOG_ERR, "caph_enter_casper(): %m"); 1404 exit(EXIT_FAILURE); 1405 } 1406 capsyslog = cap_service_open(capcas, "system.syslog"); 1407 if (capsyslog == NULL) { 1408 logmsg(LOG_ERR, "cap_service_open(system.syslog): %m"); 1409 exit(EXIT_FAILURE); 1410 } 1411 cap_close(capcas); 1412 } 1413 1414 static void 1415 usage(void) 1416 { 1417 xo_error("%s\n%s\n%s\n%s\n", 1418 "usage: savecore -c [-v] [device ...]", 1419 " savecore -C [-v] [device ...]", 1420 " savecore -L [-fvZz] [-m maxdumps] [directory]", 1421 " savecore [-fkuvz] [-m maxdumps] [directory [device ...]]"); 1422 exit(EXIT_FAILURE); 1423 } 1424 1425 int 1426 main(int argc, char **argv) 1427 { 1428 cap_rights_t rights; 1429 const char *savedir; 1430 char **devs; 1431 int i, ch, error, savedirfd; 1432 1433 checkfor = compress = clear = force = keep = livecore = false; 1434 verbose = 0; 1435 nfound = nsaved = nerr = 0; 1436 savedir = "."; 1437 comp_desired = KERNELDUMP_COMP_NONE; 1438 1439 openlog("savecore", LOG_PERROR, LOG_DAEMON); 1440 signal(SIGINFO, infohandler); 1441 1442 argc = xo_parse_args(argc, argv); 1443 if (argc < 0) 1444 exit(EXIT_FAILURE); 1445 1446 while ((ch = getopt(argc, argv, "CcfkLm:uvZz")) != -1) 1447 switch(ch) { 1448 case 'C': 1449 checkfor = true; 1450 break; 1451 case 'c': 1452 clear = true; 1453 break; 1454 case 'f': 1455 force = true; 1456 break; 1457 case 'k': 1458 keep = true; 1459 break; 1460 case 'L': 1461 livecore = true; 1462 break; 1463 case 'm': 1464 maxdumps = atoi(optarg); 1465 if (maxdumps <= 0) { 1466 logmsg(LOG_ERR, "Invalid maxdump value"); 1467 exit(EXIT_FAILURE); 1468 } 1469 break; 1470 case 'u': 1471 uncompress = true; 1472 break; 1473 case 'v': 1474 verbose++; 1475 break; 1476 case 'Z': 1477 /* No on-the-fly compression with zstd at the moment. */ 1478 if (!livecore) 1479 usage(); 1480 compress = true; 1481 comp_desired = KERNELDUMP_COMP_ZSTD; 1482 break; 1483 case 'z': 1484 compress = true; 1485 comp_desired = KERNELDUMP_COMP_GZIP; 1486 break; 1487 case '?': 1488 default: 1489 usage(); 1490 } 1491 if (checkfor && (clear || force || keep)) 1492 usage(); 1493 if (clear && (compress || keep)) 1494 usage(); 1495 if (maxdumps > 0 && (checkfor || clear)) 1496 usage(); 1497 if (compress && uncompress) 1498 usage(); 1499 if (livecore && (checkfor || clear || uncompress || keep)) 1500 usage(); 1501 argc -= optind; 1502 argv += optind; 1503 if (argc >= 1 && !checkfor && !clear) { 1504 error = chdir(argv[0]); 1505 if (error) { 1506 logmsg(LOG_ERR, "chdir(%s): %m", argv[0]); 1507 exit(EXIT_FAILURE); 1508 } 1509 savedir = argv[0]; 1510 argc--; 1511 argv++; 1512 } 1513 if (livecore) { 1514 if (argc > 0) 1515 usage(); 1516 1517 /* Always need /dev/mem to invoke the dump */ 1518 devs = malloc(sizeof(char *)); 1519 devs[0] = strdup("/dev/mem"); 1520 argc++; 1521 } else if (argc == 0) 1522 devs = enum_dumpdevs(&argc); 1523 else 1524 devs = devify(argc, argv); 1525 1526 savedirfd = open(savedir, O_RDONLY | O_DIRECTORY); 1527 if (savedirfd < 0) { 1528 logmsg(LOG_ERR, "open(%s): %m", savedir); 1529 exit(EXIT_FAILURE); 1530 } 1531 (void)cap_rights_init(&rights, CAP_CREATE, CAP_FCNTL, CAP_FSTATAT, 1532 CAP_FSTATFS, CAP_PREAD, CAP_SYMLINKAT, CAP_FTRUNCATE, CAP_UNLINKAT, 1533 CAP_WRITE); 1534 if (livecore) 1535 cap_rights_set(&rights, CAP_RENAMEAT_SOURCE, 1536 CAP_RENAMEAT_TARGET); 1537 if (caph_rights_limit(savedirfd, &rights) < 0) { 1538 logmsg(LOG_ERR, "cap_rights_limit(): %m"); 1539 exit(EXIT_FAILURE); 1540 } 1541 1542 /* Enter capability mode. */ 1543 init_caps(argc, devs); 1544 1545 for (i = 0; i < argc; i++) 1546 DoFile(savedir, savedirfd, devs[i]); 1547 1548 if (nfound == 0) { 1549 if (checkfor) { 1550 if (verbose) 1551 printf("No dump exists\n"); 1552 exit(EXIT_FAILURE); 1553 } 1554 if (verbose) 1555 logmsg(LOG_WARNING, "no dumps found"); 1556 } else if (nsaved == 0) { 1557 if (nerr != 0) { 1558 if (verbose) 1559 logmsg(LOG_WARNING, 1560 "unsaved dumps found but not saved"); 1561 exit(EXIT_FAILURE); 1562 } else if (verbose) 1563 logmsg(LOG_WARNING, "no unsaved dumps found"); 1564 } else if (verbose) { 1565 logmsg(LOG_NOTICE, "%d cores saved in %s\n", nsaved, savedir); 1566 } 1567 1568 exit(EXIT_SUCCESS); 1569 } 1570 1571 static void 1572 infohandler(int sig __unused) 1573 { 1574 got_siginfo = 1; 1575 } 1576