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,
103 "Failed to parse storage setting");
105 typedef enum CoredumpCompression {
106 COREDUMP_COMPRESSION_NONE,
107 COREDUMP_COMPRESSION_XZ,
108 _COREDUMP_COMPRESSION_MAX,
109 _COREDUMP_COMPRESSION_INVALID = -1
110 } CoredumpCompression;
112 static const char* const coredump_compression_table[_COREDUMP_COMPRESSION_MAX] = {
113 [COREDUMP_COMPRESSION_NONE] = "none",
114 [COREDUMP_COMPRESSION_XZ] = "xz",
117 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_compression, CoredumpCompression);
118 static DEFINE_CONFIG_PARSE_ENUM(config_parse_coredump_compression, coredump_compression,
120 "Failed to parse compression setting");
122 static CoredumpStorage arg_storage = COREDUMP_STORAGE_EXTERNAL;
123 static CoredumpCompression arg_compression = COREDUMP_COMPRESSION_XZ;
124 static unsigned arg_compression_level = LZMA_PRESET_DEFAULT;
125 static off_t arg_process_size_max = PROCESS_SIZE_MAX;
126 static off_t arg_external_size_max = EXTERNAL_SIZE_MAX;
127 static size_t arg_journal_size_max = JOURNAL_SIZE_MAX;
128 static off_t arg_keep_free = (off_t) -1;
129 static off_t arg_max_use = (off_t) -1;
131 static int parse_config(void) {
133 static const ConfigTableItem items[] = {
134 { "Coredump", "Storage", config_parse_coredump_storage, 0, &arg_storage },
135 { "Coredump", "Compression", config_parse_coredump_compression, 0, &arg_compression },
136 { "Coredump", "CompressionLevel", config_parse_unsigned, 0, &arg_compression_level },
138 { "Coredump", "ProcessSizeMax", config_parse_iec_off, 0, &arg_process_size_max },
139 { "Coredump", "ExternalSizeMax", config_parse_iec_off, 0, &arg_external_size_max },
140 { "Coredump", "JournalSizeMax", config_parse_iec_size, 0, &arg_journal_size_max },
141 { "Coredump", "KeepFree", config_parse_iec_off, 0, &arg_keep_free },
142 { "Coredump", "MaxUse", config_parse_iec_off, 0, &arg_max_use },
148 "/etc/systemd/coredump.conf",
151 config_item_table_lookup,
158 if (arg_compression_level > 9) {
159 log_warning("Invalid CompressionLevel %u, ignoring.", arg_compression_level);
160 arg_compression_level = LZMA_PRESET_DEFAULT;
165 static int fix_acl(int fd, uid_t uid) {
168 _cleanup_(acl_freep) acl_t acl = NULL;
170 acl_permset_t permset;
172 if (uid <= SYSTEM_UID_MAX)
175 /* Make sure normal users can read (but not write or delete)
176 * their own coredumps */
178 acl = acl_get_fd(fd);
180 log_error("Failed to get ACL: %m");
184 if (acl_create_entry(&acl, &entry) < 0 ||
185 acl_set_tag_type(entry, ACL_USER) < 0 ||
186 acl_set_qualifier(entry, &uid) < 0) {
187 log_error("Failed to patch ACL: %m");
191 if (acl_get_permset(entry, &permset) < 0 ||
192 acl_add_perm(permset, ACL_READ) < 0 ||
193 calc_acl_mask_if_needed(&acl) < 0) {
194 log_warning("Failed to patch ACL: %m");
198 if (acl_set_fd(fd, acl) < 0) {
199 log_error("Failed to apply ACL: %m");
207 static int fix_xattr(int fd, const char *info[_INFO_LEN]) {
209 static const char * const xattrs[_INFO_LEN] = {
210 [INFO_PID] = "user.coredump.pid",
211 [INFO_UID] = "user.coredump.uid",
212 [INFO_GID] = "user.coredump.gid",
213 [INFO_SIGNAL] = "user.coredump.signal",
214 [INFO_TIMESTAMP] = "user.coredump.timestamp",
215 [INFO_COMM] = "user.coredump.comm",
216 [INFO_EXE] = "user.coredump.exe",
222 /* Attach some metadata to coredumps via extended
223 * attributes. Just because we can. */
225 for (i = 0; i < _INFO_LEN; i++) {
228 if (isempty(info[i]) || !xattrs[i])
231 k = fsetxattr(fd, xattrs[i], info[i], strlen(info[i]), XATTR_CREATE);
239 #define filename_escape(s) xescape((s), "./ ")
241 static int fix_permissions(int fd, const char *filename, const char *target,
242 const char *info[_INFO_LEN], uid_t uid) {
244 /* Ignore errors on these */
250 log_error("Failed to sync coredump %s: %m", filename);
254 if (rename(filename, target) < 0) {
255 log_error("Failed to rename coredump %s -> %s: %m", filename, target);
262 static int maybe_remove_external_coredump(const char *filename, off_t size) {
264 /* Returns 1 if might remove, 0 if will not remove, <0 on error. */
266 if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
267 size <= arg_external_size_max)
273 if (unlink(filename) < 0) {
274 log_error("Failed to unlink %s: %m", filename);
281 static int save_external_coredump(
282 const char *info[_INFO_LEN],
288 _cleanup_free_ char *p = NULL, *t = NULL, *c = NULL, *fn = NULL, *tmp = NULL, *u = NULL;
289 _cleanup_close_ int fd = -1;
295 assert(ret_filename);
299 c = filename_escape(info[INFO_COMM]);
303 p = filename_escape(info[INFO_PID]);
307 u = filename_escape(info[INFO_UID]);
311 t = filename_escape(info[INFO_TIMESTAMP]);
315 r = sd_id128_get_boot(&boot);
317 log_error("Failed to determine boot ID: %s", strerror(-r));
322 "/var/lib/systemd/coredump/core.%s.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
325 SD_ID128_FORMAT_VAL(boot),
331 tmp = tempfn_random(fn);
335 mkdir_p_label("/var/lib/systemd/coredump", 0755);
337 fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
339 log_error("Failed to create coredump file %s: %m", tmp);
343 r = copy_bytes(STDIN_FILENO, fd, arg_process_size_max);
345 log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", info[INFO_PID], info[INFO_COMM]);
347 } else if (IN_SET(r, -EDQUOT, -ENOSPC)) {
348 log_error("Not enough disk space for coredump of %s (%s), refusing.", info[INFO_PID], info[INFO_COMM]);
351 log_error("Failed to dump coredump to file: %s", strerror(-r));
355 if (fstat(fd, &st) < 0) {
356 log_error("Failed to fstat coredump %s: %m", tmp);
360 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
361 log_error("Failed to seek on %s: %m", tmp);
366 /* If we will remove the coredump anyway, do not compress. */
367 if (maybe_remove_external_coredump(NULL, st.st_size) == 0
368 && arg_compression == COREDUMP_COMPRESSION_XZ) {
370 _cleanup_free_ char *fn2 = NULL;
372 _cleanup_close_ int fd2 = -1;
374 tmp2 = strappenda(tmp, ".xz");
375 fd2 = open(tmp2, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
377 log_error("Failed to create file %s: %m", tmp2);
381 r = compress_stream(fd, fd2, arg_compression_level, -1);
383 log_error("Failed to compress %s: %s", tmp2, strerror(-r));
384 unlink_noerrno(tmp2);
388 fn2 = strappend(fn, ".xz");
394 r = fix_permissions(fd2, tmp2, fn2, info, uid);
398 *ret_filename = fn2; /* compressed */
399 *ret_fd = fd; /* uncompressed */
400 *ret_size = st.st_size; /* uncompressed */
408 unlink_noerrno(tmp2);
413 r = fix_permissions(fd, tmp, fn, info, uid);
419 *ret_size = st.st_size;
431 static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
432 _cleanup_free_ char *field = NULL;
439 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
440 log_warning("Failed to seek: %m");
444 field = malloc(9 + size);
446 log_warning("Failed to allocate memory for coredump, coredump will not be stored.");
450 memcpy(field, "COREDUMP=", 9);
452 n = read(fd, field + 9, size);
454 log_error("Failed to read core data: %s", strerror(-n));
457 if ((size_t) n < size) {
458 log_error("Core data too short.");
463 *ret_size = size + 9;
470 int main(int argc, char* argv[]) {
472 _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
473 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
474 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
475 *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL,
476 *exe = NULL, *comm = NULL, *filename = NULL;
477 const char *info[_INFO_LEN];
479 _cleanup_close_ int coredump_fd = -1;
481 struct iovec iovec[18];
484 uid_t uid, owner_uid;
489 /* Make sure we never enter a loop */
490 prctl(PR_SET_DUMPABLE, 0);
492 /* First, log to a safe place, since we don't know what
493 * crashed and it might be journald which we'd rather not log
495 log_set_target(LOG_TARGET_KMSG);
498 if (argc < INFO_COMM + 1) {
499 log_error("Not enough arguments passed from kernel (%d, expected %d).",
500 argc - 1, INFO_COMM + 1 - 1);
505 /* Ignore all parse errors */
507 log_debug("Selected storage '%s'.",
508 coredump_storage_to_string(arg_storage));
509 log_debug("Selected compression %s:%u.",
510 coredump_compression_to_string(arg_compression),
511 arg_compression_level);
513 r = parse_uid(argv[INFO_UID + 1], &uid);
515 log_error("Failed to parse UID.");
519 r = parse_pid(argv[INFO_PID + 1], &pid);
521 log_error("Failed to parse PID.");
525 r = parse_gid(argv[INFO_GID + 1], &gid);
527 log_error("Failed to parse GID.");
531 if (get_process_comm(pid, &comm) < 0) {
532 log_warning("Failed to get COMM, falling back to the commandline.");
533 comm = strv_join(argv + INFO_COMM + 1, " ");
536 if (get_process_exe(pid, &exe) < 0)
537 log_warning("Failed to get EXE.");
539 info[INFO_PID] = argv[INFO_PID + 1];
540 info[INFO_UID] = argv[INFO_UID + 1];
541 info[INFO_GID] = argv[INFO_GID + 1];
542 info[INFO_SIGNAL] = argv[INFO_SIGNAL + 1];
543 info[INFO_TIMESTAMP] = argv[INFO_TIMESTAMP + 1];
544 info[INFO_COMM] = comm;
545 info[INFO_EXE] = exe;
547 if (cg_pid_get_unit(pid, &t) >= 0) {
549 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
551 /* If we are journald, we cut things short,
552 * don't write to the journal, but still
553 * create a coredump. */
555 if (arg_storage != COREDUMP_STORAGE_NONE)
556 arg_storage = COREDUMP_STORAGE_EXTERNAL;
558 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
562 r = maybe_remove_external_coredump(filename, coredump_size);
566 log_info("Detected coredump of the journal daemon itself, diverted to %s.", filename);
570 core_unit = strappend("COREDUMP_UNIT=", t);
571 } else if (cg_pid_get_user_unit(pid, &t) >= 0)
572 core_unit = strappend("COREDUMP_USER_UNIT=", t);
575 IOVEC_SET_STRING(iovec[j++], core_unit);
577 /* OK, now we know it's not the journal, hence we can make use
579 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
582 core_pid = strappend("COREDUMP_PID=", info[INFO_PID]);
584 IOVEC_SET_STRING(iovec[j++], core_pid);
586 core_uid = strappend("COREDUMP_UID=", info[INFO_UID]);
588 IOVEC_SET_STRING(iovec[j++], core_uid);
590 core_gid = strappend("COREDUMP_GID=", info[INFO_GID]);
592 IOVEC_SET_STRING(iovec[j++], core_gid);
594 core_signal = strappend("COREDUMP_SIGNAL=", info[INFO_SIGNAL]);
596 IOVEC_SET_STRING(iovec[j++], core_signal);
598 if (sd_pid_get_session(pid, &t) >= 0) {
599 core_session = strappend("COREDUMP_SESSION=", t);
603 IOVEC_SET_STRING(iovec[j++], core_session);
606 if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
607 asprintf(&core_owner_uid, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
610 IOVEC_SET_STRING(iovec[j++], core_owner_uid);
613 if (sd_pid_get_slice(pid, &t) >= 0) {
614 core_slice = strappend("COREDUMP_SLICE=", t);
618 IOVEC_SET_STRING(iovec[j++], core_slice);
622 core_comm = strappend("COREDUMP_COMM=", comm);
624 IOVEC_SET_STRING(iovec[j++], core_comm);
628 core_exe = strappend("COREDUMP_EXE=", exe);
630 IOVEC_SET_STRING(iovec[j++], core_exe);
633 if (get_process_cmdline(pid, 0, false, &t) >= 0) {
634 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
638 IOVEC_SET_STRING(iovec[j++], core_cmdline);
641 if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
642 core_cgroup = strappend("COREDUMP_CGROUP=", t);
646 IOVEC_SET_STRING(iovec[j++], core_cgroup);
649 core_timestamp = strjoin("COREDUMP_TIMESTAMP=", info[INFO_TIMESTAMP], "000000", NULL);
651 IOVEC_SET_STRING(iovec[j++], core_timestamp);
653 IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
654 IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
656 /* Vacuum before we write anything again */
657 coredump_vacuum(-1, arg_keep_free, arg_max_use);
659 /* Always stream the coredump to disk, if that's possible */
660 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
662 /* skip whole core dumping part */
665 /* If we don't want to keep the coredump on disk, remove it
666 * now, as later on we will lack the privileges for
667 * it. However, we keep the fd to it, so that we can still
668 * process it and log it. */
669 r = maybe_remove_external_coredump(filename, coredump_size);
673 const char *coredump_filename;
675 coredump_filename = strappenda("COREDUMP_FILENAME=", filename);
676 IOVEC_SET_STRING(iovec[j++], coredump_filename);
679 /* Vacuum again, but exclude the coredump we just created */
680 coredump_vacuum(coredump_fd, arg_keep_free, arg_max_use);
682 /* Now, let's drop privileges to become the user who owns the
683 * segfaulted process and allocate the coredump memory under
684 * his uid. This also ensures that the credentials journald
685 * will see are the ones of the coredumping user, thus making
686 * sure the user himself gets access to the core dump. */
687 if (setresgid(gid, gid, gid) < 0 ||
688 setresuid(uid, uid, uid) < 0) {
689 log_error("Failed to drop privileges: %m");
695 /* Try to get a strack trace if we can */
696 if (coredump_size <= arg_process_size_max) {
697 _cleanup_free_ char *stacktrace = NULL;
699 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
701 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.\n\n", stacktrace, NULL);
703 log_warning("Failed to generate stack trace: %s", strerror(-r));
709 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.", NULL);
711 IOVEC_SET_STRING(iovec[j++], core_message);
713 /* Optionally store the entire coredump in the journal */
714 if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
715 coredump_size <= (off_t) arg_journal_size_max) {
718 /* Store the coredump itself in the journal */
720 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
722 iovec[j].iov_base = coredump_data;
723 iovec[j].iov_len = sz;
728 r = sd_journal_sendv(iovec, j);
730 log_error("Failed to log coredump: %s", strerror(-r));
733 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;