[systemd-devel] [PATCH] [RFCv2] Optionally save core dumps as plain files

Oleksii Shevchuk alxchk at gmail.com
Fri Feb 15 11:31:16 PST 2013


Introduce configuration file: /etc/systemd/coredump.conf with
configurable uid/gid parameters, optional backends to journal
and files, per storage size limits
---
 Makefile-man.am                  |   1 +
 Makefile.am                      |  13 +-
 man/coredump.conf.xml            | 142 +++++++++++
 src/core/manager.c               |   1 +
 src/journal/coredump-gperf.gperf |  23 ++
 src/journal/coredump.c           | 537 ++++++++++++++++++++++++++++++++-------
 src/journal/coredump.conf        |  16 ++
 src/journal/coredump.h           |  47 ++++
 src/journal/journald-server.h    |   2 +-
 9 files changed, 685 insertions(+), 97 deletions(-)
 create mode 100644 man/coredump.conf.xml
 create mode 100644 src/journal/coredump-gperf.gperf
 create mode 100644 src/journal/coredump.conf
 create mode 100644 src/journal/coredump.h

diff --git a/Makefile-man.am b/Makefile-man.am
index e4eb26c..5e3360e 100644
--- a/Makefile-man.am
+++ b/Makefile-man.am
@@ -8,6 +8,7 @@ MANPAGES += \
 	man/hostname.5 \
 	man/journalctl.1 \
 	man/journald.conf.5 \
+	man/coredump.conf.5 \
 	man/kernel-command-line.7 \
 	man/locale.conf.5 \
 	man/localtime.5 \
diff --git a/Makefile.am b/Makefile.am
index 10934eb..3d59b14 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2609,7 +2609,8 @@ nodist_systemunit_DATA += \
 	units/systemd-journal-flush.service
 
 dist_pkgsysconf_DATA += \
-	src/journal/journald.conf
+	src/journal/journald.conf \
+	src/journal/coredump.conf
 
 pkgconfiglib_DATA += \
 	src/journal/libsystemd-journal.pc
@@ -2628,10 +2629,12 @@ EXTRA_DIST += \
 	src/journal/libsystemd-journal.sym \
 	units/systemd-journald.service.in \
 	units/systemd-journal-flush.service.in \
-	src/journal/journald-gperf.gperf
+	src/journal/journald-gperf.gperf \
+	src/journal/coredump-gperf.gperf
 
 CLEANFILES += \
-	src/journal/journald-gperf.c
+	src/journal/journald-gperf.c \
+	src/journal/coredump-gperf.c
 
 # ------------------------------------------------------------------------------
 if HAVE_MICROHTTPD
@@ -2675,10 +2678,12 @@ EXTRA_DIST += \
 # ------------------------------------------------------------------------------
 if ENABLE_COREDUMP
 systemd_coredump_SOURCES = \
-	src/journal/coredump.c
+	src/journal/coredump.c \
+	src/journal/coredump-gperf.c
 
 systemd_coredump_LDADD = \
 	libsystemd-journal-internal.la \
+	libsystemd-id128-internal.la \
 	libsystemd-label.la \
 	libsystemd-shared.la
 
