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 off_t arg_process_size_max = PROCESS_SIZE_MAX;
125 static off_t arg_external_size_max = EXTERNAL_SIZE_MAX;
126 static size_t arg_journal_size_max = JOURNAL_SIZE_MAX;
127 static off_t arg_keep_free = (off_t) -1;
128 static off_t arg_max_use = (off_t) -1;
130 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", "ProcessSizeMax", config_parse_iec_off, 0, &arg_process_size_max },
135 { "Coredump", "ExternalSizeMax", config_parse_iec_off, 0, &arg_external_size_max },
136 { "Coredump", "JournalSizeMax", config_parse_iec_size, 0, &arg_journal_size_max },
137 { "Coredump", "KeepFree", config_parse_iec_off, 0, &arg_keep_free },
138 { "Coredump", "MaxUse", config_parse_iec_off, 0, &arg_max_use },
144 "/etc/systemd/coredump.conf",
147 config_item_table_lookup,
154 static int fix_acl(int fd, uid_t uid) {
157 _cleanup_(acl_freep) acl_t acl = NULL;
159 acl_permset_t permset;
161 if (uid <= SYSTEM_UID_MAX)
164 /* Make sure normal users can read (but not write or delete)
165 * their own coredumps */
167 acl = acl_get_fd(fd);
169 log_error("Failed to get ACL: %m");
173 if (acl_create_entry(&acl, &entry) < 0 ||
174 acl_set_tag_type(entry, ACL_USER) < 0 ||
175 acl_set_qualifier(entry, &uid) < 0) {
176 log_error("Failed to patch ACL: %m");
180 if (acl_get_permset(entry, &permset) < 0 ||
181 acl_add_perm(permset, ACL_READ) < 0 ||
182 calc_acl_mask_if_needed(&acl) < 0) {
183 log_warning("Failed to patch ACL: %m");
187 if (acl_set_fd(fd, acl) < 0) {
188 log_error("Failed to apply ACL: %m");
196 static int fix_xattr(int fd, const char *info[_INFO_LEN]) {
198 static const char * const xattrs[_INFO_LEN] = {
199 [INFO_PID] = "user.coredump.pid",
200 [INFO_UID] = "user.coredump.uid",
201 [INFO_GID] = "user.coredump.gid",
202 [INFO_SIGNAL] = "user.coredump.signal",
203 [INFO_TIMESTAMP] = "user.coredump.timestamp",
204 [INFO_COMM] = "user.coredump.comm",
205 [INFO_EXE] = "user.coredump.exe",
211 /* Attach some metadata to coredumps via extended
212 * attributes. Just because we can. */
214 for (i = 0; i < _INFO_LEN; i++) {
217 if (isempty(info[i]) || !xattrs[i])
220 k = fsetxattr(fd, xattrs[i], info[i], strlen(info[i]), XATTR_CREATE);
228 #define filename_escape(s) xescape((s), "./ ")
230 static int fix_permissions(int fd, const char *filename, const char *target,
231 const char *info[_INFO_LEN], uid_t uid) {
233 /* Ignore errors on these */
239 log_error("Failed to sync coredump %s: %m", filename);
243 if (rename(filename, target) < 0) {
244 log_error("Failed to rename coredump %s -> %s: %m", filename, target);
251 static int maybe_remove_external_coredump(const char *filename, off_t size) {
253 /* Returns 1 if might remove, 0 if will not remove, <0 on error. */
255 if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
256 size <= arg_external_size_max)
262 if (unlink(filename) < 0 && errno != ENOENT) {
263 log_error("Failed to unlink %s: %m", filename);
270 static int save_external_coredump(
271 const char *info[_INFO_LEN],
277 _cleanup_free_ char *p = NULL, *t = NULL, *c = NULL, *fn = NULL, *tmp = NULL, *u = NULL;
278 _cleanup_close_ int fd = -1;
284 assert(ret_filename);
288 c = filename_escape(info[INFO_COMM]);
292 p = filename_escape(info[INFO_PID]);
296 u = filename_escape(info[INFO_UID]);
300 t = filename_escape(info[INFO_TIMESTAMP]);
304 r = sd_id128_get_boot(&boot);
306 log_error("Failed to determine boot ID: %s", strerror(-r));
311 "/var/lib/systemd/coredump/core.%s.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
314 SD_ID128_FORMAT_VAL(boot),
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);
355 /* If we will remove the coredump anyway, do not compress. */
356 if (maybe_remove_external_coredump(NULL, st.st_size) == 0
357 && arg_compression == COREDUMP_COMPRESSION_XZ) {
359 _cleanup_free_ char *fn2 = NULL;
361 _cleanup_close_ int fd2 = -1;
363 tmp2 = strappenda(tmp, ".xz");
364 fd2 = open(tmp2, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
366 log_error("Failed to create file %s: %m", tmp2);
370 r = compress_stream(fd, fd2, LZMA_PRESET_DEFAULT, -1);
372 log_error("Failed to compress %s: %s", tmp2, strerror(-r));
373 unlink_noerrno(tmp2);
377 fn2 = strappend(fn, ".xz");
383 r = fix_permissions(fd2, tmp2, fn2, info, uid);
387 *ret_filename = fn2; /* compressed */
388 *ret_fd = fd; /* uncompressed */
389 *ret_size = st.st_size; /* uncompressed */
397 unlink_noerrno(tmp2);
402 r = fix_permissions(fd, tmp, fn, info, uid);
408 *ret_size = st.st_size;
420 static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
421 _cleanup_free_ char *field = NULL;
428 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
429 log_warning("Failed to seek: %m");
433 field = malloc(9 + size);
435 log_warning("Failed to allocate memory for coredump, coredump will not be stored.");
439 memcpy(field, "COREDUMP=", 9);
441 n = read(fd, field + 9, size);
443 log_error("Failed to read core data: %s", strerror(-n));
446 if ((size_t) n < size) {
447 log_error("Core data too short.");
452 *ret_size = size + 9;
459 int main(int argc, char* argv[]) {
461 _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
462 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
463 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
464 *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL,
465 *exe = NULL, *comm = NULL, *filename = NULL;
466 const char *info[_INFO_LEN];
468 _cleanup_close_ int coredump_fd = -1;
470 struct iovec iovec[18];
473 uid_t uid, owner_uid;
478 /* Make sure we never enter a loop */
479 prctl(PR_SET_DUMPABLE, 0);
481 /* First, log to a safe place, since we don't know what
482 * crashed and it might be journald which we'd rather not log
484 log_set_target(LOG_TARGET_KMSG);
487 if (argc < INFO_COMM + 1) {
488 log_error("Not enough arguments passed from kernel (%d, expected %d).",
489 argc - 1, INFO_COMM + 1 - 1);
494 /* Ignore all parse errors */
496 log_debug("Selected storage '%s'.",
497 coredump_storage_to_string(arg_storage));
498 log_debug("Selected compression %s.",
499 coredump_compression_to_string(arg_compression));
501 r = parse_uid(argv[INFO_UID + 1], &uid);
503 log_error("Failed to parse UID.");
507 r = parse_pid(argv[INFO_PID + 1], &pid);
509 log_error("Failed to parse PID.");
513 r = parse_gid(argv[INFO_GID + 1], &gid);
515 log_error("Failed to parse GID.");
519 if (get_process_comm(pid, &comm) < 0) {
520 log_warning("Failed to get COMM, falling back to the commandline.");
521 comm = strv_join(argv + INFO_COMM + 1, " ");
524 if (get_process_exe(pid, &exe) < 0)
525 log_warning("Failed to get EXE.");
527 info[INFO_PID] = argv[INFO_PID + 1];
528 info[INFO_UID] = argv[INFO_UID + 1];
529 info[INFO_GID] = argv[INFO_GID + 1];
530 info[INFO_SIGNAL] = argv[INFO_SIGNAL + 1];
531 info[INFO_TIMESTAMP] = argv[INFO_TIMESTAMP + 1];
532 info[INFO_COMM] = comm;
533 info[INFO_EXE] = exe;
535 if (cg_pid_get_unit(pid, &t) >= 0) {
537 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
539 /* If we are journald, we cut things short,
540 * don't write to the journal, but still
541 * create a coredump. */
543 if (arg_storage != COREDUMP_STORAGE_NONE)
544 arg_storage = COREDUMP_STORAGE_EXTERNAL;
546 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
550 r = maybe_remove_external_coredump(filename, coredump_size);
554 log_info("Detected coredump of the journal daemon itself, diverted to %s.", filename);
558 core_unit = strappend("COREDUMP_UNIT=", t);
559 } else if (cg_pid_get_user_unit(pid, &t) >= 0)
560 core_unit = strappend("COREDUMP_USER_UNIT=", t);
563 IOVEC_SET_STRING(iovec[j++], core_unit);
565 /* OK, now we know it's not the journal, hence we can make use
567 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
570 core_pid = strappend("COREDUMP_PID=", info[INFO_PID]);
572 IOVEC_SET_STRING(iovec[j++], core_pid);
574 core_uid = strappend("COREDUMP_UID=", info[INFO_UID]);
576 IOVEC_SET_STRING(iovec[j++], core_uid);
578 core_gid = strappend("COREDUMP_GID=", info[INFO_GID]);
580 IOVEC_SET_STRING(iovec[j++], core_gid);
582 core_signal = strappend("COREDUMP_SIGNAL=", info[INFO_SIGNAL]);
584 IOVEC_SET_STRING(iovec[j++], core_signal);
586 if (sd_pid_get_session(pid, &t) >= 0) {
587 core_session = strappend("COREDUMP_SESSION=", t);
591 IOVEC_SET_STRING(iovec[j++], core_session);
594 if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
595 asprintf(&core_owner_uid, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
598 IOVEC_SET_STRING(iovec[j++], core_owner_uid);
601 if (sd_pid_get_slice(pid, &t) >= 0) {
602 core_slice = strappend("COREDUMP_SLICE=", t);
606 IOVEC_SET_STRING(iovec[j++], core_slice);
610 core_comm = strappend("COREDUMP_COMM=", comm);
612 IOVEC_SET_STRING(iovec[j++], core_comm);
616 core_exe = strappend("COREDUMP_EXE=", exe);
618 IOVEC_SET_STRING(iovec[j++], core_exe);
621 if (get_process_cmdline(pid, 0, false, &t) >= 0) {
622 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
626 IOVEC_SET_STRING(iovec[j++], core_cmdline);
629 if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
630 core_cgroup = strappend("COREDUMP_CGROUP=", t);
634 IOVEC_SET_STRING(iovec[j++], core_cgroup);
637 core_timestamp = strjoin("COREDUMP_TIMESTAMP=", info[INFO_TIMESTAMP], "000000", NULL);
639 IOVEC_SET_STRING(iovec[j++], core_timestamp);
641 IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
642 IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
644 /* Vacuum before we write anything again */
645 coredump_vacuum(-1, arg_keep_free, arg_max_use);
647 /* Always stream the coredump to disk, if that's possible */
648 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
650 /* skip whole core dumping part */
653 /* If we don't want to keep the coredump on disk, remove it
654 * now, as later on we will lack the privileges for
655 * it. However, we keep the fd to it, so that we can still
656 * process it and log it. */
657 r = maybe_remove_external_coredump(filename, coredump_size);
661 const char *coredump_filename;
663 coredump_filename = strappenda("COREDUMP_FILENAME=", filename);
664 IOVEC_SET_STRING(iovec[j++], coredump_filename);
667 /* Vacuum again, but exclude the coredump we just created */
668 coredump_vacuum(coredump_fd, arg_keep_free, arg_max_use);
670 /* Now, let's drop privileges to become the user who owns the
671 * segfaulted process and allocate the coredump memory under
672 * his uid. This also ensures that the credentials journald
673 * will see are the ones of the coredumping user, thus making
674 * sure the user himself gets access to the core dump. */
675 if (setresgid(gid, gid, gid) < 0 ||
676 setresuid(uid, uid, uid) < 0) {
677 log_error("Failed to drop privileges: %m");
683 /* Try to get a strack trace if we can */
684 if (coredump_size <= arg_process_size_max) {
685 _cleanup_free_ char *stacktrace = NULL;
687 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
689 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.\n\n", stacktrace, NULL);
691 log_warning("Failed to generate stack trace: %s", strerror(-r));
697 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.", NULL);
699 IOVEC_SET_STRING(iovec[j++], core_message);
701 /* Optionally store the entire coredump in the journal */
702 if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
703 coredump_size <= (off_t) arg_journal_size_max) {
706 /* Store the coredump itself in the journal */
708 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
710 iovec[j].iov_base = coredump_data;
711 iovec[j].iov_len = sz;
716 r = sd_journal_sendv(iovec, j);
718 log_error("Failed to log coredump: %s", strerror(-r));
721 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;