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/xattr.h>
30 # include <elfutils/libdwfl.h>
33 #include "sd-journal.h"
42 #include "cgroup-util.h"
43 #include "conf-parser.h"
45 #include "stacktrace.h"
48 #include "capability.h"
49 #include "journald-native.h"
50 #include "coredump-vacuum.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_many("/etc/systemd/coredump.conf",
118 CONF_DIRS_NULSTR("systemd/coredump.conf"),
120 config_item_table_lookup, items,
124 static int fix_acl(int fd, uid_t uid) {
127 _cleanup_(acl_freep) acl_t acl = NULL;
129 acl_permset_t permset;
133 if (uid <= SYSTEM_UID_MAX)
136 /* Make sure normal users can read (but not write or delete)
137 * their own coredumps */
139 acl = acl_get_fd(fd);
141 return log_error_errno(errno, "Failed to get ACL: %m");
143 if (acl_create_entry(&acl, &entry) < 0 ||
144 acl_set_tag_type(entry, ACL_USER) < 0 ||
145 acl_set_qualifier(entry, &uid) < 0) {
146 log_error_errno(errno, "Failed to patch ACL: %m");
150 if (acl_get_permset(entry, &permset) < 0 ||
151 acl_add_perm(permset, ACL_READ) < 0 ||
152 calc_acl_mask_if_needed(&acl) < 0) {
153 log_warning_errno(errno, "Failed to patch ACL: %m");
157 if (acl_set_fd(fd, acl) < 0)
158 return log_error_errno(errno, "Failed to apply ACL: %m");
164 static int fix_xattr(int fd, const char *info[_INFO_LEN]) {
166 static const char * const xattrs[_INFO_LEN] = {
167 [INFO_PID] = "user.coredump.pid",
168 [INFO_UID] = "user.coredump.uid",
169 [INFO_GID] = "user.coredump.gid",
170 [INFO_SIGNAL] = "user.coredump.signal",
171 [INFO_TIMESTAMP] = "user.coredump.timestamp",
172 [INFO_COMM] = "user.coredump.comm",
173 [INFO_EXE] = "user.coredump.exe",
181 /* Attach some metadata to coredumps via extended
182 * attributes. Just because we can. */
184 for (i = 0; i < _INFO_LEN; i++) {
187 if (isempty(info[i]) || !xattrs[i])
190 k = fsetxattr(fd, xattrs[i], info[i], strlen(info[i]), XATTR_CREATE);
198 #define filename_escape(s) xescape((s), "./ ")
200 static int fix_permissions(
202 const char *filename,
204 const char *info[_INFO_LEN],
212 /* Ignore errors on these */
218 return log_error_errno(errno, "Failed to sync coredump %s: %m", filename);
220 if (rename(filename, target) < 0)
221 return log_error_errno(errno, "Failed to rename coredump %s -> %s: %m", filename, target);
226 static int maybe_remove_external_coredump(const char *filename, off_t size) {
228 /* Returns 1 if might remove, 0 if will not remove, < 0 on error. */
230 if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
231 size <= arg_external_size_max)
237 if (unlink(filename) < 0 && errno != ENOENT)
238 return log_error_errno(errno, "Failed to unlink %s: %m", filename);
243 static int make_filename(const char *info[_INFO_LEN], char **ret) {
244 _cleanup_free_ char *c = NULL, *u = NULL, *p = NULL, *t = NULL;
250 c = filename_escape(info[INFO_COMM]);
254 u = filename_escape(info[INFO_UID]);
258 r = sd_id128_get_boot(&boot);
262 p = filename_escape(info[INFO_PID]);
266 t = filename_escape(info[INFO_TIMESTAMP]);
271 "/var/lib/systemd/coredump/core.%s.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
274 SD_ID128_FORMAT_VAL(boot),
282 static int save_external_coredump(
283 const char *info[_INFO_LEN],
289 _cleanup_free_ char *fn = NULL, *tmp = NULL;
290 _cleanup_close_ int fd = -1;
295 assert(ret_filename);
299 r = make_filename(info, &fn);
301 return log_error_errno(r, "Failed to determine coredump file name: %m");
303 r = tempfn_random(fn, &tmp);
305 return log_error_errno(r, "Failed to determine temporary file name: %m");
307 mkdir_p_label("/var/lib/systemd/coredump", 0755);
309 fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
311 return log_error_errno(errno, "Failed to create coredump file %s: %m", tmp);
313 r = copy_bytes(STDIN_FILENO, fd, arg_process_size_max, false);
315 log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", info[INFO_PID], info[INFO_COMM]);
317 } else if (IN_SET(r, -EDQUOT, -ENOSPC)) {
318 log_error("Not enough disk space for coredump of %s (%s), refusing.", info[INFO_PID], info[INFO_COMM]);
321 log_error_errno(r, "Failed to dump coredump to file: %m");
325 if (fstat(fd, &st) < 0) {
326 log_error_errno(errno, "Failed to fstat coredump %s: %m", tmp);
330 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
331 log_error_errno(errno, "Failed to seek on %s: %m", tmp);
335 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
336 /* If we will remove the coredump anyway, do not compress. */
337 if (maybe_remove_external_coredump(NULL, st.st_size) == 0
340 _cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL;
341 _cleanup_close_ int fd_compressed = -1;
343 fn_compressed = strappend(fn, COMPRESSED_EXT);
344 if (!fn_compressed) {
349 r = tempfn_random(fn_compressed, &tmp_compressed);
351 log_error_errno(r, "Failed to determine temporary file name for %s: %m", fn_compressed);
355 fd_compressed = open(tmp_compressed, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
356 if (fd_compressed < 0) {
357 log_error_errno(errno, "Failed to create file %s: %m", tmp_compressed);
361 r = compress_stream(fd, fd_compressed, -1);
363 log_error_errno(r, "Failed to compress %s: %m", tmp_compressed);
364 goto fail_compressed;
367 r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, info, uid);
369 goto fail_compressed;
371 /* OK, this worked, we can get rid of the uncompressed version now */
374 *ret_filename = fn_compressed; /* compressed */
375 *ret_fd = fd; /* uncompressed */
376 *ret_size = st.st_size; /* uncompressed */
378 fn_compressed = NULL;
384 unlink_noerrno(tmp_compressed);
389 r = fix_permissions(fd, tmp, fn, info, uid);
395 *ret_size = st.st_size;
407 static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
408 _cleanup_free_ char *field = NULL;
415 if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
416 return log_warning_errno(errno, "Failed to seek: %m");
418 field = malloc(9 + size);
420 log_warning("Failed to allocate memory for coredump, coredump will not be stored.");
424 memcpy(field, "COREDUMP=", 9);
426 n = read(fd, field + 9, size);
428 return log_error_errno((int) n, "Failed to read core data: %m");
429 if ((size_t) n < size) {
430 log_error("Core data too short.");
435 *ret_size = size + 9;
442 /* Joins /proc/[pid]/fd/ and /proc/[pid]/fdinfo/ into the following lines:
456 static int compose_open_fds(pid_t pid, char **open_fds) {
457 _cleanup_closedir_ DIR *proc_fd_dir = NULL;
458 _cleanup_close_ int proc_fdinfo_fd = -1;
459 _cleanup_free_ char *buffer = NULL;
460 _cleanup_fclose_ FILE *stream = NULL;
461 const char *fddelim = "", *path;
462 struct dirent *dent = NULL;
467 assert(open_fds != NULL);
469 path = procfs_file_alloca(pid, "fd");
470 proc_fd_dir = opendir(path);
474 proc_fdinfo_fd = openat(dirfd(proc_fd_dir), "../fdinfo", O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC|O_PATH);
475 if (proc_fdinfo_fd < 0)
478 stream = open_memstream(&buffer, &size);
482 FOREACH_DIRENT(dent, proc_fd_dir, return -errno) {
483 _cleanup_fclose_ FILE *fdinfo = NULL;
484 _cleanup_free_ char *fdname = NULL;
488 r = readlinkat_malloc(dirfd(proc_fd_dir), dent->d_name, &fdname);
492 fprintf(stream, "%s%s:%s\n", fddelim, dent->d_name, fdname);
495 /* Use the directory entry from /proc/[pid]/fd with /proc/[pid]/fdinfo */
496 fd = openat(proc_fdinfo_fd, dent->d_name, O_NOFOLLOW|O_CLOEXEC|O_RDONLY);
500 fdinfo = fdopen(fd, "re");
501 if (fdinfo == NULL) {
506 FOREACH_LINE(line, fdinfo, break) {
508 if (!endswith(line, "\n"))
526 int main(int argc, char* argv[]) {
528 /* The small core field we allocate on the stack, to keep things simple */
530 *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
531 *core_session = NULL, *core_exe = NULL, *core_comm = NULL, *core_cmdline = NULL,
532 *core_cgroup = NULL, *core_cwd = NULL, *core_root = NULL, *core_unit = NULL,
535 /* The larger ones we allocate on the heap */
537 *core_timestamp = NULL, *core_message = NULL, *coredump_data = NULL, *core_owner_uid = NULL,
538 *core_open_fds = NULL, *core_proc_status = NULL, *core_proc_maps = NULL, *core_proc_limits = NULL,
539 *core_proc_cgroup = NULL, *core_environ = NULL;
541 _cleanup_free_ char *exe = NULL, *comm = NULL, *filename = NULL;
542 const char *info[_INFO_LEN];
544 _cleanup_close_ int coredump_fd = -1;
546 struct iovec iovec[26];
549 uid_t uid, owner_uid;
555 /* Make sure we never enter a loop */
556 prctl(PR_SET_DUMPABLE, 0);
558 /* First, log to a safe place, since we don't know what
559 * crashed and it might be journald which we'd rather not log
561 log_set_target(LOG_TARGET_KMSG);
564 if (argc < INFO_COMM + 1) {
565 log_error("Not enough arguments passed from kernel (%d, expected %d).",
566 argc - 1, INFO_COMM + 1 - 1);
571 /* Ignore all parse errors */
574 log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
575 log_debug("Selected compression %s.", yes_no(arg_compress));
577 r = parse_uid(argv[INFO_UID + 1], &uid);
579 log_error("Failed to parse UID.");
583 r = parse_pid(argv[INFO_PID + 1], &pid);
585 log_error("Failed to parse PID.");
589 r = parse_gid(argv[INFO_GID + 1], &gid);
591 log_error("Failed to parse GID.");
595 if (get_process_comm(pid, &comm) < 0) {
596 log_warning("Failed to get COMM, falling back to the command line.");
597 comm = strv_join(argv + INFO_COMM + 1, " ");
600 if (get_process_exe(pid, &exe) < 0)
601 log_warning("Failed to get EXE.");
603 info[INFO_PID] = argv[INFO_PID + 1];
604 info[INFO_UID] = argv[INFO_UID + 1];
605 info[INFO_GID] = argv[INFO_GID + 1];
606 info[INFO_SIGNAL] = argv[INFO_SIGNAL + 1];
607 info[INFO_TIMESTAMP] = argv[INFO_TIMESTAMP + 1];
608 info[INFO_COMM] = comm;
609 info[INFO_EXE] = exe;
611 if (cg_pid_get_unit(pid, &t) >= 0) {
613 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
616 /* If we are journald, we cut things short,
617 * don't write to the journal, but still
618 * create a coredump. */
620 if (arg_storage != COREDUMP_STORAGE_NONE)
621 arg_storage = COREDUMP_STORAGE_EXTERNAL;
623 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
627 r = maybe_remove_external_coredump(filename, coredump_size);
631 log_info("Detected coredump of the journal daemon itself, diverted to %s.", filename);
635 core_unit = strjoina("COREDUMP_UNIT=", t);
638 } else if (cg_pid_get_user_unit(pid, &t) >= 0) {
639 core_unit = strjoina("COREDUMP_USER_UNIT=", t);
644 IOVEC_SET_STRING(iovec[j++], core_unit);
646 /* OK, now we know it's not the journal, hence we can make use
648 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
651 core_pid = strjoina("COREDUMP_PID=", info[INFO_PID]);
652 IOVEC_SET_STRING(iovec[j++], core_pid);
654 core_uid = strjoina("COREDUMP_UID=", info[INFO_UID]);
655 IOVEC_SET_STRING(iovec[j++], core_uid);
657 core_gid = strjoina("COREDUMP_GID=", info[INFO_GID]);
658 IOVEC_SET_STRING(iovec[j++], core_gid);
660 core_signal = strjoina("COREDUMP_SIGNAL=", info[INFO_SIGNAL]);
661 IOVEC_SET_STRING(iovec[j++], core_signal);
663 if (sd_pid_get_session(pid, &t) >= 0) {
664 core_session = strjoina("COREDUMP_SESSION=", t);
667 IOVEC_SET_STRING(iovec[j++], core_session);
670 if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
671 r = asprintf(&core_owner_uid,
672 "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
674 IOVEC_SET_STRING(iovec[j++], core_owner_uid);
677 if (sd_pid_get_slice(pid, &t) >= 0) {
678 core_slice = strjoina("COREDUMP_SLICE=", t);
681 IOVEC_SET_STRING(iovec[j++], core_slice);
685 core_comm = strjoina("COREDUMP_COMM=", comm);
686 IOVEC_SET_STRING(iovec[j++], core_comm);
690 core_exe = strjoina("COREDUMP_EXE=", exe);
691 IOVEC_SET_STRING(iovec[j++], core_exe);
694 if (get_process_cmdline(pid, 0, false, &t) >= 0) {
695 core_cmdline = strjoina("COREDUMP_CMDLINE=", t);
698 IOVEC_SET_STRING(iovec[j++], core_cmdline);
701 if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
702 core_cgroup = strjoina("COREDUMP_CGROUP=", t);
705 IOVEC_SET_STRING(iovec[j++], core_cgroup);
708 if (compose_open_fds(pid, &t) >= 0) {
709 core_open_fds = strappend("COREDUMP_OPEN_FDS=", t);
713 IOVEC_SET_STRING(iovec[j++], core_open_fds);
716 p = procfs_file_alloca(pid, "status");
717 if (read_full_file(p, &t, NULL) >= 0) {
718 core_proc_status = strappend("COREDUMP_PROC_STATUS=", t);
721 if (core_proc_status)
722 IOVEC_SET_STRING(iovec[j++], core_proc_status);
725 p = procfs_file_alloca(pid, "maps");
726 if (read_full_file(p, &t, NULL) >= 0) {
727 core_proc_maps = strappend("COREDUMP_PROC_MAPS=", t);
731 IOVEC_SET_STRING(iovec[j++], core_proc_maps);
734 p = procfs_file_alloca(pid, "limits");
735 if (read_full_file(p, &t, NULL) >= 0) {
736 core_proc_limits = strappend("COREDUMP_PROC_LIMITS=", t);
739 if (core_proc_limits)
740 IOVEC_SET_STRING(iovec[j++], core_proc_limits);
743 p = procfs_file_alloca(pid, "cgroup");
744 if (read_full_file(p, &t, NULL) >=0) {
745 core_proc_cgroup = strappend("COREDUMP_PROC_CGROUP=", t);
748 if (core_proc_cgroup)
749 IOVEC_SET_STRING(iovec[j++], core_proc_cgroup);
752 if (get_process_cwd(pid, &t) >= 0) {
753 core_cwd = strjoina("COREDUMP_CWD=", t);
756 IOVEC_SET_STRING(iovec[j++], core_cwd);
759 if (get_process_root(pid, &t) >= 0) {
760 core_root = strjoina("COREDUMP_ROOT=", t);
763 IOVEC_SET_STRING(iovec[j++], core_root);
766 if (get_process_environ(pid, &t) >= 0) {
767 core_environ = strappend("COREDUMP_ENVIRON=", t);
771 IOVEC_SET_STRING(iovec[j++], core_environ);
774 core_timestamp = strjoin("COREDUMP_TIMESTAMP=", info[INFO_TIMESTAMP], "000000", NULL);
776 IOVEC_SET_STRING(iovec[j++], core_timestamp);
778 IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
779 IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
781 /* Vacuum before we write anything again */
782 coredump_vacuum(-1, arg_keep_free, arg_max_use);
784 /* Always stream the coredump to disk, if that's possible */
785 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
787 /* skip whole core dumping part */
790 /* If we don't want to keep the coredump on disk, remove it
791 * now, as later on we will lack the privileges for
792 * it. However, we keep the fd to it, so that we can still
793 * process it and log it. */
794 r = maybe_remove_external_coredump(filename, coredump_size);
798 const char *coredump_filename;
800 coredump_filename = strjoina("COREDUMP_FILENAME=", filename);
801 IOVEC_SET_STRING(iovec[j++], coredump_filename);
804 /* Vacuum again, but exclude the coredump we just created */
805 coredump_vacuum(coredump_fd, arg_keep_free, arg_max_use);
807 /* Now, let's drop privileges to become the user who owns the
808 * segfaulted process and allocate the coredump memory under
809 * the user's uid. This also ensures that the credentials
810 * journald will see are the ones of the coredumping user,
811 * thus making sure the user gets access to the core
812 * dump. Let's also get rid of all capabilities, if we run as
813 * root, we won't need them anymore. */
814 r = drop_privileges(uid, gid, 0);
816 log_error_errno(r, "Failed to drop privileges: %m");
821 /* Try to get a strack trace if we can */
822 if (coredump_size <= arg_process_size_max) {
823 _cleanup_free_ char *stacktrace = NULL;
825 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
827 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.\n\n", stacktrace, NULL);
828 else if (r == -EINVAL)
829 log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno()));
831 log_warning_errno(r, "Failed to generate stack trace: %m");
837 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.", NULL);
839 IOVEC_SET_STRING(iovec[j++], core_message);
841 /* Optionally store the entire coredump in the journal */
842 if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
843 coredump_size <= (off_t) arg_journal_size_max) {
846 /* Store the coredump itself in the journal */
848 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
850 iovec[j].iov_base = coredump_data;
851 iovec[j].iov_len = sz;
856 r = sd_journal_sendv(iovec, j);
858 log_error_errno(r, "Failed to log coredump: %m");
861 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;