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 <attr/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"
48 /* The maximum size up to which we process coredumps */
49 #define PROCESS_SIZE_MAX ((off_t) (2LLU*1024LLU*1024LLU*1024LLU))
51 /* The maximum size up to which we leave the coredump around on
53 #define EXTERNAL_SIZE_MAX PROCESS_SIZE_MAX
55 /* The maximum size up to which we store the coredump in the
57 #define JOURNAL_SIZE_MAX ((size_t) (767LU*1024LU*1024LU))
59 /* Make sure to not make this larger than the maximum journal entry
60 * size. See ENTRY_SIZE_MAX in journald-native.c. */
61 assert_cc(JOURNAL_SIZE_MAX <= ENTRY_SIZE_MAX);
73 typedef enum CoredumpStorage {
74 COREDUMP_STORAGE_NONE,
75 COREDUMP_STORAGE_EXTERNAL,
76 COREDUMP_STORAGE_JOURNAL,
77 COREDUMP_STORAGE_BOTH,
78 _COREDUMP_STORAGE_MAX,
79 _COREDUMP_STORAGE_INVALID = -1
82 static CoredumpStorage arg_storage = COREDUMP_STORAGE_EXTERNAL;
83 static off_t arg_process_size_max = PROCESS_SIZE_MAX;
84 static off_t arg_external_size_max = EXTERNAL_SIZE_MAX;
85 static size_t arg_journal_size_max = JOURNAL_SIZE_MAX;
87 static const char* const coredump_storage_table[_COREDUMP_STORAGE_MAX] = {
88 [COREDUMP_STORAGE_NONE] = "none",
89 [COREDUMP_STORAGE_EXTERNAL] = "external",
90 [COREDUMP_STORAGE_JOURNAL] = "journal",
91 [COREDUMP_STORAGE_BOTH] = "both",
94 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_storage, CoredumpStorage);
95 static DEFINE_CONFIG_PARSE_ENUM(config_parse_coredump_storage, coredump_storage, CoredumpStorage, "Failed to parse storage setting");
97 static int parse_config(void) {
99 static const ConfigTableItem items[] = {
100 { "Coredump", "ProcessSizeMax", config_parse_iec_off, 0, &arg_process_size_max },
101 { "Coredump", "ExternalSizeMax", config_parse_iec_off, 0, &arg_external_size_max },
102 { "Coredump", "JournalSizeMax", config_parse_iec_size, 0, &arg_journal_size_max },
103 { "Coredump", "Storage", config_parse_coredump_storage, 0, &arg_storage },
109 "/etc/systemd/coredump.conf",
112 config_item_table_lookup,
119 static int fix_acl(int fd, uid_t uid) {
122 _cleanup_(acl_freep) acl_t acl = NULL;
124 acl_permset_t permset;
126 if (uid <= SYSTEM_UID_MAX)
129 /* Make sure normal users can read (but not write or delete)
130 * their own coredumps */
132 acl = acl_get_fd(fd);
134 log_error("Failed to get ACL: %m");
138 if (acl_create_entry(&acl, &entry) < 0 ||
139 acl_set_tag_type(entry, ACL_USER) < 0 ||
140 acl_set_qualifier(entry, &uid) < 0) {
141 log_error("Failed to patch ACL: %m");
145 if (acl_get_permset(entry, &permset) < 0 ||
146 acl_add_perm(permset, ACL_READ) < 0 ||
147 calc_acl_mask_if_needed(&acl) < 0) {
148 log_warning("Failed to patch ACL: %m");
152 if (acl_set_fd(fd, acl) < 0) {
153 log_error("Failed to apply ACL: %m");
161 static int fix_xattr(int fd, char *argv[]) {
164 /* Attach some metadate to coredumps via extended
165 * attributes. Just because we can. */
167 if (!isempty(argv[ARG_PID]))
168 if (fsetxattr(fd, "user.coredump.pid", argv[ARG_PID], strlen(argv[ARG_PID]), XATTR_CREATE) < 0)
171 if (!isempty(argv[ARG_UID]))
172 if (fsetxattr(fd, "user.coredump.uid", argv[ARG_UID], strlen(argv[ARG_UID]), XATTR_CREATE) < 0)
175 if (!isempty(argv[ARG_GID]))
176 if (fsetxattr(fd, "user.coredump.gid", argv[ARG_GID], strlen(argv[ARG_GID]), XATTR_CREATE) < 0)
179 if (!isempty(argv[ARG_SIGNAL]))
180 if (fsetxattr(fd, "user.coredump.signal", argv[ARG_SIGNAL], strlen(argv[ARG_SIGNAL]), XATTR_CREATE) < 0)
183 if (!isempty(argv[ARG_TIMESTAMP]))
184 if (fsetxattr(fd, "user.coredump.timestamp", argv[ARG_TIMESTAMP], strlen(argv[ARG_TIMESTAMP]), XATTR_CREATE) < 0)
187 if (!isempty(argv[ARG_COMM]))
188 if (fsetxattr(fd, "user.coredump.comm", argv[ARG_COMM], strlen(argv[ARG_COMM]), XATTR_CREATE) < 0)
194 #define filename_escape(s) xescape((s), "./")
196 static int save_external_coredump(char **argv, uid_t uid, char **ret_filename, int *ret_fd, off_t *ret_size) {
197 _cleanup_free_ char *p = NULL, *t = NULL, *c = NULL, *fn = NULL, *tmp = NULL;
198 _cleanup_close_ int fd = -1;
204 assert(ret_filename);
208 c = filename_escape(argv[ARG_COMM]);
212 p = filename_escape(argv[ARG_PID]);
216 t = filename_escape(argv[ARG_TIMESTAMP]);
220 r = sd_id128_get_boot(&boot);
222 log_error("Failed to determine boot ID: %s", strerror(-r));
227 "/var/lib/systemd/coredump/core.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
229 SD_ID128_FORMAT_VAL(boot),
235 tmp = tempfn_random(fn);
239 mkdir_p_label("/var/lib/systemd/coredump", 0755);
241 fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
243 log_error("Failed to create coredump file: %m");
247 r = copy_bytes(STDIN_FILENO, fd);
249 log_error("Failed to dump coredump to file: %s", strerror(-r));
253 /* Ignore errors on these */
259 log_error("Failed to sync coredump: %m");
264 if (fstat(fd, &st) < 0) {
265 log_error("Failed to fstat coredump: %m");
270 if (rename(tmp, fn) < 0) {
271 log_error("Failed to rename coredump: %m");
278 *ret_size = st.st_size;
290 static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
291 _cleanup_free_ char *field = NULL;
298 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
299 log_warning("Failed to seek: %m");
303 field = malloc(9 + size);
305 log_warning("Failed to allocate memory fore coredump, coredump will not be stored.");
309 memcpy(field, "COREDUMP=", 9);
311 n = read(fd, field + 9, size);
313 log_error("Failed to read core data: %s", strerror(-n));
316 if ((size_t) n < size) {
317 log_error("Core data too short.");
322 *ret_size = size + 9;
329 static int maybe_remove_external_coredump(const char *filename, off_t size) {
334 if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
335 size <= arg_external_size_max)
338 if (unlink(filename) < 0) {
339 log_error("Failed to unlink %s: %m", filename);
346 int main(int argc, char* argv[]) {
348 _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
349 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
350 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
351 *coredump_filename = NULL, *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL,
354 _cleanup_close_ int coredump_fd = -1;
356 struct iovec iovec[17];
359 uid_t uid, owner_uid;
364 /* Make sure we never enter a loop */
365 prctl(PR_SET_DUMPABLE, 0);
367 /* First, log to a safe place, since we don't know what
368 * crashed and it might be journald which we'd rather not log
370 log_set_target(LOG_TARGET_KMSG);
373 if (argc != _ARG_MAX) {
374 log_error("Invalid number of arguments passed from kernel.");
379 /* Ignore all parse errors */
381 log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
383 r = parse_uid(argv[ARG_UID], &uid);
385 log_error("Failed to parse UID.");
389 r = parse_pid(argv[ARG_PID], &pid);
391 log_error("Failed to parse PID.");
395 r = parse_gid(argv[ARG_GID], &gid);
397 log_error("Failed to parse GID.");
401 if (cg_pid_get_unit(pid, &t) >= 0) {
403 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
405 /* If we are journald, we cut things short,
406 * don't write to the journal, but still
407 * create a coredump. */
409 if (arg_storage != COREDUMP_STORAGE_NONE)
410 arg_storage = COREDUMP_STORAGE_EXTERNAL;
412 r = save_external_coredump(argv, uid, &coredump_filename, &coredump_fd, &coredump_size);
416 r = maybe_remove_external_coredump(coredump_filename, coredump_size);
420 log_info("Detected coredump of the journal daemon itself, diverted to %s.", coredump_filename);
424 core_unit = strappend("COREDUMP_UNIT=", t);
425 } else if (cg_pid_get_user_unit(pid, &t) >= 0)
426 core_unit = strappend("COREDUMP_USER_UNIT=", t);
429 IOVEC_SET_STRING(iovec[j++], core_unit);
431 /* OK, now we know it's not the journal, hence we can make use
433 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
436 core_pid = strappend("COREDUMP_PID=", argv[ARG_PID]);
438 IOVEC_SET_STRING(iovec[j++], core_pid);
440 core_uid = strappend("COREDUMP_UID=", argv[ARG_UID]);
442 IOVEC_SET_STRING(iovec[j++], core_uid);
444 core_gid = strappend("COREDUMP_GID=", argv[ARG_GID]);
446 IOVEC_SET_STRING(iovec[j++], core_gid);
448 core_signal = strappend("COREDUMP_SIGNAL=", argv[ARG_SIGNAL]);
450 IOVEC_SET_STRING(iovec[j++], core_signal);
452 core_comm = strappend("COREDUMP_COMM=", argv[ARG_COMM]);
454 IOVEC_SET_STRING(iovec[j++], core_comm);
456 if (sd_pid_get_session(pid, &t) >= 0) {
457 core_session = strappend("COREDUMP_SESSION=", t);
461 IOVEC_SET_STRING(iovec[j++], core_session);
464 if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
465 asprintf(&core_owner_uid, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
468 IOVEC_SET_STRING(iovec[j++], core_owner_uid);
471 if (sd_pid_get_slice(pid, &t) >= 0) {
472 core_slice = strappend("COREDUMP_SLICE=", t);
476 IOVEC_SET_STRING(iovec[j++], core_slice);
479 if (get_process_exe(pid, &exe) >= 0) {
480 core_exe = strappend("COREDUMP_EXE=", exe);
482 IOVEC_SET_STRING(iovec[j++], core_exe);
485 if (get_process_cmdline(pid, 0, false, &t) >= 0) {
486 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
490 IOVEC_SET_STRING(iovec[j++], core_cmdline);
493 if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
494 core_cgroup = strappend("COREDUMP_CGROUP=", t);
498 IOVEC_SET_STRING(iovec[j++], core_cgroup);
501 core_timestamp = strjoin("COREDUMP_TIMESTAMP=", argv[ARG_TIMESTAMP], "000000", NULL);
503 IOVEC_SET_STRING(iovec[j++], core_timestamp);
505 IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
506 IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
508 /* Always stream the coredump to disk, if that's possible */
509 r = save_external_coredump(argv, uid, &coredump_filename, &coredump_fd, &coredump_size);
513 /* If we don't want to keep the coredump on disk, remove it
514 * now, as later on we will lack the privileges for
515 * it. However, we keep the fd to it, so that we can still
516 * process it and log it. */
517 r = maybe_remove_external_coredump(coredump_filename, coredump_size);
521 /* Now, let's drop privileges to become the user who owns the
522 * segfaulted process and allocate the coredump memory under
523 * his uid. This also ensures that the credentials journald
524 * will see are the ones of the coredumping user, thus making
525 * sure the user himself gets access to the core dump. */
526 if (setresgid(gid, gid, gid) < 0 ||
527 setresuid(uid, uid, uid) < 0) {
528 log_error("Failed to drop privileges: %m");
534 /* Try to get a strack trace if we can */
535 if (coredump_size <= arg_process_size_max) {
536 _cleanup_free_ char *stacktrace = NULL;
538 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
540 core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") of user ", argv[ARG_UID], " dumped core.\n\n", stacktrace, NULL);
542 log_warning("Failed to generate stack trace: %s", strerror(-r));
547 core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") of user ", argv[ARG_UID], " dumped core.", NULL);
549 IOVEC_SET_STRING(iovec[j++], core_message);
551 /* Optionally store the entire coredump in the journal */
552 if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
553 coredump_size <= (off_t) arg_journal_size_max) {
556 /* Store the coredump itself in the journal */
558 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
560 iovec[j].iov_base = coredump_data;
561 iovec[j].iov_len = sz;
566 r = sd_journal_sendv(iovec, j);
568 log_error("Failed to log coredump: %s", strerror(-r));
571 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;