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;
144 if (uid <= SYSTEM_UID_MAX)
147 /* Make sure normal users can read (but not write or delete)
148 * their own coredumps */
150 acl = acl_get_fd(fd);
152 log_error("Failed to get ACL: %m");
156 if (acl_create_entry(&acl, &entry) < 0 ||
157 acl_set_tag_type(entry, ACL_USER) < 0 ||
158 acl_set_qualifier(entry, &uid) < 0) {
159 log_error("Failed to patch ACL: %m");
163 if (acl_get_permset(entry, &permset) < 0 ||
164 acl_add_perm(permset, ACL_READ) < 0 ||
165 calc_acl_mask_if_needed(&acl) < 0) {
166 log_warning("Failed to patch ACL: %m");
170 if (acl_set_fd(fd, acl) < 0) {
171 log_error("Failed to apply ACL: %m");
179 static int fix_xattr(int fd, const char *info[_INFO_LEN]) {
181 static const char * const xattrs[_INFO_LEN] = {
182 [INFO_PID] = "user.coredump.pid",
183 [INFO_UID] = "user.coredump.uid",
184 [INFO_GID] = "user.coredump.gid",
185 [INFO_SIGNAL] = "user.coredump.signal",
186 [INFO_TIMESTAMP] = "user.coredump.timestamp",
187 [INFO_COMM] = "user.coredump.comm",
188 [INFO_EXE] = "user.coredump.exe",
196 /* Attach some metadata to coredumps via extended
197 * attributes. Just because we can. */
199 for (i = 0; i < _INFO_LEN; i++) {
202 if (isempty(info[i]) || !xattrs[i])
205 k = fsetxattr(fd, xattrs[i], info[i], strlen(info[i]), XATTR_CREATE);
213 #define filename_escape(s) xescape((s), "./ ")
215 static int fix_permissions(
217 const char *filename,
219 const char *info[_INFO_LEN],
227 /* Ignore errors on these */
233 log_error("Failed to sync coredump %s: %m", filename);
237 if (rename(filename, target) < 0) {
238 log_error("Failed to rename coredump %s -> %s: %m", filename, target);
245 static int maybe_remove_external_coredump(const char *filename, off_t size) {
247 /* Returns 1 if might remove, 0 if will not remove, < 0 on error. */
249 if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
250 size <= arg_external_size_max)
256 if (unlink(filename) < 0 && errno != ENOENT) {
257 log_error("Failed to unlink %s: %m", filename);
264 static int make_filename(const char *info[_INFO_LEN], char **ret) {
265 _cleanup_free_ char *c = NULL, *u = NULL, *p = NULL, *t = NULL;
271 c = filename_escape(info[INFO_COMM]);
275 u = filename_escape(info[INFO_UID]);
279 r = sd_id128_get_boot(&boot);
283 p = filename_escape(info[INFO_PID]);
287 t = filename_escape(info[INFO_TIMESTAMP]);
292 "/var/lib/systemd/coredump/core.%s.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
295 SD_ID128_FORMAT_VAL(boot),
303 static int save_external_coredump(
304 const char *info[_INFO_LEN],
310 _cleanup_free_ char *fn = NULL, *tmp = NULL;
311 _cleanup_close_ int fd = -1;
316 assert(ret_filename);
320 r = make_filename(info, &fn);
322 log_error("Failed to determine coredump file name: %s", strerror(-r));
326 tmp = tempfn_random(fn);
330 mkdir_p_label("/var/lib/systemd/coredump", 0755);
332 fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
334 log_error("Failed to create coredump file %s: %m", tmp);
338 r = copy_bytes(STDIN_FILENO, fd, arg_process_size_max);
340 log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", info[INFO_PID], info[INFO_COMM]);
342 } else if (IN_SET(r, -EDQUOT, -ENOSPC)) {
343 log_error("Not enough disk space for coredump of %s (%s), refusing.", info[INFO_PID], info[INFO_COMM]);
346 log_error("Failed to dump coredump to file: %s", strerror(-r));
350 if (fstat(fd, &st) < 0) {
351 log_error("Failed to fstat coredump %s: %m", tmp);
355 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
356 log_error("Failed to seek on %s: %m", tmp);
361 /* If we will remove the coredump anyway, do not compress. */
362 if (maybe_remove_external_coredump(NULL, st.st_size) == 0
365 _cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL;
366 _cleanup_close_ int fd_compressed = -1;
368 fn_compressed = strappend(fn, ".xz");
369 if (!fn_compressed) {
374 tmp_compressed = tempfn_random(fn_compressed);
375 if (!tmp_compressed) {
380 fd_compressed = open(tmp_compressed, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
381 if (fd_compressed < 0) {
382 log_error("Failed to create file %s: %m", tmp_compressed);
386 r = compress_stream(fd, fd_compressed, LZMA_PRESET_DEFAULT, -1);
388 log_error("Failed to compress %s: %s", tmp_compressed, strerror(-r));
389 goto fail_compressed;
392 r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, info, uid);
394 goto fail_compressed;
396 /* OK, this worked, we can get rid of the uncompressed version now */
399 *ret_filename = fn_compressed; /* compressed */
400 *ret_fd = fd; /* uncompressed */
401 *ret_size = st.st_size; /* uncompressed */
403 fn_compressed = NULL;
409 unlink_noerrno(tmp_compressed);
414 r = fix_permissions(fd, tmp, fn, info, uid);
420 *ret_size = st.st_size;
432 static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
433 _cleanup_free_ char *field = NULL;
440 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
441 log_warning("Failed to seek: %m");
445 field = malloc(9 + size);
447 log_warning("Failed to allocate memory for coredump, coredump will not be stored.");
451 memcpy(field, "COREDUMP=", 9);
453 n = read(fd, field + 9, size);
455 log_error("Failed to read core data: %s", strerror(-n));
458 if ((size_t) n < size) {
459 log_error("Core data too short.");
464 *ret_size = size + 9;
471 int main(int argc, char* argv[]) {
473 _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
474 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
475 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
476 *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL,
477 *exe = NULL, *comm = NULL, *filename = NULL;
478 const char *info[_INFO_LEN];
480 _cleanup_close_ int coredump_fd = -1;
482 struct iovec iovec[18];
485 uid_t uid, owner_uid;
490 /* Make sure we never enter a loop */
491 prctl(PR_SET_DUMPABLE, 0);
493 /* First, log to a safe place, since we don't know what
494 * crashed and it might be journald which we'd rather not log
496 log_set_target(LOG_TARGET_KMSG);
499 if (argc < INFO_COMM + 1) {
500 log_error("Not enough arguments passed from kernel (%d, expected %d).",
501 argc - 1, INFO_COMM + 1 - 1);
506 /* Ignore all parse errors */
509 log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
510 log_debug("Selected compression %s.", yes_no(arg_compress));
512 r = parse_uid(argv[INFO_UID + 1], &uid);
514 log_error("Failed to parse UID.");
518 r = parse_pid(argv[INFO_PID + 1], &pid);
520 log_error("Failed to parse PID.");
524 r = parse_gid(argv[INFO_GID + 1], &gid);
526 log_error("Failed to parse GID.");
530 if (get_process_comm(pid, &comm) < 0) {
531 log_warning("Failed to get COMM, falling back to the commandline.");
532 comm = strv_join(argv + INFO_COMM + 1, " ");
535 if (get_process_exe(pid, &exe) < 0)
536 log_warning("Failed to get EXE.");
538 info[INFO_PID] = argv[INFO_PID + 1];
539 info[INFO_UID] = argv[INFO_UID + 1];
540 info[INFO_GID] = argv[INFO_GID + 1];
541 info[INFO_SIGNAL] = argv[INFO_SIGNAL + 1];
542 info[INFO_TIMESTAMP] = argv[INFO_TIMESTAMP + 1];
543 info[INFO_COMM] = comm;
544 info[INFO_EXE] = exe;
546 if (cg_pid_get_unit(pid, &t) >= 0) {
548 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
550 /* If we are journald, we cut things short,
551 * don't write to the journal, but still
552 * create a coredump. */
554 if (arg_storage != COREDUMP_STORAGE_NONE)
555 arg_storage = COREDUMP_STORAGE_EXTERNAL;
557 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
561 r = maybe_remove_external_coredump(filename, coredump_size);
565 log_info("Detected coredump of the journal daemon itself, diverted to %s.", filename);
569 core_unit = strappend("COREDUMP_UNIT=", t);
570 } else if (cg_pid_get_user_unit(pid, &t) >= 0)
571 core_unit = strappend("COREDUMP_USER_UNIT=", t);
574 IOVEC_SET_STRING(iovec[j++], core_unit);
576 /* OK, now we know it's not the journal, hence we can make use
578 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
581 core_pid = strappend("COREDUMP_PID=", info[INFO_PID]);
583 IOVEC_SET_STRING(iovec[j++], core_pid);
585 core_uid = strappend("COREDUMP_UID=", info[INFO_UID]);
587 IOVEC_SET_STRING(iovec[j++], core_uid);
589 core_gid = strappend("COREDUMP_GID=", info[INFO_GID]);
591 IOVEC_SET_STRING(iovec[j++], core_gid);
593 core_signal = strappend("COREDUMP_SIGNAL=", info[INFO_SIGNAL]);
595 IOVEC_SET_STRING(iovec[j++], core_signal);
597 if (sd_pid_get_session(pid, &t) >= 0) {
598 core_session = strappend("COREDUMP_SESSION=", t);
602 IOVEC_SET_STRING(iovec[j++], core_session);
605 if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
606 asprintf(&core_owner_uid, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
609 IOVEC_SET_STRING(iovec[j++], core_owner_uid);
612 if (sd_pid_get_slice(pid, &t) >= 0) {
613 core_slice = strappend("COREDUMP_SLICE=", t);
617 IOVEC_SET_STRING(iovec[j++], core_slice);
621 core_comm = strappend("COREDUMP_COMM=", comm);
623 IOVEC_SET_STRING(iovec[j++], core_comm);
627 core_exe = strappend("COREDUMP_EXE=", exe);
629 IOVEC_SET_STRING(iovec[j++], core_exe);
632 if (get_process_cmdline(pid, 0, false, &t) >= 0) {
633 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
637 IOVEC_SET_STRING(iovec[j++], core_cmdline);
640 if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
641 core_cgroup = strappend("COREDUMP_CGROUP=", t);
645 IOVEC_SET_STRING(iovec[j++], core_cgroup);
648 core_timestamp = strjoin("COREDUMP_TIMESTAMP=", info[INFO_TIMESTAMP], "000000", NULL);
650 IOVEC_SET_STRING(iovec[j++], core_timestamp);
652 IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
653 IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
655 /* Vacuum before we write anything again */
656 coredump_vacuum(-1, arg_keep_free, arg_max_use);
658 /* Always stream the coredump to disk, if that's possible */
659 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
661 /* skip whole core dumping part */
664 /* If we don't want to keep the coredump on disk, remove it
665 * now, as later on we will lack the privileges for
666 * it. However, we keep the fd to it, so that we can still
667 * process it and log it. */
668 r = maybe_remove_external_coredump(filename, coredump_size);
672 const char *coredump_filename;
674 coredump_filename = strappenda("COREDUMP_FILENAME=", filename);
675 IOVEC_SET_STRING(iovec[j++], coredump_filename);
678 /* Vacuum again, but exclude the coredump we just created */
679 coredump_vacuum(coredump_fd, arg_keep_free, arg_max_use);
681 /* Now, let's drop privileges to become the user who owns the
682 * segfaulted process and allocate the coredump memory under
683 * his uid. This also ensures that the credentials journald
684 * will see are the ones of the coredumping user, thus making
685 * sure the user himself gets access to the core dump. */
686 if (setresgid(gid, gid, gid) < 0 ||
687 setresuid(uid, uid, uid) < 0) {
688 log_error("Failed to drop privileges: %m");
694 /* Try to get a strack trace if we can */
695 if (coredump_size <= arg_process_size_max) {
696 _cleanup_free_ char *stacktrace = NULL;
698 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
700 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.\n\n", stacktrace, NULL);
702 log_warning("Failed to generate stack trace: %s", strerror(-r));
708 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.", NULL);
710 IOVEC_SET_STRING(iovec[j++], core_message);
712 /* Optionally store the entire coredump in the journal */
713 if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
714 coredump_size <= (off_t) arg_journal_size_max) {
717 /* Store the coredump itself in the journal */
719 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
721 iovec[j].iov_base = coredump_data;
722 iovec[j].iov_len = sz;
727 r = sd_journal_sendv(iovec, j);
729 log_error("Failed to log coredump: %s", strerror(-r));
732 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;