diff --git a/man/coredump.conf.xml b/man/coredump.conf.xml
new file mode 100644
index 0000000..cac2065
--- /dev/null
+++ b/man/coredump.conf.xml
@@ -0,0 +1,142 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<?xml-stylesheet type="text/xsl" href="http://docbook.sourceforge.net/release/xsl/current/xhtml/docbook.xsl"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+        "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+  This file is part of systemd.
+
+  Copyright 2013 Lennart Poettering
+                 Oleksii Shevchuk
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+-->
+
+<refentry id="coredump.conf">
+        <refentryinfo>
+                <title>coredump.conf</title>
+                <productname>systemd</productname>
+
+                <authorgroup>
+                        <author>
+                                <contrib>Developer</contrib>
+                                <firstname>Lennart</firstname>
+                                <surname>Poettering</surname>
+                                <email>lennart at poettering.net</email>
+                        </author>
+                        <author>
+                                <contrib>Developer</contrib>
+                                <firstname>Oleksii</firstname>
+                                <surname>Shevchuk</surname>
+                                <email>alxchk at gmail.com</email>
+                        </author>
+                </authorgroup>
+        </refentryinfo>
+
+        <refmeta>
+                <refentrytitle>coredump.conf</refentrytitle>
+                <manvolnum>5</manvolnum>
+        </refmeta>
+
+        <refnamediv>
+                <refname>coredump.conf</refname>
+                <refpurpose>Coredump utility configuration file</refpurpose>
+        </refnamediv>
+
+        <refsynopsisdiv>
+                <para><filename>/etc/systemd/coredump.conf</filename></para>
+        </refsynopsisdiv>
+
+        <refsect1>
+                <title>Description</title>
+
+                <para>This files configures several parameters of the
+                systemd-coredump utility.</para>
+
+        </refsect1>
+
+        <refsect1>
+                <title>Options</title>
+
+                <para>All options are configured in the
+                <literal>[Coredump]</literal> section:</para>
+
+                <variablelist>
+
+                        <varlistentry>
+                                <term><varname>User=</varname></term>
+                                <term><varname>Group=</varname></term>
+
+                                <listitem><para>Enforce UID/GID of
+                                <literal>systemd-coredump</literal> while loading
+                                and storing coredump. If not setted, then uid/gid of
+                                crashed process will be used.
+                                </para></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                                <term><varname>StoreIntoJournal=</varname></term>
+
+                                <listitem><para>Tales a boolean value. If enabled
+                                (the default) core dump will be stored in the
+                                COREDUMP= field of log message.
+                                </para></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                                <term><varname>StoreIntoFile=</varname></term>
+
+                                <listitem><para>Takes a boolean
+                                value. If enabled, core dump will be stored at file at
+                                /var/log/coredump/MACHINE-ID/COMM-TMPSUFFIX location.
+                                Storage directory will be created if not exists. If
+                                <literal>User=</literal> and <literal>Group=</literal>
+                                not setted, then directory will be created with 1777
+                                permissions, if not exists. In other case, directory
+                                will be created with 0770 permissions, and with specified
+                                user/group owner. If only one of <literal>User=</literal>,
+                                <literal>Group=</literal> specified, then missing owner
+                                will be root. Field COREDUMP_FILE= will be added to log
+                                message with COMM-TMPSUFFIX value.</para></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                                <term><varname>JournalMaxCoreSize=</varname></term>
+                                <term><varname>FileMaxCoreSize=</varname></term>
+
+                                <listitem><para>Enforce size limits on the core
+                                dumps stored. The option prefixed with <literal>Journal</literal>
+                                apply to the COREDUMP= journal message field. The option prefixed
+                                with <literal>File</literal> apply to the files when stored on a
+                                persistent file system, more specifically
+                                <filename>/var/log/coredump/MACHINE-ID</filename>.
+                                </para></listitem>
+                        </varlistentry>
+
+                </variablelist>
+
+        </refsect1>
+
+        <refsect1>
+                  <title>See Also</title>
+                  <para>
+                          <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+                          <citerefentry><refentrytitle>systemd-coredumpctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+                          <citerefentry><refentrytitle>journalctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+                          <citerefentry><refentrytitle>systemd.journal-fields</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+                          <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+                  </para>
+        </refsect1>
+
+</refentry>
diff --git a/src/core/manager.c b/src/core/manager.c
index 28f169d..c9f410e 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -51,6 +51,7 @@
 #include "hashmap.h"
 #include "macro.h"
 #include "strv.h"
+#include "env-util.h"
 #include "log.h"
 #include "util.h"
 #include "mkdir.h"
