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"
44 #include "cgroup-util.h"
45 #include "journald-native.h"
46 #include "conf-parser.h"
48 #include "stacktrace.h"
49 #include "path-util.h"
51 #include "coredump-vacuum.h"
55 # include "acl-util.h"
58 /* The maximum size up to which we process coredumps */
59 #define PROCESS_SIZE_MAX ((off_t) (2LLU*1024LLU*1024LLU*1024LLU))
61 /* The maximum size up to which we leave the coredump around on
63 #define EXTERNAL_SIZE_MAX PROCESS_SIZE_MAX
65 /* The maximum size up to which we store the coredump in the
67 #define JOURNAL_SIZE_MAX ((size_t) (767LU*1024LU*1024LU))
69 /* Make sure to not make this larger than the maximum journal entry
70 * size. See DATA_SIZE_MAX in journald-native.c. */
71 assert_cc(JOURNAL_SIZE_MAX <= DATA_SIZE_MAX);
84 typedef enum CoredumpStorage {
85 COREDUMP_STORAGE_NONE,
86 COREDUMP_STORAGE_EXTERNAL,
87 COREDUMP_STORAGE_JOURNAL,
88 COREDUMP_STORAGE_BOTH,
89 _COREDUMP_STORAGE_MAX,
90 _COREDUMP_STORAGE_INVALID = -1
93 static const char* const coredump_storage_table[_COREDUMP_STORAGE_MAX] = {
94 [COREDUMP_STORAGE_NONE] = "none",
95 [COREDUMP_STORAGE_EXTERNAL] = "external",
96 [COREDUMP_STORAGE_JOURNAL] = "journal",
97 [COREDUMP_STORAGE_BOTH] = "both",
100 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_storage, CoredumpStorage);
101 static DEFINE_CONFIG_PARSE_ENUM(config_parse_coredump_storage, coredump_storage, CoredumpStorage, "Failed to parse storage setting");
103 static CoredumpStorage arg_storage = COREDUMP_STORAGE_EXTERNAL;
104 static bool arg_compress = true;
105 static off_t arg_process_size_max = PROCESS_SIZE_MAX;
106 static off_t arg_external_size_max = EXTERNAL_SIZE_MAX;
107 static size_t arg_journal_size_max = JOURNAL_SIZE_MAX;
108 static off_t arg_keep_free = (off_t) -1;
109 static off_t arg_max_use = (off_t) -1;
111 static int parse_config(void) {
112 static const ConfigTableItem items[] = {
113 { "Coredump", "Storage", config_parse_coredump_storage, 0, &arg_storage },
114 { "Coredump", "Compress", config_parse_bool, 0, &arg_compress },
115 { "Coredump", "ProcessSizeMax", config_parse_iec_off, 0, &arg_process_size_max },
116 { "Coredump", "ExternalSizeMax", config_parse_iec_off, 0, &arg_external_size_max },
117 { "Coredump", "JournalSizeMax", config_parse_iec_size, 0, &arg_journal_size_max },
118 { "Coredump", "KeepFree", config_parse_iec_off, 0, &arg_keep_free },
119 { "Coredump", "MaxUse", config_parse_iec_off, 0, &arg_max_use },
123 return config_parse(NULL, "/etc/systemd/coredump.conf", NULL,
125 config_item_table_lookup, items,
126 false, false, true, NULL);
129 static int fix_acl(int fd, uid_t uid) {
132 _cleanup_(acl_freep) acl_t acl = NULL;
134 acl_permset_t permset;
138 if (uid <= SYSTEM_UID_MAX)
141 /* Make sure normal users can read (but not write or delete)
142 * their own coredumps */
144 acl = acl_get_fd(fd);
146 return log_error_errno(errno, "Failed to get ACL: %m");
148 if (acl_create_entry(&acl, &entry) < 0 ||
149 acl_set_tag_type(entry, ACL_USER) < 0 ||
150 acl_set_qualifier(entry, &uid) < 0) {
151 log_error_errno(errno, "Failed to patch ACL: %m");
155 if (acl_get_permset(entry, &permset) < 0 ||
156 acl_add_perm(permset, ACL_READ) < 0 ||
157 calc_acl_mask_if_needed(&acl) < 0) {
158 log_warning_errno(errno, "Failed to patch ACL: %m");
162 if (acl_set_fd(fd, acl) < 0)
163 return log_error_errno(errno, "Failed to apply ACL: %m");
169 static int fix_xattr(int fd, const char *info[_INFO_LEN]) {
171 static const char * const xattrs[_INFO_LEN] = {
172 [INFO_PID] = "user.coredump.pid",
173 [INFO_UID] = "user.coredump.uid",
174 [INFO_GID] = "user.coredump.gid",
175 [INFO_SIGNAL] = "user.coredump.signal",
176 [INFO_TIMESTAMP] = "user.coredump.timestamp",
177 [INFO_COMM] = "user.coredump.comm",
178 [INFO_EXE] = "user.coredump.exe",
186 /* Attach some metadata to coredumps via extended
187 * attributes. Just because we can. */
189 for (i = 0; i < _INFO_LEN; i++) {
192 if (isempty(info[i]) || !xattrs[i])
195 k = fsetxattr(fd, xattrs[i], info[i], strlen(info[i]), XATTR_CREATE);
203 #define filename_escape(s) xescape((s), "./ ")
205 static int fix_permissions(
207 const char *filename,
209 const char *info[_INFO_LEN],
217 /* Ignore errors on these */
223 return log_error_errno(errno, "Failed to sync coredump %s: %m", filename);
225 if (rename(filename, target) < 0)
226 return log_error_errno(errno, "Failed to rename coredump %s -> %s: %m", filename, target);
231 static int maybe_remove_external_coredump(const char *filename, off_t size) {
233 /* Returns 1 if might remove, 0 if will not remove, < 0 on error. */
235 if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
236 size <= arg_external_size_max)
242 if (unlink(filename) < 0 && errno != ENOENT)
243 return log_error_errno(errno, "Failed to unlink %s: %m", filename);
248 static int make_filename(const char *info[_INFO_LEN], char **ret) {
249 _cleanup_free_ char *c = NULL, *u = NULL, *p = NULL, *t = NULL;
255 c = filename_escape(info[INFO_COMM]);
259 u = filename_escape(info[INFO_UID]);
263 r = sd_id128_get_boot(&boot);
267 p = filename_escape(info[INFO_PID]);
271 t = filename_escape(info[INFO_TIMESTAMP]);
276 "/var/lib/systemd/coredump/core.%s.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
279 SD_ID128_FORMAT_VAL(boot),
287 static int save_external_coredump(
288 const char *info[_INFO_LEN],
294 _cleanup_free_ char *fn = NULL, *tmp = NULL;
295 _cleanup_close_ int fd = -1;
300 assert(ret_filename);
304 r = make_filename(info, &fn);
306 return log_error_errno(r, "Failed to determine coredump file name: %m");
308 tmp = tempfn_random(fn);
312 mkdir_p_label("/var/lib/systemd/coredump", 0755);
314 fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
316 return log_error_errno(errno, "Failed to create coredump file %s: %m", tmp);
318 r = copy_bytes(STDIN_FILENO, fd, arg_process_size_max);
320 log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", info[INFO_PID], info[INFO_COMM]);
322 } else if (IN_SET(r, -EDQUOT, -ENOSPC)) {
323 log_error("Not enough disk space for coredump of %s (%s), refusing.", info[INFO_PID], info[INFO_COMM]);
326 log_error_errno(r, "Failed to dump coredump to file: %m");
330 if (fstat(fd, &st) < 0) {
331 log_error_errno(errno, "Failed to fstat coredump %s: %m", tmp);
335 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
336 log_error_errno(errno, "Failed to seek on %s: %m", tmp);
340 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
341 /* If we will remove the coredump anyway, do not compress. */
342 if (maybe_remove_external_coredump(NULL, st.st_size) == 0
345 _cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL;
346 _cleanup_close_ int fd_compressed = -1;
348 fn_compressed = strappend(fn, COMPRESSED_EXT);
349 if (!fn_compressed) {
354 tmp_compressed = tempfn_random(fn_compressed);
355 if (!tmp_compressed) {
360 fd_compressed = open(tmp_compressed, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
361 if (fd_compressed < 0) {
362 log_error_errno(errno, "Failed to create file %s: %m", tmp_compressed);
366 r = compress_stream(fd, fd_compressed, -1);
368 log_error_errno(r, "Failed to compress %s: %m", tmp_compressed);
369 goto fail_compressed;
372 r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, info, uid);
374 goto fail_compressed;
376 /* OK, this worked, we can get rid of the uncompressed version now */
379 *ret_filename = fn_compressed; /* compressed */
380 *ret_fd = fd; /* uncompressed */
381 *ret_size = st.st_size; /* uncompressed */
383 fn_compressed = NULL;
389 unlink_noerrno(tmp_compressed);
394 r = fix_permissions(fd, tmp, fn, info, uid);
400 *ret_size = st.st_size;
412 static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
413 _cleanup_free_ char *field = NULL;
420 if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
421 return log_warning_errno(errno, "Failed to seek: %m");
423 field = malloc(9 + size);
425 log_warning("Failed to allocate memory for coredump, coredump will not be stored.");
429 memcpy(field, "COREDUMP=", 9);
431 n = read(fd, field + 9, size);
433 return log_error_errno((int) n, "Failed to read core data: %m");
434 if ((size_t) n < size) {
435 log_error("Core data too short.");
440 *ret_size = size + 9;
447 /* Joins /proc/[pid]/fd/ and /proc/[pid]/fdinfo/ into the following lines:
461 static int compose_open_fds(pid_t pid, char **open_fds) {
462 _cleanup_fclose_ FILE *stream = NULL;
464 const char *fddelim = "", *path;
465 struct dirent *dent = NULL;
466 _cleanup_closedir_ DIR *proc_fd_dir = NULL;
467 _cleanup_close_ int proc_fdinfo_fd = -1;
471 assert(open_fds != NULL);
473 path = procfs_file_alloca(pid, "fd");
474 proc_fd_dir = opendir(path);
478 proc_fdinfo_fd = openat(dirfd(proc_fd_dir), "../fdinfo",
479 O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC|O_PATH);
480 if (proc_fdinfo_fd < 0)
483 stream = open_memstream(open_fds, &ignored_size);
487 for (dent = readdir(proc_fd_dir); dent != NULL; dent = readdir(proc_fd_dir)) {
488 _cleanup_free_ char *fdname = NULL;
490 _cleanup_fclose_ FILE *fdinfo = NULL;
493 if (dent->d_name[0] == '.' || strcmp(dent->d_name, "..") == 0)
496 r = readlinkat_malloc(dirfd(proc_fd_dir), dent->d_name, &fdname);
500 fprintf(stream, "%s%s:%s\n", fddelim, dent->d_name, fdname);
503 /* Use the directory entry from /proc/[pid]/fd with /proc/[pid]/fdinfo */
504 fd = openat(proc_fdinfo_fd, dent->d_name, O_NOFOLLOW|O_CLOEXEC|O_RDONLY);
508 fdinfo = fdopen(fd, "re");
509 if (fdinfo == NULL) {
514 while(fgets(line, sizeof(line), fdinfo) != NULL)
515 fprintf(stream, "%s%s",
516 line, strchr(line, '\n') == NULL ? "\n" : "");
522 int main(int argc, char* argv[]) {
524 _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
525 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
526 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
527 *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL, *core_open_fds = NULL,
528 *core_proc_status = NULL, *core_proc_maps = NULL, *core_proc_limits = NULL, *core_proc_cgroup = NULL,
529 *core_cwd = NULL, *core_root = NULL, *core_environ = NULL,
530 *exe = NULL, *comm = NULL, *filename = NULL;
531 const char *info[_INFO_LEN];
533 _cleanup_close_ int coredump_fd = -1;
535 struct iovec iovec[26];
538 uid_t uid, owner_uid;
544 /* Make sure we never enter a loop */
545 prctl(PR_SET_DUMPABLE, 0);
547 /* First, log to a safe place, since we don't know what
548 * crashed and it might be journald which we'd rather not log
550 log_set_target(LOG_TARGET_KMSG);
553 if (argc < INFO_COMM + 1) {
554 log_error("Not enough arguments passed from kernel (%d, expected %d).",
555 argc - 1, INFO_COMM + 1 - 1);
560 /* Ignore all parse errors */
563 log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
564 log_debug("Selected compression %s.", yes_no(arg_compress));
566 r = parse_uid(argv[INFO_UID + 1], &uid);
568 log_error("Failed to parse UID.");
572 r = parse_pid(argv[INFO_PID + 1], &pid);
574 log_error("Failed to parse PID.");
578 r = parse_gid(argv[INFO_GID + 1], &gid);
580 log_error("Failed to parse GID.");
584 if (get_process_comm(pid, &comm) < 0) {
585 log_warning("Failed to get COMM, falling back to the command line.");
586 comm = strv_join(argv + INFO_COMM + 1, " ");
589 if (get_process_exe(pid, &exe) < 0)
590 log_warning("Failed to get EXE.");
592 info[INFO_PID] = argv[INFO_PID + 1];
593 info[INFO_UID] = argv[INFO_UID + 1];
594 info[INFO_GID] = argv[INFO_GID + 1];
595 info[INFO_SIGNAL] = argv[INFO_SIGNAL + 1];
596 info[INFO_TIMESTAMP] = argv[INFO_TIMESTAMP + 1];
597 info[INFO_COMM] = comm;
598 info[INFO_EXE] = exe;
600 if (cg_pid_get_unit(pid, &t) >= 0) {
602 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
604 /* If we are journald, we cut things short,
605 * don't write to the journal, but still
606 * create a coredump. */
608 if (arg_storage != COREDUMP_STORAGE_NONE)
609 arg_storage = COREDUMP_STORAGE_EXTERNAL;
611 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
615 r = maybe_remove_external_coredump(filename, coredump_size);
619 log_info("Detected coredump of the journal daemon itself, diverted to %s.", filename);
623 core_unit = strappend("COREDUMP_UNIT=", t);
624 } else if (cg_pid_get_user_unit(pid, &t) >= 0)
625 core_unit = strappend("COREDUMP_USER_UNIT=", t);
628 IOVEC_SET_STRING(iovec[j++], core_unit);
630 /* OK, now we know it's not the journal, hence we can make use
632 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
635 core_pid = strappend("COREDUMP_PID=", info[INFO_PID]);
637 IOVEC_SET_STRING(iovec[j++], core_pid);
639 core_uid = strappend("COREDUMP_UID=", info[INFO_UID]);
641 IOVEC_SET_STRING(iovec[j++], core_uid);
643 core_gid = strappend("COREDUMP_GID=", info[INFO_GID]);
645 IOVEC_SET_STRING(iovec[j++], core_gid);
647 core_signal = strappend("COREDUMP_SIGNAL=", info[INFO_SIGNAL]);
649 IOVEC_SET_STRING(iovec[j++], core_signal);
651 if (sd_pid_get_session(pid, &t) >= 0) {
652 core_session = strappend("COREDUMP_SESSION=", t);
656 IOVEC_SET_STRING(iovec[j++], core_session);
659 if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
660 r = asprintf(&core_owner_uid,
661 "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
663 IOVEC_SET_STRING(iovec[j++], core_owner_uid);
666 if (sd_pid_get_slice(pid, &t) >= 0) {
667 core_slice = strappend("COREDUMP_SLICE=", t);
671 IOVEC_SET_STRING(iovec[j++], core_slice);
675 core_comm = strappend("COREDUMP_COMM=", comm);
677 IOVEC_SET_STRING(iovec[j++], core_comm);
681 core_exe = strappend("COREDUMP_EXE=", exe);
683 IOVEC_SET_STRING(iovec[j++], core_exe);
686 if (get_process_cmdline(pid, 0, false, &t) >= 0) {
687 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
691 IOVEC_SET_STRING(iovec[j++], core_cmdline);
694 if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
695 core_cgroup = strappend("COREDUMP_CGROUP=", t);
699 IOVEC_SET_STRING(iovec[j++], core_cgroup);
702 if (compose_open_fds(pid, &t) >= 0) {
703 core_open_fds = strappend("COREDUMP_OPEN_FDS=", t);
707 IOVEC_SET_STRING(iovec[j++], core_open_fds);
710 p = procfs_file_alloca(pid, "status");
711 if (read_full_file(p, &t, NULL) >= 0) {
712 core_proc_status = strappend("COREDUMP_PROC_STATUS=", t);
715 if (core_proc_status)
716 IOVEC_SET_STRING(iovec[j++], core_proc_status);
719 p = procfs_file_alloca(pid, "maps");
720 if (read_full_file(p, &t, NULL) >= 0) {
721 core_proc_maps = strappend("COREDUMP_PROC_MAPS=", t);
725 IOVEC_SET_STRING(iovec[j++], core_proc_maps);
728 p = procfs_file_alloca(pid, "limits");
729 if (read_full_file(p, &t, NULL) >= 0) {
730 core_proc_limits = strappend("COREDUMP_PROC_LIMITS=", t);
733 if (core_proc_limits)
734 IOVEC_SET_STRING(iovec[j++], core_proc_limits);
737 p = procfs_file_alloca(pid, "cgroup");
738 if (read_full_file(p, &t, NULL) >=0) {
739 core_proc_cgroup = strappend("COREDUMP_PROC_CGROUP=", t);
742 if (core_proc_cgroup)
743 IOVEC_SET_STRING(iovec[j++], core_proc_cgroup);
746 if (get_process_cwd(pid, &t) >= 0) {
747 core_cwd = strappend("COREDUMP_CWD=", t);
751 IOVEC_SET_STRING(iovec[j++], core_cwd);
754 if (get_process_root(pid, &t) >= 0) {
755 core_root = strappend("COREDUMP_ROOT=", t);
759 IOVEC_SET_STRING(iovec[j++], core_root);
762 if (get_process_environ(pid, &t) >= 0) {
763 core_environ = strappend("COREDUMP_ENVIRON=", t);
767 IOVEC_SET_STRING(iovec[j++], core_environ);
770 core_timestamp = strjoin("COREDUMP_TIMESTAMP=", info[INFO_TIMESTAMP], "000000", NULL);
772 IOVEC_SET_STRING(iovec[j++], core_timestamp);
774 IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
775 IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
777 /* Vacuum before we write anything again */
778 coredump_vacuum(-1, arg_keep_free, arg_max_use);
780 /* Always stream the coredump to disk, if that's possible */
781 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
783 /* skip whole core dumping part */
786 /* If we don't want to keep the coredump on disk, remove it
787 * now, as later on we will lack the privileges for
788 * it. However, we keep the fd to it, so that we can still
789 * process it and log it. */
790 r = maybe_remove_external_coredump(filename, coredump_size);
794 const char *coredump_filename;
796 coredump_filename = strappenda("COREDUMP_FILENAME=", filename);
797 IOVEC_SET_STRING(iovec[j++], coredump_filename);
800 /* Vacuum again, but exclude the coredump we just created */
801 coredump_vacuum(coredump_fd, arg_keep_free, arg_max_use);
803 /* Now, let's drop privileges to become the user who owns the
804 * segfaulted process and allocate the coredump memory under
805 * the user's uid. This also ensures that the credentials
806 * journald will see are the ones of the coredumping user,
807 * thus making sure the user gets access to the core dump. */
808 if (setresgid(gid, gid, gid) < 0 ||
809 setresuid(uid, uid, uid) < 0) {
810 log_error_errno(errno, "Failed to drop privileges: %m");
816 /* Try to get a strack trace if we can */
817 if (coredump_size <= arg_process_size_max) {
818 _cleanup_free_ char *stacktrace = NULL;
820 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
822 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.\n\n", stacktrace, NULL);
823 else if (r == -EINVAL)
824 log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno()));
826 log_warning_errno(r, "Failed to generate stack trace: %m");
832 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.", NULL);
834 IOVEC_SET_STRING(iovec[j++], core_message);
836 /* Optionally store the entire coredump in the journal */
837 if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
838 coredump_size <= (off_t) arg_journal_size_max) {
841 /* Store the coredump itself in the journal */
843 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
845 iovec[j].iov_base = coredump_data;
846 iovec[j].iov_len = sz;
851 r = sd_journal_sendv(iovec, j);
853 log_error_errno(r, "Failed to log coredump: %m");
856 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;