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"
48 # include "acl-util.h"
54 # define LZMA_PRESET_DEFAULT 0
57 /* The maximum size up to which we process coredumps */
58 #define PROCESS_SIZE_MAX ((off_t) (2LLU*1024LLU*1024LLU*1024LLU))
60 /* The maximum size up to which we leave the coredump around on
62 #define EXTERNAL_SIZE_MAX PROCESS_SIZE_MAX
64 /* The maximum size up to which we store the coredump in the
66 #define JOURNAL_SIZE_MAX ((size_t) (767LU*1024LU*1024LU))
68 /* Make sure to not make this larger than the maximum journal entry
69 * size. See ENTRY_SIZE_MAX in journald-native.c. */
70 assert_cc(JOURNAL_SIZE_MAX <= ENTRY_SIZE_MAX);
83 typedef enum CoredumpStorage {
84 COREDUMP_STORAGE_NONE,
85 COREDUMP_STORAGE_EXTERNAL,
86 COREDUMP_STORAGE_JOURNAL,
87 COREDUMP_STORAGE_BOTH,
88 _COREDUMP_STORAGE_MAX,
89 _COREDUMP_STORAGE_INVALID = -1
92 static const char* const coredump_storage_table[_COREDUMP_STORAGE_MAX] = {
93 [COREDUMP_STORAGE_NONE] = "none",
94 [COREDUMP_STORAGE_EXTERNAL] = "external",
95 [COREDUMP_STORAGE_JOURNAL] = "journal",
96 [COREDUMP_STORAGE_BOTH] = "both",
99 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_storage, CoredumpStorage);
100 static DEFINE_CONFIG_PARSE_ENUM(config_parse_coredump_storage, coredump_storage,
102 "Failed to parse storage setting");
104 typedef enum CoredumpCompression {
105 COREDUMP_COMPRESSION_NONE,
106 COREDUMP_COMPRESSION_XZ,
107 _COREDUMP_COMPRESSION_MAX,
108 _COREDUMP_COMPRESSION_INVALID = -1
109 } CoredumpCompression;
111 static const char* const coredump_compression_table[_COREDUMP_COMPRESSION_MAX] = {
112 [COREDUMP_COMPRESSION_NONE] = "none",
113 [COREDUMP_COMPRESSION_XZ] = "xz",
116 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_compression, CoredumpCompression);
117 static DEFINE_CONFIG_PARSE_ENUM(config_parse_coredump_compression, coredump_compression,
119 "Failed to parse compression setting");
121 static CoredumpStorage arg_storage = COREDUMP_STORAGE_EXTERNAL;
122 static CoredumpCompression arg_compression = COREDUMP_COMPRESSION_XZ;
123 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;
129 static int parse_config(void) {
131 static const ConfigTableItem items[] = {
132 { "Coredump", "Storage", config_parse_coredump_storage, 0, &arg_storage },
133 { "Coredump", "Compression", config_parse_coredump_compression, 0, &arg_compression },
134 { "Coredump", "CompressionLevel", config_parse_unsigned, 0, &arg_compression_level },
136 { "Coredump", "ProcessSizeMax", config_parse_iec_off, 0, &arg_process_size_max },
137 { "Coredump", "ExternalSizeMax", config_parse_iec_off, 0, &arg_external_size_max },
138 { "Coredump", "JournalSizeMax", config_parse_iec_size, 0, &arg_journal_size_max },
144 "/etc/systemd/coredump.conf",
147 config_item_table_lookup,
154 if (arg_compression_level > 9) {
155 log_warning("Invalid CompressionLevel %u, ignoring.", arg_compression_level);
156 arg_compression_level = LZMA_PRESET_DEFAULT;
161 static int fix_acl(int fd, uid_t uid) {
164 _cleanup_(acl_freep) acl_t acl = NULL;
166 acl_permset_t permset;
168 if (uid <= SYSTEM_UID_MAX)
171 /* Make sure normal users can read (but not write or delete)
172 * their own coredumps */
174 acl = acl_get_fd(fd);
176 log_error("Failed to get ACL: %m");
180 if (acl_create_entry(&acl, &entry) < 0 ||
181 acl_set_tag_type(entry, ACL_USER) < 0 ||
182 acl_set_qualifier(entry, &uid) < 0) {
183 log_error("Failed to patch ACL: %m");
187 if (acl_get_permset(entry, &permset) < 0 ||
188 acl_add_perm(permset, ACL_READ) < 0 ||
189 calc_acl_mask_if_needed(&acl) < 0) {
190 log_warning("Failed to patch ACL: %m");
194 if (acl_set_fd(fd, acl) < 0) {
195 log_error("Failed to apply ACL: %m");
203 static int fix_xattr(int fd, const char *info[_INFO_LEN]) {
205 static const char * const xattrs[_INFO_LEN] = {
206 [INFO_PID] = "user.coredump.pid",
207 [INFO_UID] = "user.coredump.uid",
208 [INFO_GID] = "user.coredump.gid",
209 [INFO_SIGNAL] = "user.coredump.signal",
210 [INFO_TIMESTAMP] = "user.coredump.timestamp",
211 [INFO_COMM] = "user.coredump.comm",
212 [INFO_EXE] = "user.coredump.exe",
218 /* Attach some metadata to coredumps via extended
219 * attributes. Just because we can. */
221 for (i = 0; i < _INFO_LEN; i++) {
224 if (isempty(info[i]) || !xattrs[i])
227 k = fsetxattr(fd, xattrs[i], info[i], strlen(info[i]), XATTR_CREATE);
235 #define filename_escape(s) xescape((s), "./ ")
237 static int fix_permissions(int fd, const char *filename, const char *target,
238 const char *info[_INFO_LEN], uid_t uid) {
240 /* Ignore errors on these */
246 log_error("Failed to sync coredump %s: %m", filename);
250 if (rename(filename, target) < 0) {
251 log_error("Failed to rename coredump %s -> %s: %m", filename, target);
258 static int maybe_remove_external_coredump(const char *filename, off_t size) {
260 /* Returns 1 if might remove, 0 if will not remove, <0 on error. */
262 if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
263 size <= arg_external_size_max)
269 if (unlink(filename) < 0) {
270 log_error("Failed to unlink %s: %m", filename);
278 static int save_external_coredump(const char *info[_INFO_LEN],
284 _cleanup_free_ char *p = NULL, *t = NULL, *c = NULL, *fn = NULL, *tmp = NULL;
285 _cleanup_close_ int fd = -1;
291 assert(ret_filename);
295 c = filename_escape(info[INFO_COMM]);
299 p = filename_escape(info[INFO_PID]);
303 t = filename_escape(info[INFO_TIMESTAMP]);
307 r = sd_id128_get_boot(&boot);
309 log_error("Failed to determine boot ID: %s", strerror(-r));
314 "/var/lib/systemd/coredump/core.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
316 SD_ID128_FORMAT_VAL(boot),
322 tmp = tempfn_random(fn);
326 mkdir_p_label("/var/lib/systemd/coredump", 0755);
328 fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
330 log_error("Failed to create coredump file %s: %m", tmp);
334 r = copy_bytes(STDIN_FILENO, fd, arg_process_size_max);
336 log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.",
337 info[INFO_PID], info[INFO_COMM]);
339 } else if (IN_SET(r, -EDQUOT, -ENOSPC)) {
340 log_error("Not enough disk space for coredump of %s (%s), refusing.",
341 info[INFO_PID], info[INFO_COMM]);
344 log_error("Failed to dump coredump to file: %s", strerror(-r));
348 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
349 log_error("Failed to seek on %s: %m", tmp);
353 if (fstat(fd, &st) < 0) {
354 log_error("Failed to fstat coredump %s: %m", tmp);
359 /* If we will remove the coredump anyway, do not compress. */
360 if (maybe_remove_external_coredump(NULL, st.st_size) == 0
361 && arg_compression == COREDUMP_COMPRESSION_XZ) {
363 _cleanup_free_ char *fn2 = NULL;
365 _cleanup_close_ int fd2 = -1;
367 tmp2 = strappenda(tmp, ".xz");
368 fd2 = open(tmp2, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
370 log_error("Failed to create file %s: %m", tmp2);
374 r = compress_stream(fd, fd2, arg_compression_level, -1);
376 log_error("Failed to compress %s: %s", tmp2, strerror(-r));
377 unlink_noerrno(tmp2);
381 fn2 = strappend(fn, ".xz");
387 r = fix_permissions(fd2, tmp2, fn2, info, uid);
391 *ret_filename = fn2; /* compressed */
392 *ret_fd = fd; /* uncompressed */
393 *ret_size = st.st_size; /* uncompressed */
401 unlink_noerrno(tmp2);
406 r = fix_permissions(fd, tmp, fn, info, uid);
412 *ret_size = st.st_size;
424 static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
425 _cleanup_free_ char *field = NULL;
432 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
433 log_warning("Failed to seek: %m");
437 field = malloc(9 + size);
439 log_warning("Failed to allocate memory for coredump, coredump will not be stored.");
443 memcpy(field, "COREDUMP=", 9);
445 n = read(fd, field + 9, size);
447 log_error("Failed to read core data: %s", strerror(-n));
450 if ((size_t) n < size) {
451 log_error("Core data too short.");
456 *ret_size = size + 9;
463 int main(int argc, char* argv[]) {
465 _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
466 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
467 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
468 *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL,
469 *exe = NULL, *comm = NULL, *filename = NULL;
470 const char *info[_INFO_LEN];
472 _cleanup_close_ int coredump_fd = -1;
474 struct iovec iovec[18];
477 uid_t uid, owner_uid;
482 /* Make sure we never enter a loop */
483 prctl(PR_SET_DUMPABLE, 0);
485 /* First, log to a safe place, since we don't know what
486 * crashed and it might be journald which we'd rather not log
488 log_set_target(LOG_TARGET_KMSG);
491 if (argc < INFO_COMM + 1) {
492 log_error("Not enough arguments passed from kernel (%d, expected %d).",
493 argc - 1, INFO_COMM + 1 - 1);
498 /* Ignore all parse errors */
500 log_debug("Selected storage '%s'.",
501 coredump_storage_to_string(arg_storage));
502 log_debug("Selected compression %s:%u.",
503 coredump_compression_to_string(arg_storage),
504 arg_compression_level);
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 /* Always stream the coredump to disk, if that's possible */
650 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
652 /* skip whole core dumping part */
655 /* If we don't want to keep the coredump on disk, remove it
656 * now, as later on we will lack the privileges for
657 * it. However, we keep the fd to it, so that we can still
658 * process it and log it. */
659 r = maybe_remove_external_coredump(filename, coredump_size);
663 const char *coredump_filename;
665 coredump_filename = strappenda("COREDUMP_FILENAME=", filename);
666 IOVEC_SET_STRING(iovec[j++], coredump_filename);
669 /* Now, let's drop privileges to become the user who owns the
670 * segfaulted process and allocate the coredump memory under
671 * his uid. This also ensures that the credentials journald
672 * will see are the ones of the coredumping user, thus making
673 * sure the user himself gets access to the core dump. */
674 if (setresgid(gid, gid, gid) < 0 ||
675 setresuid(uid, uid, uid) < 0) {
676 log_error("Failed to drop privileges: %m");
682 /* Try to get a strack trace if we can */
683 if (coredump_size <= arg_process_size_max) {
684 _cleanup_free_ char *stacktrace = NULL;
686 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
688 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.\n\n", stacktrace, NULL);
690 log_warning("Failed to generate stack trace: %s", strerror(-r));
696 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.", NULL);
698 IOVEC_SET_STRING(iovec[j++], core_message);
700 /* Optionally store the entire coredump in the journal */
701 if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
702 coredump_size <= (off_t) arg_journal_size_max) {
705 /* Store the coredump itself in the journal */
707 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
709 iovec[j].iov_base = coredump_data;
710 iovec[j].iov_len = sz;
715 r = sd_journal_sendv(iovec, j);
717 log_error("Failed to log coredump: %s", strerror(-r));
720 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;