Page MenuHomeFreeBSD

D34347.diff
No OneTemporary

D34347.diff

diff --git a/sbin/savecore/savecore.8 b/sbin/savecore/savecore.8
--- a/sbin/savecore/savecore.8
+++ b/sbin/savecore/savecore.8
@@ -28,7 +28,7 @@
.\" From: @(#)savecore.8 8.1 (Berkeley) 6/5/93
.\" $FreeBSD$
.\"
-.Dd November 17, 2020
+.Dd April 4, 2022
.Dt SAVECORE 8
.Os
.Sh NAME
@@ -44,6 +44,11 @@
.Op Fl v
.Op Ar device ...
.Nm
+.Fl L
+.Op Fl fvZz
+.Op Fl m Ar maxdumps
+.Op Ar directory
+.Nm
.Op Fl -libxo
.Op Fl fkuvz
.Op Fl m Ar maxdumps
@@ -86,6 +91,11 @@
dump header information is inconsistent.
.It Fl k
Do not clear the dump after saving it.
+.It Fl L
+Instruct
+.Nm
+to generate and save a kernel dump of the running system, rather than
+copying one from a dump device.
.It Fl m Ar maxdumps
Maximum number of dumps to store.
Once the number of stored dumps is equal to
@@ -97,6 +107,14 @@
.It Fl v
Print out some additional debugging information.
Specify twice for more information.
+.It Fl Z
+Compress the dump (see
+.Xr zstd 1 ) .
+This option is only supported in conjunction with the
+.Fl L
+option.
+Regular dumps can be configured for compression with zstd using
+.Xr dumpon 8 .
.It Fl z
Compress the dump (see
.Xr gzip 1 ) .
@@ -104,6 +122,10 @@
do so by
.Xr dumpon 8 .
In this case, the option has no effect.
+.Pp
+If used in conjunction with the
+.Fl L
+option, the requested live dump will be compressed with gzip.
.El
.Pp
The
@@ -171,9 +193,11 @@
.Xr rc 8 ) .
.Sh SEE ALSO
.Xr gzip 1 ,
+.Xr zstd 1 ,
.Xr getbootfile 3 ,
.Xr libxo 3 ,
.Xr xo_parse_args 3 ,
+.Xr mem 4 ,
.Xr textdump 4 ,
.Xr tar 5 ,
.Xr crashinfo 8 ,
diff --git a/sbin/savecore/savecore.c b/sbin/savecore/savecore.c
--- a/sbin/savecore/savecore.c
+++ b/sbin/savecore/savecore.c
@@ -68,6 +68,7 @@
#include <sys/param.h>
#include <sys/disk.h>
#include <sys/kerneldump.h>
+#include <sys/memrange.h>
#include <sys/mount.h>
#include <sys/stat.h>
@@ -106,9 +107,11 @@
static cap_channel_t *capsyslog;
static fileargs_t *capfa;
static bool checkfor, compress, uncompress, clear, force, keep; /* flags */
+static bool livecore; /* flags cont. */
static int verbose;
static int nfound, nsaved, nerr; /* statistics */
static int maxdumps;
+static uint8_t comp_desired;
extern FILE *zdopen(int, const char *);
@@ -393,6 +396,12 @@
(void)unlinkat(savedirfd, path, 0);
(void)snprintf(path, sizeof(path), "textdump.tar.%d.gz", bounds);
(void)unlinkat(savedirfd, path, 0);
+ (void)snprintf(path, sizeof(path), "livecore.%d", bounds);
+ (void)unlinkat(savedirfd, path, 0);
+ (void)snprintf(path, sizeof(path), "livecore.%d.gz", bounds);
+ (void)unlinkat(savedirfd, path, 0);
+ (void)snprintf(path, sizeof(path), "livecore.%d.zst", bounds);
+ (void)unlinkat(savedirfd, path, 0);
}
static void
@@ -408,6 +417,9 @@
(void)unlinkat(savedirfd, "vmcore_encrypted.last.gz", 0);
(void)unlinkat(savedirfd, "textdump.tar.last", 0);
(void)unlinkat(savedirfd, "textdump.tar.last.gz", 0);
+ (void)unlinkat(savedirfd, "livecore.last", 0);
+ (void)unlinkat(savedirfd, "livecore.last.gz", 0);
+ (void)unlinkat(savedirfd, "livecore.last.zst", 0);
}
/*
@@ -731,6 +743,183 @@
return (0);
}
+static void
+DoLiveFile(const char *savedir, int savedirfd, const char *device)
+{
+ char infoname[32], corename[32], linkname[32], tmpname[32];
+ struct mem_livedump_arg marg;
+ struct kerneldumpheader kdhl;
+ xo_handle_t *xostdout;
+ off_t dumplength;
+ uint32_t version;
+ int fddev, fdcore;
+ int bounds;
+ int error, status;
+
+ bounds = getbounds(savedirfd);
+ status = STATUS_UNKNOWN;
+
+ xostdout = xo_create_to_file(stdout, XO_STYLE_TEXT, 0);
+ if (xostdout == NULL) {
+ logmsg(LOG_ERR, "xo_create_to_file() failed: %m");
+ return;
+ }
+
+ /*
+ * Create a temporary file. We will invoke the live dump and its
+ * contents will be written to this fd. After validating and removing
+ * the kernel dump header from the tail-end of this file, it will be
+ * renamed to its definitive filename (e.g. livecore.2.gz).
+ *
+ * If any errors are encountered before the rename, the temporary file
+ * is unlinked.
+ */
+ strcpy(tmpname, "livecore.tmp.XXXXXX");
+ fdcore = mkostempsat(savedirfd, tmpname, 0, 0);
+ if (fdcore < 0) {
+ logmsg(LOG_ERR, "error opening temp file: %m");
+ return;
+ }
+
+ fddev = fileargs_open(capfa, device);
+ if (fddev < 0) {
+ logmsg(LOG_ERR, "%s: %m", device);
+ goto unlinkexit;
+ }
+
+ bzero(&marg, sizeof(marg));
+ marg.fd = fdcore;
+ marg.compression = comp_desired;
+ if (ioctl(fddev, MEM_KERNELDUMP, &marg) == -1) {
+ logmsg(LOG_ERR,
+ "failed to invoke live-dump on system: %m");
+ close(fddev);
+ goto unlinkexit;
+ }
+
+ /* Close /dev/mem fd, we are finished with it. */
+ close(fddev);
+
+ /* Seek to the end of the file, minus the size of the header. */
+ if (lseek(fdcore, -(off_t)sizeof(kdhl), SEEK_END) == -1) {
+ logmsg(LOG_ERR, "failed to lseek: %m");
+ goto unlinkexit;
+ }
+
+ if (read(fdcore, &kdhl, sizeof(kdhl)) != sizeof(kdhl)) {
+ logmsg(LOG_ERR, "failed to read kernel dump header: %m");
+ goto unlinkexit;
+ }
+ /* Reset cursor */
+ (void)lseek(fdcore, 0, SEEK_SET);
+
+ /* Validate the dump header. */
+ version = dtoh32(kdhl.version);
+ if (compare_magic(&kdhl, KERNELDUMPMAGIC)) {
+ if (version != KERNELDUMPVERSION) {
+ logmsg(LOG_ERR,
+ "unknown version (%d) in dump header on %s",
+ version, device);
+ goto unlinkexit;
+ } else if (kdhl.compression != comp_desired) {
+ /* This should be impossible. */
+ logmsg(LOG_ERR,
+ "dump compression (%u) doesn't match request (%u)",
+ kdhl.compression, comp_desired);
+ if (!force)
+ goto unlinkexit;
+ }
+ } else {
+ logmsg(LOG_ERR, "magic mismatch on live dump header");
+ goto unlinkexit;
+ }
+ if (kerneldump_parity(&kdhl)) {
+ logmsg(LOG_ERR,
+ "parity error on last dump header on %s", device);
+ nerr++;
+ status = STATUS_BAD;
+ if (!force)
+ goto unlinkexit;
+ } else {
+ status = STATUS_GOOD;
+ }
+
+ nfound++;
+ dumplength = dtoh64(kdhl.dumplength);
+ if (dtoh32(kdhl.dumpkeysize) != 0) {
+ logmsg(LOG_ERR,
+ "dump header unexpectedly reported keysize > 0");
+ goto unlinkexit;
+ }
+
+ /* Remove the vestigial kernel dump header. */
+ error = ftruncate(fdcore, dumplength);
+ if (error != 0) {
+ logmsg(LOG_ERR, "failed to truncate the core file: %m");
+ goto unlinkexit;
+ }
+
+ if (verbose >= 2) {
+ printf("\nDump header:\n");
+ printheader(xostdout, &kdhl, device, bounds, -1);
+ printf("\n");
+ }
+ logmsg(LOG_ALERT, "livedump");
+
+ writebounds(savedirfd, bounds + 1);
+ saved_dump_remove(savedirfd, bounds);
+
+ snprintf(corename, sizeof(corename), "livecore.%d", bounds);
+ if (compress)
+ strcat(corename, kdhl.compression == KERNELDUMP_COMP_ZSTD ?
+ ".zst" : ".gz");
+
+ if (verbose)
+ printf("renaming %s to %s\n", tmpname, corename);
+ if (renameat(savedirfd, tmpname, savedirfd, corename) != 0) {
+ logmsg(LOG_ERR, "renameat failed: %m");
+ goto unlinkexit;
+ }
+
+ snprintf(infoname, sizeof(infoname), "info.%d", bounds);
+ if (write_header_info(xostdout, &kdhl, savedirfd, infoname, device,
+ bounds, status) != 0) {
+ nerr++;
+ return;
+ }
+
+ logmsg(LOG_NOTICE, "writing %score to %s/%s",
+ compress ? "compressed " : "", savedir, corename);
+
+ if (verbose)
+ printf("\n");
+
+ symlinks_remove(savedirfd);
+ if (symlinkat(infoname, savedirfd, "info.last") == -1) {
+ logmsg(LOG_WARNING, "unable to create symlink %s/%s: %m",
+ savedir, "info.last");
+ }
+
+ snprintf(linkname, sizeof(linkname), "livecore.last");
+ if (compress)
+ strcat(linkname, kdhl.compression == KERNELDUMP_COMP_ZSTD ?
+ ".zst" : ".gz");
+ if (symlinkat(corename, savedirfd, linkname) == -1) {
+ logmsg(LOG_WARNING, "unable to create symlink %s/%s: %m",
+ savedir, linkname);
+ }
+
+ nsaved++;
+ if (verbose)
+ printf("dump saved\n");
+
+ close(fdcore);
+ return;
+unlinkexit:
+ funlinkat(savedirfd, tmpname, fdcore, 0);
+ close(fdcore);
+}
+
static void
DoFile(const char *savedir, int savedirfd, const char *device)
{
@@ -748,6 +937,12 @@
uint32_t dumpkeysize;
bool iscompressed, isencrypted, istextdump, ret;
+ /* Live kernel dumps are handled separately. */
+ if (livecore) {
+ DoLiveFile(savedir, savedirfd, device);
+ return;
+ }
+
bounds = getbounds(savedirfd);
dumpkey = NULL;
mediasize = 0;
@@ -1220,9 +1415,10 @@
static void
usage(void)
{
- xo_error("%s\n%s\n%s\n",
+ xo_error("%s\n%s\n%s\n%s\n",
"usage: savecore -c [-v] [device ...]",
" savecore -C [-v] [device ...]",
+ " savecore -L [-fvZz] [-m maxdumps] [directory]",
" savecore [-fkuvz] [-m maxdumps] [directory [device ...]]");
exit(1);
}
@@ -1235,10 +1431,11 @@
char **devs;
int i, ch, error, savedirfd;
- checkfor = compress = clear = force = keep = false;
+ checkfor = compress = clear = force = keep = livecore = false;
verbose = 0;
nfound = nsaved = nerr = 0;
savedir = ".";
+ comp_desired = KERNELDUMP_COMP_NONE;
openlog("savecore", LOG_PERROR, LOG_DAEMON);
signal(SIGINFO, infohandler);
@@ -1247,7 +1444,7 @@
if (argc < 0)
exit(1);
- while ((ch = getopt(argc, argv, "Ccfkm:uvz")) != -1)
+ while ((ch = getopt(argc, argv, "CcfkLm:uvZz")) != -1)
switch(ch) {
case 'C':
checkfor = true;
@@ -1261,6 +1458,9 @@
case 'k':
keep = true;
break;
+ case 'L':
+ livecore = true;
+ break;
case 'm':
maxdumps = atoi(optarg);
if (maxdumps <= 0) {
@@ -1274,8 +1474,16 @@
case 'v':
verbose++;
break;
+ case 'Z':
+ /* No on-the-fly compression with zstd at the moment. */
+ if (!livecore)
+ usage();
+ compress = true;
+ comp_desired = KERNELDUMP_COMP_ZSTD;
+ break;
case 'z':
compress = true;
+ comp_desired = KERNELDUMP_COMP_GZIP;
break;
case '?':
default:
@@ -1289,6 +1497,8 @@
usage();
if (compress && uncompress)
usage();
+ if (livecore && (checkfor || clear || uncompress || keep))
+ usage();
argc -= optind;
argv += optind;
if (argc >= 1 && !checkfor && !clear) {
@@ -1301,7 +1511,15 @@
argc--;
argv++;
}
- if (argc == 0)
+ if (livecore) {
+ if (argc > 0)
+ usage();
+
+ /* Always need /dev/mem to invoke the dump */
+ devs = malloc(sizeof(char *));
+ devs[0] = strdup("/dev/mem");
+ argc++;
+ } else if (argc == 0)
devs = enum_dumpdevs(&argc);
else
devs = devify(argc, argv);
@@ -1314,6 +1532,9 @@
(void)cap_rights_init(&rights, CAP_CREATE, CAP_FCNTL, CAP_FSTATAT,
CAP_FSTATFS, CAP_PREAD, CAP_SYMLINKAT, CAP_FTRUNCATE, CAP_UNLINKAT,
CAP_WRITE);
+ if (livecore)
+ cap_rights_set(&rights, CAP_RENAMEAT_SOURCE,
+ CAP_RENAMEAT_TARGET);
if (caph_rights_limit(savedirfd, &rights) < 0) {
logmsg(LOG_ERR, "cap_rights_limit(): %m");
exit(1);

File Metadata

Mime Type
text/plain
Expires
Wed, Feb 5, 9:16 PM (21 h, 20 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16481038
Default Alt Text
D34347.diff (10 KB)

Event Timeline