diff --git a/src/journal/coredump-gperf.gperf b/src/journal/coredump-gperf.gperf
new file mode 100644
index 0000000..66409a3
--- /dev/null
+++ b/src/journal/coredump-gperf.gperf
@@ -0,0 +1,23 @@
+%{
+#include <stddef.h>
+#include <sys/socket.h>
+#include "coredump.h"
+#include "conf-parser.h"
+%}
+struct ConfigPerfItem;
+%null_strings
+%language=ANSI-C
+%define slot-name section_and_lvalue
+%define hash-function-name coredump_gperf_hash
+%define lookup-function-name coredump_gperf_lookup
+%readonly-tables
+%omit-struct-type
+%struct-type
+%includes
+%%
+Coredump.User,               config_parse_string,     0, offsetof(Coredump, user)
+Coredump.Group,              config_parse_string,     0, offsetof(Coredump, group)
+Coredump.StoreIntoJournal,   config_parse_bool,       0, offsetof(Coredump, to_journal)
+Coredump.StoreIntoFile,      config_parse_bool,       0, offsetof(Coredump, to_file)
+Coredump.JournalMaxCoreSize, config_parse_bytes_off,  0, offsetof(Coredump, journal_max_size)
+Coredump.FileMaxCoreSize,    config_parse_bytes_off,  0, offsetof(Coredump, file_max_size)
diff --git a/src/journal/coredump.c b/src/journal/coredump.c
index 021b4c6..7a6490d 100644
--- a/src/journal/coredump.c
+++ b/src/journal/coredump.c
@@ -3,7 +3,8 @@
 /***
   This file is part of systemd.
 
-  Copyright 2012 Lennart Poettering
+  Copyright 2012-2013 Lennart Poettering
+                      Oleksii Shevchuk
 
   systemd is free software; you can redistribute it and/or modify it
   under the terms of the GNU Lesser General Public License as published by
@@ -22,6 +23,9 @@
 #include <errno.h>
 #include <unistd.h>
 #include <stdio.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <sys/mman.h>
 #include <sys/prctl.h>
 
 #include <systemd/sd-journal.h>
@@ -35,9 +39,12 @@
 #include "macro.h"
 #include "mkdir.h"
 #include "special.h"
+#include "sd-id128.h"
+#include "coredump.h"
 #include "cgroup-util.h"
 
-#define COREDUMP_MAX (24*1024*1024)
+#define COREDUMP_MAX_DEFAULT ( 24 * 1024 * 1024 )
+#define COREDUMP_CONFIG "/etc/systemd/coredump.conf"
 
 enum {
         ARG_PID = 1,
@@ -49,26 +56,132 @@ enum {
         _ARG_MAX
 };
 
-static int divert_coredump(void) {
+static int coredump_drop_creds(uid_t uid, uid_t gid) {
+        if (setresgid(gid, gid, gid) < 0 ||
+            setresuid(uid, uid, uid) < 0) {
+                log_error("Failed to drop privileges: %m");
+                return -errno;
+        }
+
+        umask(0377);
+
+        return 0;
+}
+
+static int coredump_load_configuration(Coredump *s, uid_t uid, gid_t gid) {
+        int r;
         _cleanup_fclose_ FILE *f = NULL;
 
-        log_info("Detected coredump of the journal daemon itself, diverting coredump to /var/lib/systemd/coredump/.");
+        assert_se(s);
 
-        mkdir_p_label("/var/lib/systemd/coredump", 0755);
+        s->user = NULL;
+        s->group = NULL;
+        s->uid = uid;
+        s->gid = gid;
+        s->to_file = false;
+        s->to_journal = true;
+        s->journal_max_size = COREDUMP_MAX_DEFAULT;
+        s->file_max_size = SIZE_MAX;
 
-        f = fopen("/var/lib/systemd/coredump/core.systemd-journald", "we");
+        f = fopen(COREDUMP_CONFIG, "re");
         if (!f) {
-                log_error("Failed to create coredump file: %m");
+                if (errno == ENOENT)
+                        return 0;
+
+                log_warning("Failed to open configuration file " COREDUMP_CONFIG ": %m");
                 return -errno;
         }
 
+        r = config_parse(COREDUMP_CONFIG, f, "Coredump\0", config_item_perf_lookup,
+                         (void*) coredump_gperf_lookup, false, s);
+        if (r < 0)
+                log_warning("Failed to parse configuration file: %s", strerror(-r));
+
+        if (s->user) {
+                const char * user = s->user;
+                r = get_user_creds(&user, &s->uid, NULL, NULL, NULL);
+                if (r) {
+                        log_warning("Failed to get user %s creds", s->user);
+                        free(s->user);
+                        s->user = NULL;
+                }
+        }
+
+        if (s->group) {
+                const char * group = s->group;
+                r = get_group_creds(&group, &s->gid);
+                if (r) {
+                        log_warning("Failed to get group %s creds", s->group);
+                        free(s->group);
+                        s->group = NULL;
+                }
+        }
+
+        return r;
+}
+
+static void coredump_cleanup_configuration(Coredump *c) {
+        free(c->user);
+        free(c->group);
+}
+
+static char * coredump_directory(void) {
+        char * directory = NULL;
+        sd_id128_t machineid;
+        char buffer[33];
+
+        int r = sd_id128_get_machine(&machineid);
+        if (r)
+                return NULL;
+
+        directory = strjoin(COREDUMP_DIRECTORY,
+                            "/",
+                            sd_id128_to_string(machineid, buffer),
+                            NULL);
+
+        return directory;
+}
+
+static int coredump_ensure_directory_exists(Coredump *config) {
+        int r;
+        mode_t mode = S_IRUSR | S_IWUSR | S_IXUSR;
+
+        _cleanup_free_ char *d = coredump_directory();
+        if (! d)
+                return log_oom();
+
+        r = mkdir_parents_label(d, 0755);
+        if (r)
+                return -errno;
+
+        if (config->group)
+                mode |= S_IRGRP | S_IWGRP | S_IXGRP;
+        else
+                if (!config->user) {
+                        mode |= S_IRGRP | S_IWGRP | S_IXGRP;
+                        mode |= S_IROTH | S_IWOTH | S_IXOTH;
+                        mode |= S_ISVTX;
+                }
+
+        r = mkdir_safe_label(d, mode,
+                             config->user ? config->uid : 0,
+                             config->group ? config->gid : 0);
+        if (r && (errno != EEXIST))
+                return -errno;
+
+        return 0;
+}
+
+static int coredump_stdio_to_file(FILE *corefile, size_t max_size) {
+        size_t offset = 0;
         for (;;) {
                 uint8_t buffer[4096];
                 size_t l, q;
 
-                l = fread(buffer, 1, sizeof(buffer), stdin);
+                l = fread(buffer, 1, MIN(sizeof(buffer), max_size - offset),
+                          stdin);
                 if (l <= 0) {
-                        if (ferror(f)) {
+                        if (ferror(corefile)) {
                                 log_error("Failed to read coredump: %m");
                                 return -errno;
                         }
@@ -76,16 +189,21 @@ static int divert_coredump(void) {
                         break;
                 }
 
-                q = fwrite(buffer, 1, l, f);
+                q = fwrite(buffer, 1, l, corefile);
                 if (q != l) {
                         log_error("Failed to write coredump: %m");
                         return -errno;
                 }
+
+                if ((offset += q) >= max_size) {
+                        log_warning("Truncating coredump (%lu)", offset);
+                        break;
+                }
         }
 
-        fflush(f);
+        fflush(corefile);
 
-        if (ferror(f)) {
+        if (ferror(corefile)) {
                 log_error("Failed to write coredump: %m");
                 return -errno;
         }
@@ -93,74 +211,224 @@ static int divert_coredump(void) {
         return 0;
 }
 
-int main(int argc, char* argv[]) {
-        int r, j = 0;
-        _cleanup_free_ char *p = NULL;
-        ssize_t n;
-        pid_t pid;
-        uid_t uid;
-        gid_t gid;
-        struct iovec iovec[14];
-        _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, *t = NULL;
+static int coredump_create_file(const char * ucomm, char ** name,
+                                   bool private) {
+        _cleanup_free_ char *template = NULL, *comm = NULL, *directory = NULL;
+        int fd, r;
 
-        prctl(PR_SET_DUMPABLE, 0);
+        assert_se(ucomm);
+        assert_se(name);
 
-        if (argc != _ARG_MAX) {
-                log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
-                log_open();
+        directory = private ? strdup("/tmp") : coredump_directory();
+        if (! directory)
+                return log_oom();
 
-                log_error("Invalid number of arguments passed from kernel.");
-                r = -EINVAL;
-                goto finish;
+        comm = xescape(ucomm, "/ ");
+        if (! comm)
+                return log_oom();
+
+        template = strjoin(directory, "/", comm, "-XXXXXX", NULL);
+        if (! template)
+                return log_oom();
+
+        fd = mkostemp(template, O_CREAT | O_EXCL | O_CLOEXEC | O_APPEND | O_RDWR);
+        if (fd < 0)
+                return -errno;
+
+        r = fchmod(fd, S_IRUSR | S_IRGRP);
+        if (r) {
+                close(fd);
+                return -errno;
         }
 
-        r = parse_pid(argv[ARG_PID], &pid);
-        if (r < 0) {
-                log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
-                log_open();
+        if (private)
+                unlink(template);
 
-                log_error("Failed to parse PID.");
-                goto finish;
+        *name = strdup(template + strlen(directory) + 1);
+        if (! *name) {
+                close(fd);
+                return log_oom();
         }
 
-        if (cg_pid_get_unit(pid, &t) >= 0) {
+        return fd;
+}
 
-                if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
-                        /* Make sure we don't make use of the journal,
-                         * if it's the journal which is crashing */
-                        log_set_target(LOG_TARGET_KMSG);
-                        log_open();
+static void coredump_cleanup_str(char *mem, size_t size) {
+        (void)size;
+        free(mem);
+}
 
