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) {
134 static const ConfigTableItem items[] = {
135 { "Coredump", "Storage", config_parse_coredump_storage, 0, &arg_storage },
136 { "Coredump", "Compression", config_parse_coredump_compression, 0, &arg_compression },
137 { "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;
167 static int fix_acl(int fd, uid_t uid) {
170 _cleanup_(acl_freep) acl_t acl = NULL;
172 acl_permset_t permset;
174 if (uid <= SYSTEM_UID_MAX)
177 /* Make sure normal users can read (but not write or delete)
178 * their own coredumps */
180 acl = acl_get_fd(fd);
182 log_error("Failed to get ACL: %m");
186 if (acl_create_entry(&acl, &entry) < 0 ||
187 acl_set_tag_type(entry, ACL_USER) < 0 ||
188 acl_set_qualifier(entry, &uid) < 0) {
189 log_error("Failed to patch ACL: %m");
193 if (acl_get_permset(entry, &permset) < 0 ||
194 acl_add_perm(permset, ACL_READ) < 0 ||
195 calc_acl_mask_if_needed(&acl) < 0) {
196 log_warning("Failed to patch ACL: %m");
200 if (acl_set_fd(fd, acl) < 0) {
201 log_error("Failed to apply ACL: %m");
209 static int fix_xattr(int fd, const char *info[_INFO_LEN]) {
211 static const char * const xattrs[_INFO_LEN] = {
212 [INFO_PID] = "user.coredump.pid",
213 [INFO_UID] = "user.coredump.uid",
214 [INFO_GID] = "user.coredump.gid",
215 [INFO_SIGNAL] = "user.coredump.signal",
216 [INFO_TIMESTAMP] = "user.coredump.timestamp",
217 [INFO_COMM] = "user.coredump.comm",
218 [INFO_EXE] = "user.coredump.exe",
224 /* Attach some metadata to coredumps via extended
225 * attributes. Just because we can. */
227 for (i = 0; i < _INFO_LEN; i++) {
230 if (isempty(info[i]) || !xattrs[i])
233 k = fsetxattr(fd, xattrs[i], info[i], strlen(info[i]), XATTR_CREATE);
241 #define filename_escape(s) xescape((s), "./ ")
243 static int fix_permissions(int fd, const char *filename, const char *target,
244 const char *info[_INFO_LEN], uid_t uid) {
246 /* Ignore errors on these */
252 log_error("Failed to sync coredump %s: %m", filename);
256 if (rename(filename, target) < 0) {
257 log_error("Failed to rename coredump %s -> %s: %m", filename, target);
264 static int maybe_remove_external_coredump(const char *filename, off_t size) {
266 /* Returns 1 if might remove, 0 if will not remove, <0 on error. */
268 if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
269 size <= arg_external_size_max)
275 if (unlink(filename) < 0 && errno != ENOENT) {
276 log_error("Failed to unlink %s: %m", filename);
283 static int save_external_coredump(
284 const char *info[_INFO_LEN],
290 _cleanup_free_ char *p = NULL, *t = NULL, *c = NULL, *fn = NULL, *tmp = NULL, *u = NULL;
291 _cleanup_close_ int fd = -1;
297 assert(ret_filename);
301 c = filename_escape(info[INFO_COMM]);
305 p = filename_escape(info[INFO_PID]);
309 u = filename_escape(info[INFO_UID]);
313 t = filename_escape(info[INFO_TIMESTAMP]);
317 r = sd_id128_get_boot(&boot);
319 log_error("Failed to determine boot ID: %s", strerror(-r));
324 "/var/lib/systemd/coredump/core.%s.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
327 SD_ID128_FORMAT_VAL(boot),
333 tmp = tempfn_random(fn);
337 mkdir_p_label("/var/lib/systemd/coredump", 0755);
339 fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
341 log_error("Failed to create coredump file %s: %m", tmp);
345 r = copy_bytes(STDIN_FILENO, fd, arg_process_size_max);
347 log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", info[INFO_PID], info[INFO_COMM]);
349 } else if (IN_SET(r, -EDQUOT, -ENOSPC)) {
350 log_error("Not enough disk space for coredump of %s (%s), refusing.", info[INFO_PID], info[INFO_COMM]);
353 log_error("Failed to dump coredump to file: %s", strerror(-r));
357 if (fstat(fd, &st) < 0) {
358 log_error("Failed to fstat coredump %s: %m", tmp);
362 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
363 log_error("Failed to seek on %s: %m", tmp);
368 /* If we will remove the coredump anyway, do not compress. */
369 if (maybe_remove_external_coredump(NULL, st.st_size) == 0
370 && arg_compression == COREDUMP_COMPRESSION_XZ) {
372 _cleanup_free_ char *fn2 = NULL;
374 _cleanup_close_ int fd2 = -1;
376 tmp2 = strappenda(tmp, ".xz");
377 fd2 = open(tmp2, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
379 log_error("Failed to create file %s: %m", tmp2);
383 r = compress_stream(fd, fd2, arg_compression_level, -1);
385 log_error("Failed to compress %s: %s", tmp2, strerror(-r));
386 unlink_noerrno(tmp2);
390 fn2 = strappend(fn, ".xz");
396 r = fix_permissions(fd2, tmp2, fn2, info, uid);
400 *ret_filename = fn2; /* compressed */
401 *ret_fd = fd; /* uncompressed */
402 *ret_size = st.st_size; /* uncompressed */
410 unlink_noerrno(tmp2);
415 r = fix_permissions(fd, tmp, fn, info, uid);
421 *ret_size = st.st_size;
433 static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
434 _cleanup_free_ char *field = NULL;
441 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
442 log_warning("Failed to seek: %m");
446 field = malloc(9 + size);
448 log_warning("Failed to allocate memory for coredump, coredump will not be stored.");
452 memcpy(field, "COREDUMP=", 9);
454 n = read(fd, field + 9, size);
456 log_error("Failed to read core data: %s", strerror(-n));
459 if ((size_t) n < size) {
460 log_error("Core data too short.");
465 *ret_size = size + 9;
472 int main(int argc, char* argv[]) {
474 _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
475 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
476 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
477 *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL,
478 *exe = NULL, *comm = NULL, *filename = NULL;
479 const char *info[_INFO_LEN];
481 _cleanup_close_ int coredump_fd = -1;
483 struct iovec iovec[18];
486 uid_t uid, owner_uid;
491 /* Make sure we never enter a loop */
492 prctl(PR_SET_DUMPABLE, 0);
494 /* First, log to a safe place, since we don't know what
495 * crashed and it might be journald which we'd rather not log
497 log_set_target(LOG_TARGET_KMSG);
500 if (argc < INFO_COMM + 1) {
501 log_error("Not enough arguments passed from kernel (%d, expected %d).",
502 argc - 1, INFO_COMM + 1 - 1);
507 /* Ignore all parse errors */
509 log_debug("Selected storage '%s'.",
510 coredump_storage_to_string(arg_storage));
511 log_debug("Selected compression %s:%u.",
512 coredump_compression_to_string(arg_compression),
513 arg_compression_level);
515 r = parse_uid(argv[INFO_UID + 1], &uid);
517 log_error("Failed to parse UID.");
521 r = parse_pid(argv[INFO_PID + 1], &pid);
523 log_error("Failed to parse PID.");
527 r = parse_gid(argv[INFO_GID + 1], &gid);
529 log_error("Failed to parse GID.");
533 if (get_process_comm(pid, &comm) < 0) {
534 log_warning("Failed to get COMM, falling back to the commandline.");
535 comm = strv_join(argv + INFO_COMM + 1, " ");
538 if (get_process_exe(pid, &exe) < 0)
539 log_warning("Failed to get EXE.");
541 info[INFO_PID] = argv[INFO_PID + 1];
542 info[INFO_UID] = argv[INFO_UID + 1];
543 info[INFO_GID] = argv[INFO_GID + 1];
544 info[INFO_SIGNAL] = argv[INFO_SIGNAL + 1];
545 info[INFO_TIMESTAMP] = argv[INFO_TIMESTAMP + 1];
546 info[INFO_COMM] = comm;
547 info[INFO_EXE] = exe;
549 if (cg_pid_get_unit(pid, &t) >= 0) {
551 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
553 /* If we are journald, we cut things short,
554 * don't write to the journal, but still
555 * create a coredump. */
557 if (arg_storage != COREDUMP_STORAGE_NONE)
558 arg_storage = COREDUMP_STORAGE_EXTERNAL;
560 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
564 r = maybe_remove_external_coredump(filename, coredump_size);
568 log_info("Detected coredump of the journal daemon itself, diverted to %s.", filename);
572 core_unit = strappend("COREDUMP_UNIT=", t);
573 } else if (cg_pid_get_user_unit(pid, &t) >= 0)
574 core_unit = strappend("COREDUMP_USER_UNIT=", t);
577 IOVEC_SET_STRING(iovec[j++], core_unit);
579 /* OK, now we know it's not the journal, hence we can make use
581 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
584 core_pid = strappend("COREDUMP_PID=", info[INFO_PID]);
586 IOVEC_SET_STRING(iovec[j++], core_pid);
588 core_uid = strappend("COREDUMP_UID=", info[INFO_UID]);
590 IOVEC_SET_STRING(iovec[j++], core_uid);
592 core_gid = strappend("COREDUMP_GID=", info[INFO_GID]);
594 IOVEC_SET_STRING(iovec[j++], core_gid);
596 core_signal = strappend("COREDUMP_SIGNAL=", info[INFO_SIGNAL]);
598 IOVEC_SET_STRING(iovec[j++], core_signal);
600 if (sd_pid_get_session(pid, &t) >= 0) {
601 core_session = strappend("COREDUMP_SESSION=", t);
605 IOVEC_SET_STRING(iovec[j++], core_session);
608 if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
609 asprintf(&core_owner_uid, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
612 IOVEC_SET_STRING(iovec[j++], core_owner_uid);
615 if (sd_pid_get_slice(pid, &t) >= 0) {
616 core_slice = strappend("COREDUMP_SLICE=", t);
620 IOVEC_SET_STRING(iovec[j++], core_slice);
624 core_comm = strappend("COREDUMP_COMM=", comm);
626 IOVEC_SET_STRING(iovec[j++], core_comm);
630 core_exe = strappend("COREDUMP_EXE=", exe);
632 IOVEC_SET_STRING(iovec[j++], core_exe);
635 if (get_process_cmdline(pid, 0, false, &t) >= 0) {
636 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
640 IOVEC_SET_STRING(iovec[j++], core_cmdline);
643 if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
644 core_cgroup = strappend("COREDUMP_CGROUP=", t);
648 IOVEC_SET_STRING(iovec[j++], core_cgroup);
651 core_timestamp = strjoin("COREDUMP_TIMESTAMP=", info[INFO_TIMESTAMP], "000000", NULL);
653 IOVEC_SET_STRING(iovec[j++], core_timestamp);
655 IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
656 IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
658 /* Vacuum before we write anything again */
659 coredump_vacuum(-1, arg_keep_free, arg_max_use);
661 /* Always stream the coredump to disk, if that's possible */
662 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
664 /* skip whole core dumping part */
667 /* If we don't want to keep the coredump on disk, remove it
668 * now, as later on we will lack the privileges for
669 * it. However, we keep the fd to it, so that we can still
670 * process it and log it. */
671 r = maybe_remove_external_coredump(filename, coredump_size);
675 const char *coredump_filename;
677 coredump_filename = strappenda("COREDUMP_FILENAME=", filename);
678 IOVEC_SET_STRING(iovec[j++], coredump_filename);
681 /* Vacuum again, but exclude the coredump we just created */
682 coredump_vacuum(coredump_fd, arg_keep_free, arg_max_use);
684 /* Now, let's drop privileges to become the user who owns the
685 * segfaulted process and allocate the coredump memory under
686 * his uid. This also ensures that the credentials journald
687 * will see are the ones of the coredumping user, thus making
688 * sure the user himself gets access to the core dump. */
689 if (setresgid(gid, gid, gid) < 0 ||
690 setresuid(uid, uid, uid) < 0) {
691 log_error("Failed to drop privileges: %m");
697 /* Try to get a strack trace if we can */
698 if (coredump_size <= arg_process_size_max) {
699 _cleanup_free_ char *stacktrace = NULL;
701 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
703 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.\n\n", stacktrace, NULL);
705 log_warning("Failed to generate stack trace: %s", strerror(-r));
711 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.", NULL);
713 IOVEC_SET_STRING(iovec[j++], core_message);
715 /* Optionally store the entire coredump in the journal */
716 if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
717 coredump_size <= (off_t) arg_journal_size_max) {
720 /* Store the coredump itself in the journal */
722 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
724 iovec[j].iov_base = coredump_data;
725 iovec[j].iov_len = sz;
730 r = sd_journal_sendv(iovec, j);
732 log_error("Failed to log coredump: %s", strerror(-r));
735 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;