18fae3551SRodney W. Grimes /*- 28a16b7a1SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 38a16b7a1SPedro F. Giffuni * 4d503fad0SPoul-Henning Kamp * Copyright (c) 2002 Poul-Henning Kamp 5d503fad0SPoul-Henning Kamp * Copyright (c) 2002 Networks Associates Technology, Inc. 6d503fad0SPoul-Henning Kamp * All rights reserved. 7d503fad0SPoul-Henning Kamp * 8d503fad0SPoul-Henning Kamp * This software was developed for the FreeBSD Project by Poul-Henning Kamp 9d503fad0SPoul-Henning Kamp * and NAI Labs, the Security Research Division of Network Associates, Inc. 10d503fad0SPoul-Henning Kamp * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 11d503fad0SPoul-Henning Kamp * DARPA CHATS research program. 128fae3551SRodney W. Grimes * 138fae3551SRodney W. Grimes * Redistribution and use in source and binary forms, with or without 148fae3551SRodney W. Grimes * modification, are permitted provided that the following conditions 158fae3551SRodney W. Grimes * are met: 168fae3551SRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 178fae3551SRodney W. Grimes * notice, this list of conditions and the following disclaimer. 188fae3551SRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 198fae3551SRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 208fae3551SRodney W. Grimes * documentation and/or other materials provided with the distribution. 21d503fad0SPoul-Henning Kamp * 3. The names of the authors may not be used to endorse or promote 22d503fad0SPoul-Henning Kamp * products derived from this software without specific prior written 23d503fad0SPoul-Henning Kamp * permission. 248fae3551SRodney W. Grimes * 25d503fad0SPoul-Henning Kamp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 268fae3551SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 278fae3551SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28d503fad0SPoul-Henning Kamp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 298fae3551SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 308fae3551SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 318fae3551SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 328fae3551SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 338fae3551SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 348fae3551SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 358fae3551SRodney W. Grimes * SUCH DAMAGE. 36edc4f96eSBill Fenner * 37edc4f96eSBill Fenner * Copyright (c) 1986, 1992, 1993 38edc4f96eSBill Fenner * The Regents of the University of California. All rights reserved. 39edc4f96eSBill Fenner * 40edc4f96eSBill Fenner * Redistribution and use in source and binary forms, with or without 41edc4f96eSBill Fenner * modification, are permitted provided that the following conditions 42edc4f96eSBill Fenner * are met: 43edc4f96eSBill Fenner * 1. Redistributions of source code must retain the above copyright 44edc4f96eSBill Fenner * notice, this list of conditions and the following disclaimer. 45edc4f96eSBill Fenner * 2. Redistributions in binary form must reproduce the above copyright 46edc4f96eSBill Fenner * notice, this list of conditions and the following disclaimer in the 47edc4f96eSBill Fenner * documentation and/or other materials provided with the distribution. 48fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors 49edc4f96eSBill Fenner * may be used to endorse or promote products derived from this software 50edc4f96eSBill Fenner * without specific prior written permission. 51edc4f96eSBill Fenner * 52edc4f96eSBill Fenner * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 53edc4f96eSBill Fenner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 54edc4f96eSBill Fenner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 55edc4f96eSBill Fenner * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 56edc4f96eSBill Fenner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 57edc4f96eSBill Fenner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 58edc4f96eSBill Fenner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 59edc4f96eSBill Fenner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 60edc4f96eSBill Fenner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 61edc4f96eSBill Fenner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 62edc4f96eSBill Fenner * SUCH DAMAGE. 638fae3551SRodney W. Grimes */ 648fae3551SRodney W. Grimes 654b8b6734SPhilippe Charnier #include <sys/cdefs.h> 664b8b6734SPhilippe Charnier __FBSDID("$FreeBSD$"); 674b8b6734SPhilippe Charnier 68edc4f96eSBill Fenner #include <sys/param.h> 692dd527b3SPoul-Henning Kamp #include <sys/disk.h> 70d503fad0SPoul-Henning Kamp #include <sys/kerneldump.h> 713203428dSMaxime Henrion #include <sys/mount.h> 725da217f6SMarcel Moolenaar #include <sys/stat.h> 73d7fffd06SMark Johnston 74d7fffd06SMark Johnston #include <capsicum_helpers.h> 75f09cb4f2SEnji Cooper #include <ctype.h> 765da217f6SMarcel Moolenaar #include <errno.h> 775da217f6SMarcel Moolenaar #include <fcntl.h> 785da217f6SMarcel Moolenaar #include <fstab.h> 793203428dSMaxime Henrion #include <paths.h> 8045385614SRobert Millan #include <signal.h> 81edc4f96eSBill Fenner #include <stdarg.h> 82480f31c2SKonrad Witaszczyk #include <stdbool.h> 835da217f6SMarcel Moolenaar #include <stdio.h> 845da217f6SMarcel Moolenaar #include <stdlib.h> 855da217f6SMarcel Moolenaar #include <string.h> 86edc4f96eSBill Fenner #include <syslog.h> 875da217f6SMarcel Moolenaar #include <time.h> 885da217f6SMarcel Moolenaar #include <unistd.h> 89d7fffd06SMark Johnston 90d7fffd06SMark Johnston #include <libcasper.h> 91d7fffd06SMark Johnston #include <casper/cap_fileargs.h> 92d7fffd06SMark Johnston #include <casper/cap_syslog.h> 93d7fffd06SMark Johnston 94d16528b2SScott Long #include <libxo/xo.h> 955da217f6SMarcel Moolenaar 96acc66230SMarcel Moolenaar /* The size of the buffer used for I/O. */ 97acc66230SMarcel Moolenaar #define BUFFERSIZE (1024*1024) 98acc66230SMarcel Moolenaar 995fb7027cSDavid E. O'Brien #define STATUS_BAD 0 1005fb7027cSDavid E. O'Brien #define STATUS_GOOD 1 1015fb7027cSDavid E. O'Brien #define STATUS_UNKNOWN 2 1025fb7027cSDavid E. O'Brien 103d7fffd06SMark Johnston static cap_channel_t *capsyslog; 104d7fffd06SMark Johnston static fileargs_t *capfa; 1050de93324SGleb Smirnoff static bool checkfor, compress, clear, force, keep; /* flags */ 1060de93324SGleb Smirnoff static int verbose; 1079f676d2eSRuslan Ermilov static int nfound, nsaved, nerr; /* statistics */ 108eeff0b1bSPawel Jakub Dawidek static int maxdumps; 109dff462c3SKris Kennaway 110d7fffd06SMark Johnston extern FILE *zdopen(int, const char *); 111edc4f96eSBill Fenner 1120d239eefSUlf Lilleengen static sig_atomic_t got_siginfo; 1130d239eefSUlf Lilleengen static void infohandler(int); 1140d239eefSUlf Lilleengen 115d503fad0SPoul-Henning Kamp static void 116d7fffd06SMark Johnston logmsg(int pri, const char *fmt, ...) 117d7fffd06SMark Johnston { 118d7fffd06SMark Johnston va_list ap; 119d7fffd06SMark Johnston 120d7fffd06SMark Johnston va_start(ap, fmt); 121d7fffd06SMark Johnston if (capsyslog != NULL) 122d7fffd06SMark Johnston cap_vsyslog(capsyslog, pri, fmt, ap); 123d7fffd06SMark Johnston else 124d7fffd06SMark Johnston vsyslog(pri, fmt, ap); 125d7fffd06SMark Johnston va_end(ap); 126d7fffd06SMark Johnston } 127d7fffd06SMark Johnston 128d7fffd06SMark Johnston static FILE * 129d7fffd06SMark Johnston xfopenat(int dirfd, const char *path, int flags, const char *modestr, ...) 130d7fffd06SMark Johnston { 131d7fffd06SMark Johnston va_list ap; 132d7fffd06SMark Johnston FILE *fp; 133d7fffd06SMark Johnston mode_t mode; 134d7fffd06SMark Johnston int error, fd; 135d7fffd06SMark Johnston 136d7fffd06SMark Johnston if ((flags & O_CREAT) == O_CREAT) { 137d7fffd06SMark Johnston va_start(ap, modestr); 138d7fffd06SMark Johnston mode = (mode_t)va_arg(ap, int); 139d7fffd06SMark Johnston va_end(ap); 140d7fffd06SMark Johnston } else 141d7fffd06SMark Johnston mode = 0; 142d7fffd06SMark Johnston 143d7fffd06SMark Johnston fd = openat(dirfd, path, flags, mode); 144d7fffd06SMark Johnston if (fd < 0) 145d7fffd06SMark Johnston return (NULL); 146d7fffd06SMark Johnston fp = fdopen(fd, modestr); 147d7fffd06SMark Johnston if (fp == NULL) { 148d7fffd06SMark Johnston error = errno; 149d7fffd06SMark Johnston (void)close(fd); 150d7fffd06SMark Johnston errno = error; 151d7fffd06SMark Johnston } 152d7fffd06SMark Johnston return (fp); 153d7fffd06SMark Johnston } 154d7fffd06SMark Johnston 155d7fffd06SMark Johnston static void 156cf8eb490SMark Johnston printheader(xo_handle_t *xo, const struct kerneldumpheader *h, 157cf8eb490SMark Johnston const char *device, int bounds, const int status) 158d503fad0SPoul-Henning Kamp { 1595cb87b0cSMarcel Moolenaar uint64_t dumplen; 160d503fad0SPoul-Henning Kamp time_t t; 161a532f299SEric van Gyzen struct tm tm; 162a532f299SEric van Gyzen char time_str[64]; 1635fb7027cSDavid E. O'Brien const char *stat_str; 1646026dcd7SMark Johnston const char *comp_str; 1658fae3551SRodney W. Grimes 166d16528b2SScott Long xo_flush_h(xo); 167cf8eb490SMark Johnston xo_emit_h(xo, "{Lwc:Dump header from device}{:dump_device/%s}\n", 168cf8eb490SMark Johnston device); 169cf8eb490SMark Johnston xo_emit_h(xo, "{P: }{Lwc:Architecture}{:architecture/%s}\n", 170cf8eb490SMark Johnston h->architecture); 171cf8eb490SMark Johnston xo_emit_h(xo, 172cf8eb490SMark Johnston "{P: }{Lwc:Architecture Version}{:architecture_version/%u}\n", 173cf8eb490SMark Johnston dtoh32(h->architectureversion)); 1745cb87b0cSMarcel Moolenaar dumplen = dtoh64(h->dumplength); 175cf8eb490SMark Johnston xo_emit_h(xo, "{P: }{Lwc:Dump Length}{:dump_length_bytes/%lld}\n", 176cf8eb490SMark Johnston (long long)dumplen); 177cf8eb490SMark Johnston xo_emit_h(xo, "{P: }{Lwc:Blocksize}{:blocksize/%d}\n", 178cf8eb490SMark Johnston dtoh32(h->blocksize)); 1796026dcd7SMark Johnston switch (h->compression) { 1806026dcd7SMark Johnston case KERNELDUMP_COMP_NONE: 1816026dcd7SMark Johnston comp_str = "none"; 1826026dcd7SMark Johnston break; 1836026dcd7SMark Johnston case KERNELDUMP_COMP_GZIP: 1846026dcd7SMark Johnston comp_str = "gzip"; 1856026dcd7SMark Johnston break; 1866026dcd7SMark Johnston case KERNELDUMP_COMP_ZSTD: 1876026dcd7SMark Johnston comp_str = "zstd"; 1886026dcd7SMark Johnston break; 1896026dcd7SMark Johnston default: 1906026dcd7SMark Johnston comp_str = "???"; 1916026dcd7SMark Johnston break; 1926026dcd7SMark Johnston } 1936026dcd7SMark Johnston xo_emit_h(xo, "{P: }{Lwc:Compression}{:compression/%s}\n", comp_str); 1945cb87b0cSMarcel Moolenaar t = dtoh64(h->dumptime); 195a532f299SEric van Gyzen localtime_r(&t, &tm); 196a532f299SEric van Gyzen if (strftime(time_str, sizeof(time_str), "%F %T %z", &tm) == 0) 197a532f299SEric van Gyzen time_str[0] = '\0'; 198a532f299SEric van Gyzen xo_emit_h(xo, "{P: }{Lwc:Dumptime}{:dumptime/%s}\n", time_str); 199d16528b2SScott Long xo_emit_h(xo, "{P: }{Lwc:Hostname}{:hostname/%s}\n", h->hostname); 200d16528b2SScott Long xo_emit_h(xo, "{P: }{Lwc:Magic}{:magic/%s}\n", h->magic); 201cf8eb490SMark Johnston xo_emit_h(xo, "{P: }{Lwc:Version String}{:version_string/%s}", 202cf8eb490SMark Johnston h->versionstring); 203cf8eb490SMark Johnston xo_emit_h(xo, "{P: }{Lwc:Panic String}{:panic_string/%s}\n", 204cf8eb490SMark Johnston h->panicstring); 205d16528b2SScott Long xo_emit_h(xo, "{P: }{Lwc:Dump Parity}{:dump_parity/%u}\n", h->parity); 206d16528b2SScott Long xo_emit_h(xo, "{P: }{Lwc:Bounds}{:bounds/%d}\n", bounds); 2075fb7027cSDavid E. O'Brien 2085fb7027cSDavid E. O'Brien switch (status) { 2095fb7027cSDavid E. O'Brien case STATUS_BAD: 2105fb7027cSDavid E. O'Brien stat_str = "bad"; 2115fb7027cSDavid E. O'Brien break; 2125fb7027cSDavid E. O'Brien case STATUS_GOOD: 2135fb7027cSDavid E. O'Brien stat_str = "good"; 2145fb7027cSDavid E. O'Brien break; 2155fb7027cSDavid E. O'Brien default: 2165fb7027cSDavid E. O'Brien stat_str = "unknown"; 217cf8eb490SMark Johnston break; 2185fb7027cSDavid E. O'Brien } 219d16528b2SScott Long xo_emit_h(xo, "{P: }{Lwc:Dump Status}{:dump_status/%s}\n", stat_str); 220d16528b2SScott Long xo_flush_h(xo); 221d503fad0SPoul-Henning Kamp } 2228fae3551SRodney W. Grimes 223edc4f96eSBill Fenner static int 224d7fffd06SMark Johnston getbounds(int savedirfd) 225cf8eb490SMark Johnston { 226edc4f96eSBill Fenner FILE *fp; 227edc4f96eSBill Fenner char buf[6]; 228edc4f96eSBill Fenner int ret; 229edc4f96eSBill Fenner 230b761400bSMike Silbersack /* 231b761400bSMike Silbersack * If we are just checking, then we haven't done a chdir to the dump 232b761400bSMike Silbersack * directory and we should not try to read a bounds file. 233b761400bSMike Silbersack */ 234b761400bSMike Silbersack if (checkfor) 235b761400bSMike Silbersack return (0); 236b761400bSMike Silbersack 237edc4f96eSBill Fenner ret = 0; 238edc4f96eSBill Fenner 239d7fffd06SMark Johnston if ((fp = xfopenat(savedirfd, "bounds", O_RDONLY, "r")) == NULL) { 240d32e7ba7SRobert Watson if (verbose) 241d32e7ba7SRobert Watson printf("unable to open bounds file, using 0\n"); 2429520784eSDoug White return (ret); 243edc4f96eSBill Fenner } 244d7fffd06SMark Johnston if (fgets(buf, sizeof(buf), fp) == NULL) { 2455e93a4b4SBryan Drewery if (feof(fp)) 246d7fffd06SMark Johnston logmsg(LOG_WARNING, "bounds file is empty, using 0"); 2475e93a4b4SBryan Drewery else 248d7fffd06SMark Johnston logmsg(LOG_WARNING, "bounds file: %s", strerror(errno)); 249edc4f96eSBill Fenner fclose(fp); 2509520784eSDoug White return (ret); 251edc4f96eSBill Fenner } 252edc4f96eSBill Fenner 253edc4f96eSBill Fenner errno = 0; 254edc4f96eSBill Fenner ret = (int)strtol(buf, NULL, 10); 255edc4f96eSBill Fenner if (ret == 0 && (errno == EINVAL || errno == ERANGE)) 256d7fffd06SMark Johnston logmsg(LOG_WARNING, "invalid value found in bounds, using 0"); 2575e93a4b4SBryan Drewery fclose(fp); 2589520784eSDoug White return (ret); 2599520784eSDoug White } 260edc4f96eSBill Fenner 2619520784eSDoug White static void 262d7fffd06SMark Johnston writebounds(int savedirfd, int bounds) 263cf8eb490SMark Johnston { 2649520784eSDoug White FILE *fp; 265edc4f96eSBill Fenner 266d7fffd06SMark Johnston if ((fp = xfopenat(savedirfd, "bounds", O_WRONLY | O_CREAT | O_TRUNC, 2672e4c75c1SMark Johnston "w", 0644)) == NULL) { 268d7fffd06SMark Johnston logmsg(LOG_WARNING, "unable to write to bounds file: %m"); 2699520784eSDoug White return; 270edc4f96eSBill Fenner } 271edc4f96eSBill Fenner 272edc4f96eSBill Fenner if (verbose) 2739520784eSDoug White printf("bounds number: %d\n", bounds); 274edc4f96eSBill Fenner 2759520784eSDoug White fprintf(fp, "%d\n", bounds); 276edc4f96eSBill Fenner fclose(fp); 277edc4f96eSBill Fenner } 278edc4f96eSBill Fenner 279480f31c2SKonrad Witaszczyk static bool 280d7fffd06SMark Johnston writekey(int savedirfd, const char *keyname, uint8_t *dumpkey, 281d7fffd06SMark Johnston uint32_t dumpkeysize) 282480f31c2SKonrad Witaszczyk { 283480f31c2SKonrad Witaszczyk int fd; 284480f31c2SKonrad Witaszczyk 285d7fffd06SMark Johnston fd = openat(savedirfd, keyname, O_WRONLY | O_CREAT | O_TRUNC, 0600); 286480f31c2SKonrad Witaszczyk if (fd == -1) { 287d7fffd06SMark Johnston logmsg(LOG_ERR, "Unable to open %s to write the key: %m.", 288480f31c2SKonrad Witaszczyk keyname); 289480f31c2SKonrad Witaszczyk return (false); 290480f31c2SKonrad Witaszczyk } 291480f31c2SKonrad Witaszczyk 292480f31c2SKonrad Witaszczyk if (write(fd, dumpkey, dumpkeysize) != (ssize_t)dumpkeysize) { 293d7fffd06SMark Johnston logmsg(LOG_ERR, "Unable to write the key to %s: %m.", keyname); 294480f31c2SKonrad Witaszczyk close(fd); 295480f31c2SKonrad Witaszczyk return (false); 296480f31c2SKonrad Witaszczyk } 297480f31c2SKonrad Witaszczyk 298480f31c2SKonrad Witaszczyk close(fd); 299480f31c2SKonrad Witaszczyk return (true); 300480f31c2SKonrad Witaszczyk } 301480f31c2SKonrad Witaszczyk 302eeff0b1bSPawel Jakub Dawidek static off_t 303d7fffd06SMark Johnston file_size(int savedirfd, const char *path) 304eeff0b1bSPawel Jakub Dawidek { 305eeff0b1bSPawel Jakub Dawidek struct stat sb; 306eeff0b1bSPawel Jakub Dawidek 307d7fffd06SMark Johnston /* Ignore all errors, this file may not exist. */ 308d7fffd06SMark Johnston if (fstatat(savedirfd, path, &sb, 0) == -1) 309eeff0b1bSPawel Jakub Dawidek return (0); 310eeff0b1bSPawel Jakub Dawidek return (sb.st_size); 311eeff0b1bSPawel Jakub Dawidek } 312eeff0b1bSPawel Jakub Dawidek 313eeff0b1bSPawel Jakub Dawidek static off_t 314d7fffd06SMark Johnston saved_dump_size(int savedirfd, int bounds) 315eeff0b1bSPawel Jakub Dawidek { 316eeff0b1bSPawel Jakub Dawidek static char path[PATH_MAX]; 317eeff0b1bSPawel Jakub Dawidek off_t dumpsize; 318eeff0b1bSPawel Jakub Dawidek 319eeff0b1bSPawel Jakub Dawidek dumpsize = 0; 320eeff0b1bSPawel Jakub Dawidek 321eeff0b1bSPawel Jakub Dawidek (void)snprintf(path, sizeof(path), "info.%d", bounds); 322d7fffd06SMark Johnston dumpsize += file_size(savedirfd, path); 323eeff0b1bSPawel Jakub Dawidek (void)snprintf(path, sizeof(path), "vmcore.%d", bounds); 324d7fffd06SMark Johnston dumpsize += file_size(savedirfd, path); 325eeff0b1bSPawel Jakub Dawidek (void)snprintf(path, sizeof(path), "vmcore.%d.gz", bounds); 326d7fffd06SMark Johnston dumpsize += file_size(savedirfd, path); 3276026dcd7SMark Johnston (void)snprintf(path, sizeof(path), "vmcore.%d.zst", bounds); 328d7fffd06SMark Johnston dumpsize += file_size(savedirfd, path); 329eeff0b1bSPawel Jakub Dawidek (void)snprintf(path, sizeof(path), "textdump.tar.%d", bounds); 330d7fffd06SMark Johnston dumpsize += file_size(savedirfd, path); 331eeff0b1bSPawel Jakub Dawidek (void)snprintf(path, sizeof(path), "textdump.tar.%d.gz", bounds); 332d7fffd06SMark Johnston dumpsize += file_size(savedirfd, path); 333eeff0b1bSPawel Jakub Dawidek 334eeff0b1bSPawel Jakub Dawidek return (dumpsize); 335eeff0b1bSPawel Jakub Dawidek } 336eeff0b1bSPawel Jakub Dawidek 337eeff0b1bSPawel Jakub Dawidek static void 338d7fffd06SMark Johnston saved_dump_remove(int savedirfd, int bounds) 339eeff0b1bSPawel Jakub Dawidek { 340eeff0b1bSPawel Jakub Dawidek static char path[PATH_MAX]; 341eeff0b1bSPawel Jakub Dawidek 342eeff0b1bSPawel Jakub Dawidek (void)snprintf(path, sizeof(path), "info.%d", bounds); 343d7fffd06SMark Johnston (void)unlinkat(savedirfd, path, 0); 344eeff0b1bSPawel Jakub Dawidek (void)snprintf(path, sizeof(path), "vmcore.%d", bounds); 345d7fffd06SMark Johnston (void)unlinkat(savedirfd, path, 0); 346eeff0b1bSPawel Jakub Dawidek (void)snprintf(path, sizeof(path), "vmcore.%d.gz", bounds); 347d7fffd06SMark Johnston (void)unlinkat(savedirfd, path, 0); 3486026dcd7SMark Johnston (void)snprintf(path, sizeof(path), "vmcore.%d.zst", bounds); 349d7fffd06SMark Johnston (void)unlinkat(savedirfd, path, 0); 350eeff0b1bSPawel Jakub Dawidek (void)snprintf(path, sizeof(path), "textdump.tar.%d", bounds); 351d7fffd06SMark Johnston (void)unlinkat(savedirfd, path, 0); 352eeff0b1bSPawel Jakub Dawidek (void)snprintf(path, sizeof(path), "textdump.tar.%d.gz", bounds); 353d7fffd06SMark Johnston (void)unlinkat(savedirfd, path, 0); 354eeff0b1bSPawel Jakub Dawidek } 355eeff0b1bSPawel Jakub Dawidek 356165557e2SPawel Jakub Dawidek static void 357d7fffd06SMark Johnston symlinks_remove(int savedirfd) 358165557e2SPawel Jakub Dawidek { 359165557e2SPawel Jakub Dawidek 360d7fffd06SMark Johnston (void)unlinkat(savedirfd, "info.last", 0); 361d7fffd06SMark Johnston (void)unlinkat(savedirfd, "key.last", 0); 362d7fffd06SMark Johnston (void)unlinkat(savedirfd, "vmcore.last", 0); 363d7fffd06SMark Johnston (void)unlinkat(savedirfd, "vmcore.last.gz", 0); 364d7fffd06SMark Johnston (void)unlinkat(savedirfd, "vmcore.last.zst", 0); 365d7fffd06SMark Johnston (void)unlinkat(savedirfd, "vmcore_encrypted.last", 0); 366d7fffd06SMark Johnston (void)unlinkat(savedirfd, "vmcore_encrypted.last.gz", 0); 367d7fffd06SMark Johnston (void)unlinkat(savedirfd, "textdump.tar.last", 0); 368d7fffd06SMark Johnston (void)unlinkat(savedirfd, "textdump.tar.last.gz", 0); 369165557e2SPawel Jakub Dawidek } 370165557e2SPawel Jakub Dawidek 3713203428dSMaxime Henrion /* 3723203428dSMaxime Henrion * Check that sufficient space is available on the disk that holds the 3733203428dSMaxime Henrion * save directory. 3743203428dSMaxime Henrion */ 3753203428dSMaxime Henrion static int 376d7fffd06SMark Johnston check_space(const char *savedir, int savedirfd, off_t dumpsize, int bounds) 3773203428dSMaxime Henrion { 378d7fffd06SMark Johnston char buf[100]; 379d7fffd06SMark Johnston struct statfs fsbuf; 3803203428dSMaxime Henrion FILE *fp; 381f09cb4f2SEnji Cooper off_t available, minfree, spacefree, totfree, needed; 3823203428dSMaxime Henrion 383d7fffd06SMark Johnston if (fstatfs(savedirfd, &fsbuf) < 0) { 384d7fffd06SMark Johnston logmsg(LOG_ERR, "%s: %m", savedir); 385edc4f96eSBill Fenner exit(1); 386edc4f96eSBill Fenner } 3873203428dSMaxime Henrion spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024; 3883203428dSMaxime Henrion totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024; 3893203428dSMaxime Henrion 390d7fffd06SMark Johnston if ((fp = xfopenat(savedirfd, "minfree", O_RDONLY, "r")) == NULL) 3913203428dSMaxime Henrion minfree = 0; 3923203428dSMaxime Henrion else { 3933203428dSMaxime Henrion if (fgets(buf, sizeof(buf), fp) == NULL) 3943203428dSMaxime Henrion minfree = 0; 395f09cb4f2SEnji Cooper else { 396f09cb4f2SEnji Cooper char *endp; 397f09cb4f2SEnji Cooper 398f09cb4f2SEnji Cooper errno = 0; 399f09cb4f2SEnji Cooper minfree = strtoll(buf, &endp, 10); 400f09cb4f2SEnji Cooper if (minfree == 0 && errno != 0) 401f09cb4f2SEnji Cooper minfree = -1; 402f09cb4f2SEnji Cooper else { 403f09cb4f2SEnji Cooper while (*endp != '\0' && isspace(*endp)) 404f09cb4f2SEnji Cooper endp++; 405f09cb4f2SEnji Cooper if (*endp != '\0' || minfree < 0) 406f09cb4f2SEnji Cooper minfree = -1; 407f09cb4f2SEnji Cooper } 408f09cb4f2SEnji Cooper if (minfree < 0) 409d7fffd06SMark Johnston logmsg(LOG_WARNING, 410f09cb4f2SEnji Cooper "`minfree` didn't contain a valid size " 411f09cb4f2SEnji Cooper "(`%s`). Defaulting to 0", buf); 412f09cb4f2SEnji Cooper } 4133203428dSMaxime Henrion (void)fclose(fp); 4143203428dSMaxime Henrion } 4153203428dSMaxime Henrion 416f09cb4f2SEnji Cooper available = minfree > 0 ? spacefree - minfree : totfree; 417edc4f96eSBill Fenner needed = dumpsize / 1024 + 2; /* 2 for info file */ 418d7fffd06SMark Johnston needed -= saved_dump_size(savedirfd, bounds); 419f09cb4f2SEnji Cooper if (available < needed) { 420d7fffd06SMark Johnston logmsg(LOG_WARNING, 421f09cb4f2SEnji Cooper "no dump: not enough free space on device (need at least " 4226f11c9caSEnji Cooper "%jdkB for dump; %jdkB available; %jdkB reserved)", 423f09cb4f2SEnji Cooper (intmax_t)needed, 424f09cb4f2SEnji Cooper (intmax_t)available + minfree, 425f09cb4f2SEnji Cooper (intmax_t)minfree); 4263203428dSMaxime Henrion return (0); 4273203428dSMaxime Henrion } 4283203428dSMaxime Henrion if (spacefree - needed < 0) 429d7fffd06SMark Johnston logmsg(LOG_WARNING, 430edc4f96eSBill Fenner "dump performed, but free space threshold crossed"); 4313203428dSMaxime Henrion return (1); 4323203428dSMaxime Henrion } 4333203428dSMaxime Henrion 4344e287bd8SMark Johnston static bool 4354e287bd8SMark Johnston compare_magic(const struct kerneldumpheader *kdh, const char *magic) 4364e287bd8SMark Johnston { 4374e287bd8SMark Johnston 4384e287bd8SMark Johnston return (strncmp(kdh->magic, magic, sizeof(kdh->magic)) == 0); 4394e287bd8SMark Johnston } 4404e287bd8SMark Johnston 441edc4f96eSBill Fenner #define BLOCKSIZE (1<<12) 442edc4f96eSBill Fenner #define BLOCKMASK (~(BLOCKSIZE-1)) 4438fae3551SRodney W. Grimes 44410187caeSRobert Watson static int 44564a16434SMark Johnston DoRegularFile(int fd, off_t dumpsize, u_int sectorsize, bool sparse, char *buf, 446480f31c2SKonrad Witaszczyk const char *device, const char *filename, FILE *fp) 44710187caeSRobert Watson { 44810187caeSRobert Watson int he, hs, nr, nw, wl; 4490d239eefSUlf Lilleengen off_t dmpcnt, origsize; 45010187caeSRobert Watson 45110187caeSRobert Watson dmpcnt = 0; 4520d239eefSUlf Lilleengen origsize = dumpsize; 45310187caeSRobert Watson he = 0; 45410187caeSRobert Watson while (dumpsize > 0) { 45510187caeSRobert Watson wl = BUFFERSIZE; 45610187caeSRobert Watson if (wl > dumpsize) 45710187caeSRobert Watson wl = dumpsize; 45864a16434SMark Johnston nr = read(fd, buf, roundup(wl, sectorsize)); 45964a16434SMark Johnston if (nr != (int)roundup(wl, sectorsize)) { 46010187caeSRobert Watson if (nr == 0) 461d7fffd06SMark Johnston logmsg(LOG_WARNING, 46210187caeSRobert Watson "WARNING: EOF on dump device"); 46310187caeSRobert Watson else 464d7fffd06SMark Johnston logmsg(LOG_ERR, "read error on %s: %m", device); 46510187caeSRobert Watson nerr++; 46610187caeSRobert Watson return (-1); 46710187caeSRobert Watson } 46864a16434SMark Johnston if (!sparse) { 46910187caeSRobert Watson nw = fwrite(buf, 1, wl, fp); 47010187caeSRobert Watson } else { 47110187caeSRobert Watson for (nw = 0; nw < nr; nw = he) { 47210187caeSRobert Watson /* find a contiguous block of zeroes */ 47310187caeSRobert Watson for (hs = nw; hs < nr; hs += BLOCKSIZE) { 47410187caeSRobert Watson for (he = hs; he < nr && buf[he] == 0; 47510187caeSRobert Watson ++he) 47610187caeSRobert Watson /* nothing */ ; 47710187caeSRobert Watson /* is the hole long enough to matter? */ 47810187caeSRobert Watson if (he >= hs + BLOCKSIZE) 47910187caeSRobert Watson break; 48010187caeSRobert Watson } 48110187caeSRobert Watson 48210187caeSRobert Watson /* back down to a block boundary */ 48310187caeSRobert Watson he &= BLOCKMASK; 48410187caeSRobert Watson 48510187caeSRobert Watson /* 48610187caeSRobert Watson * 1) Don't go beyond the end of the buffer. 48710187caeSRobert Watson * 2) If the end of the buffer is less than 48810187caeSRobert Watson * BLOCKSIZE bytes away, we're at the end 48910187caeSRobert Watson * of the file, so just grab what's left. 49010187caeSRobert Watson */ 49110187caeSRobert Watson if (hs + BLOCKSIZE > nr) 49210187caeSRobert Watson hs = he = nr; 49310187caeSRobert Watson 49410187caeSRobert Watson /* 49510187caeSRobert Watson * At this point, we have a partial ordering: 49610187caeSRobert Watson * nw <= hs <= he <= nr 497cf8eb490SMark Johnston * If hs > nw, buf[nw..hs] contains non-zero 498cf8eb490SMark Johnston * data. If he > hs, buf[hs..he] is all zeroes. 49910187caeSRobert Watson */ 50010187caeSRobert Watson if (hs > nw) 50110187caeSRobert Watson if (fwrite(buf + nw, hs - nw, 1, fp) 50210187caeSRobert Watson != 1) 50310187caeSRobert Watson break; 50410187caeSRobert Watson if (he > hs) 50510187caeSRobert Watson if (fseeko(fp, he - hs, SEEK_CUR) == -1) 50610187caeSRobert Watson break; 50710187caeSRobert Watson } 50810187caeSRobert Watson } 50910187caeSRobert Watson if (nw != wl) { 510d7fffd06SMark Johnston logmsg(LOG_ERR, 51110187caeSRobert Watson "write error on %s file: %m", filename); 512d7fffd06SMark Johnston logmsg(LOG_WARNING, 51310187caeSRobert Watson "WARNING: vmcore may be incomplete"); 51410187caeSRobert Watson nerr++; 51510187caeSRobert Watson return (-1); 51610187caeSRobert Watson } 51710187caeSRobert Watson if (verbose) { 51810187caeSRobert Watson dmpcnt += wl; 51910187caeSRobert Watson printf("%llu\r", (unsigned long long)dmpcnt); 52010187caeSRobert Watson fflush(stdout); 52110187caeSRobert Watson } 52210187caeSRobert Watson dumpsize -= wl; 5230d239eefSUlf Lilleengen if (got_siginfo) { 5240d239eefSUlf Lilleengen printf("%s %.1lf%%\n", filename, (100.0 - (100.0 * 5250d239eefSUlf Lilleengen (double)dumpsize / (double)origsize))); 5260d239eefSUlf Lilleengen got_siginfo = 0; 5270d239eefSUlf Lilleengen } 52810187caeSRobert Watson } 52910187caeSRobert Watson return (0); 53010187caeSRobert Watson } 53110187caeSRobert Watson 53210187caeSRobert Watson /* 53310187caeSRobert Watson * Specialized version of dump-reading logic for use with textdumps, which 53410187caeSRobert Watson * are written backwards from the end of the partition, and must be reversed 53510187caeSRobert Watson * before being written to the file. Textdumps are small, so do a bit less 53610187caeSRobert Watson * work to optimize/sparsify. 53710187caeSRobert Watson */ 53810187caeSRobert Watson static int 53910187caeSRobert Watson DoTextdumpFile(int fd, off_t dumpsize, off_t lasthd, char *buf, 54010187caeSRobert Watson const char *device, const char *filename, FILE *fp) 54110187caeSRobert Watson { 54210187caeSRobert Watson int nr, nw, wl; 54310187caeSRobert Watson off_t dmpcnt, totsize; 54410187caeSRobert Watson 54510187caeSRobert Watson totsize = dumpsize; 54610187caeSRobert Watson dmpcnt = 0; 54710187caeSRobert Watson wl = 512; 54810187caeSRobert Watson if ((dumpsize % wl) != 0) { 549d7fffd06SMark Johnston logmsg(LOG_ERR, "textdump uneven multiple of 512 on %s", 55010187caeSRobert Watson device); 55110187caeSRobert Watson nerr++; 55210187caeSRobert Watson return (-1); 55310187caeSRobert Watson } 55410187caeSRobert Watson while (dumpsize > 0) { 55510187caeSRobert Watson nr = pread(fd, buf, wl, lasthd - (totsize - dumpsize) - wl); 55610187caeSRobert Watson if (nr != wl) { 55710187caeSRobert Watson if (nr == 0) 558d7fffd06SMark Johnston logmsg(LOG_WARNING, 55910187caeSRobert Watson "WARNING: EOF on dump device"); 56010187caeSRobert Watson else 561d7fffd06SMark Johnston logmsg(LOG_ERR, "read error on %s: %m", device); 56210187caeSRobert Watson nerr++; 56310187caeSRobert Watson return (-1); 56410187caeSRobert Watson } 56510187caeSRobert Watson nw = fwrite(buf, 1, wl, fp); 56610187caeSRobert Watson if (nw != wl) { 567d7fffd06SMark Johnston logmsg(LOG_ERR, 56810187caeSRobert Watson "write error on %s file: %m", filename); 569d7fffd06SMark Johnston logmsg(LOG_WARNING, 57010187caeSRobert Watson "WARNING: textdump may be incomplete"); 57110187caeSRobert Watson nerr++; 57210187caeSRobert Watson return (-1); 57310187caeSRobert Watson } 57410187caeSRobert Watson if (verbose) { 57510187caeSRobert Watson dmpcnt += wl; 57610187caeSRobert Watson printf("%llu\r", (unsigned long long)dmpcnt); 57710187caeSRobert Watson fflush(stdout); 57810187caeSRobert Watson } 57910187caeSRobert Watson dumpsize -= wl; 58010187caeSRobert Watson } 58110187caeSRobert Watson return (0); 58210187caeSRobert Watson } 58310187caeSRobert Watson 584d503fad0SPoul-Henning Kamp static void 585d7fffd06SMark Johnston DoFile(const char *savedir, int savedirfd, const char *device) 586d503fad0SPoul-Henning Kamp { 587d16528b2SScott Long xo_handle_t *xostdout, *xoinfo; 588165557e2SPawel Jakub Dawidek static char infoname[PATH_MAX], corename[PATH_MAX], linkname[PATH_MAX]; 589480f31c2SKonrad Witaszczyk static char keyname[PATH_MAX]; 5908df55592SConrad Meyer static char *buf = NULL; 5918df55592SConrad Meyer char *temp = NULL; 592d503fad0SPoul-Henning Kamp struct kerneldumpheader kdhf, kdhl; 593480f31c2SKonrad Witaszczyk uint8_t *dumpkey; 59464a16434SMark Johnston off_t mediasize, dumpextent, dumplength, firsthd, lasthd; 595d7fffd06SMark Johnston FILE *core, *info; 596d7fffd06SMark Johnston int fdcore, fddev, error; 5975fb7027cSDavid E. O'Brien int bounds, status; 598d16528b2SScott Long u_int sectorsize, xostyle; 599480f31c2SKonrad Witaszczyk uint32_t dumpkeysize; 60064a16434SMark Johnston bool iscompressed, isencrypted, istextdump, ret; 601edc4f96eSBill Fenner 602d7fffd06SMark Johnston bounds = getbounds(savedirfd); 6033c7ccf15SKonrad Witaszczyk dumpkey = NULL; 604edc4f96eSBill Fenner mediasize = 0; 6055fb7027cSDavid E. O'Brien status = STATUS_UNKNOWN; 6065da217f6SMarcel Moolenaar 607d16528b2SScott Long xostdout = xo_create_to_file(stdout, XO_STYLE_TEXT, 0); 608d16528b2SScott Long if (xostdout == NULL) { 609d7fffd06SMark Johnston logmsg(LOG_ERR, "%s: %m", infoname); 610d16528b2SScott Long return; 611d16528b2SScott Long } 612d16528b2SScott Long 613eeff0b1bSPawel Jakub Dawidek if (maxdumps > 0 && bounds == maxdumps) 614eeff0b1bSPawel Jakub Dawidek bounds = 0; 615eeff0b1bSPawel Jakub Dawidek 6166db5f8a7SMarcel Moolenaar if (buf == NULL) { 617acc66230SMarcel Moolenaar buf = malloc(BUFFERSIZE); 6186db5f8a7SMarcel Moolenaar if (buf == NULL) { 619d7fffd06SMark Johnston logmsg(LOG_ERR, "%m"); 6206db5f8a7SMarcel Moolenaar return; 6216db5f8a7SMarcel Moolenaar } 6226db5f8a7SMarcel Moolenaar } 6236db5f8a7SMarcel Moolenaar 6245da217f6SMarcel Moolenaar if (verbose) 625edc4f96eSBill Fenner printf("checking for kernel dump on device %s\n", device); 6268fae3551SRodney W. Grimes 627d7fffd06SMark Johnston fddev = fileargs_open(capfa, device); 628d7fffd06SMark Johnston if (fddev < 0) { 629d7fffd06SMark Johnston logmsg(LOG_ERR, "%s: %m", device); 630d503fad0SPoul-Henning Kamp return; 631d503fad0SPoul-Henning Kamp } 6326db5f8a7SMarcel Moolenaar 633d7fffd06SMark Johnston error = ioctl(fddev, DIOCGMEDIASIZE, &mediasize); 634d503fad0SPoul-Henning Kamp if (!error) 635d7fffd06SMark Johnston error = ioctl(fddev, DIOCGSECTORSIZE, §orsize); 636d503fad0SPoul-Henning Kamp if (error) { 637d7fffd06SMark Johnston logmsg(LOG_ERR, 638edc4f96eSBill Fenner "couldn't find media and/or sector size of %s: %m", device); 6395da217f6SMarcel Moolenaar goto closefd; 640d503fad0SPoul-Henning Kamp } 6415da217f6SMarcel Moolenaar 6425da217f6SMarcel Moolenaar if (verbose) { 643397b5714SEnji Cooper printf("mediasize = %lld bytes\n", (long long)mediasize); 644397b5714SEnji Cooper printf("sectorsize = %u bytes\n", sectorsize); 6455da217f6SMarcel Moolenaar } 6465da217f6SMarcel Moolenaar 6475dc5dab6SConrad Meyer if (sectorsize < sizeof(kdhl)) { 648d7fffd06SMark Johnston logmsg(LOG_ERR, 6495dc5dab6SConrad Meyer "Sector size is less the kernel dump header %zu", 6505dc5dab6SConrad Meyer sizeof(kdhl)); 6515dc5dab6SConrad Meyer goto closefd; 6525dc5dab6SConrad Meyer } 6535dc5dab6SConrad Meyer 654d503fad0SPoul-Henning Kamp lasthd = mediasize - sectorsize; 6555dc5dab6SConrad Meyer temp = malloc(sectorsize); 6565dc5dab6SConrad Meyer if (temp == NULL) { 657d7fffd06SMark Johnston logmsg(LOG_ERR, "%m"); 6583ae587fdSEnji Cooper goto closefd; 6595dc5dab6SConrad Meyer } 660d7fffd06SMark Johnston if (lseek(fddev, lasthd, SEEK_SET) != lasthd || 661d7fffd06SMark Johnston read(fddev, temp, sectorsize) != (ssize_t)sectorsize) { 662d7fffd06SMark Johnston logmsg(LOG_ERR, 663edc4f96eSBill Fenner "error reading last dump header at offset %lld in %s: %m", 6645da217f6SMarcel Moolenaar (long long)lasthd, device); 6655da217f6SMarcel Moolenaar goto closefd; 666d503fad0SPoul-Henning Kamp } 6675dc5dab6SConrad Meyer memcpy(&kdhl, temp, sizeof(kdhl)); 66864a16434SMark Johnston iscompressed = istextdump = false; 6694e287bd8SMark Johnston if (compare_magic(&kdhl, TEXTDUMPMAGIC)) { 67010187caeSRobert Watson if (verbose) 67110187caeSRobert Watson printf("textdump magic on last dump header on %s\n", 67210187caeSRobert Watson device); 67364a16434SMark Johnston istextdump = true; 674df0b82f1SRobert Watson if (dtoh32(kdhl.version) != KERNELDUMP_TEXT_VERSION) { 675d7fffd06SMark Johnston logmsg(LOG_ERR, 676df0b82f1SRobert Watson "unknown version (%d) in last dump header on %s", 677df0b82f1SRobert Watson dtoh32(kdhl.version), device); 678df0b82f1SRobert Watson 679df0b82f1SRobert Watson status = STATUS_BAD; 680*32fbec42SGleb Smirnoff if (!force) 681df0b82f1SRobert Watson goto closefd; 682df0b82f1SRobert Watson } 6834e287bd8SMark Johnston } else if (compare_magic(&kdhl, KERNELDUMPMAGIC)) { 684df0b82f1SRobert Watson if (dtoh32(kdhl.version) != KERNELDUMPVERSION) { 685d7fffd06SMark Johnston logmsg(LOG_ERR, 686df0b82f1SRobert Watson "unknown version (%d) in last dump header on %s", 687df0b82f1SRobert Watson dtoh32(kdhl.version), device); 688df0b82f1SRobert Watson 689df0b82f1SRobert Watson status = STATUS_BAD; 690*32fbec42SGleb Smirnoff if (!force) 691df0b82f1SRobert Watson goto closefd; 692df0b82f1SRobert Watson } 69364a16434SMark Johnston switch (kdhl.compression) { 69464a16434SMark Johnston case KERNELDUMP_COMP_NONE: 69564a16434SMark Johnston break; 69664a16434SMark Johnston case KERNELDUMP_COMP_GZIP: 6976026dcd7SMark Johnston case KERNELDUMP_COMP_ZSTD: 69864a16434SMark Johnston if (compress && verbose) 69964a16434SMark Johnston printf("dump is already compressed\n"); 70064a16434SMark Johnston compress = false; 70164a16434SMark Johnston iscompressed = true; 70264a16434SMark Johnston break; 70364a16434SMark Johnston default: 704d7fffd06SMark Johnston logmsg(LOG_ERR, "unknown compression type %d on %s", 70564a16434SMark Johnston kdhl.compression, device); 70664a16434SMark Johnston break; 70764a16434SMark Johnston } 708df0b82f1SRobert Watson } else { 7095da217f6SMarcel Moolenaar if (verbose) 710edc4f96eSBill Fenner printf("magic mismatch on last dump header on %s\n", 7115da217f6SMarcel Moolenaar device); 712edc4f96eSBill Fenner 7135fb7027cSDavid E. O'Brien status = STATUS_BAD; 714*32fbec42SGleb Smirnoff if (!force) 715edc4f96eSBill Fenner goto closefd; 716edc4f96eSBill Fenner 7174e287bd8SMark Johnston if (compare_magic(&kdhl, KERNELDUMPMAGIC_CLEARED)) { 718edc4f96eSBill Fenner if (verbose) 719edc4f96eSBill Fenner printf("forcing magic on %s\n", device); 72064a16434SMark Johnston memcpy(kdhl.magic, KERNELDUMPMAGIC, sizeof(kdhl.magic)); 721edc4f96eSBill Fenner } else { 722d7fffd06SMark Johnston logmsg(LOG_ERR, "unable to force dump - bad magic"); 7235da217f6SMarcel Moolenaar goto closefd; 724d503fad0SPoul-Henning Kamp } 7255cb87b0cSMarcel Moolenaar if (dtoh32(kdhl.version) != KERNELDUMPVERSION) { 726d7fffd06SMark Johnston logmsg(LOG_ERR, 727edc4f96eSBill Fenner "unknown version (%d) in last dump header on %s", 7285da217f6SMarcel Moolenaar dtoh32(kdhl.version), device); 7295fb7027cSDavid E. O'Brien 7305fb7027cSDavid E. O'Brien status = STATUS_BAD; 731*32fbec42SGleb Smirnoff if (!force) 7325da217f6SMarcel Moolenaar goto closefd; 7335da217f6SMarcel Moolenaar } 734df0b82f1SRobert Watson } 7355da217f6SMarcel Moolenaar 7365da217f6SMarcel Moolenaar nfound++; 7375da217f6SMarcel Moolenaar if (clear) 7385da217f6SMarcel Moolenaar goto nuke; 7395da217f6SMarcel Moolenaar 7405da217f6SMarcel Moolenaar if (kerneldump_parity(&kdhl)) { 741d7fffd06SMark Johnston logmsg(LOG_ERR, 742edc4f96eSBill Fenner "parity error on last dump header on %s", device); 7433203428dSMaxime Henrion nerr++; 7445fb7027cSDavid E. O'Brien status = STATUS_BAD; 745*32fbec42SGleb Smirnoff if (!force) 7465da217f6SMarcel Moolenaar goto closefd; 747d503fad0SPoul-Henning Kamp } 74864a16434SMark Johnston dumpextent = dtoh64(kdhl.dumpextent); 74964a16434SMark Johnston dumplength = dtoh64(kdhl.dumplength); 750480f31c2SKonrad Witaszczyk dumpkeysize = dtoh32(kdhl.dumpkeysize); 75164a16434SMark Johnston firsthd = lasthd - dumpextent - sectorsize - dumpkeysize; 752d7fffd06SMark Johnston if (lseek(fddev, firsthd, SEEK_SET) != firsthd || 753d7fffd06SMark Johnston read(fddev, temp, sectorsize) != (ssize_t)sectorsize) { 754d7fffd06SMark Johnston logmsg(LOG_ERR, 755edc4f96eSBill Fenner "error reading first dump header at offset %lld in %s: %m", 7565da217f6SMarcel Moolenaar (long long)firsthd, device); 7573203428dSMaxime Henrion nerr++; 7585da217f6SMarcel Moolenaar goto closefd; 759d503fad0SPoul-Henning Kamp } 7605dc5dab6SConrad Meyer memcpy(&kdhf, temp, sizeof(kdhf)); 7615fb7027cSDavid E. O'Brien 7625fb7027cSDavid E. O'Brien if (verbose >= 2) { 7635fb7027cSDavid E. O'Brien printf("First dump headers:\n"); 764d16528b2SScott Long printheader(xostdout, &kdhf, device, bounds, -1); 7655fb7027cSDavid E. O'Brien 7665fb7027cSDavid E. O'Brien printf("\nLast dump headers:\n"); 767d16528b2SScott Long printheader(xostdout, &kdhl, device, bounds, -1); 7685fb7027cSDavid E. O'Brien printf("\n"); 7695fb7027cSDavid E. O'Brien } 7705fb7027cSDavid E. O'Brien 7715dc5dab6SConrad Meyer if (memcmp(&kdhl, &kdhf, sizeof(kdhl))) { 772d7fffd06SMark Johnston logmsg(LOG_ERR, 773edc4f96eSBill Fenner "first and last dump headers disagree on %s", device); 7743203428dSMaxime Henrion nerr++; 7755fb7027cSDavid E. O'Brien status = STATUS_BAD; 776*32fbec42SGleb Smirnoff if (!force) 7775da217f6SMarcel Moolenaar goto closefd; 7785fb7027cSDavid E. O'Brien } else { 7795fb7027cSDavid E. O'Brien status = STATUS_GOOD; 780d503fad0SPoul-Henning Kamp } 7815da217f6SMarcel Moolenaar 782628d16a3SDoug Barton if (checkfor) { 783628d16a3SDoug Barton printf("A dump exists on %s\n", device); 784d7fffd06SMark Johnston close(fddev); 785628d16a3SDoug Barton exit(0); 786628d16a3SDoug Barton } 787628d16a3SDoug Barton 7885dc5dab6SConrad Meyer if (kdhl.panicstring[0] != '\0') 789d7fffd06SMark Johnston logmsg(LOG_ALERT, "reboot after panic: %.*s", 7906123d3f6SAlan Somers (int)sizeof(kdhl.panicstring), kdhl.panicstring); 791edc4f96eSBill Fenner else 792d7fffd06SMark Johnston logmsg(LOG_ALERT, "reboot"); 793edc4f96eSBill Fenner 7945da217f6SMarcel Moolenaar if (verbose) 795edc4f96eSBill Fenner printf("Checking for available free space\n"); 796eeff0b1bSPawel Jakub Dawidek 797d7fffd06SMark Johnston if (!check_space(savedir, savedirfd, dumplength, bounds)) { 7983203428dSMaxime Henrion nerr++; 7993203428dSMaxime Henrion goto closefd; 8003203428dSMaxime Henrion } 801edc4f96eSBill Fenner 802d7fffd06SMark Johnston writebounds(savedirfd, bounds + 1); 8039520784eSDoug White 804d7fffd06SMark Johnston saved_dump_remove(savedirfd, bounds); 805eeff0b1bSPawel Jakub Dawidek 806eeff0b1bSPawel Jakub Dawidek snprintf(infoname, sizeof(infoname), "info.%d", bounds); 807edc4f96eSBill Fenner 8085da217f6SMarcel Moolenaar /* 8095fb7027cSDavid E. O'Brien * Create or overwrite any existing dump header files. 8105da217f6SMarcel Moolenaar */ 811d7fffd06SMark Johnston if ((info = xfopenat(savedirfd, infoname, 812d7fffd06SMark Johnston O_WRONLY | O_CREAT | O_TRUNC, "w", 0600)) == NULL) { 813d7fffd06SMark Johnston logmsg(LOG_ERR, "open(%s): %m", infoname); 8143203428dSMaxime Henrion nerr++; 8155da217f6SMarcel Moolenaar goto closefd; 816d503fad0SPoul-Henning Kamp } 817d16528b2SScott Long 818480f31c2SKonrad Witaszczyk isencrypted = (dumpkeysize > 0); 819d7fffd06SMark Johnston if (compress) 820eeff0b1bSPawel Jakub Dawidek snprintf(corename, sizeof(corename), "%s.%d.gz", 821480f31c2SKonrad Witaszczyk istextdump ? "textdump.tar" : 822480f31c2SKonrad Witaszczyk (isencrypted ? "vmcore_encrypted" : "vmcore"), bounds); 823d7fffd06SMark Johnston else if (iscompressed && !isencrypted) 8246026dcd7SMark Johnston snprintf(corename, sizeof(corename), "vmcore.%d.%s", bounds, 8256026dcd7SMark Johnston (kdhl.compression == KERNELDUMP_COMP_GZIP) ? "gz" : "zst"); 826d7fffd06SMark Johnston else 827eeff0b1bSPawel Jakub Dawidek snprintf(corename, sizeof(corename), "%s.%d", 828480f31c2SKonrad Witaszczyk istextdump ? "textdump.tar" : 829480f31c2SKonrad Witaszczyk (isencrypted ? "vmcore_encrypted" : "vmcore"), bounds); 830d7fffd06SMark Johnston fdcore = openat(savedirfd, corename, O_WRONLY | O_CREAT | O_TRUNC, 831d7fffd06SMark Johnston 0600); 832d7fffd06SMark Johnston if (fdcore < 0) { 833d7fffd06SMark Johnston logmsg(LOG_ERR, "open(%s): %m", corename); 834d7fffd06SMark Johnston fclose(info); 8353203428dSMaxime Henrion nerr++; 8365da217f6SMarcel Moolenaar goto closefd; 837d503fad0SPoul-Henning Kamp } 838edc4f96eSBill Fenner 839d7fffd06SMark Johnston if (compress) 840d7fffd06SMark Johnston core = zdopen(fdcore, "w"); 841d7fffd06SMark Johnston else 842d7fffd06SMark Johnston core = fdopen(fdcore, "w"); 843d7fffd06SMark Johnston if (core == NULL) { 844d7fffd06SMark Johnston logmsg(LOG_ERR, "%s: %m", corename); 845d7fffd06SMark Johnston (void)close(fdcore); 846d7fffd06SMark Johnston (void)fclose(info); 84776f23bd5SKevin Lo nerr++; 848d7fffd06SMark Johnston goto closefd; 84976f23bd5SKevin Lo } 850d7fffd06SMark Johnston fdcore = -1; 85176f23bd5SKevin Lo 852d16528b2SScott Long xostyle = xo_get_style(NULL); 853d16528b2SScott Long xoinfo = xo_create_to_file(info, xostyle, 0); 854d16528b2SScott Long if (xoinfo == NULL) { 855d7fffd06SMark Johnston logmsg(LOG_ERR, "%s: %m", infoname); 856d7fffd06SMark Johnston fclose(info); 857d16528b2SScott Long nerr++; 8586123d3f6SAlan Somers goto closeall; 859d16528b2SScott Long } 860d16528b2SScott Long xo_open_container_h(xoinfo, "crashdump"); 8615da217f6SMarcel Moolenaar 862d16528b2SScott Long if (verbose) 863d16528b2SScott Long printheader(xostdout, &kdhl, device, bounds, status); 864d16528b2SScott Long 865d16528b2SScott Long printheader(xoinfo, &kdhl, device, bounds, status); 866d16528b2SScott Long xo_close_container_h(xoinfo, "crashdump"); 867d16528b2SScott Long xo_flush_h(xoinfo); 868d16528b2SScott Long xo_finish_h(xoinfo); 869edc4f96eSBill Fenner fclose(info); 8705da217f6SMarcel Moolenaar 871480f31c2SKonrad Witaszczyk if (isencrypted) { 872480f31c2SKonrad Witaszczyk dumpkey = calloc(1, dumpkeysize); 873480f31c2SKonrad Witaszczyk if (dumpkey == NULL) { 874d7fffd06SMark Johnston logmsg(LOG_ERR, "Unable to allocate kernel dump key."); 875480f31c2SKonrad Witaszczyk nerr++; 876480f31c2SKonrad Witaszczyk goto closeall; 877480f31c2SKonrad Witaszczyk } 878480f31c2SKonrad Witaszczyk 879d7fffd06SMark Johnston if (read(fddev, dumpkey, dumpkeysize) != (ssize_t)dumpkeysize) { 880d7fffd06SMark Johnston logmsg(LOG_ERR, "Unable to read kernel dump key: %m."); 881480f31c2SKonrad Witaszczyk nerr++; 882480f31c2SKonrad Witaszczyk goto closeall; 883480f31c2SKonrad Witaszczyk } 884480f31c2SKonrad Witaszczyk 885480f31c2SKonrad Witaszczyk snprintf(keyname, sizeof(keyname), "key.%d", bounds); 886d7fffd06SMark Johnston ret = writekey(savedirfd, keyname, dumpkey, dumpkeysize); 887480f31c2SKonrad Witaszczyk explicit_bzero(dumpkey, dumpkeysize); 888480f31c2SKonrad Witaszczyk if (!ret) { 889480f31c2SKonrad Witaszczyk nerr++; 890480f31c2SKonrad Witaszczyk goto closeall; 891480f31c2SKonrad Witaszczyk } 892480f31c2SKonrad Witaszczyk } 893480f31c2SKonrad Witaszczyk 894d7fffd06SMark Johnston logmsg(LOG_NOTICE, "writing %s%score to %s/%s", 895480f31c2SKonrad Witaszczyk isencrypted ? "encrypted " : "", compress ? "compressed " : "", 896480f31c2SKonrad Witaszczyk savedir, corename); 8975da217f6SMarcel Moolenaar 89810187caeSRobert Watson if (istextdump) { 899d7fffd06SMark Johnston if (DoTextdumpFile(fddev, dumplength, lasthd, buf, device, 900d7fffd06SMark Johnston corename, core) < 0) 9015da217f6SMarcel Moolenaar goto closeall; 902edc4f96eSBill Fenner } else { 903d7fffd06SMark Johnston if (DoRegularFile(fddev, dumplength, sectorsize, 90464a16434SMark Johnston !(compress || iscompressed || isencrypted), buf, device, 905d7fffd06SMark Johnston corename, core) < 0) { 9065da217f6SMarcel Moolenaar goto closeall; 907d503fad0SPoul-Henning Kamp } 908480f31c2SKonrad Witaszczyk } 909edc4f96eSBill Fenner if (verbose) 910edc4f96eSBill Fenner printf("\n"); 911edc4f96eSBill Fenner 912d7fffd06SMark Johnston if (fclose(core) < 0) { 913d7fffd06SMark Johnston logmsg(LOG_ERR, "error on %s: %m", corename); 914edc4f96eSBill Fenner nerr++; 91527e21758SChristian Brueffer goto closefd; 916edc4f96eSBill Fenner } 917eeff0b1bSPawel Jakub Dawidek 918d7fffd06SMark Johnston symlinks_remove(savedirfd); 919d7fffd06SMark Johnston if (symlinkat(infoname, savedirfd, "info.last") == -1) { 920d7fffd06SMark Johnston logmsg(LOG_WARNING, "unable to create symlink %s/%s: %m", 921165557e2SPawel Jakub Dawidek savedir, "info.last"); 922165557e2SPawel Jakub Dawidek } 923480f31c2SKonrad Witaszczyk if (isencrypted) { 924d7fffd06SMark Johnston if (symlinkat(keyname, savedirfd, "key.last") == -1) { 925d7fffd06SMark Johnston logmsg(LOG_WARNING, 926480f31c2SKonrad Witaszczyk "unable to create symlink %s/%s: %m", savedir, 927480f31c2SKonrad Witaszczyk "key.last"); 928480f31c2SKonrad Witaszczyk } 929480f31c2SKonrad Witaszczyk } 93064a16434SMark Johnston if (compress || iscompressed) { 9316026dcd7SMark Johnston snprintf(linkname, sizeof(linkname), "%s.last.%s", 932480f31c2SKonrad Witaszczyk istextdump ? "textdump.tar" : 9336026dcd7SMark Johnston (isencrypted ? "vmcore_encrypted" : "vmcore"), 9346026dcd7SMark Johnston (kdhl.compression == KERNELDUMP_COMP_ZSTD) ? "zst" : "gz"); 935165557e2SPawel Jakub Dawidek } else { 936165557e2SPawel Jakub Dawidek snprintf(linkname, sizeof(linkname), "%s.last", 937480f31c2SKonrad Witaszczyk istextdump ? "textdump.tar" : 938480f31c2SKonrad Witaszczyk (isencrypted ? "vmcore_encrypted" : "vmcore")); 939165557e2SPawel Jakub Dawidek } 940d7fffd06SMark Johnston if (symlinkat(corename, savedirfd, linkname) == -1) { 941d7fffd06SMark Johnston logmsg(LOG_WARNING, "unable to create symlink %s/%s: %m", 942165557e2SPawel Jakub Dawidek savedir, linkname); 943165557e2SPawel Jakub Dawidek } 944165557e2SPawel Jakub Dawidek 9453203428dSMaxime Henrion nsaved++; 9465da217f6SMarcel Moolenaar 9475da217f6SMarcel Moolenaar if (verbose) 948edc4f96eSBill Fenner printf("dump saved\n"); 9495da217f6SMarcel Moolenaar 9505da217f6SMarcel Moolenaar nuke: 95141060469SPawel Jakub Dawidek if (!keep) { 9525da217f6SMarcel Moolenaar if (verbose) 953edc4f96eSBill Fenner printf("clearing dump header\n"); 9545dc5dab6SConrad Meyer memcpy(kdhl.magic, KERNELDUMPMAGIC_CLEARED, sizeof(kdhl.magic)); 9555dc5dab6SConrad Meyer memcpy(temp, &kdhl, sizeof(kdhl)); 956d7fffd06SMark Johnston if (lseek(fddev, lasthd, SEEK_SET) != lasthd || 957d7fffd06SMark Johnston write(fddev, temp, sectorsize) != (ssize_t)sectorsize) 958d7fffd06SMark Johnston logmsg(LOG_ERR, 959edc4f96eSBill Fenner "error while clearing the dump header: %m"); 9605da217f6SMarcel Moolenaar } 961d16528b2SScott Long xo_close_container_h(xostdout, "crashdump"); 962d16528b2SScott Long xo_finish_h(xostdout); 9633c7ccf15SKonrad Witaszczyk free(dumpkey); 9648df55592SConrad Meyer free(temp); 965d7fffd06SMark Johnston close(fddev); 9665da217f6SMarcel Moolenaar return; 9675da217f6SMarcel Moolenaar 9685da217f6SMarcel Moolenaar closeall: 969d7fffd06SMark Johnston fclose(core); 9705da217f6SMarcel Moolenaar 9715da217f6SMarcel Moolenaar closefd: 9723c7ccf15SKonrad Witaszczyk free(dumpkey); 9738df55592SConrad Meyer free(temp); 974d7fffd06SMark Johnston close(fddev); 975d7fffd06SMark Johnston } 976d7fffd06SMark Johnston 97781884a24SAlan Somers /* Prepend "/dev/" to any arguments that don't already have it */ 97881884a24SAlan Somers static char ** 97981884a24SAlan Somers devify(int argc, char **argv) 98081884a24SAlan Somers { 98181884a24SAlan Somers char **devs; 98281884a24SAlan Somers int i, l; 98381884a24SAlan Somers 98481884a24SAlan Somers devs = malloc(argc * sizeof(*argv)); 98581884a24SAlan Somers if (devs == NULL) { 98681884a24SAlan Somers logmsg(LOG_ERR, "malloc(): %m"); 98781884a24SAlan Somers exit(1); 98881884a24SAlan Somers } 98981884a24SAlan Somers for (i = 0; i < argc; i++) { 99081884a24SAlan Somers if (strncmp(argv[i], _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) 99181884a24SAlan Somers devs[i] = strdup(argv[i]); 99281884a24SAlan Somers else { 99381884a24SAlan Somers char *fullpath; 99481884a24SAlan Somers 99581884a24SAlan Somers fullpath = malloc(PATH_MAX); 99681884a24SAlan Somers if (fullpath == NULL) { 99781884a24SAlan Somers logmsg(LOG_ERR, "malloc(): %m"); 99881884a24SAlan Somers exit(1); 99981884a24SAlan Somers } 100081884a24SAlan Somers l = snprintf(fullpath, PATH_MAX, "%s%s", _PATH_DEV, 100181884a24SAlan Somers argv[i]); 100281884a24SAlan Somers if (l < 0) { 100381884a24SAlan Somers logmsg(LOG_ERR, "snprintf(): %m"); 100481884a24SAlan Somers exit(1); 100581884a24SAlan Somers } else if (l >= PATH_MAX) { 100681884a24SAlan Somers logmsg(LOG_ERR, "device name too long"); 100781884a24SAlan Somers exit(1); 100881884a24SAlan Somers } 100981884a24SAlan Somers devs[i] = fullpath; 101081884a24SAlan Somers } 101181884a24SAlan Somers } 101281884a24SAlan Somers return (devs); 101381884a24SAlan Somers } 101481884a24SAlan Somers 1015d7fffd06SMark Johnston static char ** 1016d7fffd06SMark Johnston enum_dumpdevs(int *argcp) 1017d7fffd06SMark Johnston { 1018d7fffd06SMark Johnston struct fstab *fsp; 1019d7fffd06SMark Johnston char **argv; 1020d7fffd06SMark Johnston int argc, n; 1021d7fffd06SMark Johnston 1022d7fffd06SMark Johnston /* 1023d7fffd06SMark Johnston * We cannot use getfsent(3) in capability mode, so we must 1024d7fffd06SMark Johnston * scan /etc/fstab and build up a list of candidate devices 1025d7fffd06SMark Johnston * before proceeding. 1026d7fffd06SMark Johnston */ 1027d7fffd06SMark Johnston argc = 0; 1028d7fffd06SMark Johnston n = 8; 1029d7fffd06SMark Johnston argv = malloc(n * sizeof(*argv)); 1030d7fffd06SMark Johnston if (argv == NULL) { 1031d7fffd06SMark Johnston logmsg(LOG_ERR, "malloc(): %m"); 1032d7fffd06SMark Johnston exit(1); 1033d7fffd06SMark Johnston } 1034d7fffd06SMark Johnston for (;;) { 1035d7fffd06SMark Johnston fsp = getfsent(); 1036d7fffd06SMark Johnston if (fsp == NULL) 1037d7fffd06SMark Johnston break; 1038d7fffd06SMark Johnston if (strcmp(fsp->fs_vfstype, "swap") != 0 && 1039d7fffd06SMark Johnston strcmp(fsp->fs_vfstype, "dump") != 0) 1040d7fffd06SMark Johnston continue; 1041d7fffd06SMark Johnston if (argc >= n) { 1042d7fffd06SMark Johnston n *= 2; 1043d7fffd06SMark Johnston argv = realloc(argv, n * sizeof(*argv)); 1044d7fffd06SMark Johnston if (argv == NULL) { 1045d7fffd06SMark Johnston logmsg(LOG_ERR, "realloc(): %m"); 1046d7fffd06SMark Johnston exit(1); 1047d7fffd06SMark Johnston } 1048d7fffd06SMark Johnston } 1049d7fffd06SMark Johnston argv[argc] = strdup(fsp->fs_spec); 1050d7fffd06SMark Johnston if (argv[argc] == NULL) { 1051d7fffd06SMark Johnston logmsg(LOG_ERR, "strdup(): %m"); 1052d7fffd06SMark Johnston exit(1); 1053d7fffd06SMark Johnston } 1054d7fffd06SMark Johnston argc++; 1055d7fffd06SMark Johnston } 1056d7fffd06SMark Johnston *argcp = argc; 1057d7fffd06SMark Johnston return (argv); 1058d7fffd06SMark Johnston } 1059d7fffd06SMark Johnston 1060d7fffd06SMark Johnston static void 1061d7fffd06SMark Johnston init_caps(int argc, char **argv) 1062d7fffd06SMark Johnston { 1063d7fffd06SMark Johnston cap_rights_t rights; 1064d7fffd06SMark Johnston cap_channel_t *capcas; 1065d7fffd06SMark Johnston 1066d7fffd06SMark Johnston capcas = cap_init(); 1067d7fffd06SMark Johnston if (capcas == NULL) { 1068d7fffd06SMark Johnston logmsg(LOG_ERR, "cap_init(): %m"); 1069d7fffd06SMark Johnston exit(1); 1070d7fffd06SMark Johnston } 1071d7fffd06SMark Johnston /* 1072d7fffd06SMark Johnston * The fileargs capability does not currently provide a way to limit 1073d7fffd06SMark Johnston * ioctls. 1074d7fffd06SMark Johnston */ 1075d7fffd06SMark Johnston (void)cap_rights_init(&rights, CAP_PREAD, CAP_WRITE, CAP_IOCTL); 1076d7fffd06SMark Johnston capfa = fileargs_init(argc, argv, checkfor || keep ? O_RDONLY : O_RDWR, 1077d76eef34SEd Maste 0, &rights, FA_OPEN); 1078d7fffd06SMark Johnston if (capfa == NULL) { 1079d7fffd06SMark Johnston logmsg(LOG_ERR, "fileargs_init(): %m"); 1080d7fffd06SMark Johnston exit(1); 1081d7fffd06SMark Johnston } 1082d7fffd06SMark Johnston caph_cache_catpages(); 1083d7fffd06SMark Johnston caph_cache_tzdata(); 1084d7fffd06SMark Johnston if (caph_enter_casper() != 0) { 1085d7fffd06SMark Johnston logmsg(LOG_ERR, "caph_enter_casper(): %m"); 1086d7fffd06SMark Johnston exit(1); 1087d7fffd06SMark Johnston } 1088d7fffd06SMark Johnston capsyslog = cap_service_open(capcas, "system.syslog"); 1089d7fffd06SMark Johnston if (capsyslog == NULL) { 1090d7fffd06SMark Johnston logmsg(LOG_ERR, "cap_service_open(system.syslog): %m"); 1091d7fffd06SMark Johnston exit(1); 1092d7fffd06SMark Johnston } 1093d7fffd06SMark Johnston cap_close(capcas); 1094d503fad0SPoul-Henning Kamp } 10959b0a8ba3SPeter Wemm 1096d503fad0SPoul-Henning Kamp static void 1097d503fad0SPoul-Henning Kamp usage(void) 1098d503fad0SPoul-Henning Kamp { 1099d16528b2SScott Long xo_error("%s\n%s\n%s\n", 1100349d039bSPawel Jakub Dawidek "usage: savecore -c [-v] [device ...]", 1101349d039bSPawel Jakub Dawidek " savecore -C [-v] [device ...]", 1102eeff0b1bSPawel Jakub Dawidek " savecore [-fkvz] [-m maxdumps] [directory [device ...]]"); 1103d503fad0SPoul-Henning Kamp exit(1); 1104d503fad0SPoul-Henning Kamp } 11058fae3551SRodney W. Grimes 11068fae3551SRodney W. Grimes int 1107d503fad0SPoul-Henning Kamp main(int argc, char **argv) 11088fae3551SRodney W. Grimes { 1109d7fffd06SMark Johnston cap_rights_t rights; 1110d7fffd06SMark Johnston const char *savedir; 111181884a24SAlan Somers char **devs; 1112d7fffd06SMark Johnston int i, ch, error, savedirfd; 11138fae3551SRodney W. Grimes 11140de93324SGleb Smirnoff checkfor = compress = clear = force = keep = false; 11150de93324SGleb Smirnoff verbose = 0; 11165fb7027cSDavid E. O'Brien nfound = nsaved = nerr = 0; 1117d7fffd06SMark Johnston savedir = "."; 11185fb7027cSDavid E. O'Brien 1119edc4f96eSBill Fenner openlog("savecore", LOG_PERROR, LOG_DAEMON); 11200d239eefSUlf Lilleengen signal(SIGINFO, infohandler); 1121edc4f96eSBill Fenner 1122d16528b2SScott Long argc = xo_parse_args(argc, argv); 1123d16528b2SScott Long if (argc < 0) 1124d16528b2SScott Long exit(1); 1125d16528b2SScott Long 1126eeff0b1bSPawel Jakub Dawidek while ((ch = getopt(argc, argv, "Ccfkm:vz")) != -1) 11278fae3551SRodney W. Grimes switch(ch) { 1128628d16a3SDoug Barton case 'C': 11290de93324SGleb Smirnoff checkfor = true; 1130628d16a3SDoug Barton break; 11318fae3551SRodney W. Grimes case 'c': 11320de93324SGleb Smirnoff clear = true; 11335da217f6SMarcel Moolenaar break; 11343a6e0febSPawel Jakub Dawidek case 'f': 11350de93324SGleb Smirnoff force = true; 11363a6e0febSPawel Jakub Dawidek break; 1137532c1901SDag-Erling Smørgrav case 'k': 11380de93324SGleb Smirnoff keep = true; 11395da217f6SMarcel Moolenaar break; 1140eeff0b1bSPawel Jakub Dawidek case 'm': 1141eeff0b1bSPawel Jakub Dawidek maxdumps = atoi(optarg); 1142eeff0b1bSPawel Jakub Dawidek if (maxdumps <= 0) { 1143d7fffd06SMark Johnston logmsg(LOG_ERR, "Invalid maxdump value"); 1144eeff0b1bSPawel Jakub Dawidek exit(1); 1145eeff0b1bSPawel Jakub Dawidek } 1146eeff0b1bSPawel Jakub Dawidek break; 11475da217f6SMarcel Moolenaar case 'v': 11485fb7027cSDavid E. O'Brien verbose++; 11495da217f6SMarcel Moolenaar break; 1150edc4f96eSBill Fenner case 'z': 1151*32fbec42SGleb Smirnoff compress = true; 1152edc4f96eSBill Fenner break; 11538fae3551SRodney W. Grimes case '?': 11548fae3551SRodney W. Grimes default: 11558fae3551SRodney W. Grimes usage(); 11568fae3551SRodney W. Grimes } 1157628d16a3SDoug Barton if (checkfor && (clear || force || keep)) 1158628d16a3SDoug Barton usage(); 115922374de9SPawel Jakub Dawidek if (clear && (compress || keep)) 116022374de9SPawel Jakub Dawidek usage(); 1161eeff0b1bSPawel Jakub Dawidek if (maxdumps > 0 && (checkfor || clear)) 1162eeff0b1bSPawel Jakub Dawidek usage(); 11638fae3551SRodney W. Grimes argc -= optind; 11648fae3551SRodney W. Grimes argv += optind; 1165349d039bSPawel Jakub Dawidek if (argc >= 1 && !checkfor && !clear) { 1166d503fad0SPoul-Henning Kamp error = chdir(argv[0]); 1167edc4f96eSBill Fenner if (error) { 1168d7fffd06SMark Johnston logmsg(LOG_ERR, "chdir(%s): %m", argv[0]); 1169edc4f96eSBill Fenner exit(1); 1170edc4f96eSBill Fenner } 11713203428dSMaxime Henrion savedir = argv[0]; 1172d503fad0SPoul-Henning Kamp argc--; 1173d503fad0SPoul-Henning Kamp argv++; 11748fae3551SRodney W. Grimes } 1175d7fffd06SMark Johnston if (argc == 0) 117681884a24SAlan Somers devs = enum_dumpdevs(&argc); 117781884a24SAlan Somers else 117881884a24SAlan Somers devs = devify(argc, argv); 1179d7fffd06SMark Johnston 1180d7fffd06SMark Johnston savedirfd = open(savedir, O_RDONLY | O_DIRECTORY); 1181d7fffd06SMark Johnston if (savedirfd < 0) { 1182d7fffd06SMark Johnston logmsg(LOG_ERR, "open(%s): %m", savedir); 1183d7fffd06SMark Johnston exit(1); 11848fae3551SRodney W. Grimes } 1185d7fffd06SMark Johnston (void)cap_rights_init(&rights, CAP_CREATE, CAP_FCNTL, CAP_FSTATAT, 1186d7fffd06SMark Johnston CAP_FSTATFS, CAP_PREAD, CAP_SYMLINKAT, CAP_FTRUNCATE, CAP_UNLINKAT, 1187d7fffd06SMark Johnston CAP_WRITE); 1188d7fffd06SMark Johnston if (caph_rights_limit(savedirfd, &rights) < 0) { 1189d7fffd06SMark Johnston logmsg(LOG_ERR, "cap_rights_limit(): %m"); 1190d7fffd06SMark Johnston exit(1); 1191d7fffd06SMark Johnston } 1192d7fffd06SMark Johnston 1193d7fffd06SMark Johnston /* Enter capability mode. */ 119481884a24SAlan Somers init_caps(argc, devs); 1195d7fffd06SMark Johnston 1196d503fad0SPoul-Henning Kamp for (i = 0; i < argc; i++) 119781884a24SAlan Somers DoFile(savedir, savedirfd, devs[i]); 11985da217f6SMarcel Moolenaar 11995da217f6SMarcel Moolenaar /* Emit minimal output. */ 1200628d16a3SDoug Barton if (nfound == 0) { 1201628d16a3SDoug Barton if (checkfor) { 1202fc6f845eSConrad Meyer if (verbose) 1203628d16a3SDoug Barton printf("No dump exists\n"); 1204628d16a3SDoug Barton exit(1); 1205628d16a3SDoug Barton } 1206fc6f845eSConrad Meyer if (verbose) 1207d7fffd06SMark Johnston logmsg(LOG_WARNING, "no dumps found"); 1208fc6f845eSConrad Meyer } else if (nsaved == 0) { 1209fc6f845eSConrad Meyer if (nerr != 0) { 1210fc6f845eSConrad Meyer if (verbose) 1211d7fffd06SMark Johnston logmsg(LOG_WARNING, 1212cf8eb490SMark Johnston "unsaved dumps found but not saved"); 1213fc6f845eSConrad Meyer exit(1); 1214fc6f845eSConrad Meyer } else if (verbose) 1215d7fffd06SMark Johnston logmsg(LOG_WARNING, "no unsaved dumps found"); 12163203428dSMaxime Henrion } 12175da217f6SMarcel Moolenaar 1218c74e16f9SDag-Erling Smørgrav return (0); 1219c74e16f9SDag-Erling Smørgrav } 12200d239eefSUlf Lilleengen 12210d239eefSUlf Lilleengen static void 12220d239eefSUlf Lilleengen infohandler(int sig __unused) 12230d239eefSUlf Lilleengen { 12240d239eefSUlf Lilleengen got_siginfo = 1; 12250d239eefSUlf Lilleengen } 1226