summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2016-02-10 16:50:21 +0100
committerLennart Poettering <lennart@poettering.net>2016-02-10 16:50:21 +0100
commit059adb5ac035fec290ca3a9f2dc08a19b20b4d67 (patch)
treede2291d03ba2397bb72b34c65a4fe3deec54da2f
parent16a798deb3b560f8b27848fe292a76b362c0b581 (diff)
parenta7c723c0c00a1b8ee64fe360a5d3caf2c89cb25c (diff)
Merge pull request #2555 from poettering/coredump-fixes
Coredump fixes and more
-rw-r--r--Makefile.am33
-rw-r--r--NEWS33
-rw-r--r--README3
-rw-r--r--TODO11
-rw-r--r--configure.ac1
-rw-r--r--man/systemd-activate.xml50
-rw-r--r--man/systemd.unit.xml22
-rw-r--r--src/activate/activate.c193
-rw-r--r--src/basic/socket-util.c3
-rw-r--r--src/core/load-fragment-gperf.gperf.m48
-rw-r--r--src/core/load-fragment.c86
-rw-r--r--src/core/load-fragment.h1
-rw-r--r--src/core/main.c21
l---------src/coredump/Makefile1
-rw-r--r--src/coredump/coredump-vacuum.c (renamed from src/journal/coredump-vacuum.c)0
-rw-r--r--src/coredump/coredump-vacuum.h (renamed from src/journal/coredump-vacuum.h)0
-rw-r--r--src/coredump/coredump.c (renamed from src/journal/coredump.c)730
-rw-r--r--src/coredump/coredump.conf (renamed from src/journal/coredump.conf)0
-rw-r--r--src/coredump/coredumpctl.c (renamed from src/journal/coredumpctl.c)0
-rw-r--r--src/coredump/stacktrace.c (renamed from src/journal/stacktrace.c)0
-rw-r--r--src/coredump/stacktrace.h (renamed from src/journal/stacktrace.h)0
-rw-r--r--src/coredump/test-coredump-vacuum.c (renamed from src/journal/test-coredump-vacuum.c)0
-rw-r--r--src/resolve/resolved-def.h2
-rw-r--r--sysctl.d/50-coredump.conf.in2
-rw-r--r--sysusers.d/systemd.conf.m43
-rw-r--r--units/.gitignore1
-rw-r--r--units/systemd-coredump.socket17
-rw-r--r--units/systemd-coredump@.service.in24
28 files changed, 841 insertions, 404 deletions
diff --git a/Makefile.am b/Makefile.am
index 84f8593ba7..125319fe48 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4397,30 +4397,39 @@ systemd_socket_proxyd_LDADD = \
# ------------------------------------------------------------------------------
if ENABLE_COREDUMP
systemd_coredump_SOURCES = \
- src/journal/coredump.c \
- src/journal/coredump-vacuum.c \
- src/journal/coredump-vacuum.h
+ src/coredump/coredump.c \
+ src/coredump/coredump-vacuum.c \
+ src/coredump/coredump-vacuum.h
systemd_coredump_LDADD = \
libshared.la
if HAVE_ELFUTILS
systemd_coredump_SOURCES += \
- src/journal/stacktrace.c \
- src/journal/stacktrace.h
+ src/coredump/stacktrace.c \
+ src/coredump/stacktrace.h
systemd_coredump_LDADD += \
$(ELFUTILS_LIBS)
endif
+nodist_systemunit_DATA += \
+ units/systemd-coredump@.service
+
+dist_systemunit_DATA += \
+ units/systemd-coredump.socket
+
+SOCKETS_TARGET_WANTS += \
+ systemd-coredump.socket
+
rootlibexec_PROGRAMS += \
systemd-coredump
dist_pkgsysconf_DATA += \
- src/journal/coredump.conf
+ src/coredump/coredump.conf
coredumpctl_SOURCES = \
- src/journal/coredumpctl.c
+ src/coredump/coredumpctl.c
coredumpctl_LDADD = \
libshared.la
@@ -4432,9 +4441,9 @@ manual_tests += \
test-coredump-vacuum
test_coredump_vacuum_SOURCES = \
- src/journal/test-coredump-vacuum.c \
- src/journal/coredump-vacuum.c \
- src/journal/coredump-vacuum.h
+ src/coredump/test-coredump-vacuum.c \
+ src/coredump/coredump-vacuum.c \
+ src/coredump/coredump-vacuum.h
test_coredump_vacuum_LDADD = \
libshared.la
@@ -4453,7 +4462,8 @@ CLEANFILES += \
endif
EXTRA_DIST += \
- sysctl.d/50-coredump.conf.in
+ sysctl.d/50-coredump.conf.in \
+ units/systemd-coredump@.service.in
# ------------------------------------------------------------------------------
if ENABLE_BINFMT
@@ -4860,7 +4870,6 @@ nodist_systemunit_DATA += \
GENERAL_ALIASES += \
$(systemunitdir)/systemd-timesyncd.service $(pkgsysconfdir)/system/sysinit.target.wants/systemd-timesyncd.service
-
nodist_pkgsysconf_DATA += \
src/timesync/timesyncd.conf
diff --git a/NEWS b/NEWS
index 51c0faefd5..da26532840 100644
--- a/NEWS
+++ b/NEWS
@@ -26,6 +26,39 @@ CHANGES WITH 229:
* /dev/disk/by-path/ symlink support has been (re-)added for virtio
devices.
+ * The coredump collection logic has been reworked: when a coredump is
+ collected it is now written to disk, compressed and processed
+ (including stacktrace extraction) from a new instantiated service
+ systemd-coredump@.service, instead of directly from the
+ /proc/sys/kernel/core_pattern hook we provide. This is beneficial as
+ processing large coredumps can take up a substantial amount of
+ resources and time, and this previously happened entirely outside of
+ systemd's service supervision. With the new logic the core_pattern
+ hook only does minimal metadata collection before passing off control
+ to the new instantiated service, which is configured with a time
+ limit, a nice level and other settings to minimize negative impact on
+ the rest of the system. Also note that the new logic will honour the
+ RLIMIT_CORE setting of the crashed process, which now allows users
+ and processes to turn off coredumping for their processes by setting
+ this limit.
+
+ * The RLIMIT_CORE resource limit now defaults to "unlimited" for PID 1
+ and all forked processes by default. Previously, PID 1 would leave
+ the setting at "0" for all processes, as set by the kernel. Note that
+ the resource limit traditionally has no effect on the generated
+ coredumps on the system if the /proc/sys/kernel/core_pattern hook
+ logic is used. Since the limit is now honoured (see above) its
+ default has been changed so that the coredumping logic is enabled by
+ default for all processes, while allowing specific opt-out.
+
+ * When the stacktrace is extracted from processes of system users, this
+ is now done as "systemd-coredump" user, in order to sandbox this
+ potentially security sensitive parsing operation. (Note that when
+ processing coredumps of normal users this is done under the user ID
+ of process that crashed, as before.) Packagers should take notice
+ that it is now necessary to create the "systemd-coredump" system user
+ and group at package installation time.
+
* The systemd-activate socket activation testing tool gained support
for SOCK_DGRAM and SOCK_SEQPACKET sockets using the new --datagram
and --seqpacket switches. It also has been extended to support both
diff --git a/README b/README
index c46ac7e5de..0a2c0df47d 100644
--- a/README
+++ b/README
@@ -203,6 +203,9 @@ USERS AND GROUPS:
Similarly, the kdbus dbus1 proxy daemon requires the
"systemd-bus-proxy" system user and group to exist.
+ Similarly, the coredump support requires the
+ "systemd-coredump" system user and group to exist.
+
NSS:
systemd ships with three NSS modules:
diff --git a/TODO b/TODO
index 9ca06af63a..1d9a6a99a7 100644
--- a/TODO
+++ b/TODO
@@ -33,8 +33,6 @@ Janitorial Clean-ups:
Features:
-* rework coredump tool to move actual processing into a socket activated service
-
* cache sd_event_now() result from before the first iteration...
* remove Capabilities=, after all AmbientCapabilities= and CapabilityBoundingSet= should be enough.
@@ -43,11 +41,6 @@ Features:
* add systemctl stop --job-mode=triggering that follows TRIGGERED_BY deps and adds them to the same transaction
-* coredump logic should use prlimit() to query RLIMIT_CORE of the dumpee and honour it
-
-* Add a MaxRuntimeSec= setting for service units (or units in general) to terminate units after they ran for a certain
- amount of time
-
* Maybe add a way how users can "pin" units into memory, so that they are not subject to automatic GC?
* PID1: find a way how we can reload unit file configuration for
@@ -649,10 +642,6 @@ Features:
* coredump:
- save coredump in Windows/Mozilla minidump format
- move PID 1 segfaults to /var/lib/systemd/coredump?
- - make the handler check /proc/$PID/rlimits for RLIMIT_CORE,
- and supress coredump if turned off. Then change RLIMIT_CORE to
- infinity by default for all services. This then allows per-service
- control of coredumping.
* support crash reporting operation modes (https://live.gnome.org/GnomeOS/Design/Whiteboards/ProblemReporting)
diff --git a/configure.ac b/configure.ac
index 0374b3f39b..d05d0ba31f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1106,6 +1106,7 @@ have_coredump=no
AC_ARG_ENABLE(coredump, AS_HELP_STRING([--disable-coredump], [disable coredump hook]))
if test "x$enable_coredump" != "xno"; then
have_coredump=yes
+ M4_DEFINES="$M4_DEFINES -DENABLE_COREDUMP"
fi
AM_CONDITIONAL(ENABLE_COREDUMP, [test "$have_coredump" = "yes"])
diff --git a/man/systemd-activate.xml b/man/systemd-activate.xml
index c950a0836d..995e6eecce 100644
--- a/man/systemd-activate.xml
+++ b/man/systemd-activate.xml
@@ -60,27 +60,21 @@
<refsect1>
<title>Description</title>
- <para><command>systemd-activate</command> can be used to
- launch a socket-activated daemon from the command line for
- testing purposes. It can also be used to launch single instances
- of the daemon per connection (inetd-style).
+ <para><command>systemd-activate</command> may be used to launch a socket-activated service binary from the command
+ line for testing purposes. It may also be used to launch individual instances of the service binary per connection.
</para>
<para>The daemon to launch and its options should be specified
after options intended for <command>systemd-activate</command>.
</para>
- <para>If the <option>-a</option> option is given, file descriptor
- of the connection will be used as the standard input and output of
- the launched process. Otherwise, standard input and output will be
- inherited, and sockets will be passed through file descriptors 3
- and higher. Sockets passed through <varname>$LISTEN_FDS</varname>
- to <command>systemd-activate</command> will be passed through to
- the daemon, in the original positions. Other sockets specified
- with <option>--listen</option> will use consecutive descriptors.
- By default, <command>systemd-activate</command> listens on a
- stream socket, use <option>--datagram</option> to listen on
- a datagram socket instead (see below).
+ <para>If the <option>--inetd</option> option is given, the socket file descriptor will be used as the standard
+ input and output of the launched process. Otherwise, standard input and output will be inherited, and sockets will
+ be passed through file descriptors 3 and higher. Sockets passed through <varname>$LISTEN_FDS</varname> to
+ <command>systemd-activate</command> will be passed through to the daemon, in the original positions. Other sockets
+ specified with <option>--listen=</option> will use consecutive descriptors. By default,
+ <command>systemd-activate</command> listens on a stream socket, use <option>--datagram</option> and
+ <option>--seqpacket</option> to listen on datagram or sequential packet sockets instead (see below).
</para>
</refsect1>
@@ -101,16 +95,32 @@
<term><option>-a</option></term>
<term><option>--accept</option></term>
- <listitem><para>Launch a separate instance of daemon per
- connection and pass the connection socket as standard input
- and standard output.</para></listitem>
+ <listitem><para>Launch an instance of the service binary for each connection and pass the connection
+ socket.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-d</option></term>
<term><option>--datagram</option></term>
- <listitem><para>Listen on a datagram socket, instead of a stream socket.</para></listitem>
+ <listitem><para>Listen on a datagram socket (<constant>SOCK_DGRAM</constant>), instead of a stream socket
+ (<constant>SOCK_STREAM</constant>). May not be combined with <option>--seqpacket</option>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--seqpacket</option></term>
+
+ <listitem><para>Listen on a sequential packet socket (<constant>SOCK_SEQPACKET</constant>), instead of a stream
+ socket (<constant>SOCK_STREAM</constant>). May not be combined with
+ <option>--datagram</option>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--inetd</option></term>
+
+ <listitem><para>Use the inetd protocol for passing file descriptors, i.e. as standard input and standard
+ output, instead of the new-style protocol for passing file descriptors using <varname>$LISTEN_FDS</varname>
+ (see above).</para></listitem>
</varlistentry>
<varlistentry>
@@ -170,7 +180,7 @@
<example>
<title>Run an echo server on port 2000</title>
- <programlisting>$ /usr/lib/systemd/systemd-activate -l 2000 -a cat</programlisting>
+ <programlisting>$ /usr/lib/systemd/systemd-activate -l 2000 --inetd -a cat</programlisting>
</example>
<example>
diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml
index 2d3274bbfb..46b288f20b 100644
--- a/man/systemd.unit.xml
+++ b/man/systemd.unit.xml
@@ -728,20 +728,14 @@
<term><varname>JobTimeoutAction=</varname></term>
<term><varname>JobTimeoutRebootArgument=</varname></term>
- <listitem><para>When a job for this unit is queued, a time-out
- may be configured. If this time limit is reached, the job will
- be cancelled, the unit however will not change state or even
- enter the <literal>failed</literal> mode. This value defaults
- to 0 (job timeouts disabled), except for device units. NB:
- this timeout is independent from any unit-specific timeout
- (for example, the timeout set with
- <varname>TimeoutStartSec=</varname> in service units) as the
- job timeout has no effect on the unit itself, only on the job
- that might be pending for it. Or in other words: unit-specific
- timeouts are useful to abort unit state changes, and revert
- them. The job timeout set with this option however is useful
- to abort only the job waiting for the unit state to
- change.</para>
+ <listitem><para>When a job for this unit is queued, a time-out may be configured. If this time limit is
+ reached, the job will be cancelled, the unit however will not change state or even enter the
+ <literal>failed</literal> mode. This value defaults to <literal>infinity</literal> (job timeouts disabled),
+ except for device units. NB: this timeout is independent from any unit-specific timeout (for example, the
+ timeout set with <varname>TimeoutStartSec=</varname> in service units) as the job timeout has no effect on the
+ unit itself, only on the job that might be pending for it. Or in other words: unit-specific timeouts are useful
+ to abort unit state changes, and revert them. The job timeout set with this option however is useful to abort
+ only the job waiting for the unit state to change.</para>
<para><varname>JobTimeoutAction=</varname>
optionally configures an additional
diff --git a/src/activate/activate.c b/src/activate/activate.c
index d50566c97b..0db4967edb 100644
--- a/src/activate/activate.c
+++ b/src/activate/activate.c
@@ -37,10 +37,11 @@
static char** arg_listen = NULL;
static bool arg_accept = false;
-static bool arg_datagram = false;
+static int arg_socket_type = SOCK_STREAM;
static char** arg_args = NULL;
static char** arg_setenv = NULL;
static const char *arg_fdname = NULL;
+static bool arg_inetd = false;
static int add_epoll(int epoll_fd, int fd) {
struct epoll_event ev = {
@@ -96,12 +97,7 @@ static int open_sockets(int *epoll_fd, bool accept) {
*/
STRV_FOREACH(address, arg_listen) {
-
- if (arg_datagram)
- fd = make_socket_fd(LOG_DEBUG, *address, SOCK_DGRAM, SOCK_CLOEXEC);
- else
- fd = make_socket_fd(LOG_DEBUG, *address, SOCK_STREAM, (arg_accept*SOCK_CLOEXEC));
-
+ fd = make_socket_fd(LOG_DEBUG, *address, arg_socket_type, (arg_accept*SOCK_CLOEXEC));
if (fd < 0) {
log_open();
return log_error_errno(fd, "Failed to open '%s': %m", *address);
@@ -132,14 +128,20 @@ static int open_sockets(int *epoll_fd, bool accept) {
return count;
}
-static int launch(char* name, char **argv, char **env, int fds) {
+static int exec_process(const char* name, char **argv, char **env, int start_fd, int n_fds) {
- static const char* tocopy[] = {"TERM=", "PATH=", "USER=", "HOME="};
_cleanup_strv_free_ char **envp = NULL;
- _cleanup_free_ char *tmp = NULL;
+ _cleanup_free_ char *joined = NULL;
unsigned n_env = 0, length;
- char **s;
+ const char *tocopy;
unsigned i;
+ char **s;
+ int r;
+
+ if (arg_inetd && n_fds != 1) {
+ log_error("--inetd only supported for single file descriptors.");
+ return -EINVAL;
+ }
length = strv_length(arg_setenv);
@@ -149,6 +151,7 @@ static int launch(char* name, char **argv, char **env, int fds) {
return log_oom();
STRV_FOREACH(s, arg_setenv) {
+
if (strchr(*s, '=')) {
char *k;
@@ -172,13 +175,15 @@ static int launch(char* name, char **argv, char **env, int fds) {
envp[n_env] = strdup(n);
if (!envp[n_env])
return log_oom();
+
+ n_env ++;
}
}
- for (i = 0; i < ELEMENTSOF(tocopy); i++) {
+ FOREACH_STRING(tocopy, "TERM=", "PATH=", "USER=", "HOME=") {
const char *n;
- n = strv_find_prefix(env, tocopy[i]);
+ n = strv_find_prefix(env, tocopy);
if (!n)
continue;
@@ -189,50 +194,76 @@ static int launch(char* name, char **argv, char **env, int fds) {
n_env ++;
}
- if ((asprintf((char**)(envp + n_env++), "LISTEN_FDS=%d", fds) < 0) ||
- (asprintf((char**)(envp + n_env++), "LISTEN_PID=%d", getpid()) < 0))
- return log_oom();
+ if (arg_inetd) {
+ assert(n_fds == 1);
+
+ r = dup2(start_fd, STDIN_FILENO);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to dup connection to stdin: %m");
+
+ r = dup2(start_fd, STDOUT_FILENO);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to dup connection to stdout: %m");
- if (arg_fdname) {
- char *e;
+ start_fd = safe_close(start_fd);
+ } else {
+ if (start_fd != SD_LISTEN_FDS_START) {
+ assert(n_fds == 1);
- e = strappend("LISTEN_FDNAMES=", arg_fdname);
- if (!e)
+ r = dup2(start_fd, SD_LISTEN_FDS_START);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to dup connection: %m");
+
+ safe_close(start_fd);
+ start_fd = SD_LISTEN_FDS_START;
+ }
+
+ if (asprintf((char**)(envp + n_env++), "LISTEN_FDS=%i", n_fds) < 0)
return log_oom();
- for (i = 1; i < (unsigned) fds; i++) {
- char *c;
+ if (asprintf((char**)(envp + n_env++), "LISTEN_PID=" PID_FMT, getpid()) < 0)
+ return log_oom();
- c = strjoin(e, ":", arg_fdname, NULL);
- if (!c) {
- free(e);
+ if (arg_fdname) {
+ char *e;
+
+ e = strappend("LISTEN_FDNAMES=", arg_fdname);
+ if (!e)
return log_oom();
+
+ for (i = 1; i < (unsigned) n_fds; i++) {
+ char *c;
+
+ c = strjoin(e, ":", arg_fdname, NULL);
+ if (!c) {
+ free(e);
+ return log_oom();
+ }
+
+ free(e);
+ e = c;
}
- free(e);
- e = c;
+ envp[n_env++] = e;
}
-
- envp[n_env++] = e;
}
- tmp = strv_join(argv, " ");
- if (!tmp)
+ joined = strv_join(argv, " ");
+ if (!joined)
return log_oom();
- log_info("Execing %s (%s)", name, tmp);
+ log_info("Execing %s (%s)", name, joined);
execvpe(name, argv, envp);
- return log_error_errno(errno, "Failed to execp %s (%s): %m", name, tmp);
+ return log_error_errno(errno, "Failed to execp %s (%s): %m", name, joined);
}
-static int launch1(const char* child, char** argv, char **env, int fd) {
- _cleanup_free_ char *tmp = NULL;
+static int fork_and_exec_process(const char* child, char** argv, char **env, int fd) {
+ _cleanup_free_ char *joined = NULL;
pid_t parent_pid, child_pid;
- int r;
- tmp = strv_join(argv, " ");
- if (!tmp)
+ joined = strv_join(argv, " ");
+ if (!joined)
return log_oom();
parent_pid = getpid();
@@ -247,24 +278,6 @@ static int launch1(const char* child, char** argv, char **env, int fd) {
(void) reset_all_signal_handlers();
(void) reset_signal_mask();
- r = dup2(fd, STDIN_FILENO);
- if (r < 0) {
- log_error_errno(errno, "Failed to dup connection to stdin: %m");
- _exit(EXIT_FAILURE);
- }
-
- r = dup2(fd, STDOUT_FILENO);
- if (r < 0) {
- log_error_errno(errno, "Failed to dup connection to stdout: %m");
- _exit(EXIT_FAILURE);
- }
-
- r = close(fd);
- if (r < 0) {
- log_error_errno(errno, "Failed to close dupped connection: %m");
- _exit(EXIT_FAILURE);
- }
-
/* Make sure the child goes away when the parent dies */
if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
_exit(EXIT_FAILURE);
@@ -274,31 +287,27 @@ static int launch1(const char* child, char** argv, char **env, int fd) {
if (getppid() != parent_pid)
_exit(EXIT_SUCCESS);
- execvp(child, argv);
- log_error_errno(errno, "Failed to exec child %s: %m", child);
+ exec_process(child, argv, env, fd, 1);
_exit(EXIT_FAILURE);
}
- log_info("Spawned %s (%s) as PID %d", child, tmp, child_pid);
-
+ log_info("Spawned %s (%s) as PID %d", child, joined, child_pid);
return 0;
}
static int do_accept(const char* name, char **argv, char **envp, int fd) {
_cleanup_free_ char *local = NULL, *peer = NULL;
- _cleanup_close_ int fd2 = -1;
+ _cleanup_close_ int fd_accepted = -1;
- fd2 = accept(fd, NULL, NULL);
- if (fd2 < 0) {
- log_error_errno(errno, "Failed to accept connection on fd:%d: %m", fd);
- return fd2;
- }
+ fd_accepted = accept4(fd, NULL, NULL, 0);
+ if (fd_accepted < 0)
+ return log_error_errno(errno, "Failed to accept connection on fd:%d: %m", fd);
- getsockname_pretty(fd2, &local);
- getpeername_pretty(fd2, true, &peer);
+ getsockname_pretty(fd_accepted, &local);
+ getpeername_pretty(fd_accepted, true, &peer);
log_info("Connection from %s to %s", strna(peer), strna(local));
- return launch1(name, argv, envp, fd2);
+ return fork_and_exec_process(name, argv, envp, fd_accepted);
}
/* SIGCHLD handler. */
@@ -306,21 +315,24 @@ static void sigchld_hdl(int sig, siginfo_t *t, void *data) {
PROTECT_ERRNO;
log_info("Child %d died with code %d", t->si_pid, t->si_status);
+
/* Wait for a dead child. */
- waitpid(t->si_pid, NULL, 0);
+ (void) waitpid(t->si_pid, NULL, 0);
}
static int install_chld_handler(void) {
- int r;
- struct sigaction act = {
+ static const struct sigaction act = {
.sa_flags = SA_SIGINFO,
.sa_sigaction = sigchld_hdl,
};
+ int r;
+
r = sigaction(SIGCHLD, &act, 0);
if (r < 0)
- log_error_errno(errno, "Failed to install SIGCHLD handler: %m");
- return r;
+ return log_error_errno(errno, "Failed to install SIGCHLD handler: %m");
+
+ return 0;
}
static void help(void) {
@@ -331,8 +343,10 @@ static void help(void) {
" --version Print version string and exit\n"
" -l --listen=ADDR Listen for raw connections at ADDR\n"
" -d --datagram Listen on datagram instead of stream socket\n"
+ " --seqpacket Listen on SOCK_SEQPACKET instead of stream socket\n"
" -a --accept Spawn separate child for each connection\n"
" -E --setenv=NAME[=VALUE] Pass an environment variable to children\n"
+ " --inetd Enable inetd file descriptor passing protocol\n"
"\n"
"Note: file descriptors from sd_listen_fds() will be passed through.\n"
, program_invocation_short_name);
@@ -342,17 +356,21 @@ static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_FDNAME,
+ ARG_SEQPACKET,
+ ARG_INETD,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "datagram", no_argument, NULL, 'd' },
+ { "seqpacket", no_argument, NULL, ARG_SEQPACKET },
{ "listen", required_argument, NULL, 'l' },
{ "accept", no_argument, NULL, 'a' },
{ "setenv", required_argument, NULL, 'E' },
{ "environment", required_argument, NULL, 'E' }, /* legacy alias */
{ "fdname", required_argument, NULL, ARG_FDNAME },
+ { "inetd", no_argument, NULL, ARG_INETD },
{}
};
@@ -378,7 +396,21 @@ static int parse_argv(int argc, char *argv[]) {
break;
case 'd':
- arg_datagram = true;
+ if (arg_socket_type == SOCK_SEQPACKET) {
+ log_error("--datagram may not be combined with --seqpacket.");
+ return -EINVAL;
+ }
+
+ arg_socket_type = SOCK_DGRAM;
+ break;
+
+ case ARG_SEQPACKET:
+ if (arg_socket_type == SOCK_DGRAM) {
+ log_error("--seqpacket may not be combined with --datagram.");
+ return -EINVAL;
+ }
+
+ arg_socket_type = SOCK_SEQPACKET;
break;
case 'a':
@@ -401,6 +433,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_fdname = optarg;
break;
+ case ARG_INETD:
+ arg_inetd = true;
+ break;
+
case '?':
return -EINVAL;
@@ -414,7 +450,7 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
- if (arg_datagram && arg_accept) {
+ if (arg_socket_type == SOCK_DGRAM && arg_accept) {
log_error("Datagram sockets do not accept connections. "
"The --datagram and --accept options may not be combined.");
return -EINVAL;
@@ -462,15 +498,14 @@ int main(int argc, char **argv, char **envp) {
log_info("Communication attempt on fd %i.", event.data.fd);
if (arg_accept) {
- r = do_accept(argv[optind], argv + optind, envp,
- event.data.fd);
+ r = do_accept(argv[optind], argv + optind, envp, event.data.fd);
if (r < 0)
return EXIT_FAILURE;
} else
break;
}
- launch(argv[optind], argv + optind, envp, n);
+ exec_process(argv[optind], argv + optind, envp, SD_LISTEN_FDS_START, n);
return EXIT_SUCCESS;
}
diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c
index 7a866f2e23..49e5f5b125 100644
--- a/src/basic/socket-util.c
+++ b/src/basic/socket-util.c
@@ -871,14 +871,13 @@ int send_one_fd_sa(
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(int))];
} control = {};
- struct cmsghdr *cmsg;
-
struct msghdr mh = {
.msg_name = (struct sockaddr*) sa,
.msg_namelen = len,
.msg_control = &control,
.msg_controllen = sizeof(control),
};
+ struct cmsghdr *cmsg;
assert(transport_fd >= 0);
assert(fd >= 0);
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index c2a47c7b11..5b99398307 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -161,7 +161,7 @@ Unit.OnFailureJobMode, config_parse_job_mode, 0,
Unit.OnFailureIsolate, config_parse_job_mode_isolate, 0, offsetof(Unit, on_failure_job_mode)
Unit.IgnoreOnIsolate, config_parse_bool, 0, offsetof(Unit, ignore_on_isolate)
Unit.IgnoreOnSnapshot, config_parse_warn_compat, DISABLED_LEGACY, 0
-Unit.JobTimeoutSec, config_parse_sec, 0, offsetof(Unit, job_timeout)
+Unit.JobTimeoutSec, config_parse_sec_fix_0, 0, offsetof(Unit, job_timeout)
Unit.JobTimeoutAction, config_parse_failure_action, 0, offsetof(Unit, job_timeout_action)
Unit.JobTimeoutRebootArgument, config_parse_string, 0, offsetof(Unit, job_timeout_reboot_arg)
Unit.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_limit.interval)
@@ -215,9 +215,9 @@ Service.ExecReload, config_parse_exec, SERVICE_EXE
Service.ExecStop, config_parse_exec, SERVICE_EXEC_STOP, offsetof(Service, exec_command)
Service.ExecStopPost, config_parse_exec, SERVICE_EXEC_STOP_POST, offsetof(Service, exec_command)
Service.RestartSec, config_parse_sec, 0, offsetof(Service, restart_usec)
-Service.TimeoutSec, config_parse_service_timeout, 0, offsetof(Service, timeout_start_usec)
-Service.TimeoutStartSec, config_parse_service_timeout, 0, offsetof(Service, timeout_start_usec)
-Service.TimeoutStopSec, config_parse_service_timeout, 0, offsetof(Service, timeout_stop_usec)
+Service.TimeoutSec, config_parse_service_timeout, 0, 0
+Service.TimeoutStartSec, config_parse_service_timeout, 0, 0
+Service.TimeoutStopSec, config_parse_service_timeout, 0, 0
Service.RuntimeMaxSec, config_parse_sec, 0, offsetof(Service, runtime_max_usec)
Service.WatchdogSec, config_parse_sec, 0, offsetof(Service, watchdog_usec)
Service.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_limit.interval)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 8e5ef935f7..e0c318c110 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -1711,18 +1711,20 @@ int config_parse_bus_name(
return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
}
-int config_parse_service_timeout(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_service_timeout(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Service *s = userdata;
+ usec_t usec;
int r;
assert(filename);
@@ -1730,25 +1732,63 @@ int config_parse_service_timeout(const char *unit,
assert(rvalue);
assert(s);
- r = config_parse_sec(unit, filename, line, section, section_line, lvalue, ltype,
- rvalue, data, userdata);
- if (r < 0)
- return r;
+ /* This is called for three cases: TimeoutSec=, TimeoutStopSec= and TimeoutStartSec=. */
- if (streq(lvalue, "TimeoutSec")) {
- s->start_timeout_defined = true;
- s->timeout_stop_usec = s->timeout_start_usec;
- } else if (streq(lvalue, "TimeoutStartSec"))
- s->start_timeout_defined = true;
+ r = parse_sec(rvalue, &usec);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue);
+ return 0;
+ }
/* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
* immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
* all other timeouts. */
+ if (usec <= 0)
+ usec = USEC_INFINITY;
+
+ if (!streq(lvalue, "TimeoutStopSec")) {
+ s->start_timeout_defined = true;
+ s->timeout_start_usec = usec;
+ }
+
+ if (!streq(lvalue, "TimeoutStartSec"))
+ s->timeout_stop_usec = usec;
+
+ return 0;
+}
+
+int config_parse_sec_fix_0(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ usec_t *usec = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(usec);
+
+ /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for
+ * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a
+ * timeout. */
+
+ r = parse_sec(rvalue, usec);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue);
+ return 0;
+ }
- if (s->timeout_start_usec <= 0)
- s->timeout_start_usec = USEC_INFINITY;
- if (s->timeout_stop_usec <= 0)
- s->timeout_stop_usec = USEC_INFINITY;
+ if (*usec <= 0)
+ *usec = USEC_INFINITY;
return 0;
}
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index da215195cb..5fb5910919 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -109,6 +109,7 @@ int config_parse_bus_name(const char* unit, const char *filename, unsigned line,
int config_parse_exec_utmp_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_working_directory(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_fdname(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_sec_fix_0(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
/* gperf prototypes */
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length);
diff --git a/src/core/main.c b/src/core/main.c
index 0e6832c122..e2088574c0 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -172,19 +172,15 @@ noreturn static void crash(int sig) {
if (pid < 0)
log_emergency_errno(errno, "Caught <%s>, cannot fork for core dump: %m", signal_to_string(sig));
else if (pid == 0) {
- struct rlimit rl = {
- .rlim_cur = RLIM_INFINITY,
- .rlim_max = RLIM_INFINITY,
- };
-
/* Enable default signal handler for core dump */
+
sa = (struct sigaction) {
.sa_handler = SIG_DFL,
};
(void) sigaction(sig, &sa, NULL);
- /* Don't limit the core dump size */
- (void) setrlimit(RLIMIT_CORE, &rl);
+ /* Don't limit the coredump size */
+ (void) setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(RLIM_INFINITY));
/* Just to be sure... */
(void) chdir("/");
@@ -1466,6 +1462,17 @@ int main(int argc, char *argv[]) {
kernel_timestamp = DUAL_TIMESTAMP_NULL;
}
+ if (getpid() == 1) {
+ /* Don't limit the core dump size, so that coredump handlers such as systemd-coredump (which honour the limit)
+ * will process core dumps for system services by default. */
+ (void) setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(RLIM_INFINITY));
+
+ /* But at the same time, turn off the core_pattern logic by default, so that no coredumps are stored
+ * until the systemd-coredump tool is enabled via sysctl. */
+ if (!skip_setup)
+ (void) write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0);
+ }
+
/* Initialize default unit */
r = free_and_strdup(&arg_default_unit, SPECIAL_DEFAULT_TARGET);
if (r < 0) {
diff --git a/src/coredump/Makefile b/src/coredump/Makefile
new file mode 120000
index 0000000000..d0b0e8e008
--- /dev/null
+++ b/src/coredump/Makefile
@@ -0,0 +1 @@
+../Makefile \ No newline at end of file
diff --git a/src/journal/coredump-vacuum.c b/src/coredump/coredump-vacuum.c
index f02b6dbd87..f02b6dbd87 100644
--- a/src/journal/coredump-vacuum.c
+++ b/src/coredump/coredump-vacuum.c
diff --git a/src/journal/coredump-vacuum.h b/src/coredump/coredump-vacuum.h
index 4b7b9f2d98..4b7b9f2d98 100644
--- a/src/journal/coredump-vacuum.h
+++ b/src/coredump/coredump-vacuum.h
diff --git a/src/journal/coredump.c b/src/coredump/coredump.c
index 8b1c670cc6..085909c20c 100644
--- a/src/journal/coredump.c
+++ b/src/coredump/coredump.c
@@ -24,12 +24,13 @@
#include <unistd.h>
#ifdef HAVE_ELFUTILS
-# include <dwarf.h>
-# include <elfutils/libdwfl.h>
+#include <dwarf.h>
+#include <elfutils/libdwfl.h>
#endif
#include "sd-journal.h"
#include "sd-login.h"
+#include "sd-daemon.h"
#include "acl-util.h"
#include "alloc-util.h"
@@ -51,6 +52,7 @@
#include "mkdir.h"
#include "parse-util.h"
#include "process-util.h"
+#include "socket-util.h"
#include "special.h"
#include "stacktrace.h"
#include "string-table.h"
@@ -62,12 +64,10 @@
/* The maximum size up to which we process coredumps */
#define PROCESS_SIZE_MAX ((uint64_t) (2LLU*1024LLU*1024LLU*1024LLU))
-/* The maximum size up to which we leave the coredump around on
- * disk */
+/* The maximum size up to which we leave the coredump around on disk */
#define EXTERNAL_SIZE_MAX PROCESS_SIZE_MAX
-/* The maximum size up to which we store the coredump in the
- * journal */
+/* The maximum size up to which we store the coredump in the journal */
#define JOURNAL_SIZE_MAX ((size_t) (767LU*1024LU*1024LU))
/* Make sure to not make this larger than the maximum journal entry
@@ -75,14 +75,17 @@
assert_cc(JOURNAL_SIZE_MAX <= DATA_SIZE_MAX);
enum {
- INFO_PID,
- INFO_UID,
- INFO_GID,
- INFO_SIGNAL,
- INFO_TIMESTAMP,
- INFO_COMM,
- INFO_EXE,
- _INFO_LEN
+ /* We use this as array indexes for a couple of special fields we use for naming coredumping files, and
+ * attaching xattrs */
+ CONTEXT_PID,
+ CONTEXT_UID,
+ CONTEXT_GID,
+ CONTEXT_SIGNAL,
+ CONTEXT_TIMESTAMP,
+ CONTEXT_RLIMIT,
+ CONTEXT_COMM,
+ CONTEXT_EXE,
+ _CONTEXT_MAX
};
typedef enum CoredumpStorage {
@@ -173,16 +176,16 @@ static int fix_acl(int fd, uid_t uid) {
return 0;
}
-static int fix_xattr(int fd, const char *info[_INFO_LEN]) {
+static int fix_xattr(int fd, const char *context[_CONTEXT_MAX]) {
- static const char * const xattrs[_INFO_LEN] = {
- [INFO_PID] = "user.coredump.pid",
- [INFO_UID] = "user.coredump.uid",
- [INFO_GID] = "user.coredump.gid",
- [INFO_SIGNAL] = "user.coredump.signal",
- [INFO_TIMESTAMP] = "user.coredump.timestamp",
- [INFO_COMM] = "user.coredump.comm",
- [INFO_EXE] = "user.coredump.exe",
+ static const char * const xattrs[_CONTEXT_MAX] = {
+ [CONTEXT_PID] = "user.coredump.pid",
+ [CONTEXT_UID] = "user.coredump.uid",
+ [CONTEXT_GID] = "user.coredump.gid",
+ [CONTEXT_SIGNAL] = "user.coredump.signal",
+ [CONTEXT_TIMESTAMP] = "user.coredump.timestamp",
+ [CONTEXT_COMM] = "user.coredump.comm",
+ [CONTEXT_EXE] = "user.coredump.exe",
};
int r = 0;
@@ -193,13 +196,13 @@ static int fix_xattr(int fd, const char *info[_INFO_LEN]) {
/* Attach some metadata to coredumps via extended
* attributes. Just because we can. */
- for (i = 0; i < _INFO_LEN; i++) {
+ for (i = 0; i < _CONTEXT_MAX; i++) {
int k;
- if (isempty(info[i]) || !xattrs[i])
+ if (isempty(context[i]) || !xattrs[i])
continue;
- k = fsetxattr(fd, xattrs[i], info[i], strlen(info[i]), XATTR_CREATE);
+ k = fsetxattr(fd, xattrs[i], context[i], strlen(context[i]), XATTR_CREATE);
if (k < 0 && r == 0)
r = -errno;
}
@@ -213,18 +216,18 @@ static int fix_permissions(
int fd,
const char *filename,
const char *target,
- const char *info[_INFO_LEN],
+ const char *context[_CONTEXT_MAX],
uid_t uid) {
assert(fd >= 0);
assert(filename);
assert(target);
- assert(info);
+ assert(context);
/* Ignore errors on these */
- fchmod(fd, 0640);
- fix_acl(fd, uid);
- fix_xattr(fd, info);
+ (void) fchmod(fd, 0640);
+ (void) fix_acl(fd, uid);
+ (void) fix_xattr(fd, context);
if (fsync(fd) < 0)
return log_error_errno(errno, "Failed to sync coredump %s: %m", filename);
@@ -252,18 +255,18 @@ static int maybe_remove_external_coredump(const char *filename, uint64_t size) {
return 1;
}
-static int make_filename(const char *info[_INFO_LEN], char **ret) {
+static int make_filename(const char *context[_CONTEXT_MAX], char **ret) {
_cleanup_free_ char *c = NULL, *u = NULL, *p = NULL, *t = NULL;
sd_id128_t boot = {};
int r;
- assert(info);
+ assert(context);
- c = filename_escape(info[INFO_COMM]);
+ c = filename_escape(context[CONTEXT_COMM]);
if (!c)
return -ENOMEM;
- u = filename_escape(info[INFO_UID]);
+ u = filename_escape(context[CONTEXT_UID]);
if (!u)
return -ENOMEM;
@@ -271,11 +274,11 @@ static int make_filename(const char *info[_INFO_LEN], char **ret) {
if (r < 0)
return r;
- p = filename_escape(info[INFO_PID]);
+ p = filename_escape(context[CONTEXT_PID]);
if (!p)
return -ENOMEM;
- t = filename_escape(info[INFO_TIMESTAMP]);
+ t = filename_escape(context[CONTEXT_TIMESTAMP]);
if (!t)
return -ENOMEM;
@@ -292,8 +295,8 @@ static int make_filename(const char *info[_INFO_LEN], char **ret) {
}
static int save_external_coredump(
- const char *info[_INFO_LEN],
- uid_t uid,
+ const char *context[_CONTEXT_MAX],
+ int input_fd,
char **ret_filename,
int *ret_node_fd,
int *ret_data_fd,
@@ -301,16 +304,34 @@ static int save_external_coredump(
_cleanup_free_ char *fn = NULL, *tmp = NULL;
_cleanup_close_ int fd = -1;
+ uint64_t rlimit, max_size;
struct stat st;
+ uid_t uid;
int r;
- assert(info);
+ assert(context);
assert(ret_filename);
assert(ret_node_fd);
assert(ret_data_fd);
assert(ret_size);
- r = make_filename(info, &fn);
+ r = parse_uid(context[CONTEXT_UID], &uid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse UID: %m");
+
+ r = safe_atou64(context[CONTEXT_RLIMIT], &rlimit);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse resource limit: %s", context[CONTEXT_RLIMIT]);
+ if (rlimit <= 0) {
+ /* Is coredumping disabled? Then don't bother saving/processing the coredump */
+ log_info("Core Dumping has been disabled for process %s (%s).", context[CONTEXT_PID], context[CONTEXT_COMM]);
+ return -EBADSLT;
+ }
+
+ /* Never store more than the process configured, or than we actually shall keep or process */
+ max_size = MIN(rlimit, MAX(arg_process_size_max, arg_external_size_max));
+
+ r = make_filename(context, &fn);
if (r < 0)
return log_error_errno(r, "Failed to determine coredump file name: %m");
@@ -324,12 +345,12 @@ static int save_external_coredump(
if (fd < 0)
return log_error_errno(errno, "Failed to create coredump file %s: %m", tmp);
- r = copy_bytes(STDIN_FILENO, fd, arg_process_size_max, false);
+ r = copy_bytes(input_fd, fd, max_size, false);
if (r == -EFBIG) {
- log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", info[INFO_PID], info[INFO_COMM]);
+ log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", context[CONTEXT_PID], context[CONTEXT_COMM]);
goto fail;
} else if (IN_SET(r, -EDQUOT, -ENOSPC)) {
- log_error("Not enough disk space for coredump of %s (%s), refusing.", info[INFO_PID], info[INFO_COMM]);
+ log_error("Not enough disk space for coredump of %s (%s), refusing.", context[CONTEXT_PID], context[CONTEXT_COMM]);
goto fail;
} else if (r < 0) {
log_error_errno(r, "Failed to dump coredump to file: %m");
@@ -378,7 +399,7 @@ static int save_external_coredump(
goto fail_compressed;
}
- r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, info, uid);
+ r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, context, uid);
if (r < 0)
goto fail_compressed;
@@ -396,13 +417,13 @@ static int save_external_coredump(
return 0;
fail_compressed:
- unlink_noerrno(tmp_compressed);
+ (void) unlink(tmp_compressed);
}
uncompressed:
#endif
- r = fix_permissions(fd, tmp, fn, info, uid);
+ r = fix_permissions(fd, tmp, fn, context, uid);
if (r < 0)
goto fail;
@@ -417,7 +438,7 @@ uncompressed:
return 0;
fail:
- unlink_noerrno(tmp);
+ (void) unlink(tmp);
return r;
}
@@ -539,186 +560,473 @@ static int compose_open_fds(pid_t pid, char **open_fds) {
return 0;
}
-int main(int argc, char* argv[]) {
+static int change_uid_gid(const char *context[]) {
+ uid_t uid;
+ gid_t gid;
+ int r;
- /* The small core field we allocate on the stack, to keep things simple */
- char
- *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
- *core_session = NULL, *core_exe = NULL, *core_comm = NULL, *core_cmdline = NULL,
- *core_cgroup = NULL, *core_cwd = NULL, *core_root = NULL, *core_unit = NULL,
- *core_slice = NULL;
+ r = parse_uid(context[CONTEXT_UID], &uid);
+ if (r < 0)
+ return r;
- /* The larger ones we allocate on the heap */
- _cleanup_free_ char
- *core_timestamp = NULL, *core_message = NULL, *coredump_data = NULL, *core_owner_uid = NULL,
- *core_open_fds = NULL, *core_proc_status = NULL, *core_proc_maps = NULL, *core_proc_limits = NULL,
- *core_proc_cgroup = NULL, *core_environ = NULL;
+ if (uid <= SYSTEM_UID_MAX) {
+ const char *user = "systemd-coredump";
- _cleanup_free_ char *exe = NULL, *comm = NULL, *filename = NULL;
- const char *info[_INFO_LEN];
+ r = get_user_creds(&user, &uid, &gid, NULL, NULL);
+ if (r < 0) {
+ log_warning_errno(r, "Cannot resolve %s user. Proceeding to dump core as root: %m", user);
+ uid = gid = 0;
+ }
+ } else {
+ r = parse_gid(context[CONTEXT_GID], &gid);
+ if (r < 0)
+ return r;
+ }
- _cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1;
+ return drop_privileges(uid, gid, 0);
+}
+
+static int submit_coredump(
+ const char *context[_CONTEXT_MAX],
+ struct iovec *iovec,
+ size_t n_iovec_allocated,
+ size_t n_iovec,
+ int input_fd) {
- struct iovec iovec[26];
+ _cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1;
+ _cleanup_free_ char *core_message = NULL, *filename = NULL, *coredump_data = NULL;
uint64_t coredump_size;
- int r, j = 0;
- uid_t uid, owner_uid;
- gid_t gid;
- pid_t pid;
- char *t;
- const char *p;
+ int r;
- /* Make sure we never enter a loop */
- prctl(PR_SET_DUMPABLE, 0);
+ assert(context);
+ assert(iovec);
+ assert(n_iovec_allocated >= n_iovec + 3);
+ assert(input_fd >= 0);
- /* First, log to a safe place, since we don't know what
- * crashed and it might be journald which we'd rather not log
- * to then. */
- log_set_target(LOG_TARGET_KMSG);
- log_open();
+ /* Vacuum before we write anything again */
+ (void) coredump_vacuum(-1, arg_keep_free, arg_max_use);
- if (argc < INFO_COMM + 1) {
- log_error("Not enough arguments passed from kernel (%d, expected %d).",
- argc - 1, INFO_COMM + 1 - 1);
- r = -EINVAL;
- goto finish;
+ /* Always stream the coredump to disk, if that's possible */
+ r = save_external_coredump(context, input_fd, &filename, &coredump_node_fd, &coredump_fd, &coredump_size);
+ if (r < 0)
+ /* Skip whole core dumping part */
+ goto log;
+
+ /* If we don't want to keep the coredump on disk, remove it now, as later on we will lack the privileges for
+ * it. However, we keep the fd to it, so that we can still process it and log it. */
+ r = maybe_remove_external_coredump(filename, coredump_size);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ const char *coredump_filename;
+
+ coredump_filename = strjoina("COREDUMP_FILENAME=", filename);
+ IOVEC_SET_STRING(iovec[n_iovec++], coredump_filename);
}
- /* Ignore all parse errors */
- parse_config();
+ /* Vacuum again, but exclude the coredump we just created */
+ (void) coredump_vacuum(coredump_node_fd >= 0 ? coredump_node_fd : coredump_fd, arg_keep_free, arg_max_use);
- log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
- log_debug("Selected compression %s.", yes_no(arg_compress));
+ /* Now, let's drop privileges to become the user who owns the segfaulted process and allocate the coredump
+ * memory under the user's uid. This also ensures that the credentials journald will see are the ones of the
+ * coredumping user, thus making sure the user gets access to the core dump. Let's also get rid of all
+ * capabilities, if we run as root, we won't need them anymore. */
+ r = change_uid_gid(context);
+ if (r < 0)
+ return log_error_errno(r, "Failed to drop privileges: %m");
- r = parse_uid(argv[INFO_UID + 1], &uid);
- if (r < 0) {
- log_error("Failed to parse UID.");
- goto finish;
+#ifdef HAVE_ELFUTILS
+ /* Try to get a strack trace if we can */
+ if (coredump_size <= arg_process_size_max) {
+ _cleanup_free_ char *stacktrace = NULL;
+
+ r = coredump_make_stack_trace(coredump_fd, context[CONTEXT_EXE], &stacktrace);
+ if (r >= 0)
+ core_message = strjoin("MESSAGE=Process ", context[CONTEXT_PID], " (", context[CONTEXT_COMM], ") of user ", context[CONTEXT_UID], " dumped core.\n\n", stacktrace, NULL);
+ else if (r == -EINVAL)
+ log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno()));
+ else
+ log_warning_errno(r, "Failed to generate stack trace: %m");
}
- r = parse_pid(argv[INFO_PID + 1], &pid);
- if (r < 0) {
- log_error("Failed to parse PID.");
- goto finish;
+ if (!core_message)
+#endif
+log:
+ core_message = strjoin("MESSAGE=Process ", context[CONTEXT_PID], " (", context[CONTEXT_COMM], ") of user ", context[CONTEXT_UID], " dumped core.", NULL);
+ if (core_message)
+ IOVEC_SET_STRING(iovec[n_iovec++], core_message);
+
+ /* Optionally store the entire coredump in the journal */
+ if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
+ coredump_size <= arg_journal_size_max) {
+ size_t sz = 0;
+
+ /* Store the coredump itself in the journal */
+
+ r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
+ if (r >= 0) {
+ iovec[n_iovec].iov_base = coredump_data;
+ iovec[n_iovec].iov_len = sz;
+ n_iovec++;
+ }
}
- r = parse_gid(argv[INFO_GID + 1], &gid);
- if (r < 0) {
- log_error("Failed to parse GID.");
+ assert(n_iovec <= n_iovec_allocated);
+
+ r = sd_journal_sendv(iovec, n_iovec);
+ if (r < 0)
+ return log_error_errno(r, "Failed to log coredump: %m");
+
+ return 0;
+}
+
+static void map_context_fields(const struct iovec *iovec, const char *context[]) {
+
+ static const char * const context_field_names[_CONTEXT_MAX] = {
+ [CONTEXT_PID] = "COREDUMP_PID=",
+ [CONTEXT_UID] = "COREDUMP_UID=",
+ [CONTEXT_GID] = "COREDUMP_GID=",
+ [CONTEXT_SIGNAL] = "COREDUMP_SIGNAL=",
+ [CONTEXT_TIMESTAMP] = "COREDUMP_TIMESTAMP=",
+ [CONTEXT_COMM] = "COREDUMP_COMM=",
+ [CONTEXT_EXE] = "COREDUMP_EXE=",
+ [CONTEXT_RLIMIT] = "COREDUMP_RLIMIT=",
+ };
+
+ unsigned i;
+
+ assert(iovec);
+ assert(context);
+
+ for (i = 0; i < _CONTEXT_MAX; i++) {
+ size_t l;
+
+ l = strlen(context_field_names[i]);
+ if (iovec->iov_len < l)
+ continue;
+
+ if (memcmp(iovec->iov_base, context_field_names[i], l) != 0)
+ continue;
+
+ /* Note that these strings are NUL terminated, because we made sure that a trailing NUL byte is in the
+ * buffer, though not included in the iov_len count. (see below) */
+ context[i] = (char*) iovec->iov_base + l;
+ break;
+ }
+}
+
+static int process_socket(int fd) {
+ _cleanup_close_ int coredump_fd = -1;
+ struct iovec *iovec = NULL;
+ size_t n_iovec = 0, n_iovec_allocated = 0, i;
+ const char *context[_CONTEXT_MAX] = {};
+ int r;
+
+ assert(fd >= 0);
+
+ log_set_target(LOG_TARGET_AUTO);
+ log_parse_environment();
+ log_open();
+
+ for (;;) {
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(int))];
+ } control = {};
+ struct msghdr mh = {
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ .msg_iovlen = 1,
+ };
+ ssize_t n;
+ int l;
+
+ if (!GREEDY_REALLOC(iovec, n_iovec_allocated, n_iovec + 3)) {
+ r = log_oom();
+ goto finish;
+ }
+
+ if (ioctl(fd, FIONREAD, &l) < 0) {
+ r = log_error_errno(errno, "FIONREAD failed: %m");
+ goto finish;
+ }
+
+ assert(l >= 0);
+
+ iovec[n_iovec].iov_len = l;
+ iovec[n_iovec].iov_base = malloc(l + 1);
+
+ if (!iovec[n_iovec].iov_base) {
+ r = log_oom();
+ goto finish;
+ }
+
+ mh.msg_iov = iovec + n_iovec;
+
+ n = recvmsg(fd, &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC);
+ if (n < 0) {
+ free(iovec[n_iovec].iov_base);
+ r = log_error_errno(errno, "Failed to receive datagram: %m");
+ goto finish;
+ }
+
+ if (n == 0) {
+ struct cmsghdr *cmsg, *found = NULL;
+ /* The final zero-length datagram carries the file descriptor and tells us that we're done. */
+
+ free(iovec[n_iovec].iov_base);
+
+ CMSG_FOREACH(cmsg, &mh) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
+ assert(!found);
+ found = cmsg;
+ }
+ }
+
+ if (!found) {
+ log_error("Coredump file descriptor missing.");
+ r = -EBADMSG;
+ goto finish;
+ }
+
+ assert(coredump_fd < 0);
+ coredump_fd = *(int*) CMSG_DATA(found);
+ break;
+ }
+
+ /* Add trailing NUL byte, in case these are strings */
+ ((char*) iovec[n_iovec].iov_base)[n] = 0;
+ iovec[n_iovec].iov_len = (size_t) n;
+
+ cmsg_close_all(&mh);
+ map_context_fields(iovec + n_iovec, context);
+ n_iovec++;
+ }
+
+ if (!GREEDY_REALLOC(iovec, n_iovec_allocated, n_iovec + 3)) {
+ r = log_oom();
goto finish;
}
- if (get_process_comm(pid, &comm) < 0) {
- log_warning("Failed to get COMM, falling back to the command line.");
- comm = strv_join(argv + INFO_COMM + 1, " ");
+ /* Make sure we we got all data we really need */
+ assert(context[CONTEXT_PID]);
+ assert(context[CONTEXT_UID]);
+ assert(context[CONTEXT_GID]);
+ assert(context[CONTEXT_SIGNAL]);
+ assert(context[CONTEXT_TIMESTAMP]);
+ assert(context[CONTEXT_RLIMIT]);
+ assert(context[CONTEXT_COMM]);
+ assert(coredump_fd >= 0);
+
+ r = submit_coredump(context, iovec, n_iovec_allocated, n_iovec, coredump_fd);
+
+finish:
+ for (i = 0; i < n_iovec; i++)
+ free(iovec[i].iov_base);
+ free(iovec);
+
+ return r;
+}
+
+static int send_iovec(const struct iovec iovec[], size_t n_iovec, int input_fd) {
+
+ static const union sockaddr_union sa = {
+ .un.sun_family = AF_UNIX,
+ .un.sun_path = "/run/systemd/coredump",
+ };
+ _cleanup_close_ int fd = -1;
+ size_t i;
+ int r;
+
+ assert(iovec || n_iovec <= 0);
+ assert(input_fd >= 0);
+
+ fd = socket(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to create coredump socket: %m");
+
+ if (connect(fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)) < 0)
+ return log_error_errno(errno, "Failed to connect to coredump service: %m");
+
+ for (i = 0; i < n_iovec; i++) {
+ ssize_t n;
+ assert(iovec[i].iov_len > 0);
+
+ n = send(fd, iovec[i].iov_base, iovec[i].iov_len, MSG_NOSIGNAL);
+ if (n < 0)
+ return log_error_errno(errno, "Failed to send coredump datagram: %m");
}
- if (get_process_exe(pid, &exe) < 0)
- log_warning("Failed to get EXE.");
+ r = send_one_fd(fd, input_fd, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to send coredump fd: %m");
- info[INFO_PID] = argv[INFO_PID + 1];
- info[INFO_UID] = argv[INFO_UID + 1];
- info[INFO_GID] = argv[INFO_GID + 1];
- info[INFO_SIGNAL] = argv[INFO_SIGNAL + 1];
- info[INFO_TIMESTAMP] = argv[INFO_TIMESTAMP + 1];
- info[INFO_COMM] = comm;
- info[INFO_EXE] = exe;
+ return 0;
+}
- if (cg_pid_get_unit(pid, &t) >= 0) {
+static int process_journald_crash(const char *context[], int input_fd) {
+ _cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1;
+ _cleanup_free_ char *filename = NULL;
+ uint64_t coredump_size;
+ int r;
- if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
- free(t);
+ assert(context);
+ assert(input_fd >= 0);
- /* If we are journald, we cut things short,
- * don't write to the journal, but still
- * create a coredump. */
+ /* If we are journald, we cut things short, don't write to the journal, but still create a coredump. */
- if (arg_storage != COREDUMP_STORAGE_NONE)
- arg_storage = COREDUMP_STORAGE_EXTERNAL;
+ if (arg_storage != COREDUMP_STORAGE_NONE)
+ arg_storage = COREDUMP_STORAGE_EXTERNAL;
- r = save_external_coredump(info, uid, &filename, &coredump_node_fd, &coredump_fd, &coredump_size);
- if (r < 0)
- goto finish;
+ r = save_external_coredump(context, input_fd, &filename, &coredump_node_fd, &coredump_fd, &coredump_size);
+ if (r < 0)
+ return r;
- r = maybe_remove_external_coredump(filename, coredump_size);
- if (r < 0)
- goto finish;
+ r = maybe_remove_external_coredump(filename, coredump_size);
+ if (r < 0)
+ return r;
- log_info("Detected coredump of the journal daemon itself, diverted to %s.", filename);
- goto finish;
+ log_info("Detected coredump of the journal daemon itself, diverted to %s.", filename);
+ return 0;
+}
+
+static int process_kernel(int argc, char* argv[]) {
+
+ /* The small core field we allocate on the stack, to keep things simple */
+ char
+ *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
+ *core_session = NULL, *core_exe = NULL, *core_comm = NULL, *core_cmdline = NULL,
+ *core_cgroup = NULL, *core_cwd = NULL, *core_root = NULL, *core_unit = NULL,
+ *core_user_unit = NULL, *core_slice = NULL, *core_timestamp = NULL, *core_rlimit = NULL;
+
+ /* The larger ones we allocate on the heap */
+ _cleanup_free_ char
+ *core_owner_uid = NULL, *core_open_fds = NULL, *core_proc_status = NULL,
+ *core_proc_maps = NULL, *core_proc_limits = NULL, *core_proc_cgroup = NULL, *core_environ = NULL;
+
+ _cleanup_free_ char *exe = NULL, *comm = NULL;
+ const char *context[_CONTEXT_MAX];
+ struct iovec iovec[25];
+ size_t n_iovec = 0;
+ uid_t owner_uid;
+ const char *p;
+ pid_t pid;
+ char *t;
+ int r;
+
+ if (argc < CONTEXT_COMM + 1) {
+ log_error("Not enough arguments passed from kernel (%i, expected %i).", argc - 1, CONTEXT_COMM + 1 - 1);
+ return -EINVAL;
+ }
+
+ r = parse_pid(argv[CONTEXT_PID + 1], &pid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse PID.");
+
+ r = get_process_comm(pid, &comm);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to get COMM, falling back to the command line: %m");
+ comm = strv_join(argv + CONTEXT_COMM + 1, " ");
+ if (!comm)
+ return log_oom();
+ }
+
+ r = get_process_exe(pid, &exe);
+ if (r < 0)
+ log_warning_errno(r, "Failed to get EXE, ignoring: %m");
+
+ context[CONTEXT_PID] = argv[CONTEXT_PID + 1];
+ context[CONTEXT_UID] = argv[CONTEXT_UID + 1];
+ context[CONTEXT_GID] = argv[CONTEXT_GID + 1];
+ context[CONTEXT_SIGNAL] = argv[CONTEXT_SIGNAL + 1];
+ context[CONTEXT_TIMESTAMP] = argv[CONTEXT_TIMESTAMP + 1];
+ context[CONTEXT_RLIMIT] = argv[CONTEXT_RLIMIT + 1];
+ context[CONTEXT_COMM] = comm;
+ context[CONTEXT_EXE] = exe;
+
+ if (cg_pid_get_unit(pid, &t) >= 0) {
+
+ if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
+ free(t);
+ return process_journald_crash(context, STDIN_FILENO);
}
core_unit = strjoina("COREDUMP_UNIT=", t);
free(t);
- } else if (cg_pid_get_user_unit(pid, &t) >= 0) {
- core_unit = strjoina("COREDUMP_USER_UNIT=", t);
- free(t);
+ IOVEC_SET_STRING(iovec[n_iovec++], core_unit);
}
- if (core_unit)
- IOVEC_SET_STRING(iovec[j++], core_unit);
-
- /* OK, now we know it's not the journal, hence we can make use
- * of it now. */
+ /* OK, now we know it's not the journal, hence we can make use of it now. */
log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
log_open();
- core_pid = strjoina("COREDUMP_PID=", info[INFO_PID]);
- IOVEC_SET_STRING(iovec[j++], core_pid);
+ if (cg_pid_get_user_unit(pid, &t) >= 0) {
+ core_user_unit = strjoina("COREDUMP_USER_UNIT=", t);
+ free(t);
+
+ IOVEC_SET_STRING(iovec[n_iovec++], core_user_unit);
+ }
+
+ core_pid = strjoina("COREDUMP_PID=", context[CONTEXT_PID]);
+ IOVEC_SET_STRING(iovec[n_iovec++], core_pid);
+
+ core_uid = strjoina("COREDUMP_UID=", context[CONTEXT_UID]);
+ IOVEC_SET_STRING(iovec[n_iovec++], core_uid);
- core_uid = strjoina("COREDUMP_UID=", info[INFO_UID]);
- IOVEC_SET_STRING(iovec[j++], core_uid);
+ core_gid = strjoina("COREDUMP_GID=", context[CONTEXT_GID]);
+ IOVEC_SET_STRING(iovec[n_iovec++], core_gid);
- core_gid = strjoina("COREDUMP_GID=", info[INFO_GID]);
- IOVEC_SET_STRING(iovec[j++], core_gid);
+ core_signal = strjoina("COREDUMP_SIGNAL=", context[CONTEXT_SIGNAL]);
+ IOVEC_SET_STRING(iovec[n_iovec++], core_signal);
- core_signal = strjoina("COREDUMP_SIGNAL=", info[INFO_SIGNAL]);
- IOVEC_SET_STRING(iovec[j++], core_signal);
+ core_rlimit = strjoina("COREDUMP_RLIMIT=", context[CONTEXT_RLIMIT]);
+ IOVEC_SET_STRING(iovec[n_iovec++], core_rlimit);
if (sd_pid_get_session(pid, &t) >= 0) {
core_session = strjoina("COREDUMP_SESSION=", t);
free(t);
- IOVEC_SET_STRING(iovec[j++], core_session);
+ IOVEC_SET_STRING(iovec[n_iovec++], core_session);
}
if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
- r = asprintf(&core_owner_uid,
- "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
+ r = asprintf(&core_owner_uid, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
if (r > 0)
- IOVEC_SET_STRING(iovec[j++], core_owner_uid);
+ IOVEC_SET_STRING(iovec[n_iovec++], core_owner_uid);
}
if (sd_pid_get_slice(pid, &t) >= 0) {
core_slice = strjoina("COREDUMP_SLICE=", t);
free(t);
- IOVEC_SET_STRING(iovec[j++], core_slice);
+ IOVEC_SET_STRING(iovec[n_iovec++], core_slice);
}
if (comm) {
core_comm = strjoina("COREDUMP_COMM=", comm);
- IOVEC_SET_STRING(iovec[j++], core_comm);
+ IOVEC_SET_STRING(iovec[n_iovec++], core_comm);
}
if (exe) {
core_exe = strjoina("COREDUMP_EXE=", exe);
- IOVEC_SET_STRING(iovec[j++], core_exe);
+ IOVEC_SET_STRING(iovec[n_iovec++], core_exe);
}
if (get_process_cmdline(pid, 0, false, &t) >= 0) {
core_cmdline = strjoina("COREDUMP_CMDLINE=", t);
free(t);
- IOVEC_SET_STRING(iovec[j++], core_cmdline);
+ IOVEC_SET_STRING(iovec[n_iovec++], core_cmdline);
}
if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
core_cgroup = strjoina("COREDUMP_CGROUP=", t);
free(t);
- IOVEC_SET_STRING(iovec[j++], core_cgroup);
+ IOVEC_SET_STRING(iovec[n_iovec++], core_cgroup);
}
if (compose_open_fds(pid, &t) >= 0) {
@@ -726,7 +1034,7 @@ int main(int argc, char* argv[]) {
free(t);
if (core_open_fds)
- IOVEC_SET_STRING(iovec[j++], core_open_fds);
+ IOVEC_SET_STRING(iovec[n_iovec++], core_open_fds);
}
p = procfs_file_alloca(pid, "status");
@@ -735,7 +1043,7 @@ int main(int argc, char* argv[]) {
free(t);
if (core_proc_status)
- IOVEC_SET_STRING(iovec[j++], core_proc_status);
+ IOVEC_SET_STRING(iovec[n_iovec++], core_proc_status);
}
p = procfs_file_alloca(pid, "maps");
@@ -744,7 +1052,7 @@ int main(int argc, char* argv[]) {
free(t);
if (core_proc_maps)
- IOVEC_SET_STRING(iovec[j++], core_proc_maps);
+ IOVEC_SET_STRING(iovec[n_iovec++], core_proc_maps);
}
p = procfs_file_alloca(pid, "limits");
@@ -753,7 +1061,7 @@ int main(int argc, char* argv[]) {
free(t);
if (core_proc_limits)
- IOVEC_SET_STRING(iovec[j++], core_proc_limits);
+ IOVEC_SET_STRING(iovec[n_iovec++], core_proc_limits);
}
p = procfs_file_alloca(pid, "cgroup");
@@ -762,21 +1070,21 @@ int main(int argc, char* argv[]) {
free(t);
if (core_proc_cgroup)
- IOVEC_SET_STRING(iovec[j++], core_proc_cgroup);
+ IOVEC_SET_STRING(iovec[n_iovec++], core_proc_cgroup);
}
if (get_process_cwd(pid, &t) >= 0) {
core_cwd = strjoina("COREDUMP_CWD=", t);
free(t);
- IOVEC_SET_STRING(iovec[j++], core_cwd);
+ IOVEC_SET_STRING(iovec[n_iovec++], core_cwd);
}
if (get_process_root(pid, &t) >= 0) {
core_root = strjoina("COREDUMP_ROOT=", t);
free(t);
- IOVEC_SET_STRING(iovec[j++], core_root);
+ IOVEC_SET_STRING(iovec[n_iovec++], core_root);
}
if (get_process_environ(pid, &t) >= 0) {
@@ -784,96 +1092,56 @@ int main(int argc, char* argv[]) {
free(t);
if (core_environ)
- IOVEC_SET_STRING(iovec[j++], core_environ);
+ IOVEC_SET_STRING(iovec[n_iovec++], core_environ);
}
- core_timestamp = strjoin("COREDUMP_TIMESTAMP=", info[INFO_TIMESTAMP], "000000", NULL);
- if (core_timestamp)
- IOVEC_SET_STRING(iovec[j++], core_timestamp);
+ core_timestamp = strjoina("COREDUMP_TIMESTAMP=", context[CONTEXT_TIMESTAMP], "000000", NULL);
+ IOVEC_SET_STRING(iovec[n_iovec++], core_timestamp);
- IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
+ IOVEC_SET_STRING(iovec[n_iovec++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
assert_cc(2 == LOG_CRIT);
- IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
-
- /* Vacuum before we write anything again */
- coredump_vacuum(-1, arg_keep_free, arg_max_use);
-
- /* Always stream the coredump to disk, if that's possible */
- r = save_external_coredump(info, uid, &filename, &coredump_node_fd, &coredump_fd, &coredump_size);
- if (r < 0)
- /* skip whole core dumping part */
- goto log;
+ IOVEC_SET_STRING(iovec[n_iovec++], "PRIORITY=2");
- /* If we don't want to keep the coredump on disk, remove it
- * now, as later on we will lack the privileges for
- * it. However, we keep the fd to it, so that we can still
- * process it and log it. */
- r = maybe_remove_external_coredump(filename, coredump_size);
- if (r < 0)
- goto finish;
- if (r == 0) {
- const char *coredump_filename;
+ assert(n_iovec <= ELEMENTSOF(iovec));
- coredump_filename = strjoina("COREDUMP_FILENAME=", filename);
- IOVEC_SET_STRING(iovec[j++], coredump_filename);
- }
+ return send_iovec(iovec, n_iovec, STDIN_FILENO);
+}
- /* Vacuum again, but exclude the coredump we just created */
- coredump_vacuum(coredump_node_fd >= 0 ? coredump_node_fd : coredump_fd, arg_keep_free, arg_max_use);
-
- /* Now, let's drop privileges to become the user who owns the
- * segfaulted process and allocate the coredump memory under
- * the user's uid. This also ensures that the credentials
- * journald will see are the ones of the coredumping user,
- * thus making sure the user gets access to the core
- * dump. Let's also get rid of all capabilities, if we run as
- * root, we won't need them anymore. */
- r = drop_privileges(uid, gid, 0);
- if (r < 0) {
- log_error_errno(r, "Failed to drop privileges: %m");
- goto finish;
- }
+int main(int argc, char *argv[]) {
+ int r;
-#ifdef HAVE_ELFUTILS
- /* Try to get a strack trace if we can */
- if (coredump_size <= arg_process_size_max) {
- _cleanup_free_ char *stacktrace = NULL;
+ /* First, log to a safe place, since we don't know what crashed and it might be journald which we'd rather not
+ * log to then. */
- r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
- if (r >= 0)
- core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.\n\n", stacktrace, NULL);
- else if (r == -EINVAL)
- log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno()));
- else
- log_warning_errno(r, "Failed to generate stack trace: %m");
- }
+ log_set_target(LOG_TARGET_KMSG);
+ log_open();
- if (!core_message)
-#endif
-log:
- core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.", NULL);
- if (core_message)
- IOVEC_SET_STRING(iovec[j++], core_message);
+ /* Make sure we never enter a loop */
+ (void) prctl(PR_SET_DUMPABLE, 0);
- /* Optionally store the entire coredump in the journal */
- if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
- coredump_size <= arg_journal_size_max) {
- size_t sz = 0;
+ /* Ignore all parse errors */
+ (void) parse_config();
- /* Store the coredump itself in the journal */
+ log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
+ log_debug("Selected compression %s.", yes_no(arg_compress));
- r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
- if (r >= 0) {
- iovec[j].iov_base = coredump_data;
- iovec[j].iov_len = sz;
- j++;
- }
+ r = sd_listen_fds(false);
+ if (r < 0) {
+ log_error_errno(r, "Failed to determine number of file descriptor: %m");
+ goto finish;
}
- r = sd_journal_sendv(iovec, j);
- if (r < 0)
- log_error_errno(r, "Failed to log coredump: %m");
+ /* If we got an fd passed, we are running in coredumpd mode. Otherwise we are invoked from the kernel as
+ * coredump handler */
+ if (r == 0)
+ r = process_kernel(argc, argv);
+ else if (r == 1)
+ r = process_socket(SD_LISTEN_FDS_START);
+ else {
+ log_error("Received unexpected number of file descriptors.");
+ r = -EINVAL;
+ }
finish:
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/src/journal/coredump.conf b/src/coredump/coredump.conf
index c2f0643e03..c2f0643e03 100644
--- a/src/journal/coredump.conf
+++ b/src/coredump/coredump.conf
diff --git a/src/journal/coredumpctl.c b/src/coredump/coredumpctl.c
index 0034a1a0ac..0034a1a0ac 100644
--- a/src/journal/coredumpctl.c
+++ b/src/coredump/coredumpctl.c
diff --git a/src/journal/stacktrace.c b/src/coredump/stacktrace.c
index 68806992fc..68806992fc 100644
--- a/src/journal/stacktrace.c
+++ b/src/coredump/stacktrace.c
diff --git a/src/journal/stacktrace.h b/src/coredump/stacktrace.h
index 15e9c04465..15e9c04465 100644
--- a/src/journal/stacktrace.h
+++ b/src/coredump/stacktrace.h
diff --git a/src/journal/test-coredump-vacuum.c b/src/coredump/test-coredump-vacuum.c
index 70a57f183f..70a57f183f 100644
--- a/src/journal/test-coredump-vacuum.c
+++ b/src/coredump/test-coredump-vacuum.c
diff --git a/src/resolve/resolved-def.h b/src/resolve/resolved-def.h
index d43a1fe25e..c4c1915b18 100644
--- a/src/resolve/resolved-def.h
+++ b/src/resolve/resolved-def.h
@@ -19,6 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <inttypes.h>
+
#define SD_RESOLVED_DNS (UINT64_C(1) << 0)
#define SD_RESOLVED_LLMNR_IPV4 (UINT64_C(1) << 1)
#define SD_RESOLVED_LLMNR_IPV6 (UINT64_C(1) << 2)
diff --git a/sysctl.d/50-coredump.conf.in b/sysctl.d/50-coredump.conf.in
index 5e04c821b6..5a25de4512 100644
--- a/sysctl.d/50-coredump.conf.in
+++ b/sysctl.d/50-coredump.conf.in
@@ -9,4 +9,4 @@
# and systemd-coredump(8) and core(5) for the explanation of the
# setting below.
-kernel.core_pattern=|@rootlibexecdir@/systemd-coredump %P %u %g %s %t %e
+kernel.core_pattern=|@rootlibexecdir@/systemd-coredump %P %u %g %s %t %c %e
diff --git a/sysusers.d/systemd.conf.m4 b/sysusers.d/systemd.conf.m4
index 81b1d79c37..317240a9fd 100644
--- a/sysusers.d/systemd.conf.m4
+++ b/sysusers.d/systemd.conf.m4
@@ -16,3 +16,6 @@ u systemd-resolve - "systemd Resolver"
m4_ifdef(`ENABLE_TIMESYNCD',
u systemd-timesync - "systemd Time Synchronization"
)m4_dnl
+m4_ifdef(`ENABLE_COREDUMP',
+u systemd-coredump - "systemd Core Dumper"
+)m4_dnl
diff --git a/units/.gitignore b/units/.gitignore
index c89740df05..2fff20a052 100644
--- a/units/.gitignore
+++ b/units/.gitignore
@@ -25,6 +25,7 @@
/systemd-binfmt.service
/systemd-bootchart.service
/systemd-bus-proxyd.service
+/systemd-coredump@.service
/systemd-firstboot.service
/systemd-fsck-root.service
/systemd-fsck@.service
diff --git a/units/systemd-coredump.socket b/units/systemd-coredump.socket
new file mode 100644
index 0000000000..4cb2460471
--- /dev/null
+++ b/units/systemd-coredump.socket
@@ -0,0 +1,17 @@
+# 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.
+
+[Unit]
+Description=Process Core Dump Socket
+Documentation=man:systemd-coredump(8)
+DefaultDependencies=no
+
+[Socket]
+ListenSequentialPacket=/run/systemd/coredump
+SocketMode=0600
+Accept=yes
+MaxConnections=16
diff --git a/units/systemd-coredump@.service.in b/units/systemd-coredump@.service.in
new file mode 100644
index 0000000000..588c8d629c
--- /dev/null
+++ b/units/systemd-coredump@.service.in
@@ -0,0 +1,24 @@
+# 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.
+
+[Unit]
+Description=Process Core Dump
+Documentation=man:systemd-coredump(8)
+DefaultDependencies=no
+RequiresMountsFor=/var/lib/systemd/coredump
+Conflicts=shutdown.target
+After=systemd-remount-fs.service systemd-journald.socket
+Requires=systemd-journald.socket
+Before=shutdown.target
+
+[Service]
+ExecStart=-@rootlibexecdir@/systemd-coredump
+Nice=9
+OOMScoreAdjust=500
+PrivateNetwork=yes
+ProtectSystem=full
+RuntimeMaxSec=5min