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"
50 /* The maximum size up to which we process coredumps */
51 #define PROCESS_SIZE_MAX ((off_t) (2LLU*1024LLU*1024LLU*1024LLU))
53 /* The maximum size up to which we leave the coredump around on
55 #define EXTERNAL_SIZE_MAX PROCESS_SIZE_MAX
57 /* The maximum size up to which we store the coredump in the
59 #define JOURNAL_SIZE_MAX ((size_t) (767LU*1024LU*1024LU))
61 /* Make sure to not make this larger than the maximum journal entry
62 * size. See ENTRY_SIZE_MAX in journald-native.c. */
63 assert_cc(JOURNAL_SIZE_MAX <= ENTRY_SIZE_MAX);
76 typedef enum CoredumpStorage {
77 COREDUMP_STORAGE_NONE,
78 COREDUMP_STORAGE_EXTERNAL,
79 COREDUMP_STORAGE_JOURNAL,
80 COREDUMP_STORAGE_BOTH,
81 _COREDUMP_STORAGE_MAX,
82 _COREDUMP_STORAGE_INVALID = -1
85 static CoredumpStorage arg_storage = COREDUMP_STORAGE_EXTERNAL;
86 static off_t arg_process_size_max = PROCESS_SIZE_MAX;
87 static off_t arg_external_size_max = EXTERNAL_SIZE_MAX;
88 static size_t arg_journal_size_max = JOURNAL_SIZE_MAX;
90 static const char* const coredump_storage_table[_COREDUMP_STORAGE_MAX] = {
91 [COREDUMP_STORAGE_NONE] = "none",
92 [COREDUMP_STORAGE_EXTERNAL] = "external",
93 [COREDUMP_STORAGE_JOURNAL] = "journal",
94 [COREDUMP_STORAGE_BOTH] = "both",
97 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_storage, CoredumpStorage);
98 static DEFINE_CONFIG_PARSE_ENUM(config_parse_coredump_storage, coredump_storage, CoredumpStorage, "Failed to parse storage setting");
100 static int parse_config(void) {
102 static const ConfigTableItem items[] = {
103 { "Coredump", "ProcessSizeMax", config_parse_iec_off, 0, &arg_process_size_max },
104 { "Coredump", "ExternalSizeMax", config_parse_iec_off, 0, &arg_external_size_max },
105 { "Coredump", "JournalSizeMax", config_parse_iec_size, 0, &arg_journal_size_max },
106 { "Coredump", "Storage", config_parse_coredump_storage, 0, &arg_storage },
112 "/etc/systemd/coredump.conf",
115 config_item_table_lookup,
122 static int fix_acl(int fd, uid_t uid) {
125 _cleanup_(acl_freep) acl_t acl = NULL;
127 acl_permset_t permset;
129 if (uid <= SYSTEM_UID_MAX)
132 /* Make sure normal users can read (but not write or delete)
133 * their own coredumps */
135 acl = acl_get_fd(fd);
137 log_error("Failed to get ACL: %m");
141 if (acl_create_entry(&acl, &entry) < 0 ||
142 acl_set_tag_type(entry, ACL_USER) < 0 ||
143 acl_set_qualifier(entry, &uid) < 0) {
144 log_error("Failed to patch ACL: %m");
148 if (acl_get_permset(entry, &permset) < 0 ||
149 acl_add_perm(permset, ACL_READ) < 0 ||
150 calc_acl_mask_if_needed(&acl) < 0) {
151 log_warning("Failed to patch ACL: %m");
155 if (acl_set_fd(fd, acl) < 0) {
156 log_error("Failed to apply ACL: %m");
164 static int fix_xattr(int fd, const char *info[_INFO_LEN]) {
166 static const char * const xattrs[_INFO_LEN] = {
167 [INFO_PID] = "user.coredump.pid",
168 [INFO_UID] = "user.coredump.uid",
169 [INFO_GID] = "user.coredump.gid",
170 [INFO_SIGNAL] = "user.coredump.signal",
171 [INFO_TIMESTAMP] = "user.coredump.timestamp",
172 [INFO_COMM] = "user.coredump.comm",
173 [INFO_EXE] = "user.coredump.exe",
179 /* Attach some metadata to coredumps via extended
180 * attributes. Just because we can. */
182 for (i = 0; i < _INFO_LEN; i++) {
185 if (isempty(info[i]) || !xattrs[i])
188 k = fsetxattr(fd, xattrs[i], info[i], strlen(info[i]), XATTR_CREATE);
196 #define filename_escape(s) xescape((s), "./ ")
198 static int save_external_coredump(const char *info[_INFO_LEN],
204 _cleanup_free_ char *p = NULL, *t = NULL, *c = NULL, *fn = NULL, *tmp = NULL;
205 _cleanup_close_ int fd = -1;
211 assert(ret_filename);
215 c = filename_escape(info[INFO_COMM]);
219 p = filename_escape(info[INFO_PID]);
223 t = filename_escape(info[INFO_TIMESTAMP]);
227 r = sd_id128_get_boot(&boot);
229 log_error("Failed to determine boot ID: %s", strerror(-r));
234 "/var/lib/systemd/coredump/core.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
236 SD_ID128_FORMAT_VAL(boot),
242 tmp = tempfn_random(fn);
246 mkdir_p_label("/var/lib/systemd/coredump", 0755);
248 fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
250 log_error("Failed to create coredump file: %m");
254 r = copy_bytes(STDIN_FILENO, fd, arg_process_size_max);
256 log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.",
257 info[INFO_PID], info[INFO_COMM]);
259 } else if (IN_SET(r, -EDQUOT, -ENOSPC)) {
260 log_error("Not enough disk space for coredump of %s (%s), refusing.",
261 info[INFO_PID], info[INFO_COMM]);
264 log_error("Failed to dump coredump to file: %s", strerror(-r));
268 /* Ignore errors on these */
274 log_error("Failed to sync coredump %s: %m", tmp);
279 if (fstat(fd, &st) < 0) {
280 log_error("Failed to fstat coredump %s: %m", tmp);
285 if (rename(tmp, fn) < 0) {
286 log_error("Failed to rename coredump %s -> %s: %m", tmp, fn);
293 *ret_size = st.st_size;
305 static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
306 _cleanup_free_ char *field = NULL;
313 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
314 log_warning("Failed to seek: %m");
318 field = malloc(9 + size);
320 log_warning("Failed to allocate memory fore coredump, coredump will not be stored.");
324 memcpy(field, "COREDUMP=", 9);
326 n = read(fd, field + 9, size);
328 log_error("Failed to read core data: %s", strerror(-n));
331 if ((size_t) n < size) {
332 log_error("Core data too short.");
337 *ret_size = size + 9;
344 static int maybe_remove_external_coredump(const char *filename, off_t size) {
349 if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
350 size <= arg_external_size_max)
353 if (unlink(filename) < 0) {
354 log_error("Failed to unlink %s: %m", filename);
361 int main(int argc, char* argv[]) {
363 _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
364 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
365 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
366 *coredump_filename = NULL, *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL,
367 *exe = NULL, *comm = NULL;
368 const char *info[_INFO_LEN];
370 _cleanup_close_ int coredump_fd = -1;
372 struct iovec iovec[17];
375 uid_t uid, owner_uid;
380 /* Make sure we never enter a loop */
381 prctl(PR_SET_DUMPABLE, 0);
383 /* First, log to a safe place, since we don't know what
384 * crashed and it might be journald which we'd rather not log
386 log_set_target(LOG_TARGET_KMSG);
389 if (argc < INFO_COMM + 1) {
390 log_error("Not enough arguments passed from kernel (%d, expected %d).",
391 argc - 1, INFO_COMM + 1 - 1);
396 /* Ignore all parse errors */
398 log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
400 /* Exit early if we cannot write the coredump to disk anyway */
401 if (path_is_read_only_fs("/var/lib") != 0) {
402 log_error("Coredump directory not mounted or not writable, skipping coredump.");
407 r = parse_uid(argv[INFO_UID + 1], &uid);
409 log_error("Failed to parse UID.");
413 r = parse_pid(argv[INFO_PID + 1], &pid);
415 log_error("Failed to parse PID.");
419 r = parse_gid(argv[INFO_GID + 1], &gid);
421 log_error("Failed to parse GID.");
425 if (get_process_comm(pid, &comm) < 0) {
426 log_warning("Failed to get COMM, falling back to the commandline.");
427 comm = strv_join(argv + INFO_COMM + 1, " ");
430 if (get_process_exe(pid, &exe) < 0)
431 log_warning("Failed to get EXE.");
433 info[INFO_PID] = argv[INFO_PID + 1];
434 info[INFO_UID] = argv[INFO_UID + 1];
435 info[INFO_GID] = argv[INFO_GID + 1];
436 info[INFO_SIGNAL] = argv[INFO_SIGNAL + 1];
437 info[INFO_TIMESTAMP] = argv[INFO_TIMESTAMP + 1];
438 info[INFO_COMM] = comm;
439 info[INFO_EXE] = exe;
441 if (cg_pid_get_unit(pid, &t) >= 0) {
443 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
445 /* If we are journald, we cut things short,
446 * don't write to the journal, but still
447 * create a coredump. */
449 if (arg_storage != COREDUMP_STORAGE_NONE)
450 arg_storage = COREDUMP_STORAGE_EXTERNAL;
452 r = save_external_coredump(info, uid, &coredump_filename, &coredump_fd, &coredump_size);
456 r = maybe_remove_external_coredump(coredump_filename, coredump_size);
460 log_info("Detected coredump of the journal daemon itself, diverted to %s.", coredump_filename);
464 core_unit = strappend("COREDUMP_UNIT=", t);
465 } else if (cg_pid_get_user_unit(pid, &t) >= 0)
466 core_unit = strappend("COREDUMP_USER_UNIT=", t);
469 IOVEC_SET_STRING(iovec[j++], core_unit);
471 /* OK, now we know it's not the journal, hence we can make use
473 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
476 core_pid = strappend("COREDUMP_PID=", info[INFO_PID]);
478 IOVEC_SET_STRING(iovec[j++], core_pid);
480 core_uid = strappend("COREDUMP_UID=", info[INFO_UID]);
482 IOVEC_SET_STRING(iovec[j++], core_uid);
484 core_gid = strappend("COREDUMP_GID=", info[INFO_GID]);
486 IOVEC_SET_STRING(iovec[j++], core_gid);
488 core_signal = strappend("COREDUMP_SIGNAL=", info[INFO_SIGNAL]);
490 IOVEC_SET_STRING(iovec[j++], core_signal);
492 if (sd_pid_get_session(pid, &t) >= 0) {
493 core_session = strappend("COREDUMP_SESSION=", t);
497 IOVEC_SET_STRING(iovec[j++], core_session);
500 if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
501 asprintf(&core_owner_uid, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
504 IOVEC_SET_STRING(iovec[j++], core_owner_uid);
507 if (sd_pid_get_slice(pid, &t) >= 0) {
508 core_slice = strappend("COREDUMP_SLICE=", t);
512 IOVEC_SET_STRING(iovec[j++], core_slice);
516 core_comm = strappend("COREDUMP_COMM=", comm);
518 IOVEC_SET_STRING(iovec[j++], core_comm);
522 core_exe = strappend("COREDUMP_EXE=", exe);
524 IOVEC_SET_STRING(iovec[j++], core_exe);
527 if (get_process_cmdline(pid, 0, false, &t) >= 0) {
528 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
532 IOVEC_SET_STRING(iovec[j++], core_cmdline);
535 if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
536 core_cgroup = strappend("COREDUMP_CGROUP=", t);
540 IOVEC_SET_STRING(iovec[j++], core_cgroup);
543 core_timestamp = strjoin("COREDUMP_TIMESTAMP=", info[INFO_TIMESTAMP], "000000", NULL);
545 IOVEC_SET_STRING(iovec[j++], core_timestamp);
547 IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
548 IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
550 /* Always stream the coredump to disk, if that's possible */
551 r = save_external_coredump(info, uid, &coredump_filename, &coredump_fd, &coredump_size);
555 /* If we don't want to keep the coredump on disk, remove it
556 * now, as later on we will lack the privileges for
557 * it. However, we keep the fd to it, so that we can still
558 * process it and log it. */
559 r = maybe_remove_external_coredump(coredump_filename, coredump_size);
563 /* Now, let's drop privileges to become the user who owns the
564 * segfaulted process and allocate the coredump memory under
565 * his uid. This also ensures that the credentials journald
566 * will see are the ones of the coredumping user, thus making
567 * sure the user himself gets access to the core dump. */
568 if (setresgid(gid, gid, gid) < 0 ||
569 setresuid(uid, uid, uid) < 0) {
570 log_error("Failed to drop privileges: %m");
576 /* Try to get a strack trace if we can */
577 if (coredump_size <= arg_process_size_max) {
578 _cleanup_free_ char *stacktrace = NULL;
580 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
582 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.\n\n", stacktrace, NULL);
584 log_warning("Failed to generate stack trace: %s", strerror(-r));
589 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.", NULL);
591 IOVEC_SET_STRING(iovec[j++], core_message);
593 /* Optionally store the entire coredump in the journal */
594 if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
595 coredump_size <= (off_t) arg_journal_size_max) {
598 /* Store the coredump itself in the journal */
600 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
602 iovec[j].iov_base = coredump_data;
603 iovec[j].iov_len = sz;
608 r = sd_journal_sendv(iovec, j);
610 log_error("Failed to log coredump: %s", strerror(-r));
613 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;