[systemd-devel] [PATCH] [RFCv6] Optionally save core dumps as plain files
Oleksii Shevchuk
alxchk at gmail.com
Tue Apr 30 12:32:05 PDT 2013
Introduce configuration file: /etc/systemd/coredump.conf with
configurable uid/gid parameters, optional backends to journal
and files, per storage size limits
Default filestorage choosed as /run/log/coredump or /var/log/coredump
with next reason:
1. These files produced with systemd component
2. These files registered with journal
---
Makefile-man.am | 1 +
Makefile.am | 13 +-
man/coredump.conf.xml | 142 ++++++++++
src/core/manager.c | 1 +
src/journal/coredump-gperf.gperf | 22 ++
src/journal/coredump.c | 567 +++++++++++++++++++++++++++++++--------
src/journal/coredump.conf | 15 ++
src/journal/coredump.h | 51 ++++
src/journal/journald-server.h | 2 +-
9 files changed, 703 insertions(+), 111 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 4f14a6a..c442201 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 9e0f5fb..e985bb6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2881,7 +2881,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
@@ -2900,10 +2901,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
@@ -2948,10 +2951,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..8ab92c1
--- /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>Core dump 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 set, then uid/gid of
+ crashed process will be used. This can be used
+ for allowing user or group of users
+ to have access to system core dumps.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>FileStorage=</varname></term>
+
+ <listitem><para>One of
+ <literal>volatile</literal> and,
+ <literal>persistent</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>. If value equals
+ to zero, then backend will be ommited.
+ </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 c7f8f20..d738cdb 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..c1b34a9
--- /dev/null
+++ b/src/journal/coredump-gperf.gperf
@@ -0,0 +1,22 @@
+%{
+#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.MaxJournalCoreSize, config_parse_bytes_off, 0, offsetof(Coredump, journal_max_size)
+Coredump.MaxFileCoreSize, config_parse_bytes_off, 0, offsetof(Coredump, file_max_size)
+Coredump.FileStorage, config_parse_storage, 0, offsetof(Coredump, storage)
diff --git a/src/journal/coredump.c b/src/journal/coredump.c
index fd03e38..1332150 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,13 +39,12 @@
#include "macro.h"
#include "mkdir.h"
#include "special.h"
+#include "sd-id128.h"
+#include "coredump.h"
#include "cgroup-util.h"
-/* Few programs have less than 3MiB resident */
-#define COREDUMP_MIN_START (3*1024*1024)
-/* Make sure to not make this larger than the maximum journal entry
- * size. See ENTRY_SIZE_MAX in journald-native.c. */
-#define COREDUMP_MAX (768*1024*1024)
+#define COREDUMP_MAX_DEFAULT ( 24 * 1024 * 1024 )
+#define COREDUMP_CONFIG "/etc/systemd/coredump.conf"
enum {
ARG_PID = 1,
@@ -53,26 +56,140 @@ enum {
_ARG_MAX
};
-static int divert_coredump(void) {
+static const char* const storage_table[] = {
+ [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->journal_max_size = COREDUMP_MAX_DEFAULT;
+ s->file_max_size = 0;
+ s->storage = STORAGE_VOLATILE;
- 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(NULL, COREDUMP_CONFIG, f, "Coredump\0", config_item_perf_lookup,
+ (void*) coredump_gperf_lookup, false, 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 == _STORAGE_INVALID)
+ return strdup("/tmp");
+ else
+ return strjoin(storage == STORAGE_VOLATILE ?
+ "/run/log/coredump/":
+ "/var/log/coredump/",
+ 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->storage);
+ 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;
}
@@ -80,16 +197,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;
}
@@ -97,75 +219,231 @@ static int divert_coredump(void) {
return 0;
}
-int main(int argc, char* argv[]) {
- int r, j = 0;
- char *t;
- ssize_t n;
- pid_t pid;
- uid_t uid;
- gid_t gid;
- struct iovec iovec[14];
- size_t coredump_bufsize, coredump_size;
- _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;
+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_INVALID)
+ 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) {
- core_unit = strappend("COREDUMP_UNIT=", t);
- } else if (cg_pid_get_user_unit(pid, &t) >= 0)
- core_unit = strappend("COREDUMP_USER_UNIT=", t);
+ static const char COREDUMP[] = "COREDUMP_FILE=";
- if (core_unit)
- IOVEC_SET_STRING(iovec[j++], core_unit);
+ _cleanup_fclose_ FILE *core = NULL;
+ _cleanup_free_ char *t = NULL;
- /* OK, now we know it's not the journal, hence make use of
- * it */
- log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
- log_open();
+ int r;
- r = parse_uid(argv[ARG_UID], &uid);
- if (r < 0) {
- log_error("Failed to parse UID.");
- goto finish;
+ assert_se(ucomm);
+ assert_se(message);
+ assert_se(message_size);
+
+ *message = NULL;
+
+ 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, 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;
+ static const char COREDUMP[] = "COREDUMP=";
+
+ assert_se(ucomm);
+ assert_se(message);
+ assert_se(message_size);
+
+ mfd = coredump_create_file(ucomm, &t, _STORAGE_INVALID);
+ if (mfd < 0)
+ return mfd;
+
+ tmp = fdopen(mfd, "rew+");
+ if (! tmp) {
+ close(mfd);
+ return -errno;
+ }
+
+ r = fwrite(COREDUMP, sizeof(COREDUMP), 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) {
+ static const char COREDUMP[] = "COREDUMP_FILE=";
+ _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, 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);
@@ -196,7 +474,6 @@ int main(int argc, char* argv[]) {
}
#endif
-
if (get_process_exe(pid, &t) >= 0) {
core_exe = strappend("COREDUMP_EXE=", t);
free(t);
@@ -207,71 +484,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->journal_max_size) {
+ r = coredump_store_to_mmaped_memory(argv[ARG_COMM],
+ config->file_max_size ? 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 + 9);
+ iovec[j].iov_base = journal_message;
+
+ j ++;
}
- coredump_bufsize = COREDUMP_MIN_START;
- coredump_data = malloc(coredump_bufsize);
- if (!coredump_data) {
- r = log_oom();
- goto finish;
+ if (config->file_max_size) {
+ if (config->journal_max_size)
+ r = coredump_store_memory_to_file(journal_message + 9,
+ journal_message_size - 9,
+ argv[ARG_COMM], config->storage,
+ config->file_max_size,
+ &file_message, &file_message_size);
+ else
+ r = coredump_store_to_file(argv[ARG_COMM],
+ config->storage,
+ 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;
+ }
+ }
}
- memcpy(coredump_data, "COREDUMP=", 9);
- coredump_size = 9;
+ r = sd_journal_sendv(iovec, j);
+ if (r < 0)
+ log_error("Failed to send coredump: %s", strerror(-r));
- for (;;) {
- n = loop_read(STDIN_FILENO, coredump_data + coredump_size,
- coredump_bufsize - coredump_size, false);
- if (n < 0) {
- log_error("Failed to read core dump data: %s", strerror(-n));
- r = (int) n;
- goto finish;
- } else if (n == 0)
- break;
+ 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;
+ }
- coredump_size += n;
- if (!GREEDY_REALLOC(coredump_data, coredump_bufsize, coredump_size + 1)) {
- r = log_oom();
+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.journal_max_size = 0;
+ if (! coredump_config.storage)
+ coredump_config.storage = STORAGE_VOLATILE;
+ log_info(SPECIAL_JOURNALD_SERVICE " failed. Dumping to directory");
+ }
+
+ if (coredump_config.file_max_size) {
+ r = coredump_ensure_directory_exists(&coredump_config);
+ if (r) {
+ log_error("Couldn't create coredump directory: %m");
goto finish;
}
}
- iovec[j].iov_base = coredump_data;
- iovec[j].iov_len = coredump_size;
- j++;
+ r = coredump_drop_creds(coredump_config.uid, coredump_config.gid);
+ if (r)
+ goto finish;
- r = sd_journal_sendv(iovec, j);
- if (r < 0)
- log_error("Failed to send coredump: %s", strerror(-r));
+ r = coredump_submit_message(argc, argv, pid, &coredump_config);
finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ 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..9f30337
--- /dev/null
+++ b/src/journal/coredump.conf
@@ -0,0 +1,15 @@
+# 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=
+#MaxJournalCoreSize=25M
+#MaxFileCoreSize=0
+#FileStorage=volatile
\ No newline at end of file
diff --git a/src/journal/coredump.h b/src/journal/coredump.h
new file mode 100644
index 0000000..122b244
--- /dev/null
+++ b/src/journal/coredump.h
@@ -0,0 +1,51 @@
+/*-*- 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_MESSAGE_ID SD_ID128_MAKE(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1
+
+typedef enum Storage {
+ STORAGE_VOLATILE,
+ STORAGE_PERSISTENT,
+ _STORAGE_MAX,
+ _STORAGE_INVALID = -1
+} Storage;
+
+typedef struct Coredump {
+ size_t journal_max_size;
+ size_t file_max_size;
+ Storage storage;
+ 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 *unit, 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.h b/src/journal/journald-server.h
index 86f7145..ce270e3 100644
--- a/src/journal/journald-server.h
+++ b/src/journal/journald-server.h
@@ -125,7 +125,7 @@ typedef struct Server {
bool sync_scheduled;
} 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