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);
245 log_error("Failed to dump coredump to file: %s", strerror(-r));
249 /* Ignore errors on these */
255 log_error("Failed to sync coredump: %m");
260 if (fstat(fd, &st) < 0) {
261 log_error("Failed to fstat coredump: %m");
266 if (rename(tmp, fn) < 0) {
267 log_error("Failed to rename coredump: %m");
274 *ret_size = st.st_size;
286 static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
287 _cleanup_free_ char *field = NULL;
294 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
295 log_warning("Failed to seek: %m");
299 field = malloc(9 + size);
301 log_warning("Failed to allocate memory fore coredump, coredump will not be stored.");
305 memcpy(field, "COREDUMP=", 9);
307 n = read(fd, field + 9, size);
309 log_error("Failed to read core data: %s", strerror(-n));
312 if ((size_t) n < size) {
313 log_error("Core data too short.");
318 *ret_size = size + 9;
325 static int maybe_remove_external_coredump(const char *filename, off_t size) {
330 if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
331 size <= arg_external_size_max)
334 if (unlink(filename) < 0) {
335 log_error("Failed to unlink %s: %m", filename);
342 int main(int argc, char* argv[]) {
344 _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
345 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
346 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
347 *coredump_filename = NULL, *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL,
350 _cleanup_close_ int coredump_fd = -1;
352 struct iovec iovec[17];
355 uid_t uid, owner_uid;
360 /* Make sure we never enter a loop */
361 prctl(PR_SET_DUMPABLE, 0);
363 /* First, log to a safe place, since we don't know what
364 * crashed and it might be journald which we'd rather not log
366 log_set_target(LOG_TARGET_KMSG);
369 if (argc != _ARG_MAX) {
370 log_error("Invalid number of arguments passed from kernel.");
375 /* Ignore all parse errors */
377 log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
379 /* Exit early if we cannot write the coredump to disk anyway */
380 if (path_is_read_only_fs("/var/lib") != 0) {
381 log_error("Coredump directory not mounted or not writable, skipping coredump.");
386 r = parse_uid(argv[ARG_UID], &uid);
388 log_error("Failed to parse UID.");
392 r = parse_pid(argv[ARG_PID], &pid);
394 log_error("Failed to parse PID.");
398 r = parse_gid(argv[ARG_GID], &gid);
400 log_error("Failed to parse GID.");
404 if (cg_pid_get_unit(pid, &t) >= 0) {
406 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
408 /* If we are journald, we cut things short,
409 * don't write to the journal, but still
410 * create a coredump. */
412 if (arg_storage != COREDUMP_STORAGE_NONE)
413 arg_storage = COREDUMP_STORAGE_EXTERNAL;
415 r = save_external_coredump(argv, uid, &coredump_filename, &coredump_fd, &coredump_size);
419 r = maybe_remove_external_coredump(coredump_filename, coredump_size);
423 log_info("Detected coredump of the journal daemon itself, diverted to %s.", coredump_filename);
427 core_unit = strappend("COREDUMP_UNIT=", t);
428 } else if (cg_pid_get_user_unit(pid, &t) >= 0)
429 core_unit = strappend("COREDUMP_USER_UNIT=", t);
432 IOVEC_SET_STRING(iovec[j++], core_unit);
434 /* OK, now we know it's not the journal, hence we can make use
436 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
439 core_pid = strappend("COREDUMP_PID=", argv[ARG_PID]);
441 IOVEC_SET_STRING(iovec[j++], core_pid);
443 core_uid = strappend("COREDUMP_UID=", argv[ARG_UID]);
445 IOVEC_SET_STRING(iovec[j++], core_uid);
447 core_gid = strappend("COREDUMP_GID=", argv[ARG_GID]);
449 IOVEC_SET_STRING(iovec[j++], core_gid);
451 core_signal = strappend("COREDUMP_SIGNAL=", argv[ARG_SIGNAL]);
453 IOVEC_SET_STRING(iovec[j++], core_signal);
455 core_comm = strappend("COREDUMP_COMM=", argv[ARG_COMM]);
457 IOVEC_SET_STRING(iovec[j++], core_comm);
459 if (sd_pid_get_session(pid, &t) >= 0) {
460 core_session = strappend("COREDUMP_SESSION=", t);
464 IOVEC_SET_STRING(iovec[j++], core_session);
467 if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
468 asprintf(&core_owner_uid, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
471 IOVEC_SET_STRING(iovec[j++], core_owner_uid);
474 if (sd_pid_get_slice(pid, &t) >= 0) {
475 core_slice = strappend("COREDUMP_SLICE=", t);
479 IOVEC_SET_STRING(iovec[j++], core_slice);
482 if (get_process_exe(pid, &exe) >= 0) {
483 core_exe = strappend("COREDUMP_EXE=", exe);
485 IOVEC_SET_STRING(iovec[j++], core_exe);
488 if (get_process_cmdline(pid, 0, false, &t) >= 0) {
489 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
493 IOVEC_SET_STRING(iovec[j++], core_cmdline);
496 if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
497 core_cgroup = strappend("COREDUMP_CGROUP=", t);
501 IOVEC_SET_STRING(iovec[j++], core_cgroup);
504 core_timestamp = strjoin("COREDUMP_TIMESTAMP=", argv[ARG_TIMESTAMP], "000000", NULL);
506 IOVEC_SET_STRING(iovec[j++], core_timestamp);
508 IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
509 IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
511 /* Always stream the coredump to disk, if that's possible */
512 r = save_external_coredump(argv, uid, &coredump_filename, &coredump_fd, &coredump_size);
516 /* If we don't want to keep the coredump on disk, remove it
517 * now, as later on we will lack the privileges for
518 * it. However, we keep the fd to it, so that we can still
519 * process it and log it. */
520 r = maybe_remove_external_coredump(coredump_filename, coredump_size);
524 /* Now, let's drop privileges to become the user who owns the
525 * segfaulted process and allocate the coredump memory under
526 * his uid. This also ensures that the credentials journald
527 * will see are the ones of the coredumping user, thus making
528 * sure the user himself gets access to the core dump. */
529 if (setresgid(gid, gid, gid) < 0 ||
530 setresuid(uid, uid, uid) < 0) {
531 log_error("Failed to drop privileges: %m");
537 /* Try to get a strack trace if we can */
538 if (coredump_size <= arg_process_size_max) {
539 _cleanup_free_ char *stacktrace = NULL;
541 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
543 core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") of user ", argv[ARG_UID], " dumped core.\n\n", stacktrace, NULL);
545 log_warning("Failed to generate stack trace: %s", strerror(-r));
550 core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") of user ", argv[ARG_UID], " dumped core.", NULL);
552 IOVEC_SET_STRING(iovec[j++], core_message);
554 /* Optionally store the entire coredump in the journal */
555 if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
556 coredump_size <= (off_t) arg_journal_size_max) {
559 /* Store the coredump itself in the journal */
561 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
563 iovec[j].iov_base = coredump_data;
564 iovec[j].iov_len = sz;
569 r = sd_journal_sendv(iovec, j);
571 log_error("Failed to log coredump: %s", strerror(-r));
574 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;