chiark / gitweb /
coredump: don't expose the compression level as configuration option
[elogind.git] / src / journal / coredump.c
index 1c5c99c39d8975cbed201cf576802d14d5520e6b..28cde669540da4e49e3cd5fa2e4d152fdbbcb1e7 100644 (file)
 #include "stacktrace.h"
 #include "path-util.h"
 #include "compress.h"
+#include "coredump-vacuum.h"
 
 #ifdef HAVE_ACL
-#include <sys/acl.h>
-#include "acl-util.h"
+#  include <sys/acl.h>
+#  include "acl-util.h"
+#endif
+
+#ifdef HAVE_XZ
+#  include <lzma.h>
+#else
+#  define LZMA_PRESET_DEFAULT 0
 #endif
 
 /* The maximum size up to which we process coredumps */
@@ -83,11 +90,6 @@ typedef enum CoredumpStorage {
         _COREDUMP_STORAGE_INVALID = -1
 } CoredumpStorage;
 
-static CoredumpStorage arg_storage = COREDUMP_STORAGE_EXTERNAL;
-static off_t arg_process_size_max = PROCESS_SIZE_MAX;
-static off_t arg_external_size_max = EXTERNAL_SIZE_MAX;
-static size_t arg_journal_size_max = JOURNAL_SIZE_MAX;
-
 static const char* const coredump_storage_table[_COREDUMP_STORAGE_MAX] = {
         [COREDUMP_STORAGE_NONE] = "none",
         [COREDUMP_STORAGE_EXTERNAL] = "external",
@@ -96,15 +98,44 @@ static const char* const coredump_storage_table[_COREDUMP_STORAGE_MAX] = {
 };
 
 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_storage, CoredumpStorage);
-static DEFINE_CONFIG_PARSE_ENUM(config_parse_coredump_storage, coredump_storage, CoredumpStorage, "Failed to parse storage setting");
+static DEFINE_CONFIG_PARSE_ENUM(config_parse_coredump_storage, coredump_storage,
+                                CoredumpStorage,
+                                "Failed to parse storage setting");
+
+typedef enum CoredumpCompression {
+        COREDUMP_COMPRESSION_NONE,
+        COREDUMP_COMPRESSION_XZ,
+        _COREDUMP_COMPRESSION_MAX,
+        _COREDUMP_COMPRESSION_INVALID = -1
+} CoredumpCompression;
+
+static const char* const coredump_compression_table[_COREDUMP_COMPRESSION_MAX] = {
+        [COREDUMP_COMPRESSION_NONE] = "none",
+        [COREDUMP_COMPRESSION_XZ] = "xz",
+};
 
-static int parse_config(void) {
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_compression, CoredumpCompression);
+static DEFINE_CONFIG_PARSE_ENUM(config_parse_coredump_compression, coredump_compression,
+                                CoredumpCompression,
+                                "Failed to parse compression setting");
+
+static CoredumpStorage arg_storage = COREDUMP_STORAGE_EXTERNAL;
+static CoredumpCompression arg_compression = COREDUMP_COMPRESSION_XZ;
+static off_t arg_process_size_max = PROCESS_SIZE_MAX;
+static off_t arg_external_size_max = EXTERNAL_SIZE_MAX;
+static size_t arg_journal_size_max = JOURNAL_SIZE_MAX;
+static off_t arg_keep_free = (off_t) -1;
+static off_t arg_max_use = (off_t) -1;
 
