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>
31 # include <elfutils/libdwfl.h>
34 #include "systemd/sd-journal.h"
35 #include "systemd/sd-login.h"
43 #include "cgroup-util.h"
44 #include "journald-native.h"
45 #include "conf-parser.h"
47 #include "stacktrace.h"
48 #include "path-util.h"
50 #include "coredump-vacuum.h"
54 # include "acl-util.h"
57 /* The maximum size up to which we process coredumps */
58 #define PROCESS_SIZE_MAX ((off_t) (2LLU*1024LLU*1024LLU*1024LLU))
60 /* The maximum size up to which we leave the coredump around on
62 #define EXTERNAL_SIZE_MAX PROCESS_SIZE_MAX
64 /* The maximum size up to which we store the coredump in the
66 #define JOURNAL_SIZE_MAX ((size_t) (767LU*1024LU*1024LU))
68 /* Make sure to not make this larger than the maximum journal entry
69 * size. See DATA_SIZE_MAX in journald-native.c. */
70 assert_cc(JOURNAL_SIZE_MAX <= DATA_SIZE_MAX);
83 typedef enum CoredumpStorage {
84 COREDUMP_STORAGE_NONE,
85 COREDUMP_STORAGE_EXTERNAL,
86 COREDUMP_STORAGE_JOURNAL,
87 COREDUMP_STORAGE_BOTH,
88 _COREDUMP_STORAGE_MAX,
89 _COREDUMP_STORAGE_INVALID = -1
92 static const char* const coredump_storage_table[_COREDUMP_STORAGE_MAX] = {
93 [COREDUMP_STORAGE_NONE] = "none",
94 [COREDUMP_STORAGE_EXTERNAL] = "external",
95 [COREDUMP_STORAGE_JOURNAL] = "journal",
96 [COREDUMP_STORAGE_BOTH] = "both",
99 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_storage, CoredumpStorage);
100 static DEFINE_CONFIG_PARSE_ENUM(config_parse_coredump_storage, coredump_storage, CoredumpStorage, "Failed to parse storage setting");
102 static CoredumpStorage arg_storage = COREDUMP_STORAGE_EXTERNAL;
103 static bool arg_compress = true;
104 static off_t arg_process_size_max = PROCESS_SIZE_MAX;
105 static off_t arg_external_size_max = EXTERNAL_SIZE_MAX;
106 static size_t arg_journal_size_max = JOURNAL_SIZE_MAX;
107 static off_t arg_keep_free = (off_t) -1;
108 static off_t arg_max_use = (off_t) -1;
110 static int parse_config(void) {
111 static const ConfigTableItem items[] = {
112 { "Coredump", "Storage", config_parse_coredump_storage, 0, &arg_storage },
113 { "Coredump", "Compress", config_parse_bool, 0, &arg_compress },
114 { "Coredump", "ProcessSizeMax", config_parse_iec_off, 0, &arg_process_size_max },
115 { "Coredump", "ExternalSizeMax", config_parse_iec_off, 0, &arg_external_size_max },
116 { "Coredump", "JournalSizeMax", config_parse_iec_size, 0, &arg_journal_size_max },
117 { "Coredump", "KeepFree", config_parse_iec_off, 0, &arg_keep_free },
118 { "Coredump", "MaxUse", config_parse_iec_off, 0, &arg_max_use },
122 return config_parse(NULL, "/etc/systemd/coredump.conf", NULL,
124 config_item_table_lookup, items,
125 false, false, true, NULL);
128 static int fix_acl(int fd, uid_t uid) {
131 _cleanup_(acl_freep) acl_t acl = NULL;
133 acl_permset_t permset;
137 if (uid <= SYSTEM_UID_MAX)
140 /* Make sure normal users can read (but not write or delete)
141 * their own coredumps */
143 acl = acl_get_fd(fd);
145 log_error("Failed to get ACL: %m");
149 if (acl_create_entry(&acl, &entry) < 0 ||
150 acl_set_tag_type(entry, ACL_USER) < 0 ||
151 acl_set_qualifier(entry, &uid) < 0) {
152 log_error("Failed to patch ACL: %m");
156 if (acl_get_permset(entry, &permset) < 0 ||
157 acl_add_perm(permset, ACL_READ) < 0 ||
158 calc_acl_mask_if_needed(&acl) < 0) {
159 log_warning("Failed to patch ACL: %m");
163 if (acl_set_fd(fd, acl) < 0) {
164 log_error("Failed to apply ACL: %m");
172 static int fix_xattr(int fd, const char *info[_INFO_LEN]) {
174 static const char * const xattrs[_INFO_LEN] = {
175 [INFO_PID] = "user.coredump.pid",
176 [INFO_UID] = "user.coredump.uid",
177 [INFO_GID] = "user.coredump.gid",
178 [INFO_SIGNAL] = "user.coredump.signal",
179 [INFO_TIMESTAMP] = "user.coredump.timestamp",
180 [INFO_COMM] = "user.coredump.comm",
181 [INFO_EXE] = "user.coredump.exe",
189 /* Attach some metadata to coredumps via extended
190 * attributes. Just because we can. */
192 for (i = 0; i < _INFO_LEN; i++) {
195 if (isempty(info[i]) || !xattrs[i])
198 k = fsetxattr(fd, xattrs[i], info[i], strlen(info[i]), XATTR_CREATE);
206 #define filename_escape(s) xescape((s), "./ ")
208 static int fix_permissions(
210 const char *filename,
212 const char *info[_INFO_LEN],
220 /* Ignore errors on these */
226 log_error("Failed to sync coredump %s: %m", filename);
230 if (rename(filename, target) < 0) {
231 log_error("Failed to rename coredump %s -> %s: %m", filename, target);
238 static int maybe_remove_external_coredump(const char *filename, off_t size) {
240 /* Returns 1 if might remove, 0 if will not remove, < 0 on error. */
242 if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
243 size <= arg_external_size_max)
249 if (unlink(filename) < 0 && errno != ENOENT) {
250 log_error("Failed to unlink %s: %m", filename);
257 static int make_filename(const char *info[_INFO_LEN], char **ret) {
258 _cleanup_free_ char *c = NULL, *u = NULL, *p = NULL, *t = NULL;
264 c = filename_escape(info[INFO_COMM]);
268 u = filename_escape(info[INFO_UID]);
272 r = sd_id128_get_boot(&boot);
276 p = filename_escape(info[INFO_PID]);
280 t = filename_escape(info[INFO_TIMESTAMP]);
285 "/var/lib/systemd/coredump/core.%s.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
288 SD_ID128_FORMAT_VAL(boot),
296 static int save_external_coredump(
297 const char *info[_INFO_LEN],
303 _cleanup_free_ char *fn = NULL, *tmp = NULL;
304 _cleanup_close_ int fd = -1;
309 assert(ret_filename);
313 r = make_filename(info, &fn);
315 log_error("Failed to determine coredump file name: %s", strerror(-r));
319 tmp = tempfn_random(fn);
323 mkdir_p_label("/var/lib/systemd/coredump", 0755);
325 fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
327 log_error("Failed to create coredump file %s: %m", tmp);
331 r = copy_bytes(STDIN_FILENO, fd, arg_process_size_max);
333 log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", info[INFO_PID], info[INFO_COMM]);
335 } else if (IN_SET(r, -EDQUOT, -ENOSPC)) {
336 log_error("Not enough disk space for coredump of %s (%s), refusing.", info[INFO_PID], info[INFO_COMM]);
339 log_error("Failed to dump coredump to file: %s", strerror(-r));
343 if (fstat(fd, &st) < 0) {
344 log_error("Failed to fstat coredump %s: %m", tmp);
348 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
349 log_error("Failed to seek on %s: %m", tmp);
353 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
354 /* If we will remove the coredump anyway, do not compress. */
355 if (maybe_remove_external_coredump(NULL, st.st_size) == 0
358 _cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL;
359 _cleanup_close_ int fd_compressed = -1;
361 fn_compressed = strappend(fn, COMPRESSED_EXT);
362 if (!fn_compressed) {
367 tmp_compressed = tempfn_random(fn_compressed);
368 if (!tmp_compressed) {
373 fd_compressed = open(tmp_compressed, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
374 if (fd_compressed < 0) {
375 log_error("Failed to create file %s: %m", tmp_compressed);
379 r = compress_stream(fd, fd_compressed, -1);
381 log_error("Failed to compress %s: %s", tmp_compressed, strerror(-r));
382 goto fail_compressed;
385 r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, info, uid);
387 goto fail_compressed;
389 /* OK, this worked, we can get rid of the uncompressed version now */
392 *ret_filename = fn_compressed; /* compressed */
393 *ret_fd = fd; /* uncompressed */
394 *ret_size = st.st_size; /* uncompressed */
396 fn_compressed = NULL;
402 unlink_noerrno(tmp_compressed);
407 r = fix_permissions(fd, tmp, fn, info, uid);
413 *ret_size = st.st_size;
425 static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
426 _cleanup_free_ char *field = NULL;
433 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
434 log_warning("Failed to seek: %m");
438 field = malloc(9 + size);
440 log_warning("Failed to allocate memory for coredump, coredump will not be stored.");
444 memcpy(field, "COREDUMP=", 9);
446 n = read(fd, field + 9, size);
448 log_error("Failed to read core data: %s", strerror(-n));
451 if ((size_t) n < size) {
452 log_error("Core data too short.");
457 *ret_size = size + 9;
464 int main(int argc, char* argv[]) {
466 _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
467 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
468 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
469 *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL,
470 *exe = NULL, *comm = NULL, *filename = NULL;
471 const char *info[_INFO_LEN];
473 _cleanup_close_ int coredump_fd = -1;
475 struct iovec iovec[18];
478 uid_t uid, owner_uid;
483 /* Make sure we never enter a loop */
484 prctl(PR_SET_DUMPABLE, 0);
486 /* First, log to a safe place, since we don't know what
487 * crashed and it might be journald which we'd rather not log
489 log_set_target(LOG_TARGET_KMSG);
492 if (argc < INFO_COMM + 1) {
493 log_error("Not enough arguments passed from kernel (%d, expected %d).",
494 argc - 1, INFO_COMM + 1 - 1);
499 /* Ignore all parse errors */
502 log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
503 log_debug("Selected compression %s.", yes_no(arg_compress));
505 r = parse_uid(argv[INFO_UID + 1], &uid);
507 log_error("Failed to parse UID.");
511 r = parse_pid(argv[INFO_PID + 1], &pid);
513 log_error("Failed to parse PID.");
517 r = parse_gid(argv[INFO_GID + 1], &gid);
519 log_error("Failed to parse GID.");
523 if (get_process_comm(pid, &comm) < 0) {
524 log_warning("Failed to get COMM, falling back to the commandline.");
525 comm = strv_join(argv + INFO_COMM + 1, " ");
528 if (get_process_exe(pid, &exe) < 0)
529 log_warning("Failed to get EXE.");
531 info[INFO_PID] = argv[INFO_PID + 1];
532 info[INFO_UID] = argv[INFO_UID + 1];
533 info[INFO_GID] = argv[INFO_GID + 1];
534 info[INFO_SIGNAL] = argv[INFO_SIGNAL + 1];
535 info[INFO_TIMESTAMP] = argv[INFO_TIMESTAMP + 1];
536 info[INFO_COMM] = comm;
537 info[INFO_EXE] = exe;
539 if (cg_pid_get_unit(pid, &t) >= 0) {
541 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
543 /* If we are journald, we cut things short,
544 * don't write to the journal, but still
545 * create a coredump. */
547 if (arg_storage != COREDUMP_STORAGE_NONE)
548 arg_storage = COREDUMP_STORAGE_EXTERNAL;
550 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
554 r = maybe_remove_external_coredump(filename, coredump_size);
558 log_info("Detected coredump of the journal daemon itself, diverted to %s.", filename);
562 core_unit = strappend("COREDUMP_UNIT=", t);
563 } else if (cg_pid_get_user_unit(pid, &t) >= 0)
564 core_unit = strappend("COREDUMP_USER_UNIT=", t);
567 IOVEC_SET_STRING(iovec[j++], core_unit);
569 /* OK, now we know it's not the journal, hence we can make use
571 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
574 core_pid = strappend("COREDUMP_PID=", info[INFO_PID]);
576 IOVEC_SET_STRING(iovec[j++], core_pid);
578 core_uid = strappend("COREDUMP_UID=", info[INFO_UID]);
580 IOVEC_SET_STRING(iovec[j++], core_uid);
582 core_gid = strappend("COREDUMP_GID=", info[INFO_GID]);
584 IOVEC_SET_STRING(iovec[j++], core_gid);
586 core_signal = strappend("COREDUMP_SIGNAL=", info[INFO_SIGNAL]);
588 IOVEC_SET_STRING(iovec[j++], core_signal);
590 if (sd_pid_get_session(pid, &t) >= 0) {
591 core_session = strappend("COREDUMP_SESSION=", t);
595 IOVEC_SET_STRING(iovec[j++], core_session);
598 if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
599 r = asprintf(&core_owner_uid,
600 "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
602 IOVEC_SET_STRING(iovec[j++], core_owner_uid);
605 if (sd_pid_get_slice(pid, &t) >= 0) {
606 core_slice = strappend("COREDUMP_SLICE=", t);
610 IOVEC_SET_STRING(iovec[j++], core_slice);
614 core_comm = strappend("COREDUMP_COMM=", comm);
616 IOVEC_SET_STRING(iovec[j++], core_comm);
620 core_exe = strappend("COREDUMP_EXE=", exe);
622 IOVEC_SET_STRING(iovec[j++], core_exe);
625 if (get_process_cmdline(pid, 0, false, &t) >= 0) {
626 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
630 IOVEC_SET_STRING(iovec[j++], core_cmdline);
633 if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
634 core_cgroup = strappend("COREDUMP_CGROUP=", t);
638 IOVEC_SET_STRING(iovec[j++], core_cgroup);
641 core_timestamp = strjoin("COREDUMP_TIMESTAMP=", info[INFO_TIMESTAMP], "000000", NULL);
643 IOVEC_SET_STRING(iovec[j++], core_timestamp);
645 IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
646 IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
648 /* Vacuum before we write anything again */
649 coredump_vacuum(-1, arg_keep_free, arg_max_use);
651 /* Always stream the coredump to disk, if that's possible */
652 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
654 /* skip whole core dumping part */
657 /* If we don't want to keep the coredump on disk, remove it
658 * now, as later on we will lack the privileges for
659 * it. However, we keep the fd to it, so that we can still
660 * process it and log it. */
661 r = maybe_remove_external_coredump(filename, coredump_size);
665 const char *coredump_filename;
667 coredump_filename = strappenda("COREDUMP_FILENAME=", filename);
668 IOVEC_SET_STRING(iovec[j++], coredump_filename);
671 /* Vacuum again, but exclude the coredump we just created */
672 coredump_vacuum(coredump_fd, arg_keep_free, arg_max_use);
674 /* Now, let's drop privileges to become the user who owns the
675 * segfaulted process and allocate the coredump memory under
676 * the user's uid. This also ensures that the credentials
677 * journald will see are the ones of the coredumping user,
678 * thus making sure the user gets access to the core dump. */
679 if (setresgid(gid, gid, gid) < 0 ||
680 setresuid(uid, uid, uid) < 0) {
681 log_error("Failed to drop privileges: %m");
687 /* Try to get a strack trace if we can */
688 if (coredump_size <= arg_process_size_max) {
689 _cleanup_free_ char *stacktrace = NULL;
691 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
693 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.\n\n", stacktrace, NULL);
694 else if (r == -EINVAL)
695 log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno()));
697 log_warning("Failed to generate stack trace: %s", strerror(-r));
703 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.", NULL);
705 IOVEC_SET_STRING(iovec[j++], core_message);
707 /* Optionally store the entire coredump in the journal */
708 if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
709 coredump_size <= (off_t) arg_journal_size_max) {
712 /* Store the coredump itself in the journal */
714 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
716 iovec[j].iov_base = coredump_data;
717 iovec[j].iov_len = sz;
722 r = sd_journal_sendv(iovec, j);
724 log_error("Failed to log coredump: %s", strerror(-r));
727 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;