1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2012 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
25 #include <sys/prctl.h>
26 #include <sys/types.h>
27 #include <sys/xattr.h>
29 #include <systemd/sd-journal.h>
30 #include <systemd/sd-login.h>
37 #include "cgroup-util.h"
38 #include "journald-native.h"
39 #include "conf-parser.h"
41 #include "stacktrace.h"
48 /* The maximum size up to which we process coredumps */
49 #define PROCESS_SIZE_MAX ((off_t) (2LLU*1024LLU*1024LLU*1024LLU))
51 /* The maximum size up to which we leave the coredump around on
53 #define EXTERNAL_SIZE_MAX PROCESS_SIZE_MAX
55 /* The maximum size up to which we store the coredump in the
57 #define JOURNAL_SIZE_MAX ((size_t) (767LU*1024LU*1024LU))
59 /* Make sure to not make this larger than the maximum journal entry
60 * size. See ENTRY_SIZE_MAX in journald-native.c. */
61 assert_cc(JOURNAL_SIZE_MAX <= ENTRY_SIZE_MAX);
73 typedef enum CoredumpStorage {
74 COREDUMP_STORAGE_NONE,
75 COREDUMP_STORAGE_EXTERNAL,
76 COREDUMP_STORAGE_JOURNAL,
77 COREDUMP_STORAGE_BOTH,
78 _COREDUMP_STORAGE_MAX,
79 _COREDUMP_STORAGE_INVALID = -1
82 static CoredumpStorage arg_storage = COREDUMP_STORAGE_EXTERNAL;
83 static off_t arg_process_size_max = PROCESS_SIZE_MAX;
84 static off_t arg_external_size_max = EXTERNAL_SIZE_MAX;
85 static size_t arg_journal_size_max = JOURNAL_SIZE_MAX;
87 static const char* const coredump_storage_table[_COREDUMP_STORAGE_MAX] = {
88 [COREDUMP_STORAGE_NONE] = "none",
89 [COREDUMP_STORAGE_EXTERNAL] = "external",
90 [COREDUMP_STORAGE_JOURNAL] = "journal",
91 [COREDUMP_STORAGE_BOTH] = "both",
94 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_storage, CoredumpStorage);
95 static DEFINE_CONFIG_PARSE_ENUM(config_parse_coredump_storage, coredump_storage, CoredumpStorage, "Failed to parse storage setting");
97 static int parse_config(void) {
99 static const ConfigTableItem items[] = {
100 { "Coredump", "ProcessSizeMax", config_parse_iec_off, 0, &arg_process_size_max },
101 { "Coredump", "ExternalSizeMax", config_parse_iec_off, 0, &arg_external_size_max },
102 { "Coredump", "JournalSizeMax", config_parse_iec_size, 0, &arg_journal_size_max },
103 { "Coredump", "Storage", config_parse_coredump_storage, 0, &arg_storage },
109 "/etc/systemd/coredump.conf",
112 config_item_table_lookup,
119 static int fix_acl(int fd, uid_t uid) {
122 _cleanup_(acl_freep) acl_t acl = NULL;
124 acl_permset_t permset;
126 if (uid <= SYSTEM_UID_MAX)
129 /* Make sure normal users can read (but not write or delete)
130 * their own coredumps */
132 acl = acl_get_fd(fd);
134 log_error("Failed to get ACL: %m");
138 if (acl_create_entry(&acl, &entry) < 0 ||
139 acl_set_tag_type(entry, ACL_USER) < 0 ||
140 acl_set_qualifier(entry, &uid) < 0) {
141 log_error("Failed to patch ACL: %m");
145 if (acl_get_permset(entry, &permset) < 0 ||
146 acl_add_perm(permset, ACL_READ) < 0 ||
147 calc_acl_mask_if_needed(&acl) < 0) {
148 log_warning("Failed to patch ACL: %m");
152 if (acl_set_fd(fd, acl) < 0) {
153 log_error("Failed to apply ACL: %m");
161 static int fix_xattr(int fd, char *argv[]) {
163 static const char * const xattrs[_ARG_MAX] = {
164 [ARG_PID] = "user.coredump.pid",
165 [ARG_UID] = "user.coredump.uid",
166 [ARG_GID] = "user.coredump.gid",
167 [ARG_SIGNAL] = "user.coredump.signal",
168 [ARG_TIMESTAMP] = "user.coredump.timestamp",
169 [ARG_COMM] = "user.coredump.comm",
175 /* Attach some metadate to coredumps via extended
176 * attributes. Just because we can. */
178 for (i = 0; i < _ARG_MAX; i++) {
179 if (isempty(argv[i]))
182 if (fsetxattr(fd, xattrs[i], argv[i], strlen(argv[i]), XATTR_CREATE) < 0)
189 #define filename_escape(s) xescape((s), "./")
191 static int save_external_coredump(char **argv, uid_t uid, char **ret_filename, int *ret_fd, off_t *ret_size) {
192 _cleanup_free_ char *p = NULL, *t = NULL, *c = NULL, *fn = NULL, *tmp = NULL;
193 _cleanup_close_ int fd = -1;
199 assert(ret_filename);
203 c = filename_escape(argv[ARG_COMM]);
207 p = filename_escape(argv[ARG_PID]);
211 t = filename_escape(argv[ARG_TIMESTAMP]);
215 r = sd_id128_get_boot(&boot);
217 log_error("Failed to determine boot ID: %s", strerror(-r));
222 "/var/lib/systemd/coredump/core.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
224 SD_ID128_FORMAT_VAL(boot),
230 tmp = tempfn_random(fn);
234 mkdir_p_label("/var/lib/systemd/coredump", 0755);
236 fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
238 log_error("Failed to create coredump file: %m");
242 r = copy_bytes(STDIN_FILENO, fd);
244 log_error("Failed to dump coredump to file: %s", strerror(-r));
248 /* Ignore errors on these */
254 log_error("Failed to sync coredump: %m");
259 if (fstat(fd, &st) < 0) {
260 log_error("Failed to fstat coredump: %m");
265 if (rename(tmp, fn) < 0) {
266 log_error("Failed to rename coredump: %m");
273 *ret_size = st.st_size;
285 static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
286 _cleanup_free_ char *field = NULL;
293 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
294 log_warning("Failed to seek: %m");
298 field = malloc(9 + size);
300 log_warning("Failed to allocate memory fore coredump, coredump will not be stored.");
304 memcpy(field, "COREDUMP=", 9);
306 n = read(fd, field + 9, size);
308 log_error("Failed to read core data: %s", strerror(-n));
311 if ((size_t) n < size) {
312 log_error("Core data too short.");
317 *ret_size = size + 9;
324 static int maybe_remove_external_coredump(const char *filename, off_t size) {
329 if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
330 size <= arg_external_size_max)
333 if (unlink(filename) < 0) {
334 log_error("Failed to unlink %s: %m", filename);
341 int main(int argc, char* argv[]) {
343 _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
344 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
345 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
346 *coredump_filename = NULL, *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL,
349 _cleanup_close_ int coredump_fd = -1;
351 struct iovec iovec[17];
354 uid_t uid, owner_uid;
359 /* Make sure we never enter a loop */
360 prctl(PR_SET_DUMPABLE, 0);
362 /* First, log to a safe place, since we don't know what
363 * crashed and it might be journald which we'd rather not log
365 log_set_target(LOG_TARGET_KMSG);
368 if (argc != _ARG_MAX) {
369 log_error("Invalid number of arguments passed from kernel.");
374 /* Ignore all parse errors */
376 log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
378 r = parse_uid(argv[ARG_UID], &uid);
380 log_error("Failed to parse UID.");
384 r = parse_pid(argv[ARG_PID], &pid);
386 log_error("Failed to parse PID.");
390 r = parse_gid(argv[ARG_GID], &gid);
392 log_error("Failed to parse GID.");
396 if (cg_pid_get_unit(pid, &t) >= 0) {
398 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
400 /* If we are journald, we cut things short,
401 * don't write to the journal, but still
402 * create a coredump. */
404 if (arg_storage != COREDUMP_STORAGE_NONE)
405 arg_storage = COREDUMP_STORAGE_EXTERNAL;
407 r = save_external_coredump(argv, uid, &coredump_filename, &coredump_fd, &coredump_size);
411 r = maybe_remove_external_coredump(coredump_filename, coredump_size);
415 log_info("Detected coredump of the journal daemon itself, diverted to %s.", coredump_filename);
419 core_unit = strappend("COREDUMP_UNIT=", t);
420 } else if (cg_pid_get_user_unit(pid, &t) >= 0)
421 core_unit = strappend("COREDUMP_USER_UNIT=", t);
424 IOVEC_SET_STRING(iovec[j++], core_unit);
426 /* OK, now we know it's not the journal, hence we can make use
428 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
431 core_pid = strappend("COREDUMP_PID=", argv[ARG_PID]);
433 IOVEC_SET_STRING(iovec[j++], core_pid);
435 core_uid = strappend("COREDUMP_UID=", argv[ARG_UID]);
437 IOVEC_SET_STRING(iovec[j++], core_uid);
439 core_gid = strappend("COREDUMP_GID=", argv[ARG_GID]);
441 IOVEC_SET_STRING(iovec[j++], core_gid);
443 core_signal = strappend("COREDUMP_SIGNAL=", argv[ARG_SIGNAL]);
445 IOVEC_SET_STRING(iovec[j++], core_signal);
447 core_comm = strappend("COREDUMP_COMM=", argv[ARG_COMM]);
449 IOVEC_SET_STRING(iovec[j++], core_comm);
451 if (sd_pid_get_session(pid, &t) >= 0) {
452 core_session = strappend("COREDUMP_SESSION=", t);
456 IOVEC_SET_STRING(iovec[j++], core_session);
459 if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
460 asprintf(&core_owner_uid, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
463 IOVEC_SET_STRING(iovec[j++], core_owner_uid);
466 if (sd_pid_get_slice(pid, &t) >= 0) {
467 core_slice = strappend("COREDUMP_SLICE=", t);
471 IOVEC_SET_STRING(iovec[j++], core_slice);
474 if (get_process_exe(pid, &exe) >= 0) {
475 core_exe = strappend("COREDUMP_EXE=", exe);
477 IOVEC_SET_STRING(iovec[j++], core_exe);
480 if (get_process_cmdline(pid, 0, false, &t) >= 0) {
481 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
485 IOVEC_SET_STRING(iovec[j++], core_cmdline);
488 if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
489 core_cgroup = strappend("COREDUMP_CGROUP=", t);
493 IOVEC_SET_STRING(iovec[j++], core_cgroup);
496 core_timestamp = strjoin("COREDUMP_TIMESTAMP=", argv[ARG_TIMESTAMP], "000000", NULL);
498 IOVEC_SET_STRING(iovec[j++], core_timestamp);
500 IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
501 IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
503 /* Always stream the coredump to disk, if that's possible */
504 r = save_external_coredump(argv, uid, &coredump_filename, &coredump_fd, &coredump_size);
508 /* If we don't want to keep the coredump on disk, remove it
509 * now, as later on we will lack the privileges for
510 * it. However, we keep the fd to it, so that we can still
511 * process it and log it. */
512 r = maybe_remove_external_coredump(coredump_filename, coredump_size);
516 /* Now, let's drop privileges to become the user who owns the
517 * segfaulted process and allocate the coredump memory under
518 * his uid. This also ensures that the credentials journald
519 * will see are the ones of the coredumping user, thus making
520 * sure the user himself gets access to the core dump. */
521 if (setresgid(gid, gid, gid) < 0 ||
522 setresuid(uid, uid, uid) < 0) {
523 log_error("Failed to drop privileges: %m");
529 /* Try to get a strack trace if we can */
530 if (coredump_size <= arg_process_size_max) {
531 _cleanup_free_ char *stacktrace = NULL;
533 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
535 core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") of user ", argv[ARG_UID], " dumped core.\n\n", stacktrace, NULL);
537 log_warning("Failed to generate stack trace: %s", strerror(-r));
542 core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") of user ", argv[ARG_UID], " dumped core.", NULL);
544 IOVEC_SET_STRING(iovec[j++], core_message);
546 /* Optionally store the entire coredump in the journal */
547 if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
548 coredump_size <= (off_t) arg_journal_size_max) {
551 /* Store the coredump itself in the journal */
553 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
555 iovec[j].iov_base = coredump_data;
556 iovec[j].iov_len = sz;
561 r = sd_journal_sendv(iovec, j);
563 log_error("Failed to log coredump: %s", strerror(-r));
566 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;