+static int parse_config(void) {
         static const ConfigTableItem items[] = {
-                { "Coredump", "ProcessSizeMax",  config_parse_iec_off,           0, &arg_process_size_max  },
-                { "Coredump", "ExternalSizeMax", config_parse_iec_off,           0, &arg_external_size_max },
-                { "Coredump", "JournalSizeMax",  config_parse_iec_size,          0, &arg_journal_size_max  },
-                { "Coredump", "Storage",         config_parse_coredump_storage,  0, &arg_storage           },
+                { "Coredump", "Storage",          config_parse_coredump_storage,     0, &arg_storage           },
+                { "Coredump", "Compression",      config_parse_coredump_compression, 0, &arg_compression       },
+                { "Coredump", "ProcessSizeMax",   config_parse_iec_off,              0, &arg_process_size_max  },
+                { "Coredump", "ExternalSizeMax",  config_parse_iec_off,              0, &arg_external_size_max },
+                { "Coredump", "JournalSizeMax",   config_parse_iec_size,             0, &arg_journal_size_max  },
+                { "Coredump", "KeepFree",         config_parse_iec_off,              0, &arg_keep_free         },
+                { "Coredump", "MaxUse",           config_parse_iec_off,              0, &arg_max_use           },
                 {}
         };
 
@@ -228,7 +259,7 @@ static int maybe_remove_external_coredump(const char *filename, off_t size) {
         if (!filename)
                 return 1;
 
-        if (unlink(filename) < 0) {
+        if (unlink(filename) < 0 && errno != ENOENT) {
                 log_error("Failed to unlink %s: %m", filename);
                 return -errno;
         }
@@ -236,14 +267,14 @@ static int maybe_remove_external_coredump(const char *filename, off_t size) {
         return 1;
 }
 
+static int save_external_coredump(
+                const char *info[_INFO_LEN],
+                uid_t uid,
+                char **ret_filename,
+                int *ret_fd,
+                off_t *ret_size) {
 
-static int save_external_coredump(const char *info[_INFO_LEN],
-                                  uid_t uid,
-                                  char **ret_filename,
-                                  int *ret_fd,
-                                  off_t *ret_size) {
-
-        _cleanup_free_ char *p = NULL, *t = NULL, *c = NULL, *fn = NULL, *tmp = NULL;
+        _cleanup_free_ char *p = NULL, *t = NULL, *c = NULL, *fn = NULL, *tmp = NULL, *u = NULL;
         _cleanup_close_ int fd = -1;
         sd_id128_t boot;
         struct stat st;
@@ -262,6 +293,10 @@ static int save_external_coredump(const char *info[_INFO_LEN],
         if (!p)
                 return log_oom();
 
+        u = filename_escape(info[INFO_UID]);
+        if (!u)
+                return log_oom();
+
         t = filename_escape(info[INFO_TIMESTAMP]);
         if (!t)
                 return log_oom();
@@ -273,8 +308,9 @@ static int save_external_coredump(const char *info[_INFO_LEN],
         }
 
         r = asprintf(&fn,
-                     "/var/lib/systemd/coredump/core.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
+                     "/var/lib/systemd/coredump/core.%s.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
                      c,
+                     u,
                      SD_ID128_FORMAT_VAL(boot),
                      p,
                      t);
@@ -295,31 +331,30 @@ static int save_external_coredump(const char *info[_INFO_LEN],
 
         r = copy_bytes(STDIN_FILENO, fd, arg_process_size_max);
         if (r == -E2BIG) {
-                log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.",
-                          info[INFO_PID], info[INFO_COMM]);
+                log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", info[INFO_PID], info[INFO_COMM]);
                 goto fail;
         } else if (IN_SET(r, -EDQUOT, -ENOSPC)) {
-                log_error("Not enough disk space for coredump of %s (%s), refusing.",
-                          info[INFO_PID], info[INFO_COMM]);
+                log_error("Not enough disk space for coredump of %s (%s), refusing.", info[INFO_PID], info[INFO_COMM]);
                 goto fail;
         } else if (r < 0) {
                 log_error("Failed to dump coredump to file: %s", strerror(-r));
                 goto fail;
         }
 
-        if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
-                log_error("Failed to seek on %s: %m", tmp);
-                goto uncompressed;
-        }
-
         if (fstat(fd, &st) < 0) {
                 log_error("Failed to fstat coredump %s: %m", tmp);
                 goto fail;
         }
 
+        if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
+                log_error("Failed to seek on %s: %m", tmp);
+                goto uncompressed;
+        }
+
 #ifdef HAVE_XZ
         /* If we will remove the coredump anyway, do not compress. */
-        if (maybe_remove_external_coredump(NULL, st.st_size) == 0) {
+        if (maybe_remove_external_coredump(NULL, st.st_size) == 0
+            && arg_compression == COREDUMP_COMPRESSION_XZ) {
 
                 _cleanup_free_ char *fn2 = NULL;
                 char *tmp2;
@@ -332,7 +367,7 @@ static int save_external_coredump(const char *info[_INFO_LEN],
                         goto uncompressed;
                 }
 
-                r = compress_stream(fd, fd2, -1);
+                r = compress_stream(fd, fd2, LZMA_PRESET_DEFAULT, -1);
                 if (r < 0) {
                         log_error("Failed to compress %s: %s", tmp2, strerror(-r));
                         unlink_noerrno(tmp2);
@@ -426,13 +461,13 @@ int main(int argc, char* argv[]) {
         _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
                 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
                 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
-                *coredump_filename = NULL, *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL,
-                *exe = NULL, *comm = NULL;
+                *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL,
+                *exe = NULL, *comm = NULL, *filename = NULL;
         const char *info[_INFO_LEN];
 
         _cleanup_close_ int coredump_fd = -1;
 
-        struct iovec iovec[17];
+        struct iovec iovec[18];
         off_t coredump_size;
         int r, j = 0;
         uid_t uid, owner_uid;
@@ -458,7 +493,10 @@ int main(int argc, char* argv[]) {
 
         /* Ignore all parse errors */
         parse_config();
-        log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
+        log_debug("Selected storage '%s'.",
+                  coredump_storage_to_string(arg_storage));
+        log_debug("Selected compression %s.",
+                  coredump_compression_to_string(arg_compression));
 
         r = parse_uid(argv[INFO_UID + 1], &uid);
         if (r < 0) {
@@ -505,15 +543,15 @@ int main(int argc, char* argv[]) {
                         if (arg_storage != COREDUMP_STORAGE_NONE)
                                 arg_storage = COREDUMP_STORAGE_EXTERNAL;
 
-                        r = save_external_coredump(info, uid, &coredump_filename, &coredump_fd, &coredump_size);
+                        r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
                         if (r < 0)
                                 goto finish;
 
-                        r = maybe_remove_external_coredump(coredump_filename, coredump_size);
+                        r = maybe_remove_external_coredump(filename, coredump_size);
                         if (r < 0)
                                 goto finish;
 
-                        log_info("Detected coredump of the journal daemon itself, diverted to %s.", coredump_filename);
+                        log_info("Detected coredump of the journal daemon itself, diverted to %s.", filename);
                         goto finish;
                 }
 
@@ -603,8 +641,11 @@ int main(int argc, char* argv[]) {
         IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
         IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
 
+        /* Vacuum before we write anything again */
+        coredump_vacuum(-1, arg_keep_free, arg_max_use);
+
         /* Always stream the coredump to disk, if that's possible */
-        r = save_external_coredump(info, uid, &coredump_filename, &coredump_fd, &coredump_size);
+        r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
         if (r < 0)
                 /* skip whole core dumping part */
                 goto log;
@@ -613,9 +654,18 @@ int main(int argc, char* argv[]) {
          * now, as later on we will lack the privileges for
          * it. However, we keep the fd to it, so that we can still
          * process it and log it. */
-        r = maybe_remove_external_coredump(coredump_filename, coredump_size);
+        r = maybe_remove_external_coredump(filename, coredump_size);
         if (r < 0)
                 goto finish;
+        if (r == 0) {
+                const char *coredump_filename;
+
+                coredump_filename = strappenda("COREDUMP_FILENAME=", filename);
+                IOVEC_SET_STRING(iovec[j++], coredump_filename);
+        }
+
+        /* Vacuum again, but exclude the coredump we just created */
+        coredump_vacuum(coredump_fd, arg_keep_free, arg_max_use);
 
         /* Now, let's drop privileges to become the user who owns the
          * segfaulted process and allocate the coredump memory under