X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fjournal%2Fcoredump.c;h=733373b3071d16a70710aa19e9f034ae181bf729;hp=bbec550784f8a79855c3808140af67c4092ae0ab;hb=872c8faaf2009422a91d227ae0b5c6f04c9d2c69;hpb=de0229ca36034981f6c380de39bf7ffd3c6b57fa diff --git a/src/journal/coredump.c b/src/journal/coredump.c index bbec55078..733373b30 100644 --- a/src/journal/coredump.c +++ b/src/journal/coredump.c @@ -6,29 +6,42 @@ Copyright 2012 Lennart Poettering systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. systemd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. + Lesser General Public License for more details. - You should have received a copy of the GNU General Public License + You should have received a copy of the GNU Lesser General Public License along with systemd; If not, see . ***/ #include #include +#include +#include #include + +#ifdef HAVE_LOGIND #include +#endif #include "log.h" #include "util.h" +#include "macro.h" +#include "mkdir.h" +#include "special.h" +#include "cgroup-util.h" -#define COREDUMP_MAX (24*1024*1024) +/* Few programs have less than 3MiB resident */ +#define COREDUMP_MIN_START (3*1024*1024) +/* Make sure to not make this larger than the maximum journal entry + * size. See ENTRY_SIZE_MAX in journald-native.c. */ +#define COREDUMP_MAX (767*1024*1024) enum { ARG_PID = 1, @@ -40,21 +53,69 @@ enum { _ARG_MAX }; +static int divert_coredump(void) { + _cleanup_fclose_ FILE *f = NULL; + + log_info("Detected coredump of the journal daemon itself, diverting coredump to /var/lib/systemd/coredump/."); + + mkdir_p_label("/var/lib/systemd/coredump", 0755); + + f = fopen("/var/lib/systemd/coredump/core.systemd-journald", "we"); + if (!f) { + log_error("Failed to create coredump file: %m"); + return -errno; + } + + for (;;) { + uint8_t buffer[4096]; + size_t l, q; + + l = fread(buffer, 1, sizeof(buffer), stdin); + if (l <= 0) { + if (ferror(f)) { + log_error("Failed to read coredump: %m"); + return -errno; + } + + break; + } + + q = fwrite(buffer, 1, l, f); + if (q != l) { + log_error("Failed to write coredump: %m"); + return -errno; + } + } + + fflush(f); + + if (ferror(f)) { + log_error("Failed to write coredump: %m"); + return -errno; + } + + return 0; +} + int main(int argc, char* argv[]) { int r, j = 0; - char *p = NULL; + char *t; ssize_t n; pid_t pid; + uid_t uid; + gid_t gid; struct iovec iovec[14]; - char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL, + size_t coredump_bufsize, coredump_size; + _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL, *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL, - *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *t; + *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL; - log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); - log_parse_environment(); - log_open(); + prctl(PR_SET_DUMPABLE, 0); if (argc != _ARG_MAX) { + log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); + log_open(); + log_error("Invalid number of arguments passed from kernel."); r = -EINVAL; goto finish; @@ -62,31 +123,48 @@ int main(int argc, char* argv[]) { r = parse_pid(argv[ARG_PID], &pid); if (r < 0) { + log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); + log_open(); + log_error("Failed to parse PID."); - r = -EINVAL; goto finish; } - p = malloc(9 + COREDUMP_MAX); - if (!p) { - log_error("Out of memory"); - r = -ENOMEM; - goto finish; - } + if (cg_pid_get_unit(pid, &t) >= 0) { + + if (streq(t, SPECIAL_JOURNALD_SERVICE)) { + /* Make sure we don't make use of the journal, + * if it's the journal which is crashing */ + log_set_target(LOG_TARGET_KMSG); + log_open(); + + r = divert_coredump(); + goto finish; + } + + core_unit = strappend("COREDUMP_UNIT=", t); + } else if (cg_pid_get_user_unit(pid, &t) >= 0) + core_unit = strappend("COREDUMP_USER_UNIT=", t); - memcpy(p, "COREDUMP=", 9); + if (core_unit) + IOVEC_SET_STRING(iovec[j++], core_unit); - n = loop_read(STDIN_FILENO, p + 9, COREDUMP_MAX, false); - if (n < 0) { - log_error("Failed to read core dump data: %s", strerror(-n)); - r = (int) n; + /* OK, now we know it's not the journal, hence make use of + * it */ + log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); + log_open(); + + r = parse_uid(argv[ARG_UID], &uid); + if (r < 0) { + log_error("Failed to parse UID."); goto finish; } - zero(iovec); - iovec[j].iov_base = p; - iovec[j].iov_len = 9 + n; - j++; + r = parse_gid(argv[ARG_GID], &gid); + if (r < 0) { + log_error("Failed to parse GID."); + goto finish; + } core_pid = strappend("COREDUMP_PID=", argv[ARG_PID]); if (core_pid) @@ -108,6 +186,7 @@ int main(int argc, char* argv[]) { if (core_comm) IOVEC_SET_STRING(iovec[j++], core_comm); +#ifdef HAVE_LOGIND if (sd_pid_get_session(pid, &t) >= 0) { core_session = strappend("COREDUMP_SESSION=", t); free(t); @@ -116,13 +195,7 @@ int main(int argc, char* argv[]) { IOVEC_SET_STRING(iovec[j++], core_session); } - if (sd_pid_get_unit(pid, &t) >= 0) { - core_unit = strappend("COREDUMP_UNIT=", t); - free(t); - - if (core_unit) - IOVEC_SET_STRING(iovec[j++], core_unit); - } +#endif if (get_process_exe(pid, &t) >= 0) { core_exe = strappend("COREDUMP_EXE=", t); @@ -132,7 +205,7 @@ int main(int argc, char* argv[]) { IOVEC_SET_STRING(iovec[j++], core_exe); } - if (get_process_cmdline(pid, LINE_MAX, false, &t) >= 0) { + if (get_process_cmdline(pid, 0, false, &t) >= 0) { core_cmdline = strappend("COREDUMP_CMDLINE=", t); free(t); @@ -140,34 +213,72 @@ int main(int argc, char* argv[]) { IOVEC_SET_STRING(iovec[j++], core_cmdline); } - core_timestamp = join("COREDUMP_TIMESTAMP=", argv[ARG_TIMESTAMP], "000000", NULL); + core_timestamp = strjoin("COREDUMP_TIMESTAMP=", argv[ARG_TIMESTAMP], "000000", NULL); if (core_timestamp) IOVEC_SET_STRING(iovec[j++], core_timestamp); IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1"); IOVEC_SET_STRING(iovec[j++], "PRIORITY=2"); - core_message = join("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") dumped core.", NULL); + core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") dumped core.", NULL); if (core_message) IOVEC_SET_STRING(iovec[j++], core_message); + /* Now, let's drop privileges to become the user who owns the + * segfaulted process and allocate the coredump memory under + * his uid. This also ensures that the credentials journald + * will see are the ones of the coredumping user, thus making + * sure the user himself gets access to the core dump. */ + + if (setresgid(gid, gid, gid) < 0 || + setresuid(uid, uid, uid) < 0) { + log_error("Failed to drop privileges: %m"); + r = -errno; + goto finish; + } + + coredump_bufsize = COREDUMP_MIN_START; + coredump_data = malloc(coredump_bufsize); + if (!coredump_data) { + log_warning("Failed to allocate memory for core, core will not be stored."); + goto finalize; + } + + memcpy(coredump_data, "COREDUMP=", 9); + coredump_size = 9; + + for (;;) { + n = loop_read(STDIN_FILENO, coredump_data + coredump_size, + coredump_bufsize - coredump_size, false); + if (n < 0) { + log_error("Failed to read core data: %s", strerror(-n)); + r = (int) n; + goto finish; + } else if (n == 0) + break; + + coredump_size += n; + + if (coredump_size > COREDUMP_MAX) { + log_error("Core too large, core will not be stored."); + goto finalize; + } + + if (!GREEDY_REALLOC(coredump_data, coredump_bufsize, coredump_size + 1)) { + log_warning("Failed to allocate memory for core, core will not be stored."); + goto finalize; + } + } + + iovec[j].iov_base = coredump_data; + iovec[j].iov_len = coredump_size; + j++; + +finalize: r = sd_journal_sendv(iovec, j); if (r < 0) - log_error("Failed to send coredump: %s", strerror(-r)); + log_error("Failed to log coredump: %s", strerror(-r)); finish: - free(p); - free(core_pid); - free(core_uid); - free(core_gid); - free(core_signal); - free(core_timestamp); - free(core_comm); - free(core_exe); - free(core_cmdline); - free(core_unit); - free(core_session); - free(core_message); - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; }