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"
51 /* The maximum size up to which we process coredumps */
52 #define PROCESS_SIZE_MAX ((off_t) (2LLU*1024LLU*1024LLU*1024LLU))
54 /* The maximum size up to which we leave the coredump around on
56 #define EXTERNAL_SIZE_MAX PROCESS_SIZE_MAX
58 /* The maximum size up to which we store the coredump in the
60 #define JOURNAL_SIZE_MAX ((size_t) (767LU*1024LU*1024LU))
62 /* Make sure to not make this larger than the maximum journal entry
63 * size. See ENTRY_SIZE_MAX in journald-native.c. */
64 assert_cc(JOURNAL_SIZE_MAX <= ENTRY_SIZE_MAX);
77 typedef enum CoredumpStorage {
78 COREDUMP_STORAGE_NONE,
79 COREDUMP_STORAGE_EXTERNAL,
80 COREDUMP_STORAGE_JOURNAL,
81 COREDUMP_STORAGE_BOTH,
82 _COREDUMP_STORAGE_MAX,
83 _COREDUMP_STORAGE_INVALID = -1
86 static CoredumpStorage arg_storage = COREDUMP_STORAGE_EXTERNAL;
87 static off_t arg_process_size_max = PROCESS_SIZE_MAX;
88 static off_t arg_external_size_max = EXTERNAL_SIZE_MAX;
89 static size_t arg_journal_size_max = JOURNAL_SIZE_MAX;
91 static const char* const coredump_storage_table[_COREDUMP_STORAGE_MAX] = {
92 [COREDUMP_STORAGE_NONE] = "none",
93 [COREDUMP_STORAGE_EXTERNAL] = "external",
94 [COREDUMP_STORAGE_JOURNAL] = "journal",
95 [COREDUMP_STORAGE_BOTH] = "both",
98 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_storage, CoredumpStorage);
99 static DEFINE_CONFIG_PARSE_ENUM(config_parse_coredump_storage, coredump_storage, CoredumpStorage, "Failed to parse storage setting");
101 static int parse_config(void) {
103 static const ConfigTableItem items[] = {
104 { "Coredump", "ProcessSizeMax", config_parse_iec_off, 0, &arg_process_size_max },
105 { "Coredump", "ExternalSizeMax", config_parse_iec_off, 0, &arg_external_size_max },
106 { "Coredump", "JournalSizeMax", config_parse_iec_size, 0, &arg_journal_size_max },
107 { "Coredump", "Storage", config_parse_coredump_storage, 0, &arg_storage },
113 "/etc/systemd/coredump.conf",
116 config_item_table_lookup,
123 static int fix_acl(int fd, uid_t uid) {
126 _cleanup_(acl_freep) acl_t acl = NULL;
128 acl_permset_t permset;
130 if (uid <= SYSTEM_UID_MAX)
133 /* Make sure normal users can read (but not write or delete)
134 * their own coredumps */
136 acl = acl_get_fd(fd);
138 log_error("Failed to get ACL: %m");
142 if (acl_create_entry(&acl, &entry) < 0 ||
143 acl_set_tag_type(entry, ACL_USER) < 0 ||
144 acl_set_qualifier(entry, &uid) < 0) {
145 log_error("Failed to patch ACL: %m");
149 if (acl_get_permset(entry, &permset) < 0 ||
150 acl_add_perm(permset, ACL_READ) < 0 ||
151 calc_acl_mask_if_needed(&acl) < 0) {
152 log_warning("Failed to patch ACL: %m");
156 if (acl_set_fd(fd, acl) < 0) {
157 log_error("Failed to apply ACL: %m");
165 static int fix_xattr(int fd, const char *info[_INFO_LEN]) {
167 static const char * const xattrs[_INFO_LEN] = {
168 [INFO_PID] = "user.coredump.pid",
169 [INFO_UID] = "user.coredump.uid",
170 [INFO_GID] = "user.coredump.gid",
171 [INFO_SIGNAL] = "user.coredump.signal",
172 [INFO_TIMESTAMP] = "user.coredump.timestamp",
173 [INFO_COMM] = "user.coredump.comm",
174 [INFO_EXE] = "user.coredump.exe",
180 /* Attach some metadata to coredumps via extended
181 * attributes. Just because we can. */
183 for (i = 0; i < _INFO_LEN; i++) {
186 if (isempty(info[i]) || !xattrs[i])
189 k = fsetxattr(fd, xattrs[i], info[i], strlen(info[i]), XATTR_CREATE);
197 #define filename_escape(s) xescape((s), "./ ")
199 static int fix_permissions(int fd, const char *filename, const char *target,
200 const char *info[_INFO_LEN], uid_t uid) {
202 /* Ignore errors on these */
208 log_error("Failed to sync coredump %s: %m", filename);
212 if (rename(filename, target) < 0) {
213 log_error("Failed to rename coredump %s -> %s: %m", filename, target);
220 static int maybe_remove_external_coredump(const char *filename, off_t size) {
222 /* Returns 1 if might remove, 0 if will not remove, <0 on error. */
224 if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
225 size <= arg_external_size_max)
231 if (unlink(filename) < 0) {
232 log_error("Failed to unlink %s: %m", filename);
240 static int save_external_coredump(const char *info[_INFO_LEN],
246 _cleanup_free_ char *p = NULL, *t = NULL, *c = NULL, *fn = NULL, *tmp = NULL;
247 _cleanup_close_ int fd = -1;
253 assert(ret_filename);
257 c = filename_escape(info[INFO_COMM]);
261 p = filename_escape(info[INFO_PID]);
265 t = filename_escape(info[INFO_TIMESTAMP]);
269 r = sd_id128_get_boot(&boot);
271 log_error("Failed to determine boot ID: %s", strerror(-r));
276 "/var/lib/systemd/coredump/core.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
278 SD_ID128_FORMAT_VAL(boot),
284 tmp = tempfn_random(fn);
288 mkdir_p_label("/var/lib/systemd/coredump", 0755);
290 fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
292 log_error("Failed to create coredump file %s: %m", tmp);
296 r = copy_bytes(STDIN_FILENO, fd, arg_process_size_max);
298 log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.",
299 info[INFO_PID], info[INFO_COMM]);
301 } else if (IN_SET(r, -EDQUOT, -ENOSPC)) {
302 log_error("Not enough disk space for coredump of %s (%s), refusing.",
303 info[INFO_PID], info[INFO_COMM]);
306 log_error("Failed to dump coredump to file: %s", strerror(-r));
310 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
311 log_error("Failed to seek on %s: %m", tmp);
315 if (fstat(fd, &st) < 0) {
316 log_error("Failed to fstat coredump %s: %m", tmp);
321 /* If we will remove the coredump anyway, do not compress. */
322 if (maybe_remove_external_coredump(NULL, st.st_size) == 0) {
324 _cleanup_free_ char *fn2 = NULL;
326 _cleanup_close_ int fd2 = -1;
328 tmp2 = strappenda(tmp, ".xz");
329 fd2 = open(tmp2, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
331 log_error("Failed to create file %s: %m", tmp2);
335 r = compress_stream(fd, fd2, -1);
337 log_error("Failed to compress %s: %s", tmp2, strerror(-r));
338 unlink_noerrno(tmp2);
342 fn2 = strappend(fn, ".xz");
348 r = fix_permissions(fd2, tmp2, fn2, info, uid);
352 *ret_filename = fn2; /* compressed */
353 *ret_fd = fd; /* uncompressed */
354 *ret_size = st.st_size; /* uncompressed */
362 unlink_noerrno(tmp2);
367 r = fix_permissions(fd, tmp, fn, info, uid);
373 *ret_size = st.st_size;
385 static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
386 _cleanup_free_ char *field = NULL;
393 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
394 log_warning("Failed to seek: %m");
398 field = malloc(9 + size);
400 log_warning("Failed to allocate memory for coredump, coredump will not be stored.");
404 memcpy(field, "COREDUMP=", 9);
406 n = read(fd, field + 9, size);
408 log_error("Failed to read core data: %s", strerror(-n));
411 if ((size_t) n < size) {
412 log_error("Core data too short.");
417 *ret_size = size + 9;
424 int main(int argc, char* argv[]) {
426 _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
427 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
428 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
429 *coredump_filename = NULL, *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL,
430 *exe = NULL, *comm = NULL;
431 const char *info[_INFO_LEN];
433 _cleanup_close_ int coredump_fd = -1;
435 struct iovec iovec[17];
438 uid_t uid, owner_uid;
443 /* Make sure we never enter a loop */
444 prctl(PR_SET_DUMPABLE, 0);
446 /* First, log to a safe place, since we don't know what
447 * crashed and it might be journald which we'd rather not log
449 log_set_target(LOG_TARGET_KMSG);
452 if (argc < INFO_COMM + 1) {
453 log_error("Not enough arguments passed from kernel (%d, expected %d).",
454 argc - 1, INFO_COMM + 1 - 1);
459 /* Ignore all parse errors */
461 log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
463 r = parse_uid(argv[INFO_UID + 1], &uid);
465 log_error("Failed to parse UID.");
469 r = parse_pid(argv[INFO_PID + 1], &pid);
471 log_error("Failed to parse PID.");
475 r = parse_gid(argv[INFO_GID + 1], &gid);
477 log_error("Failed to parse GID.");
481 if (get_process_comm(pid, &comm) < 0) {
482 log_warning("Failed to get COMM, falling back to the commandline.");
483 comm = strv_join(argv + INFO_COMM + 1, " ");
486 if (get_process_exe(pid, &exe) < 0)
487 log_warning("Failed to get EXE.");
489 info[INFO_PID] = argv[INFO_PID + 1];
490 info[INFO_UID] = argv[INFO_UID + 1];
491 info[INFO_GID] = argv[INFO_GID + 1];
492 info[INFO_SIGNAL] = argv[INFO_SIGNAL + 1];
493 info[INFO_TIMESTAMP] = argv[INFO_TIMESTAMP + 1];
494 info[INFO_COMM] = comm;
495 info[INFO_EXE] = exe;
497 if (cg_pid_get_unit(pid, &t) >= 0) {
499 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
501 /* If we are journald, we cut things short,
502 * don't write to the journal, but still
503 * create a coredump. */
505 if (arg_storage != COREDUMP_STORAGE_NONE)
506 arg_storage = COREDUMP_STORAGE_EXTERNAL;
508 r = save_external_coredump(info, uid, &coredump_filename, &coredump_fd, &coredump_size);
512 r = maybe_remove_external_coredump(coredump_filename, coredump_size);
516 log_info("Detected coredump of the journal daemon itself, diverted to %s.", coredump_filename);
520 core_unit = strappend("COREDUMP_UNIT=", t);
521 } else if (cg_pid_get_user_unit(pid, &t) >= 0)
522 core_unit = strappend("COREDUMP_USER_UNIT=", t);
525 IOVEC_SET_STRING(iovec[j++], core_unit);
527 /* OK, now we know it's not the journal, hence we can make use
529 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
532 core_pid = strappend("COREDUMP_PID=", info[INFO_PID]);
534 IOVEC_SET_STRING(iovec[j++], core_pid);
536 core_uid = strappend("COREDUMP_UID=", info[INFO_UID]);
538 IOVEC_SET_STRING(iovec[j++], core_uid);
540 core_gid = strappend("COREDUMP_GID=", info[INFO_GID]);
542 IOVEC_SET_STRING(iovec[j++], core_gid);
544 core_signal = strappend("COREDUMP_SIGNAL=", info[INFO_SIGNAL]);
546 IOVEC_SET_STRING(iovec[j++], core_signal);
548 if (sd_pid_get_session(pid, &t) >= 0) {
549 core_session = strappend("COREDUMP_SESSION=", t);
553 IOVEC_SET_STRING(iovec[j++], core_session);
556 if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
557 asprintf(&core_owner_uid, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
560 IOVEC_SET_STRING(iovec[j++], core_owner_uid);
563 if (sd_pid_get_slice(pid, &t) >= 0) {
564 core_slice = strappend("COREDUMP_SLICE=", t);
568 IOVEC_SET_STRING(iovec[j++], core_slice);
572 core_comm = strappend("COREDUMP_COMM=", comm);
574 IOVEC_SET_STRING(iovec[j++], core_comm);
578 core_exe = strappend("COREDUMP_EXE=", exe);
580 IOVEC_SET_STRING(iovec[j++], core_exe);
583 if (get_process_cmdline(pid, 0, false, &t) >= 0) {
584 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
588 IOVEC_SET_STRING(iovec[j++], core_cmdline);
591 if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
592 core_cgroup = strappend("COREDUMP_CGROUP=", t);
596 IOVEC_SET_STRING(iovec[j++], core_cgroup);
599 core_timestamp = strjoin("COREDUMP_TIMESTAMP=", info[INFO_TIMESTAMP], "000000", NULL);
601 IOVEC_SET_STRING(iovec[j++], core_timestamp);
603 IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
604 IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
606 /* Always stream the coredump to disk, if that's possible */
607 r = save_external_coredump(info, uid, &coredump_filename, &coredump_fd, &coredump_size);
609 /* skip whole core dumping part */
612 /* If we don't want to keep the coredump on disk, remove it
613 * now, as later on we will lack the privileges for
614 * it. However, we keep the fd to it, so that we can still
615 * process it and log it. */
616 r = maybe_remove_external_coredump(coredump_filename, coredump_size);
620 /* Now, let's drop privileges to become the user who owns the
621 * segfaulted process and allocate the coredump memory under
622 * his uid. This also ensures that the credentials journald
623 * will see are the ones of the coredumping user, thus making
624 * sure the user himself gets access to the core dump. */
625 if (setresgid(gid, gid, gid) < 0 ||
626 setresuid(uid, uid, uid) < 0) {
627 log_error("Failed to drop privileges: %m");
633 /* Try to get a strack trace if we can */
634 if (coredump_size <= arg_process_size_max) {
635 _cleanup_free_ char *stacktrace = NULL;
637 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
639 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.\n\n", stacktrace, NULL);
641 log_warning("Failed to generate stack trace: %s", strerror(-r));
647 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.", NULL);
649 IOVEC_SET_STRING(iovec[j++], core_message);
651 /* Optionally store the entire coredump in the journal */
652 if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
653 coredump_size <= (off_t) arg_journal_size_max) {
656 /* Store the coredump itself in the journal */
658 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
660 iovec[j].iov_base = coredump_data;
661 iovec[j].iov_len = sz;
666 r = sd_journal_sendv(iovec, j);
668 log_error("Failed to log coredump: %s", strerror(-r));
671 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;