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>
37 #include "cgroup-util.h"
38 #include "journald-native.h"
39 #include "conf-parser.h"
41 #include "stacktrace.h"
42 #include "path-util.h"
49 /* The maximum size up to which we process coredumps */
50 #define PROCESS_SIZE_MAX ((off_t) (2LLU*1024LLU*1024LLU*1024LLU))
52 /* The maximum size up to which we leave the coredump around on
54 #define EXTERNAL_SIZE_MAX PROCESS_SIZE_MAX
56 /* The maximum size up to which we store the coredump in the
58 #define JOURNAL_SIZE_MAX ((size_t) (767LU*1024LU*1024LU))
60 /* Make sure to not make this larger than the maximum journal entry
61 * size. See ENTRY_SIZE_MAX in journald-native.c. */
62 assert_cc(JOURNAL_SIZE_MAX <= ENTRY_SIZE_MAX);
74 typedef enum CoredumpStorage {
75 COREDUMP_STORAGE_NONE,
76 COREDUMP_STORAGE_EXTERNAL,
77 COREDUMP_STORAGE_JOURNAL,
78 COREDUMP_STORAGE_BOTH,
79 _COREDUMP_STORAGE_MAX,
80 _COREDUMP_STORAGE_INVALID = -1
83 static CoredumpStorage arg_storage = COREDUMP_STORAGE_EXTERNAL;
84 static off_t arg_process_size_max = PROCESS_SIZE_MAX;
85 static off_t arg_external_size_max = EXTERNAL_SIZE_MAX;
86 static size_t arg_journal_size_max = JOURNAL_SIZE_MAX;
88 static const char* const coredump_storage_table[_COREDUMP_STORAGE_MAX] = {
89 [COREDUMP_STORAGE_NONE] = "none",
90 [COREDUMP_STORAGE_EXTERNAL] = "external",
91 [COREDUMP_STORAGE_JOURNAL] = "journal",
92 [COREDUMP_STORAGE_BOTH] = "both",
95 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_storage, CoredumpStorage);
96 static DEFINE_CONFIG_PARSE_ENUM(config_parse_coredump_storage, coredump_storage, CoredumpStorage, "Failed to parse storage setting");
98 static int parse_config(void) {
100 static const ConfigTableItem items[] = {
101 { "Coredump", "ProcessSizeMax", config_parse_iec_off, 0, &arg_process_size_max },
102 { "Coredump", "ExternalSizeMax", config_parse_iec_off, 0, &arg_external_size_max },
103 { "Coredump", "JournalSizeMax", config_parse_iec_size, 0, &arg_journal_size_max },
104 { "Coredump", "Storage", config_parse_coredump_storage, 0, &arg_storage },
110 "/etc/systemd/coredump.conf",
113 config_item_table_lookup,
120 static int fix_acl(int fd, uid_t uid) {
123 _cleanup_(acl_freep) acl_t acl = NULL;
125 acl_permset_t permset;
127 if (uid <= SYSTEM_UID_MAX)
130 /* Make sure normal users can read (but not write or delete)
131 * their own coredumps */
133 acl = acl_get_fd(fd);
135 log_error("Failed to get ACL: %m");
139 if (acl_create_entry(&acl, &entry) < 0 ||
140 acl_set_tag_type(entry, ACL_USER) < 0 ||
141 acl_set_qualifier(entry, &uid) < 0) {
142 log_error("Failed to patch ACL: %m");
146 if (acl_get_permset(entry, &permset) < 0 ||
147 acl_add_perm(permset, ACL_READ) < 0 ||
148 calc_acl_mask_if_needed(&acl) < 0) {
149 log_warning("Failed to patch ACL: %m");
153 if (acl_set_fd(fd, acl) < 0) {
154 log_error("Failed to apply ACL: %m");
162 static int fix_xattr(int fd, char *argv[]) {
164 static const char * const xattrs[_ARG_MAX] = {
165 [ARG_PID] = "user.coredump.pid",
166 [ARG_UID] = "user.coredump.uid",
167 [ARG_GID] = "user.coredump.gid",
168 [ARG_SIGNAL] = "user.coredump.signal",
169 [ARG_TIMESTAMP] = "user.coredump.timestamp",
170 [ARG_COMM] = "user.coredump.comm",
176 /* Attach some metadate to coredumps via extended
177 * attributes. Just because we can. */
179 for (i = 0; i < _ARG_MAX; i++) {
180 if (isempty(argv[i]))
183 if (fsetxattr(fd, xattrs[i], argv[i], strlen(argv[i]), XATTR_CREATE) < 0)
190 #define filename_escape(s) xescape((s), "./ ")
192 static int save_external_coredump(char **argv, uid_t uid, char **ret_filename, int *ret_fd, off_t *ret_size) {
193 _cleanup_free_ char *p = NULL, *t = NULL, *c = NULL, *fn = NULL, *tmp = NULL;
194 _cleanup_close_ int fd = -1;
200 assert(ret_filename);
204 c = filename_escape(argv[ARG_COMM]);
208 p = filename_escape(argv[ARG_PID]);
212 t = filename_escape(argv[ARG_TIMESTAMP]);
216 r = sd_id128_get_boot(&boot);
218 log_error("Failed to determine boot ID: %s", strerror(-r));
223 "/var/lib/systemd/coredump/core.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
225 SD_ID128_FORMAT_VAL(boot),
231 tmp = tempfn_random(fn);
235 mkdir_p_label("/var/lib/systemd/coredump", 0755);
237 fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
239 log_error("Failed to create coredump file: %m");
243 r = copy_bytes(STDIN_FILENO, fd, arg_process_size_max);
245 log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", argv[ARG_PID], argv[ARG_COMM]);
247 } else if (IN_SET(r, -EDQUOT, -ENOSPC)) {
248 log_error("Not enough disk space for coredump of %s (%s), refusing.", argv[ARG_PID], argv[ARG_COMM]);
251 log_error("Failed to dump coredump to file: %s", strerror(-r));
255 /* Ignore errors on these */
261 log_error("Failed to sync coredump: %m");
266 if (fstat(fd, &st) < 0) {
267 log_error("Failed to fstat coredump: %m");
272 if (rename(tmp, fn) < 0) {
273 log_error("Failed to rename coredump: %m");
280 *ret_size = st.st_size;
292 static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
293 _cleanup_free_ char *field = NULL;
300 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
301 log_warning("Failed to seek: %m");
305 field = malloc(9 + size);
307 log_warning("Failed to allocate memory fore coredump, coredump will not be stored.");
311 memcpy(field, "COREDUMP=", 9);
313 n = read(fd, field + 9, size);
315 log_error("Failed to read core data: %s", strerror(-n));
318 if ((size_t) n < size) {
319 log_error("Core data too short.");
324 *ret_size = size + 9;
331 static int maybe_remove_external_coredump(const char *filename, off_t size) {
336 if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
337 size <= arg_external_size_max)
340 if (unlink(filename) < 0) {
341 log_error("Failed to unlink %s: %m", filename);
348 int main(int argc, char* argv[]) {
350 _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
351 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
352 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
353 *coredump_filename = NULL, *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL,
356 _cleanup_close_ int coredump_fd = -1;
358 struct iovec iovec[17];
361 uid_t uid, owner_uid;
366 /* Make sure we never enter a loop */
367 prctl(PR_SET_DUMPABLE, 0);
369 /* First, log to a safe place, since we don't know what
370 * crashed and it might be journald which we'd rather not log
372 log_set_target(LOG_TARGET_KMSG);
375 if (argc != _ARG_MAX) {
376 log_error("Invalid number of arguments passed from kernel.");
381 /* Ignore all parse errors */
383 log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
385 /* Exit early if we cannot write the coredump to disk anyway */
386 if (path_is_read_only_fs("/var/lib") != 0) {
387 log_error("Coredump directory not mounted or not writable, skipping coredump.");
392 r = parse_uid(argv[ARG_UID], &uid);
394 log_error("Failed to parse UID.");
398 r = parse_pid(argv[ARG_PID], &pid);
400 log_error("Failed to parse PID.");
404 r = parse_gid(argv[ARG_GID], &gid);
406 log_error("Failed to parse GID.");
410 if (cg_pid_get_unit(pid, &t) >= 0) {
412 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
414 /* If we are journald, we cut things short,
415 * don't write to the journal, but still
416 * create a coredump. */
418 if (arg_storage != COREDUMP_STORAGE_NONE)
419 arg_storage = COREDUMP_STORAGE_EXTERNAL;
421 r = save_external_coredump(argv, uid, &coredump_filename, &coredump_fd, &coredump_size);
425 r = maybe_remove_external_coredump(coredump_filename, coredump_size);
429 log_info("Detected coredump of the journal daemon itself, diverted to %s.", coredump_filename);
433 core_unit = strappend("COREDUMP_UNIT=", t);
434 } else if (cg_pid_get_user_unit(pid, &t) >= 0)
435 core_unit = strappend("COREDUMP_USER_UNIT=", t);
438 IOVEC_SET_STRING(iovec[j++], core_unit);
440 /* OK, now we know it's not the journal, hence we can make use
442 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
445 core_pid = strappend("COREDUMP_PID=", argv[ARG_PID]);
447 IOVEC_SET_STRING(iovec[j++], core_pid);
449 core_uid = strappend("COREDUMP_UID=", argv[ARG_UID]);
451 IOVEC_SET_STRING(iovec[j++], core_uid);
453 core_gid = strappend("COREDUMP_GID=", argv[ARG_GID]);
455 IOVEC_SET_STRING(iovec[j++], core_gid);
457 core_signal = strappend("COREDUMP_SIGNAL=", argv[ARG_SIGNAL]);
459 IOVEC_SET_STRING(iovec[j++], core_signal);
461 core_comm = strappend("COREDUMP_COMM=", argv[ARG_COMM]);
463 IOVEC_SET_STRING(iovec[j++], core_comm);
465 if (sd_pid_get_session(pid, &t) >= 0) {
466 core_session = strappend("COREDUMP_SESSION=", t);
470 IOVEC_SET_STRING(iovec[j++], core_session);
473 if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
474 asprintf(&core_owner_uid, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
477 IOVEC_SET_STRING(iovec[j++], core_owner_uid);
480 if (sd_pid_get_slice(pid, &t) >= 0) {
481 core_slice = strappend("COREDUMP_SLICE=", t);
485 IOVEC_SET_STRING(iovec[j++], core_slice);
488 if (get_process_exe(pid, &exe) >= 0) {
489 core_exe = strappend("COREDUMP_EXE=", exe);
491 IOVEC_SET_STRING(iovec[j++], core_exe);
494 if (get_process_cmdline(pid, 0, false, &t) >= 0) {
495 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
499 IOVEC_SET_STRING(iovec[j++], core_cmdline);
502 if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
503 core_cgroup = strappend("COREDUMP_CGROUP=", t);
507 IOVEC_SET_STRING(iovec[j++], core_cgroup);
510 core_timestamp = strjoin("COREDUMP_TIMESTAMP=", argv[ARG_TIMESTAMP], "000000", NULL);
512 IOVEC_SET_STRING(iovec[j++], core_timestamp);
514 IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
515 IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
517 /* Always stream the coredump to disk, if that's possible */
518 r = save_external_coredump(argv, uid, &coredump_filename, &coredump_fd, &coredump_size);
522 /* If we don't want to keep the coredump on disk, remove it
523 * now, as later on we will lack the privileges for
524 * it. However, we keep the fd to it, so that we can still
525 * process it and log it. */
526 r = maybe_remove_external_coredump(coredump_filename, coredump_size);
530 /* Now, let's drop privileges to become the user who owns the
531 * segfaulted process and allocate the coredump memory under
532 * his uid. This also ensures that the credentials journald
533 * will see are the ones of the coredumping user, thus making
534 * sure the user himself gets access to the core dump. */
535 if (setresgid(gid, gid, gid) < 0 ||
536 setresuid(uid, uid, uid) < 0) {
537 log_error("Failed to drop privileges: %m");
543 /* Try to get a strack trace if we can */
544 if (coredump_size <= arg_process_size_max) {
545 _cleanup_free_ char *stacktrace = NULL;
547 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
549 core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") of user ", argv[ARG_UID], " dumped core.\n\n", stacktrace, NULL);
551 log_warning("Failed to generate stack trace: %s", strerror(-r));
556 core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") of user ", argv[ARG_UID], " dumped core.", NULL);
558 IOVEC_SET_STRING(iovec[j++], core_message);
560 /* Optionally store the entire coredump in the journal */
561 if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
562 coredump_size <= (off_t) arg_journal_size_max) {
565 /* Store the coredump itself in the journal */
567 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
569 iovec[j].iov_base = coredump_data;
570 iovec[j].iov_len = sz;
575 r = sd_journal_sendv(iovec, j);
577 log_error("Failed to log coredump: %s", strerror(-r));
580 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;