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>
38 #include "cgroup-util.h"
39 #include "journald-native.h"
40 #include "conf-parser.h"
42 #include "stacktrace.h"
43 #include "path-util.h"
45 #include "coredump-vacuum.h"
49 # include "acl-util.h"
52 /* The maximum size up to which we process coredumps */
53 #define PROCESS_SIZE_MAX ((off_t) (2LLU*1024LLU*1024LLU*1024LLU))
55 /* The maximum size up to which we leave the coredump around on
57 #define EXTERNAL_SIZE_MAX PROCESS_SIZE_MAX
59 /* The maximum size up to which we store the coredump in the
61 #define JOURNAL_SIZE_MAX ((size_t) (767LU*1024LU*1024LU))
63 /* Make sure to not make this larger than the maximum journal entry
64 * size. See DATA_SIZE_MAX in journald-native.c. */
65 assert_cc(JOURNAL_SIZE_MAX <= DATA_SIZE_MAX);
78 typedef enum CoredumpStorage {
79 COREDUMP_STORAGE_NONE,
80 COREDUMP_STORAGE_EXTERNAL,
81 COREDUMP_STORAGE_JOURNAL,
82 COREDUMP_STORAGE_BOTH,
83 _COREDUMP_STORAGE_MAX,
84 _COREDUMP_STORAGE_INVALID = -1
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 CoredumpStorage arg_storage = COREDUMP_STORAGE_EXTERNAL;
98 static bool arg_compress = true;
99 static off_t arg_process_size_max = PROCESS_SIZE_MAX;
100 static off_t arg_external_size_max = EXTERNAL_SIZE_MAX;
101 static size_t arg_journal_size_max = JOURNAL_SIZE_MAX;
102 static off_t arg_keep_free = (off_t) -1;
103 static off_t arg_max_use = (off_t) -1;
105 static int parse_config(void) {
106 static const ConfigTableItem items[] = {
107 { "Coredump", "Storage", config_parse_coredump_storage, 0, &arg_storage },
108 { "Coredump", "Compress", config_parse_bool, 0, &arg_compress },
109 { "Coredump", "ProcessSizeMax", config_parse_iec_off, 0, &arg_process_size_max },
110 { "Coredump", "ExternalSizeMax", config_parse_iec_off, 0, &arg_external_size_max },
111 { "Coredump", "JournalSizeMax", config_parse_iec_size, 0, &arg_journal_size_max },
112 { "Coredump", "KeepFree", config_parse_iec_off, 0, &arg_keep_free },
113 { "Coredump", "MaxUse", config_parse_iec_off, 0, &arg_max_use },
117 return config_parse(NULL, "/etc/systemd/coredump.conf", NULL,
119 config_item_table_lookup, items,
120 false, false, true, NULL);
123 static int fix_acl(int fd, uid_t uid) {
126 _cleanup_(acl_freep) acl_t acl = NULL;
128 acl_permset_t permset;
132 if (uid <= SYSTEM_UID_MAX)
135 /* Make sure normal users can read (but not write or delete)
136 * their own coredumps */
138 acl = acl_get_fd(fd);
140 log_error("Failed to get ACL: %m");
144 if (acl_create_entry(&acl, &entry) < 0 ||
145 acl_set_tag_type(entry, ACL_USER) < 0 ||
146 acl_set_qualifier(entry, &uid) < 0) {
147 log_error("Failed to patch ACL: %m");
151 if (acl_get_permset(entry, &permset) < 0 ||
152 acl_add_perm(permset, ACL_READ) < 0 ||
153 calc_acl_mask_if_needed(&acl) < 0) {
154 log_warning("Failed to patch ACL: %m");
158 if (acl_set_fd(fd, acl) < 0) {
159 log_error("Failed to apply ACL: %m");
167 static int fix_xattr(int fd, const char *info[_INFO_LEN]) {
169 static const char * const xattrs[_INFO_LEN] = {
170 [INFO_PID] = "user.coredump.pid",
171 [INFO_UID] = "user.coredump.uid",
172 [INFO_GID] = "user.coredump.gid",
173 [INFO_SIGNAL] = "user.coredump.signal",
174 [INFO_TIMESTAMP] = "user.coredump.timestamp",
175 [INFO_COMM] = "user.coredump.comm",
176 [INFO_EXE] = "user.coredump.exe",
184 /* Attach some metadata to coredumps via extended
185 * attributes. Just because we can. */
187 for (i = 0; i < _INFO_LEN; i++) {
190 if (isempty(info[i]) || !xattrs[i])
193 k = fsetxattr(fd, xattrs[i], info[i], strlen(info[i]), XATTR_CREATE);
201 #define filename_escape(s) xescape((s), "./ ")
203 static int fix_permissions(
205 const char *filename,
207 const char *info[_INFO_LEN],
215 /* Ignore errors on these */
221 log_error("Failed to sync coredump %s: %m", filename);
225 if (rename(filename, target) < 0) {
226 log_error("Failed to rename coredump %s -> %s: %m", filename, target);
233 static int maybe_remove_external_coredump(const char *filename, off_t size) {
235 /* Returns 1 if might remove, 0 if will not remove, < 0 on error. */
237 if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
238 size <= arg_external_size_max)
244 if (unlink(filename) < 0 && errno != ENOENT) {
245 log_error("Failed to unlink %s: %m", filename);
252 static int make_filename(const char *info[_INFO_LEN], char **ret) {
253 _cleanup_free_ char *c = NULL, *u = NULL, *p = NULL, *t = NULL;
259 c = filename_escape(info[INFO_COMM]);
263 u = filename_escape(info[INFO_UID]);
267 r = sd_id128_get_boot(&boot);
271 p = filename_escape(info[INFO_PID]);
275 t = filename_escape(info[INFO_TIMESTAMP]);
280 "/var/lib/systemd/coredump/core.%s.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
283 SD_ID128_FORMAT_VAL(boot),
291 static int save_external_coredump(
292 const char *info[_INFO_LEN],
298 _cleanup_free_ char *fn = NULL, *tmp = NULL;
299 _cleanup_close_ int fd = -1;
304 assert(ret_filename);
308 r = make_filename(info, &fn);
310 log_error("Failed to determine coredump file name: %s", strerror(-r));
314 tmp = tempfn_random(fn);
318 mkdir_p_label("/var/lib/systemd/coredump", 0755);
320 fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
322 log_error("Failed to create coredump file %s: %m", tmp);
326 r = copy_bytes(STDIN_FILENO, fd, arg_process_size_max);
328 log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", info[INFO_PID], info[INFO_COMM]);
330 } else if (IN_SET(r, -EDQUOT, -ENOSPC)) {
331 log_error("Not enough disk space for coredump of %s (%s), refusing.", info[INFO_PID], info[INFO_COMM]);
334 log_error("Failed to dump coredump to file: %s", strerror(-r));
338 if (fstat(fd, &st) < 0) {
339 log_error("Failed to fstat coredump %s: %m", tmp);
343 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
344 log_error("Failed to seek on %s: %m", tmp);
348 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
349 /* If we will remove the coredump anyway, do not compress. */
350 if (maybe_remove_external_coredump(NULL, st.st_size) == 0
353 _cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL;
354 _cleanup_close_ int fd_compressed = -1;
356 fn_compressed = strappend(fn, COMPRESSED_EXT);
357 if (!fn_compressed) {
362 tmp_compressed = tempfn_random(fn_compressed);
363 if (!tmp_compressed) {
368 fd_compressed = open(tmp_compressed, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
369 if (fd_compressed < 0) {
370 log_error("Failed to create file %s: %m", tmp_compressed);
374 r = compress_stream(fd, fd_compressed, -1);
376 log_error("Failed to compress %s: %s", tmp_compressed, strerror(-r));
377 goto fail_compressed;
380 r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, info, uid);
382 goto fail_compressed;
384 /* OK, this worked, we can get rid of the uncompressed version now */
387 *ret_filename = fn_compressed; /* compressed */
388 *ret_fd = fd; /* uncompressed */
389 *ret_size = st.st_size; /* uncompressed */
391 fn_compressed = NULL;
397 unlink_noerrno(tmp_compressed);
402 r = fix_permissions(fd, tmp, fn, info, uid);
408 *ret_size = st.st_size;
420 static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
421 _cleanup_free_ char *field = NULL;
428 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
429 log_warning("Failed to seek: %m");
433 field = malloc(9 + size);
435 log_warning("Failed to allocate memory for coredump, coredump will not be stored.");
439 memcpy(field, "COREDUMP=", 9);
441 n = read(fd, field + 9, size);
443 log_error("Failed to read core data: %s", strerror(-n));
446 if ((size_t) n < size) {
447 log_error("Core data too short.");
452 *ret_size = size + 9;
459 int main(int argc, char* argv[]) {
461 _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
462 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
463 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
464 *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL,
465 *exe = NULL, *comm = NULL, *filename = NULL;
466 const char *info[_INFO_LEN];
468 _cleanup_close_ int coredump_fd = -1;
470 struct iovec iovec[18];
473 uid_t uid, owner_uid;
478 /* Make sure we never enter a loop */
479 prctl(PR_SET_DUMPABLE, 0);
481 /* First, log to a safe place, since we don't know what
482 * crashed and it might be journald which we'd rather not log
484 log_set_target(LOG_TARGET_KMSG);
487 if (argc < INFO_COMM + 1) {
488 log_error("Not enough arguments passed from kernel (%d, expected %d).",
489 argc - 1, INFO_COMM + 1 - 1);
494 /* Ignore all parse errors */
497 log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
498 log_debug("Selected compression %s.", yes_no(arg_compress));
500 r = parse_uid(argv[INFO_UID + 1], &uid);
502 log_error("Failed to parse UID.");
506 r = parse_pid(argv[INFO_PID + 1], &pid);
508 log_error("Failed to parse PID.");
512 r = parse_gid(argv[INFO_GID + 1], &gid);
514 log_error("Failed to parse GID.");
518 if (get_process_comm(pid, &comm) < 0) {
519 log_warning("Failed to get COMM, falling back to the commandline.");
520 comm = strv_join(argv + INFO_COMM + 1, " ");
523 if (get_process_exe(pid, &exe) < 0)
524 log_warning("Failed to get EXE.");
526 info[INFO_PID] = argv[INFO_PID + 1];
527 info[INFO_UID] = argv[INFO_UID + 1];
528 info[INFO_GID] = argv[INFO_GID + 1];
529 info[INFO_SIGNAL] = argv[INFO_SIGNAL + 1];
530 info[INFO_TIMESTAMP] = argv[INFO_TIMESTAMP + 1];
531 info[INFO_COMM] = comm;
532 info[INFO_EXE] = exe;
534 if (cg_pid_get_unit(pid, &t) >= 0) {
536 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
538 /* If we are journald, we cut things short,
539 * don't write to the journal, but still
540 * create a coredump. */
542 if (arg_storage != COREDUMP_STORAGE_NONE)
543 arg_storage = COREDUMP_STORAGE_EXTERNAL;
545 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
549 r = maybe_remove_external_coredump(filename, coredump_size);
553 log_info("Detected coredump of the journal daemon itself, diverted to %s.", filename);
557 core_unit = strappend("COREDUMP_UNIT=", t);
558 } else if (cg_pid_get_user_unit(pid, &t) >= 0)
559 core_unit = strappend("COREDUMP_USER_UNIT=", t);
562 IOVEC_SET_STRING(iovec[j++], core_unit);
564 /* OK, now we know it's not the journal, hence we can make use
566 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
569 core_pid = strappend("COREDUMP_PID=", info[INFO_PID]);
571 IOVEC_SET_STRING(iovec[j++], core_pid);
573 core_uid = strappend("COREDUMP_UID=", info[INFO_UID]);
575 IOVEC_SET_STRING(iovec[j++], core_uid);
577 core_gid = strappend("COREDUMP_GID=", info[INFO_GID]);
579 IOVEC_SET_STRING(iovec[j++], core_gid);
581 core_signal = strappend("COREDUMP_SIGNAL=", info[INFO_SIGNAL]);
583 IOVEC_SET_STRING(iovec[j++], core_signal);
585 if (sd_pid_get_session(pid, &t) >= 0) {
586 core_session = strappend("COREDUMP_SESSION=", t);
590 IOVEC_SET_STRING(iovec[j++], core_session);
593 if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
594 asprintf(&core_owner_uid, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
597 IOVEC_SET_STRING(iovec[j++], core_owner_uid);
600 if (sd_pid_get_slice(pid, &t) >= 0) {
601 core_slice = strappend("COREDUMP_SLICE=", t);
605 IOVEC_SET_STRING(iovec[j++], core_slice);
609 core_comm = strappend("COREDUMP_COMM=", comm);
611 IOVEC_SET_STRING(iovec[j++], core_comm);
615 core_exe = strappend("COREDUMP_EXE=", exe);
617 IOVEC_SET_STRING(iovec[j++], core_exe);
620 if (get_process_cmdline(pid, 0, false, &t) >= 0) {
621 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
625 IOVEC_SET_STRING(iovec[j++], core_cmdline);
628 if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
629 core_cgroup = strappend("COREDUMP_CGROUP=", t);
633 IOVEC_SET_STRING(iovec[j++], core_cgroup);
636 core_timestamp = strjoin("COREDUMP_TIMESTAMP=", info[INFO_TIMESTAMP], "000000", NULL);
638 IOVEC_SET_STRING(iovec[j++], core_timestamp);
640 IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
641 IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
643 /* Vacuum before we write anything again */
644 coredump_vacuum(-1, arg_keep_free, arg_max_use);
646 /* Always stream the coredump to disk, if that's possible */
647 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
649 /* skip whole core dumping part */
652 /* If we don't want to keep the coredump on disk, remove it
653 * now, as later on we will lack the privileges for
654 * it. However, we keep the fd to it, so that we can still
655 * process it and log it. */
656 r = maybe_remove_external_coredump(filename, coredump_size);
660 const char *coredump_filename;
662 coredump_filename = strappenda("COREDUMP_FILENAME=", filename);
663 IOVEC_SET_STRING(iovec[j++], coredump_filename);
666 /* Vacuum again, but exclude the coredump we just created */
667 coredump_vacuum(coredump_fd, arg_keep_free, arg_max_use);
669 /* Now, let's drop privileges to become the user who owns the
670 * segfaulted process and allocate the coredump memory under
671 * his uid. This also ensures that the credentials journald
672 * will see are the ones of the coredumping user, thus making
673 * sure the user himself gets access to the core dump. */
674 if (setresgid(gid, gid, gid) < 0 ||
675 setresuid(uid, uid, uid) < 0) {
676 log_error("Failed to drop privileges: %m");
682 /* Try to get a strack trace if we can */
683 if (coredump_size <= arg_process_size_max) {
684 _cleanup_free_ char *stacktrace = NULL;
686 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
688 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.\n\n", stacktrace, NULL);
690 log_warning("Failed to generate stack trace: %s", strerror(-r));
696 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.", NULL);
698 IOVEC_SET_STRING(iovec[j++], core_message);
700 /* Optionally store the entire coredump in the journal */
701 if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
702 coredump_size <= (off_t) arg_journal_size_max) {
705 /* Store the coredump itself in the journal */
707 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
709 iovec[j].iov_base = coredump_data;
710 iovec[j].iov_len = sz;
715 r = sd_journal_sendv(iovec, j);
717 log_error("Failed to log coredump: %s", strerror(-r));
720 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;