-                        r = divert_coredump();
-                        goto finish;
-                }
+static int coredump_store_to_file(const char * ucomm,
+                                  size_t max_size,
+                                  char ** message,
+                                  size_t *message_size) {
+        _cleanup_fclose_ FILE *core = NULL;
+        _cleanup_free_ char *t = NULL;
 
-                core_unit = strappend("COREDUMP_UNIT=", t);
-        } else if (cg_pid_get_user_unit(pid, &t) >= 0)
-                core_unit = strappend("COREDUMP_USER_UNIT=", t);
+        int r;
 
-        if (core_unit)
-                IOVEC_SET_STRING(iovec[j++], core_unit);
+        assert_se(ucomm);
+        assert_se(message);
+        assert_se(message_size);
 
-        /* OK, now we know it's not the journal, hence make use of
-         * it */
-        log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
-        log_open();
+        *message = NULL;
 
-        r = parse_uid(argv[ARG_UID], &uid);
-        if (r < 0) {
-                log_error("Failed to parse UID.");
-                goto finish;
+        r = coredump_create_file(ucomm, &t, false);
+        if (r < 0)
+                return r;
+
+        core = fdopen(r, "rew+");
+        if (! core) {
+                close(r);
+                return -errno;
         }
 
-        r = parse_gid(argv[ARG_GID], &gid);
-        if (r < 0) {
-                log_error("Failed to parse GID.");
+        r = coredump_stdio_to_file(core, max_size);
+        if (r)
+                return r;
+
+        *message = strjoin(COREDUMP_FILE_RECORD, t, NULL);
+
+        if (! *message)
+                return log_oom();
+
+        *message_size = strlen(*message);
+
+        return 0;
+}
+
+static void coredump_cleanup_mem(char * mem, size_t size) {
+        munmap(mem, size);
+}
+
+static int coredump_store_to_mmaped_memory(const char * ucomm,
+                                           size_t max_size,
+                                           char ** message,
+                                           size_t *message_size) {
+        int r, mfd;
+
+        _cleanup_fclose_ FILE *tmp = NULL;
+        _cleanup_free_ char *t = NULL;
+        struct stat stat;
+
+        assert_se(ucomm);
+        assert_se(message);
+        assert_se(message_size);
+
+        mfd = coredump_create_file(ucomm, &t, true);
+        if (mfd < 0)
+                return mfd;
+
+        tmp = fdopen(mfd, "rew+");
+        if (! tmp) {
+                close(mfd);
+                return -errno;
+        }
+
+        r = fwrite(COREDUMP_CORE_RECORD, COREDUMP_CORE_RECORD_SIZE, 1, tmp);
+        if (r != 1)
+                return errno ? -errno : -1;
+
+        r = coredump_stdio_to_file(tmp, max_size);
+        if (r)
+                return r;
+
+        r = fstat(fileno(tmp), &stat);
+        if (r)
+                return -errno;
+
+        *message = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE,
+                        mfd, 0);
+        if (*message == MAP_FAILED)
+                return -errno;
+
+        *message_size = stat.st_size;
+
+        return 0;
+}
+
+static int coredump_store_memory_to_file(const char * memory,
+                                         size_t memory_size,
+                                         const char * ucomm,
+                                         size_t max_size,
+                                         char ** message,
+                                         size_t *message_size) {
+        _cleanup_fclose_ FILE *core = NULL;
+        _cleanup_free_ char *t = NULL;
+        char * m = NULL;
+
+        int fd, r;
+
+        assert_se(ucomm);
+        assert_se(message);
+        assert_se(message_size);
+
+        *message = NULL;
+        memory_size = MIN(memory_size, max_size);
+
+        fd = coredump_create_file(ucomm, &t, false);
+        if (fd < 0)
+                return fd;
+
+        r = ftruncate(fd, memory_size);
+        if (r) {
+                log_error("Couldn't truncate backed file: %m");
+                close(fd);
+                return -errno;
+        }
+
+        m = mmap(NULL, memory_size, PROT_WRITE, MAP_SHARED, fd, 0);
+        if (m == MAP_FAILED) {
+                log_error("Couldn't mmap backed file: %m");
+                close(fd);
+                return -errno;
+        }
+
+        memcpy(m, memory, memory_size);
+        munmap(m, memory_size);
+        close(fd);
+
+        *message = strjoin(COREDUMP_FILE_RECORD, t, NULL);
+
+        if (! *message) {
+                *message = NULL;
+                return log_oom();
+        }
+
+        *message_size = strlen(*message);
+
+        return 0;
+}
+
+static int coredump_submit_message(int argc, const char * const * argv, pid_t pid, const Coredump * config) {
+         int r, j = 0;
+         _cleanup_free_ char *p = NULL;
+         struct iovec iovec[16];
+        _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;
+
+        char *journal_message = NULL, *file_message = NULL, *t = NULL;
+        size_t journal_message_size, file_message_size;
+
+        assert_se(argv);
+
+        if (argc != _ARG_MAX) {
+                log_error("Invalid number of arguments passed from kernel.");
+                r = -EINVAL;
                 goto finish;
         }
 
+        if (cg_pid_get_unit(pid, &t) >= 0)
+                core_unit = strappend("COREDUMP_UNIT=", t);
+        else if (cg_pid_get_user_unit(pid, &t) >= 0)
+                core_unit = strappend("COREDUMP_USER_UNIT=", t);
+
+        if (core_unit) {
+                IOVEC_SET_STRING(iovec[j++], core_unit);
+                free(t);
+        }
+
         core_pid = strappend("COREDUMP_PID=", argv[ARG_PID]);
         if (core_pid)
                 IOVEC_SET_STRING(iovec[j++], core_pid);
@@ -191,7 +459,6 @@ int main(int argc, char* argv[]) {
         }
 
 #endif
-
         if (get_process_exe(pid, &t) >= 0) {
                 core_exe = strappend("COREDUMP_EXE=", t);
                 free(t);
@@ -202,59 +469,145 @@ int main(int argc, char* argv[]) {
 
         if (get_process_cmdline(pid, 0, false, &t) >= 0) {
                 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
-                free(t);
-
                 if (core_cmdline)
                         IOVEC_SET_STRING(iovec[j++], core_cmdline);
+                free(t);
         }
 
         core_timestamp = strjoin("COREDUMP_TIMESTAMP=", argv[ARG_TIMESTAMP], "000000", NULL);
         if (core_timestamp)
                 IOVEC_SET_STRING(iovec[j++], core_timestamp);
 
-        IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
         IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
 
         core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") dumped core.", NULL);
         if (core_message)
                 IOVEC_SET_STRING(iovec[j++], core_message);
 
-        /* Now, let's drop privileges to become the user who owns the
-         * segfaulted process and allocate the coredump memory under
-         * his uid. This also ensures that the credentials journald
-         * will see are the ones of the coredumping user, thus making
-         * sure the user himself gets access to the core dump. */
+        if (config->to_journal) {
+                r = coredump_store_to_mmaped_memory(argv[ARG_COMM],
+                                                    config->to_file ? MAX(config->file_max_size, config->journal_max_size)
+                                                    : config->journal_max_size,
+                                                    &journal_message,
+                                                    &journal_message_size);
+                if (r) {
+                        log_error("Failed to store coredump to journal: %s", strerror(-r));
+                        goto finish;
+                }
 
-        if (setresgid(gid, gid, gid) < 0 ||
-            setresuid(uid, uid, uid) < 0) {
-                log_error("Failed to drop privileges: %m");
-                r = -errno;
-                goto finish;
-        }
+                iovec[j].iov_len = MIN(journal_message_size, config->journal_max_size + COREDUMP_CORE_RECORD_SIZE);
+                iovec[j].iov_base = journal_message;
 
-        p = malloc(9 + COREDUMP_MAX);
-        if (!p) {
-                r = log_oom();
-                goto finish;
+                j ++;
         }
 
-        memcpy(p, "COREDUMP=", 9);
-
-        n = loop_read(STDIN_FILENO, p + 9, COREDUMP_MAX, false);
-        if (n < 0) {
-                log_error("Failed to read core dump data: %s", strerror(-n));
-                r = (int) n;
-                goto finish;
+        if (config->to_file) {
+                if (config->to_journal)
+                        r = coredump_store_memory_to_file(journal_message + COREDUMP_CORE_RECORD_SIZE,
+                                                          journal_message_size - COREDUMP_CORE_RECORD_SIZE,
+                                                          argv[ARG_COMM], config->file_max_size,
+                                                          &file_message, &file_message_size);
+                else
+                        r = coredump_store_to_file(argv[ARG_COMM], config->file_max_size,
+                                                   &file_message, &file_message_size);
+
+                if (! r) {
+                        iovec[j].iov_len = file_message_size;
+                        iovec[j].iov_base = file_message;
+
+                        j ++;
+                } else {
+                        if (r == -EACCES)
+                                log_warning("User uid=%s gid=%s is not allowed to store coredump to file",
+                                            argv[ARG_UID], argv[ARG_GID]);
+                        else {
+                                log_error("Failed to store coredump to file: %s", strerror(-r));
+                                goto finish;
+                        }
+                }
         }
 
-        iovec[j].iov_base = p;
-        iovec[j].iov_len = 9 + n;
-        j++;
-
         r = sd_journal_sendv(iovec, j);
         if (r < 0)
                 log_error("Failed to send coredump: %s", strerror(-r));
 
-finish:
+ finish:
+        if (journal_message)
+                coredump_cleanup_mem(journal_message, journal_message_size);
+
+        if (file_message)
+                coredump_cleanup_str(file_message, file_message_size);
+
         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ }
+
+int main(int argc, const char * const * argv) {
+        Coredump coredump_config;
+        bool journal = false;
+        pid_t pid;
+        uid_t uid;
+        gid_t gid;
+        char *t;
+        int r = 0;
+
+        prctl(PR_SET_DUMPABLE, 0);
+
+        r = parse_pid(argv[ARG_PID], &pid);
+        if (r < 0) {
+                 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
+                 log_open();
+                 log_error("Failed to parse PID.");
+                 return r;
+        }
+
+        if (cg_pid_get_unit(pid, &t) >= 0) {
+                if(streq(t, SPECIAL_JOURNALD_SERVICE)) {
+                        journal = true;
+                        log_set_target(LOG_TARGET_KMSG);
+                } else
+                        log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
+                free(t);
+        } else
+                log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
+
+        log_open();
+
+        r = parse_uid(argv[ARG_UID], &uid);
+        if (r < 0) {
+                log_error("Failed to parse UID.");
+                return r;
+        }
+
+        r = parse_gid(argv[ARG_GID], &gid);
+        if (r < 0) {
+                log_error("Failed to parse GID.");
+                return r;
+        }
+
+        coredump_load_configuration(&coredump_config, uid, gid);
+
+        if (journal) {
+                coredump_config.file_max_size = SIZE_MAX;
+                coredump_config.to_journal = false;
+                coredump_config.to_file = true;
+                log_info(SPECIAL_JOURNALD_SERVICE " failed. Dumping to directory");
+        }
+
+        if (coredump_config.to_file) {
+                r = coredump_ensure_directory_exists(&coredump_config);
+                if (r) {
+                        log_error("Couldn't create coredump directory: %m");
+                        goto finish;
+                }
+        }
+
+        r = coredump_drop_creds(coredump_config.uid, coredump_config.gid);
+        if (r)
+                goto finish;
+
+        r = coredump_submit_message(argc, argv, pid, &coredump_config);
+
+finish:
+        coredump_cleanup_configuration(&coredump_config);
+        return r;
 }
diff --git a/src/journal/coredump.conf b/src/journal/coredump.conf
new file mode 100644
index 0000000..514b579
--- /dev/null
+++ b/src/journal/coredump.conf
@@ -0,0 +1,16 @@
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+#
+# See coredump.conf(5) for details
+
+[Coredump]
+#User=
+#Group=
+#StoreIntoJournal=yes
+#StoreIntoFile=no
+#MaxJournalCoreSize=25M
+#MaxFileCoreSize=4294967296
diff --git a/src/journal/coredump.h b/src/journal/coredump.h
new file mode 100644
index 0000000..83bf65c
--- /dev/null
+++ b/src/journal/coredump.h
@@ -0,0 +1,47 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2012-2013 Lennart Poettering
+                      Oleksii Shevchuk
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "conf-parser.h"
+
+#define COREDUMP_MESSAGEID "fc2e22bc6ee647b6b90729ab34a250b1"
+#define COREDUMP_DIRECTORY "/var/log/coredump"
+#define COREDUMP_CORE_LABEL "COREDUMP"
+#define COREDUMP_FILE_LABEL "COREDUMP_FILE"
+#define COREDUMP_CORE_RECORD COREDUMP_CORE_LABEL "="
+#define COREDUMP_FILE_RECORD COREDUMP_FILE_LABEL "="
+#define COREDUMP_CORE_RECORD_SIZE sizeof(COREDUMP_CORE_RECORD)
+#define COREDUMP_FILE_RECORD_SIZE sizeof(COREDUMP_FILE_RECORD)
+
+typedef struct Coredump {
+        size_t journal_max_size;
+        size_t file_max_size;
+        bool to_journal;
+        bool to_file;
+        char * user;
+        char * group;
+        uid_t uid;
+        gid_t gid;
+} Coredump;
+
+const struct ConfigPerfItem* coredump_gperf_lookup(const char *key, unsigned length);
diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h
index 9f50a29..3d05925 100644
--- a/src/journal/journald-server.h
+++ b/src/journal/journald-server.h
@@ -121,7 +121,7 @@ typedef struct Server {
         struct udev *udev;
 } Server;
 
-#define N_IOVEC_META_FIELDS 17
+#define N_IOVEC_META_FIELDS 18
 #define N_IOVEC_KERNEL_FIELDS 64
 #define N_IOVEC_UDEV_FIELDS 32
 
-- 
1.8.1.2



More information about the systemd-devel mailing list