From 5b45a16067d7b8ce12e4732de7cc14b3f804ef06 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 6 Nov 2016 10:06:32 -0500 Subject: [PATCH] coredump: with --backtrace accept a journal entry on stdin The entry must be a single entry in the journal export format, including the terminating double newline. The MESSAGE field is now generated on the sender side. The advantage is that the reporter can easily pass additional metadata. Continuing with the example of the python excepthook: COREDUMP_PYTHON_EXECUTABLE=/usr/bin/python3 COREDUMP_PYTHON_VERSION=3.5.2 (default, Sep 14 2016, 11:28:32) [GCC 6.2.1 20160901 (Red Hat 6.2.1-1)] COREDUMP_PYTHON_THREAD_INFO=sys.thread_info(name='pthread', lock='semaphore', version='NPTL 2.24') COREDUMP_PYTHON_EXCEPTION_TYPE=ZeroDivisionError COREDUMP_PYTHON_EXCEPTION_VALUE=division by zero MESSAGE=Process 29514 (systemd_coredump_exception_handler.py) of user zbyszek failed with ZeroDivisionError: division by zero Traceback (most recent call last): File "systemd_coredump_exception_handler.py", line 134, in g() File "systemd_coredump_exception_handler.py", line 133, in g f() File "systemd_coredump_exception_handler.py", line 131, in f div0 = 1 / 0 ZeroDivisionError: division by zero Local variables in innermost frame: a=3 h= One consideration is whether to use the Journal Export Format, or send packets over a UNIX socket instead. The advantage of current solution is that although parsing is more complicated on the receiver side, it is much easier to use on the sender side. I hope this can be used by various languages for which writing binary structures to a UNIX socket is harder and more likely to be done wrong than piping of a simple textyish format. --- src/coredump/coredump.c | 79 ++++++++++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 33 deletions(-) diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index 2d9819e..38ba6e9 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -820,7 +820,7 @@ static void map_context_fields(const struct iovec *iovec, const char *context[]) static int process_socket(int fd) { _cleanup_close_ int coredump_fd = -1; struct iovec *iovec = NULL; - size_t n_iovec = 0, n_iovec_allocated = 0, i; + size_t n_iovec = 0, n_allocated = 0, i; const char *context[_CONTEXT_MAX] = {}; int r; @@ -845,7 +845,7 @@ static int process_socket(int fd) { ssize_t n; ssize_t l; - if (!GREEDY_REALLOC(iovec, n_iovec_allocated, n_iovec + 3)) { + if (!GREEDY_REALLOC(iovec, n_allocated, n_iovec + 3)) { r = log_oom(); goto finish; } @@ -909,7 +909,7 @@ static int process_socket(int fd) { n_iovec++; } - if (!GREEDY_REALLOC(iovec, n_iovec_allocated, n_iovec + 3)) { + if (!GREEDY_REALLOC(iovec, n_allocated, n_iovec + 3)) { r = log_oom(); goto finish; } @@ -924,7 +924,7 @@ static int process_socket(int fd) { assert(context[CONTEXT_COMM]); assert(coredump_fd >= 0); - r = submit_coredump(context, iovec, n_iovec_allocated, n_iovec, coredump_fd); + r = submit_coredump(context, iovec, n_allocated, n_iovec, coredump_fd); finish: for (i = 0; i < n_iovec; i++) @@ -1048,7 +1048,8 @@ static int gather_pid_metadata( const char *context[_CONTEXT_MAX], char **comm_fallback, char **comm_ret, - struct iovec iovec[27], size_t *n_iovec) { + struct iovec *iovec, size_t *n_iovec) { + /* We need 25 empty slots in iovec! */ _cleanup_free_ char *exe = NULL, *comm = NULL; uid_t owner_uid; @@ -1237,13 +1238,13 @@ static int process_kernel(int argc, char* argv[]) { static int process_backtrace(int argc, char *argv[]) { const char *context[_CONTEXT_MAX]; - char *t; - _cleanup_free_ char *comm = NULL; - struct iovec iovec[27]; - size_t n_iovec = 0, i, n_to_free; - uint8_t buf[4096]; - ssize_t buf_bytes; + _cleanup_free_ char *comm = NULL, *message = NULL; + _cleanup_free_ struct iovec *iovec = NULL; + size_t n_iovec, n_allocated, n_to_free = 0, i; int r; + JournalImporter importer = { + .fd = STDIN_FILENO, + }; log_debug("Processing backtrace on stdin..."); @@ -1259,38 +1260,50 @@ static int process_backtrace(int argc, char *argv[]) { context[CONTEXT_TIMESTAMP] = argv[CONTEXT_TIMESTAMP + 2]; context[CONTEXT_RLIMIT] = argv[CONTEXT_RLIMIT + 2]; - r = gather_pid_metadata(context, argv + CONTEXT_COMM + 2, &comm, iovec, &n_iovec); + n_allocated = 32; /* 25 metadata, 2 static, +unknown input, rounded up */ + iovec = new(struct iovec, n_allocated); + if (!iovec) + return log_oom(); + + r = gather_pid_metadata(context, argv + CONTEXT_COMM + 2, &comm, iovec, &n_to_free); if (r < 0) goto finish; + n_iovec = n_to_free; - if (isempty(context[CONTEXT_SIGNAL])) - context[CONTEXT_SIGNAL] = "an exception"; - - buf_bytes = loop_read(STDIN_FILENO, buf, sizeof(buf) - 1, false); - if (buf_bytes < 0) { - log_error_errno(buf_bytes, "Failed to read backtrace from stdin: %m"); - goto finish; - } - buf[buf_bytes] = '\0'; - - t = strjoin("MESSAGE=Process ", context[CONTEXT_PID], " (", comm, ")" - " of user ", context[CONTEXT_UID], - " failed with ", context[CONTEXT_SIGNAL], - ":\n\n", buf, NULL); - if (!t) { - log_oom(); - goto finish; + while (true) { + r = journal_importer_process_data(&importer); + if (r < 0) { + log_error_errno(r, "Failed to parse journal entry on stdin: %m"); + goto finish; + } + if (r == 1) + break; } - IOVEC_SET_STRING(iovec[n_iovec++], t); - n_to_free = n_iovec; + if (!GREEDY_REALLOC(iovec, n_allocated, n_iovec + importer.iovw.count + 2)) + return log_oom(); + + if (journal_importer_eof(&importer)) { + log_warning("Did not receive a full journal entry on stdin, ignoring message sent by reporter"); - IOVEC_SET_STRING(iovec[n_iovec++], "MESSAGE_ID=1f4e0a44a88649939aaea34fc6da8c95"); + message = strjoin("MESSAGE=Process ", context[CONTEXT_PID], " (", comm, ")" + " of user ", context[CONTEXT_UID], + " failed with ", context[CONTEXT_SIGNAL]); + if (!message) { + r = log_oom(); + goto finish; + } + IOVEC_SET_STRING(iovec[n_iovec++], message); + } else { + for (i = 0; i < importer.iovw.count; i++) + iovec[n_iovec++] = importer.iovw.iovec[i]; + } + IOVEC_SET_STRING(iovec[n_iovec++], "MESSAGE_ID=1f4e0a44a88649939aaea34fc6da8c95"); assert_cc(2 == LOG_CRIT); IOVEC_SET_STRING(iovec[n_iovec++], "PRIORITY=2"); - assert(n_iovec <= ELEMENTSOF(iovec)); + assert(n_iovec <= n_allocated); r = sd_journal_sendv(iovec, n_iovec); if (r < 0) -- 2.7.4