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 @@
Description
- systemd-activate 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).
+ systemd-activate 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.
The daemon to launch and its options should be specified
after options intended for systemd-activate.
- If the 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 $LISTEN_FDS
- to systemd-activate will be passed through to
- the daemon, in the original positions. Other sockets specified
- with will use consecutive descriptors.
- By default, systemd-activate listens on a
- stream socket, use to listen on
- a datagram socket instead (see below).
+ If the 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 $LISTEN_FDS to
+ systemd-activate will be passed through to the daemon, in the original positions. Other sockets
+ specified with will use consecutive descriptors. By default,
+ systemd-activate listens on a stream socket, use and
+ to listen on datagram or sequential packet sockets instead (see below).
@@ -101,16 +95,32 @@
- Launch a separate instance of daemon per
- connection and pass the connection socket as standard input
- and standard output.
+ Launch an instance of the service binary for each connection and pass the connection
+ socket.
- Listen on a datagram socket, instead of a stream socket.
+ Listen on a datagram socket (SOCK_DGRAM), instead of a stream socket
+ (SOCK_STREAM). May not be combined with .
+
+
+
+
+
+ Listen on a sequential packet socket (SOCK_SEQPACKET), instead of a stream
+ socket (SOCK_STREAM). May not be combined with
+ .
+
+
+
+
+
+ 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 $LISTEN_FDS
+ (see above).
@@ -170,7 +180,7 @@
Run an echo server on port 2000
- $ /usr/lib/systemd/systemd-activate -l 2000 -a cat
+ $ /usr/lib/systemd/systemd-activate -l 2000 --inetd -a cat
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 @@
JobTimeoutAction=
JobTimeoutRebootArgument=
- 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 failed 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
- TimeoutStartSec= 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.
+ 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
+ failed mode. This value defaults to infinity (job timeouts disabled),
+ except for device units. NB: this timeout is independent from any unit-specific timeout (for example, the
+ timeout set with TimeoutStartSec= 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.
JobTimeoutAction=
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);
- if (arg_fdname) {
- char *e;
+ r = dup2(start_fd, STDIN_FILENO);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to dup connection to stdin: %m");
- e = strappend("LISTEN_FDNAMES=", arg_fdname);
- if (!e)
- return log_oom();
+ r = dup2(start_fd, STDOUT_FILENO);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to dup connection to stdout: %m");
- for (i = 1; i < (unsigned) fds; i++) {
- char *c;
+ start_fd = safe_close(start_fd);
+ } else {
+ if (start_fd != SD_LISTEN_FDS_START) {
+ assert(n_fds == 1);
- c = strjoin(e, ":", arg_fdname, NULL);
- if (!c) {
- free(e);
- return log_oom();
- }
+ r = dup2(start_fd, SD_LISTEN_FDS_START);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to dup connection: %m");
- free(e);
- e = c;
+ safe_close(start_fd);
+ start_fd = SD_LISTEN_FDS_START;
}
- envp[n_env++] = e;
+ if (asprintf((char**)(envp + n_env++), "LISTEN_FDS=%i", n_fds) < 0)
+ return log_oom();
+
+ if (asprintf((char**)(envp + n_env++), "LISTEN_PID=" PID_FMT, getpid()) < 0)
+ return log_oom();
+
+ 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;
+ }
+
+ 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 (s->timeout_start_usec <= 0)
- s->timeout_start_usec = USEC_INFINITY;
- if (s->timeout_stop_usec <= 0)
- s->timeout_stop_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 (*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
similarity index 100%
rename from src/journal/coredump-vacuum.c
rename to src/coredump/coredump-vacuum.c
diff --git a/src/journal/coredump-vacuum.h b/src/coredump/coredump-vacuum.h
similarity index 100%
rename from src/journal/coredump-vacuum.h
rename to src/coredump/coredump-vacuum.h
diff --git a/src/journal/coredump.c b/src/coredump/coredump.c
similarity index 53%
rename from src/journal/coredump.c
rename to src/coredump/coredump.c
index 8b1c670cc6..085909c20c 100644
--- a/src/journal/coredump.c
+++ b/src/coredump/coredump.c
@@ -24,12 +24,13 @@
#include
#ifdef HAVE_ELFUTILS
-# include
-# include
+#include
+#include
#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,310 +560,89 @@ 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;
+ }
+
+ 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) {
_cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1;
-
- struct iovec iovec[26];
+ _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);
-
- /* 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();
-
- 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;
- }
-
- /* Ignore all parse errors */
- parse_config();
-
- log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
- log_debug("Selected compression %s.", yes_no(arg_compress));
-
- r = parse_uid(argv[INFO_UID + 1], &uid);
- if (r < 0) {
- log_error("Failed to parse UID.");
- goto finish;
- }
-
- r = parse_pid(argv[INFO_PID + 1], &pid);
- if (r < 0) {
- log_error("Failed to parse PID.");
- goto finish;
- }
-
- r = parse_gid(argv[INFO_GID + 1], &gid);
- if (r < 0) {
- log_error("Failed to parse GID.");
- 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, " ");
- }
-
- if (get_process_exe(pid, &exe) < 0)
- log_warning("Failed to get EXE.");
-
- 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;
-
- if (cg_pid_get_unit(pid, &t) >= 0) {
-
- if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
- free(t);
-
- /* 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;
-
- r = save_external_coredump(info, uid, &filename, &coredump_node_fd, &coredump_fd, &coredump_size);
- if (r < 0)
- goto finish;
-
- r = maybe_remove_external_coredump(filename, coredump_size);
- if (r < 0)
- goto finish;
-
- log_info("Detected coredump of the journal daemon itself, diverted to %s.", filename);
- goto finish;
- }
-
- 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);
- }
-
- 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. */
- 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);
-
- core_uid = strjoina("COREDUMP_UID=", info[INFO_UID]);
- IOVEC_SET_STRING(iovec[j++], core_uid);
-
- core_gid = strjoina("COREDUMP_GID=", info[INFO_GID]);
- IOVEC_SET_STRING(iovec[j++], core_gid);
-
- core_signal = strjoina("COREDUMP_SIGNAL=", info[INFO_SIGNAL]);
- IOVEC_SET_STRING(iovec[j++], core_signal);
-
- if (sd_pid_get_session(pid, &t) >= 0) {
- core_session = strjoina("COREDUMP_SESSION=", t);
- free(t);
-
- IOVEC_SET_STRING(iovec[j++], core_session);
- }
-
- if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
- r = asprintf(&core_owner_uid,
- "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
- if (r > 0)
- IOVEC_SET_STRING(iovec[j++], 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);
- }
-
- if (comm) {
- core_comm = strjoina("COREDUMP_COMM=", comm);
- IOVEC_SET_STRING(iovec[j++], core_comm);
- }
-
- if (exe) {
- core_exe = strjoina("COREDUMP_EXE=", exe);
- IOVEC_SET_STRING(iovec[j++], 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);
- }
-
- 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);
- }
-
- if (compose_open_fds(pid, &t) >= 0) {
- core_open_fds = strappend("COREDUMP_OPEN_FDS=", t);
- free(t);
-
- if (core_open_fds)
- IOVEC_SET_STRING(iovec[j++], core_open_fds);
- }
-
- p = procfs_file_alloca(pid, "status");
- if (read_full_file(p, &t, NULL) >= 0) {
- core_proc_status = strappend("COREDUMP_PROC_STATUS=", t);
- free(t);
-
- if (core_proc_status)
- IOVEC_SET_STRING(iovec[j++], core_proc_status);
- }
-
- p = procfs_file_alloca(pid, "maps");
- if (read_full_file(p, &t, NULL) >= 0) {
- core_proc_maps = strappend("COREDUMP_PROC_MAPS=", t);
- free(t);
-
- if (core_proc_maps)
- IOVEC_SET_STRING(iovec[j++], core_proc_maps);
- }
-
- p = procfs_file_alloca(pid, "limits");
- if (read_full_file(p, &t, NULL) >= 0) {
- core_proc_limits = strappend("COREDUMP_PROC_LIMITS=", t);
- free(t);
-
- if (core_proc_limits)
- IOVEC_SET_STRING(iovec[j++], core_proc_limits);
- }
-
- p = procfs_file_alloca(pid, "cgroup");
- if (read_full_file(p, &t, NULL) >=0) {
- core_proc_cgroup = strappend("COREDUMP_PROC_CGROUP=", t);
- free(t);
-
- if (core_proc_cgroup)
- IOVEC_SET_STRING(iovec[j++], 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);
- }
-
- if (get_process_root(pid, &t) >= 0) {
- core_root = strjoina("COREDUMP_ROOT=", t);
- free(t);
-
- IOVEC_SET_STRING(iovec[j++], core_root);
- }
-
- if (get_process_environ(pid, &t) >= 0) {
- core_environ = strappend("COREDUMP_ENVIRON=", t);
- free(t);
-
- if (core_environ)
- IOVEC_SET_STRING(iovec[j++], core_environ);
- }
-
- core_timestamp = strjoin("COREDUMP_TIMESTAMP=", info[INFO_TIMESTAMP], "000000", NULL);
- if (core_timestamp)
- IOVEC_SET_STRING(iovec[j++], core_timestamp);
-
- IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
-
- assert_cc(2 == LOG_CRIT);
- IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
+ assert(context);
+ assert(iovec);
+ assert(n_iovec_allocated >= n_iovec + 3);
+ assert(input_fd >= 0);
/* Vacuum before we write anything again */
- coredump_vacuum(-1, arg_keep_free, arg_max_use);
+ (void) 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);
+ r = save_external_coredump(context, input_fd, &filename, &coredump_node_fd, &coredump_fd, &coredump_size);
if (r < 0)
- /* skip whole core dumping part */
+ /* 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. */
+ /* 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;
+ return r;
if (r == 0) {
const char *coredump_filename;
coredump_filename = strjoina("COREDUMP_FILENAME=", filename);
- IOVEC_SET_STRING(iovec[j++], coredump_filename);
+ IOVEC_SET_STRING(iovec[n_iovec++], coredump_filename);
}
/* 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);
+ (void) 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;
- }
+ /* 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");
#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, exe, &stacktrace);
+ r = coredump_make_stack_trace(coredump_fd, context[CONTEXT_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);
+ 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
@@ -852,9 +652,9 @@ int main(int argc, char* argv[]) {
if (!core_message)
#endif
log:
- core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.", NULL);
+ 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[j++], 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) &&
@@ -865,15 +665,483 @@ log:
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++;
+ iovec[n_iovec].iov_base = coredump_data;
+ iovec[n_iovec].iov_len = sz;
+ n_iovec++;
}
}
- r = sd_journal_sendv(iovec, j);
+ assert(n_iovec <= n_iovec_allocated);
+
+ r = sd_journal_sendv(iovec, n_iovec);
if (r < 0)
- log_error_errno(r, "Failed to log coredump: %m");
+ 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;
+ }
+
+ /* 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");
+ }
+
+ r = send_one_fd(fd, input_fd, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to send coredump fd: %m");
+
+ return 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;
+
+ 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 (arg_storage != COREDUMP_STORAGE_NONE)
+ arg_storage = COREDUMP_STORAGE_EXTERNAL;
+
+ 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)
+ return r;
+
+ 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);
+
+ IOVEC_SET_STRING(iovec[n_iovec++], core_unit);
+ }
+
+ /* 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();
+
+ 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_gid = strjoina("COREDUMP_GID=", context[CONTEXT_GID]);
+ IOVEC_SET_STRING(iovec[n_iovec++], core_gid);
+
+ core_signal = strjoina("COREDUMP_SIGNAL=", context[CONTEXT_SIGNAL]);
+ IOVEC_SET_STRING(iovec[n_iovec++], 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[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);
+ if (r > 0)
+ 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[n_iovec++], core_slice);
+ }
+
+ if (comm) {
+ core_comm = strjoina("COREDUMP_COMM=", comm);
+ IOVEC_SET_STRING(iovec[n_iovec++], core_comm);
+ }
+
+ if (exe) {
+ core_exe = strjoina("COREDUMP_EXE=", 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[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[n_iovec++], core_cgroup);
+ }
+
+ if (compose_open_fds(pid, &t) >= 0) {
+ core_open_fds = strappend("COREDUMP_OPEN_FDS=", t);
+ free(t);
+
+ if (core_open_fds)
+ IOVEC_SET_STRING(iovec[n_iovec++], core_open_fds);
+ }
+
+ p = procfs_file_alloca(pid, "status");
+ if (read_full_file(p, &t, NULL) >= 0) {
+ core_proc_status = strappend("COREDUMP_PROC_STATUS=", t);
+ free(t);
+
+ if (core_proc_status)
+ IOVEC_SET_STRING(iovec[n_iovec++], core_proc_status);
+ }
+
+ p = procfs_file_alloca(pid, "maps");
+ if (read_full_file(p, &t, NULL) >= 0) {
+ core_proc_maps = strappend("COREDUMP_PROC_MAPS=", t);
+ free(t);
+
+ if (core_proc_maps)
+ IOVEC_SET_STRING(iovec[n_iovec++], core_proc_maps);
+ }
+
+ p = procfs_file_alloca(pid, "limits");
+ if (read_full_file(p, &t, NULL) >= 0) {
+ core_proc_limits = strappend("COREDUMP_PROC_LIMITS=", t);
+ free(t);
+
+ if (core_proc_limits)
+ IOVEC_SET_STRING(iovec[n_iovec++], core_proc_limits);
+ }
+
+ p = procfs_file_alloca(pid, "cgroup");
+ if (read_full_file(p, &t, NULL) >=0) {
+ core_proc_cgroup = strappend("COREDUMP_PROC_CGROUP=", t);
+ free(t);
+
+ if (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[n_iovec++], core_cwd);
+ }
+
+ if (get_process_root(pid, &t) >= 0) {
+ core_root = strjoina("COREDUMP_ROOT=", t);
+ free(t);
+
+ IOVEC_SET_STRING(iovec[n_iovec++], core_root);
+ }
+
+ if (get_process_environ(pid, &t) >= 0) {
+ core_environ = strappend("COREDUMP_ENVIRON=", t);
+ free(t);
+
+ if (core_environ)
+ IOVEC_SET_STRING(iovec[n_iovec++], core_environ);
+ }
+
+ core_timestamp = strjoina("COREDUMP_TIMESTAMP=", context[CONTEXT_TIMESTAMP], "000000", NULL);
+ IOVEC_SET_STRING(iovec[n_iovec++], core_timestamp);
+
+ IOVEC_SET_STRING(iovec[n_iovec++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
+
+ assert_cc(2 == LOG_CRIT);
+ IOVEC_SET_STRING(iovec[n_iovec++], "PRIORITY=2");
+
+ assert(n_iovec <= ELEMENTSOF(iovec));
+
+ return send_iovec(iovec, n_iovec, STDIN_FILENO);
+}
+
+int main(int argc, char *argv[]) {
+ int r;
+
+ /* 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();
+
+ /* Make sure we never enter a loop */
+ (void) prctl(PR_SET_DUMPABLE, 0);
+
+ /* Ignore all parse errors */
+ (void) parse_config();
+
+ log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
+ log_debug("Selected compression %s.", yes_no(arg_compress));
+
+ r = sd_listen_fds(false);
+ if (r < 0) {
+ log_error_errno(r, "Failed to determine number of file descriptor: %m");
+ goto finish;
+ }
+
+ /* 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
similarity index 100%
rename from src/journal/coredump.conf
rename to src/coredump/coredump.conf
diff --git a/src/journal/coredumpctl.c b/src/coredump/coredumpctl.c
similarity index 100%
rename from src/journal/coredumpctl.c
rename to src/coredump/coredumpctl.c
diff --git a/src/journal/stacktrace.c b/src/coredump/stacktrace.c
similarity index 100%
rename from src/journal/stacktrace.c
rename to src/coredump/stacktrace.c
diff --git a/src/journal/stacktrace.h b/src/coredump/stacktrace.h
similarity index 100%
rename from src/journal/stacktrace.h
rename to src/coredump/stacktrace.h
diff --git a/src/journal/test-coredump-vacuum.c b/src/coredump/test-coredump-vacuum.c
similarity index 100%
rename from src/journal/test-coredump-vacuum.c
rename to 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 .
***/
+#include
+
#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