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 | 149 +++++++++++ src/core/manager.c | 1 + src/journal/coredump-gperf.gperf | 23 ++ src/journal/coredump.c | 554 ++++++++++++++++++++++++++++++++------- src/journal/coredump.conf | 16 ++ src/journal/coredump.h | 61 +++++ src/journal/journald-server.c | 3 +- src/journal/journald-server.h | 2 +- 10 files changed, 724 insertions(+), 99 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 d02fff0..870e8ce 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/kernel-install.8 \ man/locale.conf.5 \ diff --git a/Makefile.am b/Makefile.am index 790e501..c819e30 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2628,7 +2628,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 @@ -2647,10 +2648,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 @@ -2694,10 +2697,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..de605b5 --- /dev/null +++ b/man/coredump.conf.xml @@ -0,0 +1,149 @@ +<?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>lenn...@poettering.net</email> + </author> + <author> + <contrib>Developer</contrib> + <firstname>Oleksii</firstname> + <surname>Shevchuk</surname> + <email>alx...@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>SaveToJournal=</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>SaveToFilesystem=</varname></term> + + <listitem><para>One of + <literal>volatile</literal>, + <literal>persistent</literal> and + <literal>none</literal>. + value. If <literal>volatile</literal>, core dump will be + stored as file to /run/log/coredump/MACHINE-ID/COMM-TMPSUFFIX location; + if <literal>persistent</literal> -- to + /var/log/coredump/MACHINE-ID/COMM-TMPSUFFIX location. + Storage directory will be created if not exists. If + <literal>none</literal>, then core dump will not be stored + as file at all. + <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 5527e9d..6598d97 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..82d808b --- /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.SaveToJournal, config_parse_bool, 0, offsetof(Coredump, to_journal) +Coredump.SaveToFilesystem, config_parse_storage, 0, offsetof(Coredump, to_filesystem) +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..8337e0d 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,143 @@ enum { _ARG_MAX }; -static int divert_coredump(void) { +static const char* const storage_table[] = { + [STORAGE_NONE] = "none", + [STORAGE_VOLATILE] = "volatile", + [STORAGE_PERSISTENT] = "persistent", +}; + +DEFINE_STRING_TABLE_LOOKUP(storage, Storage); +DEFINE_CONFIG_PARSE_ENUM(config_parse_storage, storage, Storage, "Failed to parse storage setting"); + +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_filesystem = STORAGE_NONE; + 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(enum Storage storage) { + sd_id128_t machineid; + char buffer[33]; + + int r = sd_id128_get_machine(&machineid); + if (r) + return NULL; + + if(! storage) + return strdup("/tmp"); + else + return strjoin(storage == STORAGE_VOLATILE ? + COREDUMP_VOLATILE_DIRECTORY : + COREDUMP_PERSISTENT_DIRECTORY, + "/", + sd_id128_to_string(machineid, buffer), + NULL); +} + +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(config->to_filesystem); + 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 +200,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 +222,226 @@ 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, + enum Storage storage) { + _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 = coredump_directory(storage); + 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 (storage == STORAGE_NONE) + 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, + enum Storage storage, + 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, storage); + 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, STORAGE_NONE); + 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, + enum Storage storage, + 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, storage); + 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 +472,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 +482,149 @@ 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_filesystem ? 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_filesystem) { + 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->to_filesystem, + config->file_max_size, + &file_message, &file_message_size); + else + r = coredump_store_to_file(argv[ARG_COMM], + config->to_filesystem, + 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; + if (! coredump_config.to_filesystem) + coredump_config.to_filesystem = STORAGE_VOLATILE; + log_info(SPECIAL_JOURNALD_SERVICE " failed. Dumping to directory"); + } + + if (coredump_config.to_filesystem) { + 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..2ad8021 --- /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= +#SaveToJournal=yes +#SaveToFilesystem=none +#MaxJournalCoreSize=25M +#MaxFileCoreSize=4294967296 diff --git a/src/journal/coredump.h b/src/journal/coredump.h new file mode 100644 index 0000000..b68397a --- /dev/null +++ b/src/journal/coredump.h @@ -0,0 +1,61 @@ +/*-*- 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_PERSISTENT_DIRECTORY "/var/log/coredump" +#define COREDUMP_VOLATILE_DIRECTORY "/run/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 enum Storage { + STORAGE_NONE, + STORAGE_VOLATILE, + STORAGE_PERSISTENT, + _STORAGE_MAX, + _STORAGE_INVALID = -1 +} Storage; + +typedef struct Coredump { + size_t journal_max_size; + size_t file_max_size; + bool to_journal; + enum Storage to_filesystem; + char * user; + char * group; + uid_t uid; + gid_t gid; +} Coredump; + +const struct ConfigPerfItem* coredump_gperf_lookup(const char *key, unsigned length); + +int config_parse_storage(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); + +const char *storage_to_string(Storage s); +Storage storage_from_string(const char *s); diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 818bd08..7644b04 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -73,10 +73,9 @@ #define RECHECK_AVAILABLE_SPACE_USEC (30*USEC_PER_SEC) static const char* const storage_table[] = { - [STORAGE_AUTO] = "auto", + [STORAGE_NONE] = "none", [STORAGE_VOLATILE] = "volatile", [STORAGE_PERSISTENT] = "persistent", - [STORAGE_NONE] = "none" }; DEFINE_STRING_TABLE_LOOKUP(storage, Storage); 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 _______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel