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 "sd-journal.h"
43 #include "cgroup-util.h"
44 #include "conf-parser.h"
46 #include "stacktrace.h"
47 #include "path-util.h"
50 #include "capability.h"
51 #include "journald-native.h"
52 #include "coredump-vacuum.h"
54 /* The maximum size up to which we process coredumps */
55 #define PROCESS_SIZE_MAX ((off_t) (2LLU*1024LLU*1024LLU*1024LLU))
57 /* The maximum size up to which we leave the coredump around on
59 #define EXTERNAL_SIZE_MAX PROCESS_SIZE_MAX
61 /* The maximum size up to which we store the coredump in the
63 #define JOURNAL_SIZE_MAX ((size_t) (767LU*1024LU*1024LU))
65 /* Make sure to not make this larger than the maximum journal entry
66 * size. See DATA_SIZE_MAX in journald-native.c. */
67 assert_cc(JOURNAL_SIZE_MAX <= DATA_SIZE_MAX);
80 typedef enum CoredumpStorage {
81 COREDUMP_STORAGE_NONE,
82 COREDUMP_STORAGE_EXTERNAL,
83 COREDUMP_STORAGE_JOURNAL,
84 COREDUMP_STORAGE_BOTH,
85 _COREDUMP_STORAGE_MAX,
86 _COREDUMP_STORAGE_INVALID = -1
89 static const char* const coredump_storage_table[_COREDUMP_STORAGE_MAX] = {
90 [COREDUMP_STORAGE_NONE] = "none",
91 [COREDUMP_STORAGE_EXTERNAL] = "external",
92 [COREDUMP_STORAGE_JOURNAL] = "journal",
93 [COREDUMP_STORAGE_BOTH] = "both",
96 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_storage, CoredumpStorage);
97 static DEFINE_CONFIG_PARSE_ENUM(config_parse_coredump_storage, coredump_storage, CoredumpStorage, "Failed to parse storage setting");
99 static CoredumpStorage arg_storage = COREDUMP_STORAGE_EXTERNAL;
100 static bool arg_compress = true;
101 static off_t arg_process_size_max = PROCESS_SIZE_MAX;
102 static off_t arg_external_size_max = EXTERNAL_SIZE_MAX;
103 static size_t arg_journal_size_max = JOURNAL_SIZE_MAX;
104 static off_t arg_keep_free = (off_t) -1;
105 static off_t arg_max_use = (off_t) -1;
107 static int parse_config(void) {
108 static const ConfigTableItem items[] = {
109 { "Coredump", "Storage", config_parse_coredump_storage, 0, &arg_storage },
110 { "Coredump", "Compress", config_parse_bool, 0, &arg_compress },
111 { "Coredump", "ProcessSizeMax", config_parse_iec_off, 0, &arg_process_size_max },
112 { "Coredump", "ExternalSizeMax", config_parse_iec_off, 0, &arg_external_size_max },
113 { "Coredump", "JournalSizeMax", config_parse_iec_size, 0, &arg_journal_size_max },
114 { "Coredump", "KeepFree", config_parse_iec_off, 0, &arg_keep_free },
115 { "Coredump", "MaxUse", config_parse_iec_off, 0, &arg_max_use },
119 return config_parse_many("/etc/systemd/coredump.conf",
120 CONF_DIRS_NULSTR("systemd/coredump.conf"),
122 config_item_table_lookup, items,
126 static int fix_acl(int fd, uid_t uid) {
129 _cleanup_(acl_freep) acl_t acl = NULL;
131 acl_permset_t permset;
135 if (uid <= SYSTEM_UID_MAX)
138 /* Make sure normal users can read (but not write or delete)
139 * their own coredumps */
141 acl = acl_get_fd(fd);
143 return log_error_errno(errno, "Failed to get ACL: %m");
145 if (acl_create_entry(&acl, &entry) < 0 ||
146 acl_set_tag_type(entry, ACL_USER) < 0 ||
147 acl_set_qualifier(entry, &uid) < 0) {
148 log_error_errno(errno, "Failed to patch ACL: %m");
152 if (acl_get_permset(entry, &permset) < 0 ||
153 acl_add_perm(permset, ACL_READ) < 0 ||
154 calc_acl_mask_if_needed(&acl) < 0) {
155 log_warning_errno(errno, "Failed to patch ACL: %m");
159 if (acl_set_fd(fd, acl) < 0)
160 return log_error_errno(errno, "Failed to apply ACL: %m");
166 static int fix_xattr(int fd, const char *info[_INFO_LEN]) {
168 static const char * const xattrs[_INFO_LEN] = {
169 [INFO_PID] = "user.coredump.pid",
170 [INFO_UID] = "user.coredump.uid",
171 [INFO_GID] = "user.coredump.gid",
172 [INFO_SIGNAL] = "user.coredump.signal",
173 [INFO_TIMESTAMP] = "user.coredump.timestamp",
174 [INFO_COMM] = "user.coredump.comm",
175 [INFO_EXE] = "user.coredump.exe",
183 /* Attach some metadata to coredumps via extended
184 * attributes. Just because we can. */
186 for (i = 0; i < _INFO_LEN; i++) {
189 if (isempty(info[i]) || !xattrs[i])
192 k = fsetxattr(fd, xattrs[i], info[i], strlen(info[i]), XATTR_CREATE);
200 #define filename_escape(s) xescape((s), "./ ")
202 static int fix_permissions(
204 const char *filename,
206 const char *info[_INFO_LEN],
214 /* Ignore errors on these */
220 return log_error_errno(errno, "Failed to sync coredump %s: %m", filename);
222 if (rename(filename, target) < 0)
223 return log_error_errno(errno, "Failed to rename coredump %s -> %s: %m", filename, target);
228 static int maybe_remove_external_coredump(const char *filename, off_t size) {
230 /* Returns 1 if might remove, 0 if will not remove, < 0 on error. */
232 if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
233 size <= arg_external_size_max)
239 if (unlink(filename) < 0 && errno != ENOENT)
240 return log_error_errno(errno, "Failed to unlink %s: %m", filename);
245 static int make_filename(const char *info[_INFO_LEN], char **ret) {
246 _cleanup_free_ char *c = NULL, *u = NULL, *p = NULL, *t = NULL;
252 c = filename_escape(info[INFO_COMM]);
256 u = filename_escape(info[INFO_UID]);
260 r = sd_id128_get_boot(&boot);
264 p = filename_escape(info[INFO_PID]);
268 t = filename_escape(info[INFO_TIMESTAMP]);
273 "/var/lib/systemd/coredump/core.%s.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
276 SD_ID128_FORMAT_VAL(boot),
284 static int save_external_coredump(
285 const char *info[_INFO_LEN],
291 _cleanup_free_ char *fn = NULL, *tmp = NULL;
292 _cleanup_close_ int fd = -1;
297 assert(ret_filename);
301 r = make_filename(info, &fn);
303 return log_error_errno(r, "Failed to determine coredump file name: %m");
305 r = tempfn_random(fn, &tmp);
307 return log_error_errno(r, "Failed to determine temporary file name: %m");
309 mkdir_p_label("/var/lib/systemd/coredump", 0755);
311 fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
313 return log_error_errno(errno, "Failed to create coredump file %s: %m", tmp);
315 r = copy_bytes(STDIN_FILENO, fd, arg_process_size_max, false);
317 log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", info[INFO_PID], info[INFO_COMM]);
319 } else if (IN_SET(r, -EDQUOT, -ENOSPC)) {
320 log_error("Not enough disk space for coredump of %s (%s), refusing.", info[INFO_PID], info[INFO_COMM]);
323 log_error_errno(r, "Failed to dump coredump to file: %m");
327 if (fstat(fd, &st) < 0) {
328 log_error_errno(errno, "Failed to fstat coredump %s: %m", tmp);
332 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
333 log_error_errno(errno, "Failed to seek on %s: %m", tmp);
337 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
338 /* If we will remove the coredump anyway, do not compress. */
339 if (maybe_remove_external_coredump(NULL, st.st_size) == 0
342 _cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL;
343 _cleanup_close_ int fd_compressed = -1;
345 fn_compressed = strappend(fn, COMPRESSED_EXT);
346 if (!fn_compressed) {
351 r = tempfn_random(fn_compressed, &tmp_compressed);
353 log_error_errno(r, "Failed to determine temporary file name for %s: %m", fn_compressed);
357 fd_compressed = open(tmp_compressed, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
358 if (fd_compressed < 0) {
359 log_error_errno(errno, "Failed to create file %s: %m", tmp_compressed);
363 r = compress_stream(fd, fd_compressed, -1);
365 log_error_errno(r, "Failed to compress %s: %m", tmp_compressed);
366 goto fail_compressed;
369 r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, info, uid);
371 goto fail_compressed;
373 /* OK, this worked, we can get rid of the uncompressed version now */
376 *ret_filename = fn_compressed; /* compressed */
377 *ret_fd = fd; /* uncompressed */
378 *ret_size = st.st_size; /* uncompressed */
380 fn_compressed = NULL;
386 unlink_noerrno(tmp_compressed);
391 r = fix_permissions(fd, tmp, fn, info, uid);
397 *ret_size = st.st_size;
409 static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
410 _cleanup_free_ char *field = NULL;
417 if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
418 return log_warning_errno(errno, "Failed to seek: %m");
420 field = malloc(9 + size);
422 log_warning("Failed to allocate memory for coredump, coredump will not be stored.");
426 memcpy(field, "COREDUMP=", 9);
428 n = read(fd, field + 9, size);
430 return log_error_errno((int) n, "Failed to read core data: %m");
431 if ((size_t) n < size) {
432 log_error("Core data too short.");
437 *ret_size = size + 9;
444 /* Joins /proc/[pid]/fd/ and /proc/[pid]/fdinfo/ into the following lines:
458 static int compose_open_fds(pid_t pid, char **open_fds) {
459 _cleanup_closedir_ DIR *proc_fd_dir = NULL;
460 _cleanup_close_ int proc_fdinfo_fd = -1;
461 _cleanup_free_ char *buffer = NULL;
462 _cleanup_fclose_ FILE *stream = NULL;
463 const char *fddelim = "", *path;
464 struct dirent *dent = NULL;
469 assert(open_fds != NULL);
471 path = procfs_file_alloca(pid, "fd");
472 proc_fd_dir = opendir(path);
476 proc_fdinfo_fd = openat(dirfd(proc_fd_dir), "../fdinfo", O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC|O_PATH);
477 if (proc_fdinfo_fd < 0)
480 stream = open_memstream(&buffer, &size);
484 FOREACH_DIRENT(dent, proc_fd_dir, return -errno) {
485 _cleanup_fclose_ FILE *fdinfo = NULL;
486 _cleanup_free_ char *fdname = NULL;
490 r = readlinkat_malloc(dirfd(proc_fd_dir), dent->d_name, &fdname);
494 fprintf(stream, "%s%s:%s\n", fddelim, dent->d_name, fdname);
497 /* Use the directory entry from /proc/[pid]/fd with /proc/[pid]/fdinfo */
498 fd = openat(proc_fdinfo_fd, dent->d_name, O_NOFOLLOW|O_CLOEXEC|O_RDONLY);
502 fdinfo = fdopen(fd, "re");
503 if (fdinfo == NULL) {
508 FOREACH_LINE(line, fdinfo, break) {
510 if (!endswith(line, "\n"))
528 int main(int argc, char* argv[]) {
530 /* The small core field we allocate on the stack, to keep things simple */
532 *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
533 *core_session = NULL, *core_exe = NULL, *core_comm = NULL, *core_cmdline = NULL,
534 *core_cgroup = NULL, *core_cwd = NULL, *core_root = NULL, *core_unit = NULL,
537 /* The larger ones we allocate on the heap */
539 *core_timestamp = NULL, *core_message = NULL, *coredump_data = NULL, *core_owner_uid = NULL,
540 *core_open_fds = NULL, *core_proc_status = NULL, *core_proc_maps = NULL, *core_proc_limits = NULL,
541 *core_proc_cgroup = NULL, *core_environ = NULL;
543 _cleanup_free_ char *exe = NULL, *comm = NULL, *filename = NULL;
544 const char *info[_INFO_LEN];
546 _cleanup_close_ int coredump_fd = -1;
548 struct iovec iovec[26];
551 uid_t uid, owner_uid;
557 /* Make sure we never enter a loop */
558 prctl(PR_SET_DUMPABLE, 0);
560 /* First, log to a safe place, since we don't know what
561 * crashed and it might be journald which we'd rather not log
563 log_set_target(LOG_TARGET_KMSG);
566 if (argc < INFO_COMM + 1) {
567 log_error("Not enough arguments passed from kernel (%d, expected %d).",
568 argc - 1, INFO_COMM + 1 - 1);
573 /* Ignore all parse errors */
576 log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
577 log_debug("Selected compression %s.", yes_no(arg_compress));
579 r = parse_uid(argv[INFO_UID + 1], &uid);
581 log_error("Failed to parse UID.");
585 r = parse_pid(argv[INFO_PID + 1], &pid);
587 log_error("Failed to parse PID.");
591 r = parse_gid(argv[INFO_GID + 1], &gid);
593 log_error("Failed to parse GID.");
597 if (get_process_comm(pid, &comm) < 0) {
598 log_warning("Failed to get COMM, falling back to the command line.");
599 comm = strv_join(argv + INFO_COMM + 1, " ");
602 if (get_process_exe(pid, &exe) < 0)
603 log_warning("Failed to get EXE.");
605 info[INFO_PID] = argv[INFO_PID + 1];
606 info[INFO_UID] = argv[INFO_UID + 1];
607 info[INFO_GID] = argv[INFO_GID + 1];
608 info[INFO_SIGNAL] = argv[INFO_SIGNAL + 1];
609 info[INFO_TIMESTAMP] = argv[INFO_TIMESTAMP + 1];
610 info[INFO_COMM] = comm;
611 info[INFO_EXE] = exe;
613 if (cg_pid_get_unit(pid, &t) >= 0) {
615 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
618 /* If we are journald, we cut things short,
619 * don't write to the journal, but still
620 * create a coredump. */
622 if (arg_storage != COREDUMP_STORAGE_NONE)
623 arg_storage = COREDUMP_STORAGE_EXTERNAL;
625 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
629 r = maybe_remove_external_coredump(filename, coredump_size);
633 log_info("Detected coredump of the journal daemon itself, diverted to %s.", filename);
637 core_unit = strjoina("COREDUMP_UNIT=", t);
640 } else if (cg_pid_get_user_unit(pid, &t) >= 0) {
641 core_unit = strjoina("COREDUMP_USER_UNIT=", t);
646 IOVEC_SET_STRING(iovec[j++], core_unit);
648 /* OK, now we know it's not the journal, hence we can make use
650 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
653 core_pid = strjoina("COREDUMP_PID=", info[INFO_PID]);
654 IOVEC_SET_STRING(iovec[j++], core_pid);
656 core_uid = strjoina("COREDUMP_UID=", info[INFO_UID]);
657 IOVEC_SET_STRING(iovec[j++], core_uid);
659 core_gid = strjoina("COREDUMP_GID=", info[INFO_GID]);
660 IOVEC_SET_STRING(iovec[j++], core_gid);
662 core_signal = strjoina("COREDUMP_SIGNAL=", info[INFO_SIGNAL]);
663 IOVEC_SET_STRING(iovec[j++], core_signal);
665 if (sd_pid_get_session(pid, &t) >= 0) {
666 core_session = strjoina("COREDUMP_SESSION=", t);
669 IOVEC_SET_STRING(iovec[j++], core_session);
672 if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
673 r = asprintf(&core_owner_uid,
674 "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
676 IOVEC_SET_STRING(iovec[j++], core_owner_uid);
679 if (sd_pid_get_slice(pid, &t) >= 0) {
680 core_slice = strjoina("COREDUMP_SLICE=", t);
683 IOVEC_SET_STRING(iovec[j++], core_slice);
687 core_comm = strjoina("COREDUMP_COMM=", comm);
688 IOVEC_SET_STRING(iovec[j++], core_comm);
692 core_exe = strjoina("COREDUMP_EXE=", exe);
693 IOVEC_SET_STRING(iovec[j++], core_exe);
696 if (get_process_cmdline(pid, 0, false, &t) >= 0) {
697 core_cmdline = strjoina("COREDUMP_CMDLINE=", t);
700 IOVEC_SET_STRING(iovec[j++], core_cmdline);
703 if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
704 core_cgroup = strjoina("COREDUMP_CGROUP=", t);
707 IOVEC_SET_STRING(iovec[j++], core_cgroup);
710 if (compose_open_fds(pid, &t) >= 0) {
711 core_open_fds = strappend("COREDUMP_OPEN_FDS=", t);
715 IOVEC_SET_STRING(iovec[j++], core_open_fds);
718 p = procfs_file_alloca(pid, "status");
719 if (read_full_file(p, &t, NULL) >= 0) {
720 core_proc_status = strappend("COREDUMP_PROC_STATUS=", t);
723 if (core_proc_status)
724 IOVEC_SET_STRING(iovec[j++], core_proc_status);
727 p = procfs_file_alloca(pid, "maps");
728 if (read_full_file(p, &t, NULL) >= 0) {
729 core_proc_maps = strappend("COREDUMP_PROC_MAPS=", t);
733 IOVEC_SET_STRING(iovec[j++], core_proc_maps);
736 p = procfs_file_alloca(pid, "limits");
737 if (read_full_file(p, &t, NULL) >= 0) {
738 core_proc_limits = strappend("COREDUMP_PROC_LIMITS=", t);
741 if (core_proc_limits)
742 IOVEC_SET_STRING(iovec[j++], core_proc_limits);
745 p = procfs_file_alloca(pid, "cgroup");
746 if (read_full_file(p, &t, NULL) >=0) {
747 core_proc_cgroup = strappend("COREDUMP_PROC_CGROUP=", t);
750 if (core_proc_cgroup)
751 IOVEC_SET_STRING(iovec[j++], core_proc_cgroup);
754 if (get_process_cwd(pid, &t) >= 0) {
755 core_cwd = strjoina("COREDUMP_CWD=", t);
758 IOVEC_SET_STRING(iovec[j++], core_cwd);
761 if (get_process_root(pid, &t) >= 0) {
762 core_root = strjoina("COREDUMP_ROOT=", t);
765 IOVEC_SET_STRING(iovec[j++], core_root);
768 if (get_process_environ(pid, &t) >= 0) {
769 core_environ = strappend("COREDUMP_ENVIRON=", t);
773 IOVEC_SET_STRING(iovec[j++], core_environ);
776 core_timestamp = strjoin("COREDUMP_TIMESTAMP=", info[INFO_TIMESTAMP], "000000", NULL);
778 IOVEC_SET_STRING(iovec[j++], core_timestamp);
780 IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
781 IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
783 /* Vacuum before we write anything again */
784 coredump_vacuum(-1, arg_keep_free, arg_max_use);
786 /* Always stream the coredump to disk, if that's possible */
787 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
789 /* skip whole core dumping part */
792 /* If we don't want to keep the coredump on disk, remove it
793 * now, as later on we will lack the privileges for
794 * it. However, we keep the fd to it, so that we can still
795 * process it and log it. */
796 r = maybe_remove_external_coredump(filename, coredump_size);
800 const char *coredump_filename;
802 coredump_filename = strjoina("COREDUMP_FILENAME=", filename);
803 IOVEC_SET_STRING(iovec[j++], coredump_filename);
806 /* Vacuum again, but exclude the coredump we just created */
807 coredump_vacuum(coredump_fd, arg_keep_free, arg_max_use);
809 /* Now, let's drop privileges to become the user who owns the
810 * segfaulted process and allocate the coredump memory under
811 * the user's uid. This also ensures that the credentials
812 * journald will see are the ones of the coredumping user,
813 * thus making sure the user gets access to the core
814 * dump. Let's also get rid of all capabilities, if we run as
815 * root, we won't need them anymore. */
816 r = drop_privileges(uid, gid, 0);
818 log_error_errno(r, "Failed to drop privileges: %m");
823 /* Try to get a strack trace if we can */
824 if (coredump_size <= arg_process_size_max) {
825 _cleanup_free_ char *stacktrace = NULL;
827 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
829 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.\n\n", stacktrace, NULL);
830 else if (r == -EINVAL)
831 log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno()));
833 log_warning_errno(r, "Failed to generate stack trace: %m");
839 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.", NULL);
841 IOVEC_SET_STRING(iovec[j++], core_message);
843 /* Optionally store the entire coredump in the journal */
844 if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
845 coredump_size <= (off_t) arg_journal_size_max) {
848 /* Store the coredump itself in the journal */
850 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
852 iovec[j].iov_base = coredump_data;
853 iovec[j].iov_len = sz;
858 r = sd_journal_sendv(iovec, j);
860 log_error_errno(r, "Failed to log coredump: %m");
863 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;