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 log_error("Failed to get ACL: %m");
150 if (acl_create_entry(&acl, &entry) < 0 ||
151 acl_set_tag_type(entry, ACL_USER) < 0 ||
152 acl_set_qualifier(entry, &uid) < 0) {
153 log_error("Failed to patch ACL: %m");
157 if (acl_get_permset(entry, &permset) < 0 ||
158 acl_add_perm(permset, ACL_READ) < 0 ||
159 calc_acl_mask_if_needed(&acl) < 0) {
160 log_warning("Failed to patch ACL: %m");
164 if (acl_set_fd(fd, acl) < 0) {
165 log_error("Failed to apply ACL: %m");
173 static int fix_xattr(int fd, const char *info[_INFO_LEN]) {
175 static const char * const xattrs[_INFO_LEN] = {
176 [INFO_PID] = "user.coredump.pid",
177 [INFO_UID] = "user.coredump.uid",
178 [INFO_GID] = "user.coredump.gid",
179 [INFO_SIGNAL] = "user.coredump.signal",
180 [INFO_TIMESTAMP] = "user.coredump.timestamp",
181 [INFO_COMM] = "user.coredump.comm",
182 [INFO_EXE] = "user.coredump.exe",
190 /* Attach some metadata to coredumps via extended
191 * attributes. Just because we can. */
193 for (i = 0; i < _INFO_LEN; i++) {
196 if (isempty(info[i]) || !xattrs[i])
199 k = fsetxattr(fd, xattrs[i], info[i], strlen(info[i]), XATTR_CREATE);
207 #define filename_escape(s) xescape((s), "./ ")
209 static int fix_permissions(
211 const char *filename,
213 const char *info[_INFO_LEN],
221 /* Ignore errors on these */
227 log_error("Failed to sync coredump %s: %m", filename);
231 if (rename(filename, target) < 0) {
232 log_error("Failed to rename coredump %s -> %s: %m", filename, target);
239 static int maybe_remove_external_coredump(const char *filename, off_t size) {
241 /* Returns 1 if might remove, 0 if will not remove, < 0 on error. */
243 if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
244 size <= arg_external_size_max)
250 if (unlink(filename) < 0 && errno != ENOENT) {
251 log_error("Failed to unlink %s: %m", filename);
258 static int make_filename(const char *info[_INFO_LEN], char **ret) {
259 _cleanup_free_ char *c = NULL, *u = NULL, *p = NULL, *t = NULL;
265 c = filename_escape(info[INFO_COMM]);
269 u = filename_escape(info[INFO_UID]);
273 r = sd_id128_get_boot(&boot);
277 p = filename_escape(info[INFO_PID]);
281 t = filename_escape(info[INFO_TIMESTAMP]);
286 "/var/lib/systemd/coredump/core.%s.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
289 SD_ID128_FORMAT_VAL(boot),
297 static int save_external_coredump(
298 const char *info[_INFO_LEN],
304 _cleanup_free_ char *fn = NULL, *tmp = NULL;
305 _cleanup_close_ int fd = -1;
310 assert(ret_filename);
314 r = make_filename(info, &fn);
316 log_error("Failed to determine coredump file name: %s", strerror(-r));
320 tmp = tempfn_random(fn);
324 mkdir_p_label("/var/lib/systemd/coredump", 0755);
326 fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
328 log_error("Failed to create coredump file %s: %m", tmp);
332 r = copy_bytes(STDIN_FILENO, fd, arg_process_size_max);
334 log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", info[INFO_PID], info[INFO_COMM]);
336 } else if (IN_SET(r, -EDQUOT, -ENOSPC)) {
337 log_error("Not enough disk space for coredump of %s (%s), refusing.", info[INFO_PID], info[INFO_COMM]);
340 log_error("Failed to dump coredump to file: %s", strerror(-r));
344 if (fstat(fd, &st) < 0) {
345 log_error("Failed to fstat coredump %s: %m", tmp);
349 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
350 log_error("Failed to seek on %s: %m", tmp);
354 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
355 /* If we will remove the coredump anyway, do not compress. */
356 if (maybe_remove_external_coredump(NULL, st.st_size) == 0
359 _cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL;
360 _cleanup_close_ int fd_compressed = -1;
362 fn_compressed = strappend(fn, COMPRESSED_EXT);
363 if (!fn_compressed) {
368 tmp_compressed = tempfn_random(fn_compressed);
369 if (!tmp_compressed) {
374 fd_compressed = open(tmp_compressed, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
375 if (fd_compressed < 0) {
376 log_error("Failed to create file %s: %m", tmp_compressed);
380 r = compress_stream(fd, fd_compressed, -1);
382 log_error("Failed to compress %s: %s", tmp_compressed, strerror(-r));
383 goto fail_compressed;
386 r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, info, uid);
388 goto fail_compressed;
390 /* OK, this worked, we can get rid of the uncompressed version now */
393 *ret_filename = fn_compressed; /* compressed */
394 *ret_fd = fd; /* uncompressed */
395 *ret_size = st.st_size; /* uncompressed */
397 fn_compressed = NULL;
403 unlink_noerrno(tmp_compressed);
408 r = fix_permissions(fd, tmp, fn, info, uid);
414 *ret_size = st.st_size;
426 static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
427 _cleanup_free_ char *field = NULL;
434 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
435 log_warning("Failed to seek: %m");
439 field = malloc(9 + size);
441 log_warning("Failed to allocate memory for coredump, coredump will not be stored.");
445 memcpy(field, "COREDUMP=", 9);
447 n = read(fd, field + 9, size);
449 log_error("Failed to read core data: %s", strerror(-n));
452 if ((size_t) n < size) {
453 log_error("Core data too short.");
458 *ret_size = size + 9;
465 /* Joins /proc/[pid]/fd/ and /proc/[pid]/fdinfo/ into the following lines:
479 static int compose_open_fds(pid_t pid, char **open_fds) {
480 _cleanup_fclose_ FILE *stream = NULL;
481 char path[PATH_MAX], line[LINE_MAX];
483 const char *fddelim = "";
484 struct dirent *dent = NULL;
485 _cleanup_closedir_ DIR *proc_fd_dir = NULL;
489 assert(open_fds != NULL);
491 sprintf(path, "/proc/"PID_FMT"/fd", pid);
492 proc_fd_dir = opendir(path);
494 if (proc_fd_dir == NULL)
497 stream = open_memstream(open_fds, &ignored_size);
501 for (dent = readdir(proc_fd_dir); dent != NULL; dent = readdir(proc_fd_dir)) {
502 _cleanup_free_ char *fdname = NULL;
503 _cleanup_fclose_ FILE *fdinfo = NULL;
505 if (dent->d_name[0] == '.' || strcmp(dent->d_name, "..") == 0)
508 /* Too long path is unlikely a path to valid file descriptor in /proc/[pid]/fd */
510 r = snprintf(path, sizeof(path), "/proc/"PID_FMT"/fd/%s", pid, dent->d_name);
511 if (r >= (int)sizeof(path))
514 r = readlink_malloc(path, &fdname);
518 fprintf(stream, "%s%s:%s\n", fddelim, dent->d_name, fdname);
521 /* Use the directory entry from /proc/[pid]/fd with /proc/[pid]/fdinfo */
523 /* Too long path is unlikely a path to valid file descriptor info in /proc/[pid]/fdinfo */
525 r = snprintf(path, sizeof(path), "/proc/"PID_FMT"/fdinfo/%s", pid, dent->d_name);
526 if (r >= (int)sizeof(path))
529 fdinfo = fopen(path, "re");
533 while(fgets(line, sizeof(line), fdinfo) != NULL)
534 fprintf(stream, "%s%s",
535 line, strchr(line, '\n') == NULL ? "\n" : "");
541 int main(int argc, char* argv[]) {
543 _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
544 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
545 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
546 *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL, *core_open_fds = NULL,
547 *core_proc_status = NULL, *core_proc_maps = NULL, *core_proc_limits = NULL, *core_proc_cgroup = NULL,
548 *core_cwd = NULL, *core_root = NULL, *core_environ = NULL,
549 *exe = NULL, *comm = NULL, *filename = NULL;
550 const char *info[_INFO_LEN];
552 _cleanup_close_ int coredump_fd = -1;
554 struct iovec iovec[26];
557 uid_t uid, owner_uid;
563 /* Make sure we never enter a loop */
564 prctl(PR_SET_DUMPABLE, 0);
566 /* First, log to a safe place, since we don't know what
567 * crashed and it might be journald which we'd rather not log
569 log_set_target(LOG_TARGET_KMSG);
572 if (argc < INFO_COMM + 1) {
573 log_error("Not enough arguments passed from kernel (%d, expected %d).",
574 argc - 1, INFO_COMM + 1 - 1);
579 /* Ignore all parse errors */
582 log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
583 log_debug("Selected compression %s.", yes_no(arg_compress));
585 r = parse_uid(argv[INFO_UID + 1], &uid);
587 log_error("Failed to parse UID.");
591 r = parse_pid(argv[INFO_PID + 1], &pid);
593 log_error("Failed to parse PID.");
597 r = parse_gid(argv[INFO_GID + 1], &gid);
599 log_error("Failed to parse GID.");
603 if (get_process_comm(pid, &comm) < 0) {
604 log_warning("Failed to get COMM, falling back to the command line.");
605 comm = strv_join(argv + INFO_COMM + 1, " ");
608 if (get_process_exe(pid, &exe) < 0)
609 log_warning("Failed to get EXE.");
611 info[INFO_PID] = argv[INFO_PID + 1];
612 info[INFO_UID] = argv[INFO_UID + 1];
613 info[INFO_GID] = argv[INFO_GID + 1];
614 info[INFO_SIGNAL] = argv[INFO_SIGNAL + 1];
615 info[INFO_TIMESTAMP] = argv[INFO_TIMESTAMP + 1];
616 info[INFO_COMM] = comm;
617 info[INFO_EXE] = exe;
619 if (cg_pid_get_unit(pid, &t) >= 0) {
621 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
623 /* If we are journald, we cut things short,
624 * don't write to the journal, but still
625 * create a coredump. */
627 if (arg_storage != COREDUMP_STORAGE_NONE)
628 arg_storage = COREDUMP_STORAGE_EXTERNAL;
630 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
634 r = maybe_remove_external_coredump(filename, coredump_size);
638 log_info("Detected coredump of the journal daemon itself, diverted to %s.", filename);
642 core_unit = strappend("COREDUMP_UNIT=", t);
643 } else if (cg_pid_get_user_unit(pid, &t) >= 0)
644 core_unit = strappend("COREDUMP_USER_UNIT=", t);
647 IOVEC_SET_STRING(iovec[j++], core_unit);
649 /* OK, now we know it's not the journal, hence we can make use
651 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
654 core_pid = strappend("COREDUMP_PID=", info[INFO_PID]);
656 IOVEC_SET_STRING(iovec[j++], core_pid);
658 core_uid = strappend("COREDUMP_UID=", info[INFO_UID]);
660 IOVEC_SET_STRING(iovec[j++], core_uid);
662 core_gid = strappend("COREDUMP_GID=", info[INFO_GID]);
664 IOVEC_SET_STRING(iovec[j++], core_gid);
666 core_signal = strappend("COREDUMP_SIGNAL=", info[INFO_SIGNAL]);
668 IOVEC_SET_STRING(iovec[j++], core_signal);
670 if (sd_pid_get_session(pid, &t) >= 0) {
671 core_session = strappend("COREDUMP_SESSION=", t);
675 IOVEC_SET_STRING(iovec[j++], core_session);
678 if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
679 r = asprintf(&core_owner_uid,
680 "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
682 IOVEC_SET_STRING(iovec[j++], core_owner_uid);
685 if (sd_pid_get_slice(pid, &t) >= 0) {
686 core_slice = strappend("COREDUMP_SLICE=", t);
690 IOVEC_SET_STRING(iovec[j++], core_slice);
694 core_comm = strappend("COREDUMP_COMM=", comm);
696 IOVEC_SET_STRING(iovec[j++], core_comm);
700 core_exe = strappend("COREDUMP_EXE=", exe);
702 IOVEC_SET_STRING(iovec[j++], core_exe);
705 if (get_process_cmdline(pid, 0, false, &t) >= 0) {
706 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
710 IOVEC_SET_STRING(iovec[j++], core_cmdline);
713 if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
714 core_cgroup = strappend("COREDUMP_CGROUP=", t);
718 IOVEC_SET_STRING(iovec[j++], core_cgroup);
721 if (compose_open_fds(pid, &t) >= 0) {
722 core_open_fds = strappend("COREDUMP_OPEN_FDS=", t);
726 IOVEC_SET_STRING(iovec[j++], core_open_fds);
729 p = procfs_file_alloca(pid, "status");
730 if (read_full_file(p, &t, NULL) >= 0) {
731 core_proc_status = strappend("COREDUMP_PROC_STATUS=", t);
734 if (core_proc_status)
735 IOVEC_SET_STRING(iovec[j++], core_proc_status);
738 p = procfs_file_alloca(pid, "maps");
739 if (read_full_file(p, &t, NULL) >= 0) {
740 core_proc_maps = strappend("COREDUMP_PROC_MAPS=", t);
744 IOVEC_SET_STRING(iovec[j++], core_proc_maps);
747 p = procfs_file_alloca(pid, "limits");
748 if (read_full_file(p, &t, NULL) >= 0) {
749 core_proc_limits = strappend("COREDUMP_PROC_LIMITS=", t);
752 if (core_proc_limits)
753 IOVEC_SET_STRING(iovec[j++], core_proc_limits);
756 p = procfs_file_alloca(pid, "cgroup");
757 if (read_full_file(p, &t, NULL) >=0) {
758 core_proc_cgroup = strappend("COREDUMP_PROC_CGROUP=", t);
761 if (core_proc_cgroup)
762 IOVEC_SET_STRING(iovec[j++], core_proc_cgroup);
765 if (get_process_cwd(pid, &t) >= 0) {
766 core_cwd = strappend("COREDUMP_CWD=", t);
770 IOVEC_SET_STRING(iovec[j++], core_cwd);
773 if (get_process_root(pid, &t) >= 0) {
774 core_root = strappend("COREDUMP_ROOT=", t);
778 IOVEC_SET_STRING(iovec[j++], core_root);
781 if (get_process_environ(pid, &t) >= 0) {
782 core_environ = strappend("COREDUMP_ENVIRON=", t);
786 IOVEC_SET_STRING(iovec[j++], core_environ);
789 core_timestamp = strjoin("COREDUMP_TIMESTAMP=", info[INFO_TIMESTAMP], "000000", NULL);
791 IOVEC_SET_STRING(iovec[j++], core_timestamp);
793 IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
794 IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
796 /* Vacuum before we write anything again */
797 coredump_vacuum(-1, arg_keep_free, arg_max_use);
799 /* Always stream the coredump to disk, if that's possible */
800 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
802 /* skip whole core dumping part */
805 /* If we don't want to keep the coredump on disk, remove it
806 * now, as later on we will lack the privileges for
807 * it. However, we keep the fd to it, so that we can still
808 * process it and log it. */
809 r = maybe_remove_external_coredump(filename, coredump_size);
813 const char *coredump_filename;
815 coredump_filename = strappenda("COREDUMP_FILENAME=", filename);
816 IOVEC_SET_STRING(iovec[j++], coredump_filename);
819 /* Vacuum again, but exclude the coredump we just created */
820 coredump_vacuum(coredump_fd, arg_keep_free, arg_max_use);
822 /* Now, let's drop privileges to become the user who owns the
823 * segfaulted process and allocate the coredump memory under
824 * the user's uid. This also ensures that the credentials
825 * journald will see are the ones of the coredumping user,
826 * thus making sure the user gets access to the core dump. */
827 if (setresgid(gid, gid, gid) < 0 ||
828 setresuid(uid, uid, uid) < 0) {
829 log_error("Failed to drop privileges: %m");
835 /* Try to get a strack trace if we can */
836 if (coredump_size <= arg_process_size_max) {
837 _cleanup_free_ char *stacktrace = NULL;
839 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
841 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.\n\n", stacktrace, NULL);
842 else if (r == -EINVAL)
843 log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno()));
845 log_warning("Failed to generate stack trace: %s", strerror(-r));
851 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.", NULL);
853 IOVEC_SET_STRING(iovec[j++], core_message);
855 /* Optionally store the entire coredump in the journal */
856 if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
857 coredump_size <= (off_t) arg_journal_size_max) {
860 /* Store the coredump itself in the journal */
862 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
864 iovec[j].iov_base = coredump_data;
865 iovec[j].iov_len = sz;
870 r = sd_journal_sendv(iovec, j);
872 log_error("Failed to log coredump: %s", strerror(-r));
875 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;