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"
52 /* The maximum size up to which we process coredumps */
53 #define PROCESS_SIZE_MAX ((off_t) (2LLU*1024LLU*1024LLU*1024LLU))
55 /* The maximum size up to which we leave the coredump around on
57 #define EXTERNAL_SIZE_MAX PROCESS_SIZE_MAX
59 /* The maximum size up to which we store the coredump in the
61 #define JOURNAL_SIZE_MAX ((size_t) (767LU*1024LU*1024LU))
63 /* Make sure to not make this larger than the maximum journal entry
64 * size. See ENTRY_SIZE_MAX in journald-native.c. */
65 assert_cc(JOURNAL_SIZE_MAX <= ENTRY_SIZE_MAX);
78 typedef enum CoredumpStorage {
79 COREDUMP_STORAGE_NONE,
80 COREDUMP_STORAGE_EXTERNAL,
81 COREDUMP_STORAGE_JOURNAL,
82 COREDUMP_STORAGE_BOTH,
83 _COREDUMP_STORAGE_MAX,
84 _COREDUMP_STORAGE_INVALID = -1
87 static const char* const coredump_storage_table[_COREDUMP_STORAGE_MAX] = {
88 [COREDUMP_STORAGE_NONE] = "none",
89 [COREDUMP_STORAGE_EXTERNAL] = "external",
90 [COREDUMP_STORAGE_JOURNAL] = "journal",
91 [COREDUMP_STORAGE_BOTH] = "both",
94 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_storage, CoredumpStorage);
95 static DEFINE_CONFIG_PARSE_ENUM(config_parse_coredump_storage, coredump_storage, CoredumpStorage, "Failed to parse storage setting");
97 static CoredumpStorage arg_storage = COREDUMP_STORAGE_EXTERNAL;
98 static bool arg_compress = true;
99 static off_t arg_process_size_max = PROCESS_SIZE_MAX;
100 static off_t arg_external_size_max = EXTERNAL_SIZE_MAX;
101 static size_t arg_journal_size_max = JOURNAL_SIZE_MAX;
102 static off_t arg_keep_free = (off_t) -1;
103 static off_t arg_max_use = (off_t) -1;
105 static int parse_config(void) {
106 static const ConfigTableItem items[] = {
107 { "Coredump", "Storage", config_parse_coredump_storage, 0, &arg_storage },
108 { "Coredump", "Compress", config_parse_bool, 0, &arg_compress },
109 { "Coredump", "ProcessSizeMax", config_parse_iec_off, 0, &arg_process_size_max },
110 { "Coredump", "ExternalSizeMax", config_parse_iec_off, 0, &arg_external_size_max },
111 { "Coredump", "JournalSizeMax", config_parse_iec_size, 0, &arg_journal_size_max },
112 { "Coredump", "KeepFree", config_parse_iec_off, 0, &arg_keep_free },
113 { "Coredump", "MaxUse", config_parse_iec_off, 0, &arg_max_use },
119 "/etc/systemd/coredump.conf",
122 config_item_table_lookup,
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 int main(int argc, char* argv[]) {
467 _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
468 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
469 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
470 *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL,
471 *exe = NULL, *comm = NULL, *filename = NULL;
472 const char *info[_INFO_LEN];
474 _cleanup_close_ int coredump_fd = -1;
476 struct iovec iovec[18];
479 uid_t uid, owner_uid;
484 /* Make sure we never enter a loop */
485 prctl(PR_SET_DUMPABLE, 0);
487 /* First, log to a safe place, since we don't know what
488 * crashed and it might be journald which we'd rather not log
490 log_set_target(LOG_TARGET_KMSG);
493 if (argc < INFO_COMM + 1) {
494 log_error("Not enough arguments passed from kernel (%d, expected %d).",
495 argc - 1, INFO_COMM + 1 - 1);
500 /* Ignore all parse errors */
503 log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
504 log_debug("Selected compression %s.", yes_no(arg_compress));
506 r = parse_uid(argv[INFO_UID + 1], &uid);
508 log_error("Failed to parse UID.");
512 r = parse_pid(argv[INFO_PID + 1], &pid);
514 log_error("Failed to parse PID.");
518 r = parse_gid(argv[INFO_GID + 1], &gid);
520 log_error("Failed to parse GID.");
524 if (get_process_comm(pid, &comm) < 0) {
525 log_warning("Failed to get COMM, falling back to the commandline.");
526 comm = strv_join(argv + INFO_COMM + 1, " ");
529 if (get_process_exe(pid, &exe) < 0)
530 log_warning("Failed to get EXE.");
532 info[INFO_PID] = argv[INFO_PID + 1];
533 info[INFO_UID] = argv[INFO_UID + 1];
534 info[INFO_GID] = argv[INFO_GID + 1];
535 info[INFO_SIGNAL] = argv[INFO_SIGNAL + 1];
536 info[INFO_TIMESTAMP] = argv[INFO_TIMESTAMP + 1];
537 info[INFO_COMM] = comm;
538 info[INFO_EXE] = exe;
540 if (cg_pid_get_unit(pid, &t) >= 0) {
542 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
544 /* If we are journald, we cut things short,
545 * don't write to the journal, but still
546 * create a coredump. */
548 if (arg_storage != COREDUMP_STORAGE_NONE)
549 arg_storage = COREDUMP_STORAGE_EXTERNAL;
551 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
555 r = maybe_remove_external_coredump(filename, coredump_size);
559 log_info("Detected coredump of the journal daemon itself, diverted to %s.", filename);
563 core_unit = strappend("COREDUMP_UNIT=", t);
564 } else if (cg_pid_get_user_unit(pid, &t) >= 0)
565 core_unit = strappend("COREDUMP_USER_UNIT=", t);
568 IOVEC_SET_STRING(iovec[j++], core_unit);
570 /* OK, now we know it's not the journal, hence we can make use
572 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
575 core_pid = strappend("COREDUMP_PID=", info[INFO_PID]);
577 IOVEC_SET_STRING(iovec[j++], core_pid);
579 core_uid = strappend("COREDUMP_UID=", info[INFO_UID]);
581 IOVEC_SET_STRING(iovec[j++], core_uid);
583 core_gid = strappend("COREDUMP_GID=", info[INFO_GID]);
585 IOVEC_SET_STRING(iovec[j++], core_gid);
587 core_signal = strappend("COREDUMP_SIGNAL=", info[INFO_SIGNAL]);
589 IOVEC_SET_STRING(iovec[j++], core_signal);
591 if (sd_pid_get_session(pid, &t) >= 0) {
592 core_session = strappend("COREDUMP_SESSION=", t);
596 IOVEC_SET_STRING(iovec[j++], core_session);
599 if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
600 asprintf(&core_owner_uid, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
603 IOVEC_SET_STRING(iovec[j++], core_owner_uid);
606 if (sd_pid_get_slice(pid, &t) >= 0) {
607 core_slice = strappend("COREDUMP_SLICE=", t);
611 IOVEC_SET_STRING(iovec[j++], core_slice);
615 core_comm = strappend("COREDUMP_COMM=", comm);
617 IOVEC_SET_STRING(iovec[j++], core_comm);
621 core_exe = strappend("COREDUMP_EXE=", exe);
623 IOVEC_SET_STRING(iovec[j++], core_exe);
626 if (get_process_cmdline(pid, 0, false, &t) >= 0) {
627 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
631 IOVEC_SET_STRING(iovec[j++], core_cmdline);
634 if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
635 core_cgroup = strappend("COREDUMP_CGROUP=", t);
639 IOVEC_SET_STRING(iovec[j++], core_cgroup);
642 core_timestamp = strjoin("COREDUMP_TIMESTAMP=", info[INFO_TIMESTAMP], "000000", NULL);
644 IOVEC_SET_STRING(iovec[j++], core_timestamp);
646 IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
647 IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
649 /* Vacuum before we write anything again */
650 coredump_vacuum(-1, arg_keep_free, arg_max_use);
652 /* Always stream the coredump to disk, if that's possible */
653 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
655 /* skip whole core dumping part */
658 /* If we don't want to keep the coredump on disk, remove it
659 * now, as later on we will lack the privileges for
660 * it. However, we keep the fd to it, so that we can still
661 * process it and log it. */
662 r = maybe_remove_external_coredump(filename, coredump_size);
666 const char *coredump_filename;
668 coredump_filename = strappenda("COREDUMP_FILENAME=", filename);
669 IOVEC_SET_STRING(iovec[j++], coredump_filename);
672 /* Vacuum again, but exclude the coredump we just created */
673 coredump_vacuum(coredump_fd, arg_keep_free, arg_max_use);
675 /* Now, let's drop privileges to become the user who owns the
676 * segfaulted process and allocate the coredump memory under
677 * his uid. This also ensures that the credentials journald
678 * will see are the ones of the coredumping user, thus making
679 * sure the user himself gets access to the core dump. */
680 if (setresgid(gid, gid, gid) < 0 ||
681 setresuid(uid, uid, uid) < 0) {
682 log_error("Failed to drop privileges: %m");
688 /* Try to get a strack trace if we can */
689 if (coredump_size <= arg_process_size_max) {
690 _cleanup_free_ char *stacktrace = NULL;
692 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
694 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.\n\n", stacktrace, NULL);
696 log_warning("Failed to generate stack trace: %s", strerror(-r));
702 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.", NULL);
704 IOVEC_SET_STRING(iovec[j++], core_message);
706 /* Optionally store the entire coredump in the journal */
707 if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
708 coredump_size <= (off_t) arg_journal_size_max) {
711 /* Store the coredump itself in the journal */
713 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
715 iovec[j].iov_base = coredump_data;
716 iovec[j].iov_len = sz;
721 r = sd_journal_sendv(iovec, j);
723 log_error("Failed to log coredump: %s", strerror(-r));
726 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;