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>
29 #include <systemd/sd-journal.h>
30 #include <systemd/sd-login.h>
38 #include "cgroup-util.h"
39 #include "journald-native.h"
40 #include "conf-parser.h"
42 #include "stacktrace.h"
43 #include "path-util.h"
45 #include "coredump-vacuum.h"
49 # include "acl-util.h"
55 # define LZMA_PRESET_DEFAULT 0
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 ENTRY_SIZE_MAX in journald-native.c. */
71 assert_cc(JOURNAL_SIZE_MAX <= ENTRY_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 },
125 "/etc/systemd/coredump.conf",
128 config_item_table_lookup,
135 static int fix_acl(int fd, uid_t uid) {
138 _cleanup_(acl_freep) acl_t acl = NULL;
140 acl_permset_t permset;
142 if (uid <= SYSTEM_UID_MAX)
145 /* Make sure normal users can read (but not write or delete)
146 * their own coredumps */
148 acl = acl_get_fd(fd);
150 log_error("Failed to get ACL: %m");
154 if (acl_create_entry(&acl, &entry) < 0 ||
155 acl_set_tag_type(entry, ACL_USER) < 0 ||
156 acl_set_qualifier(entry, &uid) < 0) {
157 log_error("Failed to patch ACL: %m");
161 if (acl_get_permset(entry, &permset) < 0 ||
162 acl_add_perm(permset, ACL_READ) < 0 ||
163 calc_acl_mask_if_needed(&acl) < 0) {
164 log_warning("Failed to patch ACL: %m");
168 if (acl_set_fd(fd, acl) < 0) {
169 log_error("Failed to apply ACL: %m");
177 static int fix_xattr(int fd, const char *info[_INFO_LEN]) {
179 static const char * const xattrs[_INFO_LEN] = {
180 [INFO_PID] = "user.coredump.pid",
181 [INFO_UID] = "user.coredump.uid",
182 [INFO_GID] = "user.coredump.gid",
183 [INFO_SIGNAL] = "user.coredump.signal",
184 [INFO_TIMESTAMP] = "user.coredump.timestamp",
185 [INFO_COMM] = "user.coredump.comm",
186 [INFO_EXE] = "user.coredump.exe",
192 /* Attach some metadata to coredumps via extended
193 * attributes. Just because we can. */
195 for (i = 0; i < _INFO_LEN; i++) {
198 if (isempty(info[i]) || !xattrs[i])
201 k = fsetxattr(fd, xattrs[i], info[i], strlen(info[i]), XATTR_CREATE);
209 #define filename_escape(s) xescape((s), "./ ")
211 static int fix_permissions(int fd, const char *filename, const char *target,
212 const char *info[_INFO_LEN], uid_t uid) {
214 /* Ignore errors on these */
220 log_error("Failed to sync coredump %s: %m", filename);
224 if (rename(filename, target) < 0) {
225 log_error("Failed to rename coredump %s -> %s: %m", filename, target);
232 static int maybe_remove_external_coredump(const char *filename, off_t size) {
234 /* Returns 1 if might remove, 0 if will not remove, <0 on error. */
236 if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
237 size <= arg_external_size_max)
243 if (unlink(filename) < 0 && errno != ENOENT) {
244 log_error("Failed to unlink %s: %m", filename);
251 static int save_external_coredump(
252 const char *info[_INFO_LEN],
258 _cleanup_free_ char *p = NULL, *t = NULL, *c = NULL, *fn = NULL, *tmp = NULL, *u = NULL;
259 _cleanup_close_ int fd = -1;
265 assert(ret_filename);
269 c = filename_escape(info[INFO_COMM]);
273 p = filename_escape(info[INFO_PID]);
277 u = filename_escape(info[INFO_UID]);
281 t = filename_escape(info[INFO_TIMESTAMP]);
285 r = sd_id128_get_boot(&boot);
287 log_error("Failed to determine boot ID: %s", strerror(-r));
292 "/var/lib/systemd/coredump/core.%s.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
295 SD_ID128_FORMAT_VAL(boot),
301 tmp = tempfn_random(fn);
305 mkdir_p_label("/var/lib/systemd/coredump", 0755);
307 fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
309 log_error("Failed to create coredump file %s: %m", tmp);
313 r = copy_bytes(STDIN_FILENO, fd, arg_process_size_max);
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("Failed to dump coredump to file: %s", strerror(-r));
325 if (fstat(fd, &st) < 0) {
326 log_error("Failed to fstat coredump %s: %m", tmp);
330 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
331 log_error("Failed to seek on %s: %m", tmp);
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 *fn2 = NULL;
342 _cleanup_close_ int fd2 = -1;
344 tmp2 = strappenda(tmp, ".xz");
345 fd2 = open(tmp2, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
347 log_error("Failed to create file %s: %m", tmp2);
351 r = compress_stream(fd, fd2, LZMA_PRESET_DEFAULT, -1);
353 log_error("Failed to compress %s: %s", tmp2, strerror(-r));
354 unlink_noerrno(tmp2);
358 fn2 = strappend(fn, ".xz");
364 r = fix_permissions(fd2, tmp2, fn2, info, uid);
368 *ret_filename = fn2; /* compressed */
369 *ret_fd = fd; /* uncompressed */
370 *ret_size = st.st_size; /* uncompressed */
378 unlink_noerrno(tmp2);
383 r = fix_permissions(fd, tmp, fn, info, uid);
389 *ret_size = st.st_size;
401 static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
402 _cleanup_free_ char *field = NULL;
409 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
410 log_warning("Failed to seek: %m");
414 field = malloc(9 + size);
416 log_warning("Failed to allocate memory for coredump, coredump will not be stored.");
420 memcpy(field, "COREDUMP=", 9);
422 n = read(fd, field + 9, size);
424 log_error("Failed to read core data: %s", strerror(-n));
427 if ((size_t) n < size) {
428 log_error("Core data too short.");
433 *ret_size = size + 9;
440 int main(int argc, char* argv[]) {
442 _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
443 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
444 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
445 *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL,
446 *exe = NULL, *comm = NULL, *filename = NULL;
447 const char *info[_INFO_LEN];
449 _cleanup_close_ int coredump_fd = -1;
451 struct iovec iovec[18];
454 uid_t uid, owner_uid;
459 /* Make sure we never enter a loop */
460 prctl(PR_SET_DUMPABLE, 0);
462 /* First, log to a safe place, since we don't know what
463 * crashed and it might be journald which we'd rather not log
465 log_set_target(LOG_TARGET_KMSG);
468 if (argc < INFO_COMM + 1) {
469 log_error("Not enough arguments passed from kernel (%d, expected %d).",
470 argc - 1, INFO_COMM + 1 - 1);
475 /* Ignore all parse errors */
478 log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
479 log_debug("Selected compression %s.", yes_no(arg_compress));
481 r = parse_uid(argv[INFO_UID + 1], &uid);
483 log_error("Failed to parse UID.");
487 r = parse_pid(argv[INFO_PID + 1], &pid);
489 log_error("Failed to parse PID.");
493 r = parse_gid(argv[INFO_GID + 1], &gid);
495 log_error("Failed to parse GID.");
499 if (get_process_comm(pid, &comm) < 0) {
500 log_warning("Failed to get COMM, falling back to the commandline.");
501 comm = strv_join(argv + INFO_COMM + 1, " ");
504 if (get_process_exe(pid, &exe) < 0)
505 log_warning("Failed to get EXE.");
507 info[INFO_PID] = argv[INFO_PID + 1];
508 info[INFO_UID] = argv[INFO_UID + 1];
509 info[INFO_GID] = argv[INFO_GID + 1];
510 info[INFO_SIGNAL] = argv[INFO_SIGNAL + 1];
511 info[INFO_TIMESTAMP] = argv[INFO_TIMESTAMP + 1];
512 info[INFO_COMM] = comm;
513 info[INFO_EXE] = exe;
515 if (cg_pid_get_unit(pid, &t) >= 0) {
517 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
519 /* If we are journald, we cut things short,
520 * don't write to the journal, but still
521 * create a coredump. */
523 if (arg_storage != COREDUMP_STORAGE_NONE)
524 arg_storage = COREDUMP_STORAGE_EXTERNAL;
526 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
530 r = maybe_remove_external_coredump(filename, coredump_size);
534 log_info("Detected coredump of the journal daemon itself, diverted to %s.", filename);
538 core_unit = strappend("COREDUMP_UNIT=", t);
539 } else if (cg_pid_get_user_unit(pid, &t) >= 0)
540 core_unit = strappend("COREDUMP_USER_UNIT=", t);
543 IOVEC_SET_STRING(iovec[j++], core_unit);
545 /* OK, now we know it's not the journal, hence we can make use
547 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
550 core_pid = strappend("COREDUMP_PID=", info[INFO_PID]);
552 IOVEC_SET_STRING(iovec[j++], core_pid);
554 core_uid = strappend("COREDUMP_UID=", info[INFO_UID]);
556 IOVEC_SET_STRING(iovec[j++], core_uid);
558 core_gid = strappend("COREDUMP_GID=", info[INFO_GID]);
560 IOVEC_SET_STRING(iovec[j++], core_gid);
562 core_signal = strappend("COREDUMP_SIGNAL=", info[INFO_SIGNAL]);
564 IOVEC_SET_STRING(iovec[j++], core_signal);
566 if (sd_pid_get_session(pid, &t) >= 0) {
567 core_session = strappend("COREDUMP_SESSION=", t);
571 IOVEC_SET_STRING(iovec[j++], core_session);
574 if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
575 asprintf(&core_owner_uid, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
578 IOVEC_SET_STRING(iovec[j++], core_owner_uid);
581 if (sd_pid_get_slice(pid, &t) >= 0) {
582 core_slice = strappend("COREDUMP_SLICE=", t);
586 IOVEC_SET_STRING(iovec[j++], core_slice);
590 core_comm = strappend("COREDUMP_COMM=", comm);
592 IOVEC_SET_STRING(iovec[j++], core_comm);
596 core_exe = strappend("COREDUMP_EXE=", exe);
598 IOVEC_SET_STRING(iovec[j++], core_exe);
601 if (get_process_cmdline(pid, 0, false, &t) >= 0) {
602 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
606 IOVEC_SET_STRING(iovec[j++], core_cmdline);
609 if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
610 core_cgroup = strappend("COREDUMP_CGROUP=", t);
614 IOVEC_SET_STRING(iovec[j++], core_cgroup);
617 core_timestamp = strjoin("COREDUMP_TIMESTAMP=", info[INFO_TIMESTAMP], "000000", NULL);
619 IOVEC_SET_STRING(iovec[j++], core_timestamp);
621 IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
622 IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
624 /* Vacuum before we write anything again */
625 coredump_vacuum(-1, arg_keep_free, arg_max_use);
627 /* Always stream the coredump to disk, if that's possible */
628 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
630 /* skip whole core dumping part */
633 /* If we don't want to keep the coredump on disk, remove it
634 * now, as later on we will lack the privileges for
635 * it. However, we keep the fd to it, so that we can still
636 * process it and log it. */
637 r = maybe_remove_external_coredump(filename, coredump_size);
641 const char *coredump_filename;
643 coredump_filename = strappenda("COREDUMP_FILENAME=", filename);
644 IOVEC_SET_STRING(iovec[j++], coredump_filename);
647 /* Vacuum again, but exclude the coredump we just created */
648 coredump_vacuum(coredump_fd, arg_keep_free, arg_max_use);
650 /* Now, let's drop privileges to become the user who owns the
651 * segfaulted process and allocate the coredump memory under
652 * his uid. This also ensures that the credentials journald
653 * will see are the ones of the coredumping user, thus making
654 * sure the user himself gets access to the core dump. */
655 if (setresgid(gid, gid, gid) < 0 ||
656 setresuid(uid, uid, uid) < 0) {
657 log_error("Failed to drop privileges: %m");
663 /* Try to get a strack trace if we can */
664 if (coredump_size <= arg_process_size_max) {
665 _cleanup_free_ char *stacktrace = NULL;
667 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
669 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.\n\n", stacktrace, NULL);
671 log_warning("Failed to generate stack trace: %s", strerror(-r));
677 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.", NULL);
679 IOVEC_SET_STRING(iovec[j++], core_message);
681 /* Optionally store the entire coredump in the journal */
682 if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
683 coredump_size <= (off_t) arg_journal_size_max) {
686 /* Store the coredump itself in the journal */
688 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
690 iovec[j].iov_base = coredump_data;
691 iovec[j].iov_len = sz;
696 r = sd_journal_sendv(iovec, j);
698 log_error("Failed to log coredump: %s", strerror(-r));
701 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;