From a6a73a10e813d8ced0b4ae0da4d2b3fe9f00120b Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Sat, 25 Feb 2017 09:35:09 -0500 Subject: coredump: slight simplification --- src/coredump/coredump.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index 0fa830c33e..463e0abef4 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -1236,13 +1236,9 @@ static int process_kernel(int argc, char* argv[]) { context[CONTEXT_RLIMIT] = argv[CONTEXT_RLIMIT + 1]; r = gather_pid_metadata_and_process_special_crash(context, argv + CONTEXT_COMM + 1, NULL, iovec, &n_to_free); - if (r < 0) - goto finish; - if (r > 0) { - /* This was a special crash, and has already been processed. */ - r = 0; + if (r != 0) + /* Error, or a a special crash, which has already been processed. */ goto finish; - } n_iovec = n_to_free; -- cgit v1.2.3-54-g00ecf From ea5cc2a8f65535a9b3f8ba39a8df13a0c770f41d Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Sat, 25 Feb 2017 14:00:39 -0500 Subject: coredump: do not try to access unitialized CONTEXT_COMM field Most of the fields in the context array come from the kernel (passed through argv), but two are special: comm and exe. We allocate them ourselves. We forgot to initialize context[CONTEXT_COMM] with the value we allocated (introduced in 9aa820231414baa28e6bf02a033932cb69ff6b8b). To simplify things, just set context[CONTEXT_COMM] and context[CONTEXT_EXE], and free those two fields at the end. Fixes #5442. --- src/coredump/coredump.c | 90 +++++++++++++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 37 deletions(-) diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index 463e0abef4..e38cc59a36 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -78,8 +78,22 @@ assert_cc(JOURNAL_SIZE_MAX <= DATA_SIZE_MAX); enum { - /* We use this as array indexes for a couple of special fields we use for naming coredumping files, and - * attaching xattrs */ + /* We use this as array indexes for a couple of special fields we use for + * naming coredump files, and attaching xattrs, and for indexing argv[]. + + * Our pattern for man:systectl(1) kernel.core_pattern is such that the + * kernel passes fields until CONTEXT_RLIMIT as arguments in argv[]. After + * that it gets complicated: the kernel passes "comm" as one or more fields + * starting at index CONTEXT_COMM (in other words, full "comm" is under index + * CONTEXT_COMM when it does not contain spaces, which is the common + * case). This mapping is not reversible, so we prefer to retrieve "comm" + * from /proc. We only fall back to argv[CONTEXT_COMM...] when that fails. + * + * In the internal context[] array, fields before CONTEXT_COMM are the + * strings from argv[], so they should not be freed. The strings at indices + * CONTEXT_COMM and higher are allocated by us and should be freed at the + * end. + */ CONTEXT_PID, CONTEXT_UID, CONTEXT_GID, @@ -784,7 +798,7 @@ log: return 0; } -static void map_context_fields(const struct iovec *iovec, const char *context[]) { +static void map_context_fields(const struct iovec *iovec, const char* context[]) { static const char * const context_field_names[_CONTEXT_MAX] = { [CONTEXT_PID] = "COREDUMP_PID=", @@ -1054,9 +1068,8 @@ static char* set_iovec_field_free(struct iovec iovec[27], size_t *n_iovec, const } static int gather_pid_metadata_and_process_special_crash( - const char *context[_CONTEXT_MAX], + char* context[_CONTEXT_MAX], char **comm_fallback, - char **comm_ret, struct iovec *iovec, size_t *n_iovec) { /* We need 26 empty slots in iovec! @@ -1064,7 +1077,6 @@ static int gather_pid_metadata_and_process_special_crash( * Note that if we fail on oom later on, we do not roll-back changes to the iovec structure. (It remains valid, * with the first n_iovec fields initialized.) */ - _cleanup_free_ char *exe = NULL, *comm = NULL; uid_t owner_uid; pid_t pid; char *t; @@ -1075,15 +1087,15 @@ static int gather_pid_metadata_and_process_special_crash( if (r < 0) return log_error_errno(r, "Failed to parse PID \"%s\": %m", context[CONTEXT_PID]); - r = get_process_comm(pid, &comm); + r = get_process_comm(pid, &context[CONTEXT_COMM]); if (r < 0) { log_warning_errno(r, "Failed to get COMM, falling back to the command line: %m"); - comm = strv_join(comm_fallback, " "); - if (!comm) + context[CONTEXT_COMM] = strv_join(comm_fallback, " "); + if (!context[CONTEXT_COMM]) return log_oom(); } - r = get_process_exe(pid, &exe); + r = get_process_exe(pid, &context[CONTEXT_EXE]); if (r < 0) log_warning_errno(r, "Failed to get EXE, ignoring: %m"); @@ -1099,7 +1111,7 @@ static int gather_pid_metadata_and_process_special_crash( * are unlikely to work then. */ if (STR_IN_SET(t, SPECIAL_JOURNALD_SERVICE, SPECIAL_INIT_SCOPE)) { free(t); - r = process_special_crash(context, STDIN_FILENO); + r = process_special_crash((const char**) context, STDIN_FILENO); if (r < 0) return r; @@ -1132,11 +1144,11 @@ static int gather_pid_metadata_and_process_special_crash( if (!set_iovec_field(iovec, n_iovec, "COREDUMP_RLIMIT=", context[CONTEXT_RLIMIT])) return log_oom(); - if (!set_iovec_field(iovec, n_iovec, "COREDUMP_COMM=", comm)) + if (!set_iovec_field(iovec, n_iovec, "COREDUMP_COMM=", context[CONTEXT_COMM])) return log_oom(); - if (exe && - !set_iovec_field(iovec, n_iovec, "COREDUMP_EXE=", exe)) + if (context[CONTEXT_EXE] && + !set_iovec_field(iovec, n_iovec, "COREDUMP_EXE=", context[CONTEXT_EXE])) return log_oom(); if (sd_pid_get_session(pid, &t) >= 0) @@ -1206,17 +1218,12 @@ static int gather_pid_metadata_and_process_special_crash( if (safe_atoi(context[CONTEXT_SIGNAL], &signo) >= 0 && SIGNAL_VALID(signo)) set_iovec_field(iovec, n_iovec, "COREDUMP_SIGNAL_NAME=SIG", signal_to_string(signo)); - if (comm_ret) { - *comm_ret = comm; - comm = NULL; - } - return 0; /* == 0 means: we successfully acquired all metadata */ } static int process_kernel(int argc, char* argv[]) { - const char *context[_CONTEXT_MAX]; + char* context[_CONTEXT_MAX] = {}; struct iovec iovec[28]; size_t i, n_iovec, n_to_free = 0; int r; @@ -1228,14 +1235,14 @@ static int process_kernel(int argc, char* argv[]) { return -EINVAL; } - context[CONTEXT_PID] = argv[CONTEXT_PID + 1]; - context[CONTEXT_UID] = argv[CONTEXT_UID + 1]; - context[CONTEXT_GID] = argv[CONTEXT_GID + 1]; - context[CONTEXT_SIGNAL] = argv[CONTEXT_SIGNAL + 1]; - context[CONTEXT_TIMESTAMP] = argv[CONTEXT_TIMESTAMP + 1]; - context[CONTEXT_RLIMIT] = argv[CONTEXT_RLIMIT + 1]; + context[CONTEXT_PID] = argv[1 + CONTEXT_PID]; + context[CONTEXT_UID] = argv[1 + CONTEXT_UID]; + context[CONTEXT_GID] = argv[1 + CONTEXT_GID]; + context[CONTEXT_SIGNAL] = argv[1 + CONTEXT_SIGNAL]; + context[CONTEXT_TIMESTAMP] = argv[1 + CONTEXT_TIMESTAMP]; + context[CONTEXT_RLIMIT] = argv[1 + CONTEXT_RLIMIT]; - r = gather_pid_metadata_and_process_special_crash(context, argv + CONTEXT_COMM + 1, NULL, iovec, &n_to_free); + r = gather_pid_metadata_and_process_special_crash(context, argv + 1 + CONTEXT_COMM, iovec, &n_to_free); if (r != 0) /* Error, or a a special crash, which has already been processed. */ goto finish; @@ -1255,12 +1262,16 @@ static int process_kernel(int argc, char* argv[]) { for (i = 0; i < n_to_free; i++) free(iovec[i].iov_base); + /* Those fields are allocated by gather_pid_metadata_and_process_special_crash */ + free(context[CONTEXT_COMM]); + free(context[CONTEXT_EXE]); + return r; } static int process_backtrace(int argc, char *argv[]) { - const char *context[_CONTEXT_MAX]; - _cleanup_free_ char *comm = NULL, *message = NULL; + char *context[_CONTEXT_MAX] = {}; + _cleanup_free_ char *message = NULL; _cleanup_free_ struct iovec *iovec = NULL; size_t n_iovec, n_allocated, n_to_free = 0, i; int r; @@ -1275,19 +1286,19 @@ static int process_backtrace(int argc, char *argv[]) { return -EINVAL; } - context[CONTEXT_PID] = argv[CONTEXT_PID + 2]; - context[CONTEXT_UID] = argv[CONTEXT_UID + 2]; - context[CONTEXT_GID] = argv[CONTEXT_GID + 2]; - context[CONTEXT_SIGNAL] = argv[CONTEXT_SIGNAL + 2]; - context[CONTEXT_TIMESTAMP] = argv[CONTEXT_TIMESTAMP + 2]; - context[CONTEXT_RLIMIT] = argv[CONTEXT_RLIMIT + 2]; + context[CONTEXT_PID] = argv[2 + CONTEXT_PID]; + context[CONTEXT_UID] = argv[2 + CONTEXT_UID]; + context[CONTEXT_GID] = argv[2 + CONTEXT_GID]; + context[CONTEXT_SIGNAL] = argv[2 + CONTEXT_SIGNAL]; + context[CONTEXT_TIMESTAMP] = argv[2 + CONTEXT_TIMESTAMP]; + context[CONTEXT_RLIMIT] = argv[2 + CONTEXT_RLIMIT]; n_allocated = 33; /* 25 metadata, 2 static, +unknown input, rounded up */ iovec = new(struct iovec, n_allocated); if (!iovec) return log_oom(); - r = gather_pid_metadata_and_process_special_crash(context, argv + CONTEXT_COMM + 2, &comm, iovec, &n_to_free); + r = gather_pid_metadata_and_process_special_crash(context, argv + 2 + CONTEXT_COMM, iovec, &n_to_free); if (r < 0) goto finish; if (r > 0) { @@ -1313,7 +1324,8 @@ static int process_backtrace(int argc, char *argv[]) { if (journal_importer_eof(&importer)) { log_warning("Did not receive a full journal entry on stdin, ignoring message sent by reporter"); - message = strjoin("MESSAGE=Process ", context[CONTEXT_PID], " (", comm, ")" + message = strjoin("MESSAGE=Process ", context[CONTEXT_PID], + " (", context[CONTEXT_COMM], ")" " of user ", context[CONTEXT_UID], " failed with ", context[CONTEXT_SIGNAL]); if (!message) { @@ -1340,6 +1352,10 @@ static int process_backtrace(int argc, char *argv[]) { for (i = 0; i < n_to_free; i++) free(iovec[i].iov_base); + /* Those fields are allocated by gather_pid_metadata_and_process_special_crash */ + free(context[CONTEXT_COMM]); + free(context[CONTEXT_EXE]); + return r; } -- cgit v1.2.3-54-g00ecf From 0cd4e913daa5c7914a1c24bd4bccc5d5344181f0 Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Sat, 25 Feb 2017 16:38:00 -0500 Subject: coredump: when storing an incomplete coredump, add COREDUMP_TRUNCATED=yes We logged about this, but did not attach information directly to the log entry. It *would* be nice to log the full untruncated size, but afaict, to do this, we would have to read the full data from the kernel. Doing this just to log that information seems a bit excessive, in particular when the limit could be set quite low. So for now let's just add a boolean field. --- src/coredump/coredump.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index e38cc59a36..855f0e9330 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -324,7 +324,8 @@ static int save_external_coredump( char **ret_filename, int *ret_node_fd, int *ret_data_fd, - uint64_t *ret_size) { + uint64_t *ret_size, + bool *truncated) { _cleanup_free_ char *fn = NULL, *tmp = NULL; _cleanup_close_ int fd = -1; @@ -372,7 +373,9 @@ static int save_external_coredump( if (r < 0) { log_error_errno(r, "Cannot store coredump of %s (%s): %m", context[CONTEXT_PID], context[CONTEXT_COMM]); goto fail; - } else if (r == 1) + } + *truncated = r == 1; + if (*truncated) log_struct(LOG_INFO, LOG_MESSAGE("Core file was truncated to %zu bytes.", max_size), "SIZE_LIMIT=%zu", max_size, @@ -691,6 +694,8 @@ static int change_uid_gid(const char *context[]) { return drop_privileges(uid, gid, 0); } +#define SUBMIT_COREDUMP_FIELDS 4 + static int submit_coredump( const char *context[_CONTEXT_MAX], struct iovec *iovec, @@ -701,18 +706,20 @@ static int submit_coredump( _cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1; _cleanup_free_ char *core_message = NULL, *filename = NULL, *coredump_data = NULL; uint64_t coredump_size = UINT64_MAX; + bool truncated = false; int r; assert(context); assert(iovec); - assert(n_iovec_allocated >= n_iovec + 3); + assert(n_iovec_allocated >= n_iovec + SUBMIT_COREDUMP_FIELDS); assert(input_fd >= 0); /* Vacuum before we write anything again */ (void) coredump_vacuum(-1, arg_keep_free, arg_max_use); /* Always stream the coredump to disk, if that's possible */ - r = save_external_coredump(context, input_fd, &filename, &coredump_node_fd, &coredump_fd, &coredump_size); + r = save_external_coredump(context, input_fd, + &filename, &coredump_node_fd, &coredump_fd, &coredump_size, &truncated); if (r < 0) /* Skip whole core dumping part */ goto log; @@ -770,6 +777,9 @@ log: if (core_message) IOVEC_SET_STRING(iovec[n_iovec++], core_message); + if (truncated) + IOVEC_SET_STRING(iovec[n_iovec++], "COREDUMP_TRUNCATED=yes"); + /* Optionally store the entire coredump in the journal */ if (arg_storage == COREDUMP_STORAGE_JOURNAL) { if (coredump_size <= arg_journal_size_max) { @@ -861,7 +871,7 @@ static int process_socket(int fd) { ssize_t n; ssize_t l; - if (!GREEDY_REALLOC(iovec, n_allocated, n_iovec + 3)) { + if (!GREEDY_REALLOC(iovec, n_allocated, n_iovec + SUBMIT_COREDUMP_FIELDS)) { r = log_oom(); goto finish; } @@ -925,7 +935,7 @@ static int process_socket(int fd) { n_iovec++; } - if (!GREEDY_REALLOC(iovec, n_allocated, n_iovec + 3)) { + if (!GREEDY_REALLOC(iovec, n_allocated, n_iovec + SUBMIT_COREDUMP_FIELDS)) { r = log_oom(); goto finish; } @@ -1027,6 +1037,7 @@ static int process_special_crash(const char *context[], int input_fd) { _cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1; _cleanup_free_ char *filename = NULL; uint64_t coredump_size; + bool truncated; int r; assert(context); @@ -1037,7 +1048,8 @@ static int process_special_crash(const char *context[], int input_fd) { if (arg_storage != COREDUMP_STORAGE_NONE) arg_storage = COREDUMP_STORAGE_EXTERNAL; - r = save_external_coredump(context, input_fd, &filename, &coredump_node_fd, &coredump_fd, &coredump_size); + r = save_external_coredump(context, input_fd, + &filename, &coredump_node_fd, &coredump_fd, &coredump_size, &truncated); if (r < 0) return r; -- cgit v1.2.3-54-g00ecf From 7bbf2d84234db8f9dd39604a367dc56a8f5455c7 Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Sat, 25 Feb 2017 17:14:00 -0500 Subject: coredumpctl: add debug information which services count towards the warning A few times I have seen the hint unexpectedly. Add this so debug info so it's easier to see what's happening. ... Unit systemd-coredump@0-3119-0.service is failed/failed, not counting it. Unit systemd-coredump@1-3854-0.service is activating/start-pre, counting it. ... -- Notice: 1 systemd-coredump@.service unit is running, output may be incomplete. --- src/coredump/coredumpctl.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c index 4ac98d8163..810d4f95a4 100644 --- a/src/coredump/coredumpctl.c +++ b/src/coredump/coredumpctl.c @@ -936,7 +936,7 @@ static int check_units_active(void) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; int c = 0, r; - const char *state; + const char *id, *state, *substate; r = sd_bus_default_system(&bus); if (r < 0) @@ -971,11 +971,12 @@ static int check_units_active(void) { while ((r = sd_bus_message_read( reply, "(ssssssouso)", - NULL, NULL, NULL, &state, NULL, - NULL, NULL, NULL, NULL, NULL)) > 0) - if (!STR_IN_SET(state, "dead", "failed")) - c++; - + &id, NULL, NULL, &state, &substate, + NULL, NULL, NULL, NULL, NULL)) > 0) { + bool found = !STR_IN_SET(state, "dead", "failed"); + log_debug("Unit %s is %s/%s, %scounting it.", id, state, substate, found ? "" : "not "); + c += found; + } if (r < 0) return bus_log_parse_error(r); -- cgit v1.2.3-54-g00ecf From cc4419ed929b5c7c99eaed020b015b1b2e8c7a66 Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Sat, 25 Feb 2017 17:29:14 -0500 Subject: coredumpctl,man: mark truncated messages as such in output Unit systemd-coredump@1-3854-0.service is failed/failed, not counting it. TIME PID UID GID SIG COREFILE EXE Fri 2017-02-24 11:11:00 EST 10002 1000 1000 6 none /home/zbyszek/src/systemd-work/.libs/lt-Sat 2017-02-25 00:49:32 EST 26921 0 0 11 error /usr/libexec/fprintd Sat 2017-02-25 11:56:30 EST 30703 1000 1000 - - /usr/bin/python3.5 Sat 2017-02-25 13:16:54 EST 3275 1000 1000 11 present /usr/bin/bash Sat 2017-02-25 17:25:40 EST 4049 1000 1000 11 truncated /usr/bin/bash For info and gdb output, the filename is marked in red and "(truncated)" is appended. (Red is necessary because the annotation is hard to see when running under a pager.) Fixed #3883. --- man/coredumpctl.xml | 55 ++++++++++++++++++++++++++++++++++++++++++++-- src/coredump/coredump.c | 6 ++--- src/coredump/coredumpctl.c | 34 +++++++++++++++++++++------- 3 files changed, 82 insertions(+), 13 deletions(-) diff --git a/man/coredumpctl.xml b/man/coredumpctl.xml index 5204db4073..2c657fed03 100644 --- a/man/coredumpctl.xml +++ b/man/coredumpctl.xml @@ -154,6 +154,57 @@ matching specified characteristics. If no command is specified, this is the implied default. + The output is designed to be human readable and contains list contains + a table with the following columns: + + + TIME + The timestamp of the crash, as reported by the kernel. + + + + + PID + The identifier of the process that crashed. + + + + + UID + GID + The user and group identifiers of the process that crashed. + + + + + SIGNAL + The signal that caused the process to crash, when applicable. + + + + + COREFILE + Information whether the coredump was stored, and whether + it is still accessible: none means the the core was + not stored, - means that it was not available (for + example because the process was not terminated by a signal), + present means that the core file is accessible by the + current user, journal means that the core was stored + in the journal, truncated is the + same as one of the previous two, but the core was too large and was not + stored in its entirety, error means that the core file + cannot be accessed, most likely because of insufficient permissions, and + missing means that the core was stored in a file, but + this file has since been removed. + + + + EXE + The full path to the executable. For backtraces of scripts + this is the name of the interpreter. + + + It's worth noting that different restrictions apply to data saved in the journal and core dump files saved in /var/lib/systemd/coredump, see overview in @@ -223,9 +274,9 @@ MATCH - General journalctl predicates (see + General journalctl predicate (see journalctl1). - Must contain an equal sign. + Must contain an equals sign (=). diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index 855f0e9330..9e7206cc6a 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -325,7 +325,7 @@ static int save_external_coredump( int *ret_node_fd, int *ret_data_fd, uint64_t *ret_size, - bool *truncated) { + bool *ret_truncated) { _cleanup_free_ char *fn = NULL, *tmp = NULL; _cleanup_close_ int fd = -1; @@ -374,8 +374,8 @@ static int save_external_coredump( log_error_errno(r, "Cannot store coredump of %s (%s): %m", context[CONTEXT_PID], context[CONTEXT_COMM]); goto fail; } - *truncated = r == 1; - if (*truncated) + *ret_truncated = r == 1; + if (*ret_truncated) log_struct(LOG_INFO, LOG_MESSAGE("Core file was truncated to %zu bytes.", max_size), "SIZE_LIMIT=%zu", max_size, diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c index 810d4f95a4..5237e2e069 100644 --- a/src/coredump/coredumpctl.c +++ b/src/coredump/coredumpctl.c @@ -343,7 +343,7 @@ static int print_list(FILE* file, sd_journal *j, int had_legend) { _cleanup_free_ char *mid = NULL, *pid = NULL, *uid = NULL, *gid = NULL, *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL, - *filename = NULL, *coredump = NULL; + *filename = NULL, *truncated = NULL, *coredump = NULL; const void *d; size_t l; usec_t t; @@ -365,6 +365,7 @@ static int print_list(FILE* file, sd_journal *j, int had_legend) { RETRIEVE(d, l, "COREDUMP_COMM", comm); RETRIEVE(d, l, "COREDUMP_CMDLINE", cmdline); RETRIEVE(d, l, "COREDUMP_FILENAME", filename); + RETRIEVE(d, l, "COREDUMP_TRUNCATED", truncated); RETRIEVE(d, l, "COREDUMP", coredump); } @@ -380,13 +381,13 @@ static int print_list(FILE* file, sd_journal *j, int had_legend) { format_timestamp(buf, sizeof(buf), t); if (!had_legend && !arg_no_legend) - fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n", + fprintf(file, "%-*s %*s %*s %*s %*s %-*s %s\n", FORMAT_TIMESTAMP_WIDTH, "TIME", 6, "PID", 5, "UID", 5, "GID", 3, "SIG", - 8, "COREFILE", + 9, "COREFILE", "EXE"); normal_coredump = streq_ptr(mid, SD_MESSAGE_COREDUMP_STR); @@ -405,13 +406,16 @@ static int print_list(FILE* file, sd_journal *j, int had_legend) { else present = "-"; + if (STR_IN_SET(present, "present", "journal") && streq_ptr(truncated, "yes")) + present = "truncated"; + fprintf(file, "%-*s %*s %*s %*s %*s %-*s %s\n", FORMAT_TIMESTAMP_WIDTH, buf, 6, strna(pid), 5, strna(uid), 5, strna(gid), 3, normal_coredump ? strna(sgnl) : "-", - 8, present, + 9, present, strna(exe ?: (comm ?: cmdline))); return 0; @@ -425,7 +429,7 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) { *boot_id = NULL, *machine_id = NULL, *hostname = NULL, *slice = NULL, *cgroup = NULL, *owner_uid = NULL, *message = NULL, *timestamp = NULL, *filename = NULL, - *coredump = NULL; + *truncated = NULL, *coredump = NULL; const void *d; size_t l; bool normal_coredump; @@ -451,6 +455,7 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) { RETRIEVE(d, l, "COREDUMP_CGROUP", cgroup); RETRIEVE(d, l, "COREDUMP_TIMESTAMP", timestamp); RETRIEVE(d, l, "COREDUMP_FILENAME", filename); + RETRIEVE(d, l, "COREDUMP_TRUNCATED", truncated); RETRIEVE(d, l, "COREDUMP", coredump); RETRIEVE(d, l, "_BOOT_ID", boot_id); RETRIEVE(d, l, "_MACHINE_ID", machine_id); @@ -569,9 +574,22 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) { if (hostname) fprintf(file, " Hostname: %s\n", hostname); - if (filename) - fprintf(file, " Storage: %s%s\n", filename, - access(filename, R_OK) < 0 ? " (inaccessible)" : ""); + if (filename) { + bool inacc = access(filename, R_OK) < 0; + bool trunc = streq_ptr(truncated, "yes"); + + if (inacc || trunc) + fprintf(file, " Storage: %s%s (%s%s%s)%s\n", + ansi_highlight_red(), + filename, + inacc ? "inaccessible" : "", + inacc && trunc ? ", " : "", + trunc ? "truncated" : "", + ansi_normal()); + else + fprintf(file, " Storage: %s\n", filename); + } + else if (coredump) fprintf(file, " Storage: journal\n"); else -- cgit v1.2.3-54-g00ecf From 92e92d71faea0f107312f296b7756cc04281ba99 Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Sun, 26 Feb 2017 16:46:23 -0500 Subject: coredump: process special crashes in an (almost) normal way We would only log a terse message when pid1 or systemd-journald crashed. It seems better to reuse the normal code paths as much as possible, with the following differences: - if pid1 crashes, we cannot launch the helper, so we don't analyze the coredump, just write it to file directly from the helper invoked by the kernel; - if journald crashes, we can produce the backtrace, but we don't log full structured messages. With comparison to previous code, advantages are: - we go through most of the steps, so for example vacuuming is performed, - we gather and log more data. In particular for journald and pid1 crashes we generate a backtrace, and for pid1 crashes we record the metadata (fdinfo, maps, etc.), - coredumpctl shows pid1 crashes. A disavantage (inefficiency) is that we gather metadata for journald crashes which is then ignored because _TRANSPORT=kernel does not support structued messages. Messages for the systemd-journald "crash" have _TRANSPORT=kernel, and _TRANSPORT=journal for the pid1 "crash". Feb 26 16:27:55 systemd[1]: systemd-journald.service: Main process exited, code=dumped, status=11/SEGV Feb 26 16:27:55 systemd[1]: systemd-journald.service: Unit entered failed state. Feb 26 16:37:54 systemd-coredump[18801]: Process 18729 (systemd-journal) of user 0 dumped core. Feb 26 16:37:54 systemd-coredump[18801]: Coredump diverted to /var/lib/systemd/coredump/core.systemd-journal.0.36c14bf3c6ce4c38914f441038990979.18729.1488145074000000.lz4 Feb 26 16:37:54 systemd-coredump[18801]: Stack trace of thread 18729: Feb 26 16:37:54 systemd-coredump[18801]: #0 0x00007f46d6a06b8d fsync (libpthread.so.0) Feb 26 16:37:54 systemd-coredump[18801]: #1 0x00007f46d71bfc47 journal_file_set_online (libsystemd-shared-233.so) Feb 26 16:37:54 systemd-coredump[18801]: #2 0x00007f46d71c1c31 journal_file_append_object (libsystemd-shared-233.so) Feb 26 16:37:54 systemd-coredump[18801]: #3 0x00007f46d71c3405 journal_file_append_data (libsystemd-shared-233.so) Feb 26 16:37:54 systemd-coredump[18801]: #4 0x00007f46d71c4b7c journal_file_append_entry (libsystemd-shared-233.so) Feb 26 16:37:54 systemd-coredump[18801]: #5 0x00005577688cf056 write_to_journal (systemd-journald) Feb 26 16:37:54 systemd-coredump[18801]: #6 0x00005577688d2e98 dispatch_message_real (systemd-journald) Feb 26 16:37:54 kernel: systemd-coredum: 9 output lines suppressed due to ratelimiting Feb 26 16:37:54 systemd-journald[18810]: Journal started Feb 26 16:50:59 systemd-coredump[19229]: Due to PID 1 having crashed coredump collection will now be turned off. Feb 26 16:51:00 systemd[1]: Caught , dumped core as pid 19228. Feb 26 16:51:00 systemd[1]: Freezing execution. Feb 26 16:51:00 systemd-coredump[19229]: Process 19228 (systemd) of user 0 dumped core. Stack trace of thread 19228: #0 0x00007fab82075c47 kill (libc.so.6) #1 0x000055fdf7c38b6b crash (systemd) #2 0x00007fab824175c0 __restore_rt (libpthread.so.0) #3 0x00007fab82148573 epoll_wait (libc.so.6) #4 0x00007fab8366f84a sd_event_wait (libsystemd-shared-233.so) #5 0x00007fab836701de sd_event_run (libsystemd-shared-233.so) #6 0x000055fdf7c4a380 manager_loop (systemd) #7 0x000055fdf7c402c2 main (systemd) #8 0x00007fab82060401 __libc_start_main (libc.so.6) #9 0x000055fdf7c3818a _start (systemd) Poor machine ;) --- src/coredump/coredump.c | 124 ++++++++++++++++++++++-------------------------- 1 file changed, 57 insertions(+), 67 deletions(-) diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index 9e7206cc6a..270af630cf 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -102,6 +102,7 @@ enum { CONTEXT_RLIMIT, CONTEXT_COMM, CONTEXT_EXE, + CONTEXT_UNIT, _CONTEXT_MAX }; @@ -706,7 +707,7 @@ static int submit_coredump( _cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1; _cleanup_free_ char *core_message = NULL, *filename = NULL, *coredump_data = NULL; uint64_t coredump_size = UINT64_MAX; - bool truncated = false; + bool truncated = false, journald_crash; int r; assert(context); @@ -714,6 +715,8 @@ static int submit_coredump( assert(n_iovec_allocated >= n_iovec + SUBMIT_COREDUMP_FIELDS); assert(input_fd >= 0); + journald_crash = streq_ptr(context[CONTEXT_UNIT], SPECIAL_JOURNALD_SERVICE); + /* Vacuum before we write anything again */ (void) coredump_vacuum(-1, arg_keep_free, arg_max_use); @@ -758,8 +761,10 @@ static int submit_coredump( if (r >= 0) core_message = strjoin("MESSAGE=Process ", context[CONTEXT_PID], " (", context[CONTEXT_COMM], ") of user ", - context[CONTEXT_UID], " dumped core.\n\n", - stacktrace); + context[CONTEXT_UID], " dumped core.", + journald_crash ? "\nCoredump diverted to " : "", + journald_crash ? filename : "", + "\n\n", stacktrace); else if (r == -EINVAL) log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno())); else @@ -771,9 +776,21 @@ static int submit_coredump( if (!core_message) #endif log: - core_message = strjoin("MESSAGE=Process ", context[CONTEXT_PID], " (", - context[CONTEXT_COMM], ") of user ", - context[CONTEXT_UID], " dumped core."); + core_message = strjoin("MESSAGE=Process ", context[CONTEXT_PID], + " (", context[CONTEXT_COMM], ") of user ", + context[CONTEXT_UID], " dumped core.", + journald_crash ? "\nCoredump diverted to " : NULL, + journald_crash ? filename : NULL); + if (!core_message) + return log_oom(); + + if (journald_crash) { + /* We cannot log to the journal, so just print the MESSAGE. + * The target was set previously to something safe. */ + log_struct(LOG_ERR, core_message, NULL); + return 0; + } + if (core_message) IOVEC_SET_STRING(iovec[n_iovec++], core_message); @@ -810,15 +827,15 @@ log: static void map_context_fields(const struct iovec *iovec, const char* context[]) { - static const char * const context_field_names[_CONTEXT_MAX] = { + static const char * const context_field_names[] = { [CONTEXT_PID] = "COREDUMP_PID=", [CONTEXT_UID] = "COREDUMP_UID=", [CONTEXT_GID] = "COREDUMP_GID=", [CONTEXT_SIGNAL] = "COREDUMP_SIGNAL=", [CONTEXT_TIMESTAMP] = "COREDUMP_TIMESTAMP=", + [CONTEXT_RLIMIT] = "COREDUMP_RLIMIT=", [CONTEXT_COMM] = "COREDUMP_COMM=", [CONTEXT_EXE] = "COREDUMP_EXE=", - [CONTEXT_RLIMIT] = "COREDUMP_RLIMIT=", }; unsigned i; @@ -826,9 +843,12 @@ static void map_context_fields(const struct iovec *iovec, const char* context[]) assert(iovec); assert(context); - for (i = 0; i < _CONTEXT_MAX; i++) { + for (i = 0; i < ELEMENTSOF(context_field_names); i++) { size_t l; + if (!context_field_names[i]) + continue; + l = strlen(context_field_names[i]); if (iovec->iov_len < l) continue; @@ -1033,35 +1053,6 @@ static int send_iovec(const struct iovec iovec[], size_t n_iovec, int input_fd) return 0; } -static int process_special_crash(const char *context[], int input_fd) { - _cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1; - _cleanup_free_ char *filename = NULL; - uint64_t coredump_size; - bool truncated; - int r; - - assert(context); - assert(input_fd >= 0); - - /* If we are pid1 or journald, we cut things short, don't write to the journal, but still create a coredump. */ - - if (arg_storage != COREDUMP_STORAGE_NONE) - arg_storage = COREDUMP_STORAGE_EXTERNAL; - - r = save_external_coredump(context, input_fd, - &filename, &coredump_node_fd, &coredump_fd, &coredump_size, &truncated); - if (r < 0) - return r; - - r = maybe_remove_external_coredump(filename, coredump_size); - if (r < 0) - return r; - - log_notice("Detected coredump of the journal daemon or PID 1, diverted to %s.", filename); - - return 0; -} - static char* set_iovec_field(struct iovec iovec[27], size_t *n_iovec, const char *field, const char *value) { char *x; @@ -1079,7 +1070,7 @@ static char* set_iovec_field_free(struct iovec iovec[27], size_t *n_iovec, const return x; } -static int gather_pid_metadata_and_process_special_crash( +static int gather_pid_metadata( char* context[_CONTEXT_MAX], char **comm_fallback, struct iovec *iovec, size_t *n_iovec) { @@ -1111,32 +1102,22 @@ static int gather_pid_metadata_and_process_special_crash( if (r < 0) log_warning_errno(r, "Failed to get EXE, ignoring: %m"); - if (cg_pid_get_unit(pid, &t) >= 0) { + if (cg_pid_get_unit(pid, &context[CONTEXT_UNIT]) >= 0) { + if (!streq(context[CONTEXT_UNIT], SPECIAL_JOURNALD_SERVICE)) { + /* OK, now we know it's not the journal, hence we can make use of it now. */ + log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); + log_open(); + } /* If this is PID 1 disable coredump collection, we'll unlikely be able to process it later on. */ - if (streq(t, SPECIAL_INIT_SCOPE)) { + if (streq(context[CONTEXT_UNIT], SPECIAL_INIT_SCOPE)) { log_notice("Due to PID 1 having crashed coredump collection will now be turned off."); (void) write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0); } - /* Let's avoid dead-locks when processing journald and init crashes, as socket activation and logging - * are unlikely to work then. */ - if (STR_IN_SET(t, SPECIAL_JOURNALD_SERVICE, SPECIAL_INIT_SCOPE)) { - free(t); - r = process_special_crash((const char**) context, STDIN_FILENO); - if (r < 0) - return r; - - return 1; /* > 0 means: we have already processed it, because it's a special crash */ - } - - set_iovec_field_free(iovec, n_iovec, "COREDUMP_UNIT=", t); + set_iovec_field(iovec, n_iovec, "COREDUMP_UNIT=", context[CONTEXT_UNIT]); } - /* OK, now we know it's not the journal, hence we can make use of it now. */ - log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); - log_open(); - if (cg_pid_get_user_unit(pid, &t) >= 0) set_iovec_field_free(iovec, n_iovec, "COREDUMP_USER_UNIT=", t); @@ -1230,13 +1211,13 @@ static int gather_pid_metadata_and_process_special_crash( if (safe_atoi(context[CONTEXT_SIGNAL], &signo) >= 0 && SIGNAL_VALID(signo)) set_iovec_field(iovec, n_iovec, "COREDUMP_SIGNAL_NAME=SIG", signal_to_string(signo)); - return 0; /* == 0 means: we successfully acquired all metadata */ + return 0; /* we successfully acquired all metadata */ } static int process_kernel(int argc, char* argv[]) { char* context[_CONTEXT_MAX] = {}; - struct iovec iovec[28]; + struct iovec iovec[28 + SUBMIT_COREDUMP_FIELDS]; size_t i, n_iovec, n_to_free = 0; int r; @@ -1254,9 +1235,8 @@ static int process_kernel(int argc, char* argv[]) { context[CONTEXT_TIMESTAMP] = argv[1 + CONTEXT_TIMESTAMP]; context[CONTEXT_RLIMIT] = argv[1 + CONTEXT_RLIMIT]; - r = gather_pid_metadata_and_process_special_crash(context, argv + 1 + CONTEXT_COMM, iovec, &n_to_free); - if (r != 0) - /* Error, or a a special crash, which has already been processed. */ + r = gather_pid_metadata(context, argv + 1 + CONTEXT_COMM, iovec, &n_to_free); + if (r < 0) goto finish; n_iovec = n_to_free; @@ -1268,15 +1248,23 @@ static int process_kernel(int argc, char* argv[]) { assert(n_iovec <= ELEMENTSOF(iovec)); - r = send_iovec(iovec, n_iovec, STDIN_FILENO); + if (STRPTR_IN_SET(context[CONTEXT_UNIT], + SPECIAL_JOURNALD_SERVICE, + SPECIAL_INIT_SCOPE)) + r = submit_coredump((const char**) context, + iovec, ELEMENTSOF(iovec), n_iovec, + STDIN_FILENO); + else + r = send_iovec(iovec, n_iovec, STDIN_FILENO); finish: for (i = 0; i < n_to_free; i++) free(iovec[i].iov_base); - /* Those fields are allocated by gather_pid_metadata_and_process_special_crash */ + /* Those fields are allocated by gather_pid_metadata */ free(context[CONTEXT_COMM]); free(context[CONTEXT_EXE]); + free(context[CONTEXT_UNIT]); return r; } @@ -1305,12 +1293,13 @@ static int process_backtrace(int argc, char *argv[]) { context[CONTEXT_TIMESTAMP] = argv[2 + CONTEXT_TIMESTAMP]; context[CONTEXT_RLIMIT] = argv[2 + CONTEXT_RLIMIT]; - n_allocated = 33; /* 25 metadata, 2 static, +unknown input, rounded up */ + n_allocated = 33 + COREDUMP_STORAGE_EXTERNAL; + /* 25 metadata, 2 static, +unknown input, 4 storage, rounded up */ iovec = new(struct iovec, n_allocated); if (!iovec) return log_oom(); - r = gather_pid_metadata_and_process_special_crash(context, argv + 2 + CONTEXT_COMM, iovec, &n_to_free); + r = gather_pid_metadata(context, argv + 2 + CONTEXT_COMM, iovec, &n_to_free); if (r < 0) goto finish; if (r > 0) { @@ -1364,9 +1353,10 @@ static int process_backtrace(int argc, char *argv[]) { for (i = 0; i < n_to_free; i++) free(iovec[i].iov_base); - /* Those fields are allocated by gather_pid_metadata_and_process_special_crash */ + /* Those fields are allocated by gather_pid_metadata */ free(context[CONTEXT_COMM]); free(context[CONTEXT_EXE]); + free(context[CONTEXT_UNIT]); return r; } -- cgit v1.2.3-54-g00ecf From 7d8e7c0e195ad014190b0eff2663b1e3a69ad328 Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Sun, 26 Feb 2017 17:00:01 -0500 Subject: coredumpctl: use a 3s timeout for checking units This is just a hint, so we shouldn't wait too long. A short timeout helps for the case where pid1 of dbus have crashed. --- src/coredump/coredumpctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c index 5237e2e069..942c970d8a 100644 --- a/src/coredump/coredumpctl.c +++ b/src/coredump/coredumpctl.c @@ -978,7 +978,7 @@ static int check_units_active(void) { if (r < 0) return bus_log_create_error(r); - r = sd_bus_call(bus, m, 0, &error, &reply); + r = sd_bus_call(bus, m, 3 * USEC_PER_SEC, &error, &reply); if (r < 0) return log_error_errno(r, "Failed to check if any systemd-coredump@.service units are running: %s", bus_error_message(&error, r)); -- cgit v1.2.3-54-g00ecf From 4f37cbd91164f8cd7dbc90e3b3594697fec50f38 Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Sun, 26 Feb 2017 18:00:39 -0500 Subject: journalctl: move access_check() to shared/ The only functional change is that log_notice("No journal files were found.") is not printed any more with --quiet. log_error("No journal files were opened due to insufficient permissions.") is still printed. I wasn't quite sure where to put this function, but shared/ seems to be the right place and none of the existing files seem to fit too well. v2: rename journal_access_check to journal_access_check_and_warn. --- Makefile.am | 2 + src/journal/journalctl.c | 128 +-------------------------------------- src/shared/journal-util.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++ src/shared/journal-util.h | 25 ++++++++ 4 files changed, 180 insertions(+), 126 deletions(-) create mode 100644 src/shared/journal-util.c create mode 100644 src/shared/journal-util.h diff --git a/Makefile.am b/Makefile.am index 292f63c408..2a5610740e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1037,6 +1037,8 @@ libshared_la_SOURCES = \ src/shared/apparmor-util.h \ src/shared/ima-util.c \ src/shared/ima-util.h \ + src/shared/journal-util.c \ + src/shared/journal-util.h \ src/shared/ptyfwd.c \ src/shared/ptyfwd.h \ src/shared/base-filesystem.c \ diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index d785b32f1c..ad11fb314d 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -52,6 +52,7 @@ #include "journal-def.h" #include "journal-internal.h" #include "journal-qrcode.h" +#include "journal-util.h" #include "journal-vacuum.h" #include "journal-verify.h" #include "locale-util.h" @@ -1805,131 +1806,6 @@ static int verify(sd_journal *j) { return r; } -static int access_check_var_log_journal(sd_journal *j) { -#ifdef HAVE_ACL - _cleanup_strv_free_ char **g = NULL; - const char* dir; -#endif - int r; - - assert(j); - - if (arg_quiet) - return 0; - - /* If we are root, we should have access, don't warn. */ - if (getuid() == 0) - return 0; - - /* If we are in the 'systemd-journal' group, we should have - * access too. */ - r = in_group("systemd-journal"); - if (r < 0) - return log_error_errno(r, "Failed to check if we are in the 'systemd-journal' group: %m"); - if (r > 0) - return 0; - -#ifdef HAVE_ACL - if (laccess("/run/log/journal", F_OK) >= 0) - dir = "/run/log/journal"; - else - dir = "/var/log/journal"; - - /* If we are in any of the groups listed in the journal ACLs, - * then all is good, too. Let's enumerate all groups from the - * default ACL of the directory, which generally should allow - * access to most journal files too. */ - r = acl_search_groups(dir, &g); - if (r < 0) - return log_error_errno(r, "Failed to search journal ACL: %m"); - if (r > 0) - return 0; - - /* Print a pretty list, if there were ACLs set. */ - if (!strv_isempty(g)) { - _cleanup_free_ char *s = NULL; - - /* Thre are groups in the ACL, let's list them */ - r = strv_extend(&g, "systemd-journal"); - if (r < 0) - return log_oom(); - - strv_sort(g); - strv_uniq(g); - - s = strv_join(g, "', '"); - if (!s) - return log_oom(); - - log_notice("Hint: You are currently not seeing messages from other users and the system.\n" - " Users in groups '%s' can see all messages.\n" - " Pass -q to turn off this notice.", s); - return 1; - } -#endif - - /* If no ACLs were found, print a short version of the message. */ - log_notice("Hint: You are currently not seeing messages from other users and the system.\n" - " Users in the 'systemd-journal' group can see all messages. Pass -q to\n" - " turn off this notice."); - - return 1; -} - -static int access_check(sd_journal *j) { - Iterator it; - void *code; - char *path; - int r = 0; - - assert(j); - - if (hashmap_isempty(j->errors)) { - if (ordered_hashmap_isempty(j->files)) - log_notice("No journal files were found."); - - return 0; - } - - if (hashmap_contains(j->errors, INT_TO_PTR(-EACCES))) { - (void) access_check_var_log_journal(j); - - if (ordered_hashmap_isempty(j->files)) - r = log_error_errno(EACCES, "No journal files were opened due to insufficient permissions."); - } - - HASHMAP_FOREACH_KEY(path, code, j->errors, it) { - int err; - - err = abs(PTR_TO_INT(code)); - - switch (err) { - case EACCES: - continue; - - case ENODATA: - log_warning_errno(err, "Journal file %s is truncated, ignoring file.", path); - break; - - case EPROTONOSUPPORT: - log_warning_errno(err, "Journal file %1$s uses an unsupported feature, ignoring file.\n" - "Use SYSTEMD_LOG_LEVEL=debug journalctl --file=%1$s to see the details.", - path); - break; - - case EBADMSG: - log_warning_errno(err, "Journal file %s corrupted, ignoring file.", path); - break; - - default: - log_warning_errno(err, "An error was encountered while opening journal file or directory %s, ignoring file: %m", path); - break; - } - } - - return r; -} - static int flush_to_var(void) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -2241,7 +2117,7 @@ int main(int argc, char *argv[]) { goto finish; } - r = access_check(j); + r = journal_access_check_and_warn(j, arg_quiet); if (r < 0) goto finish; diff --git a/src/shared/journal-util.c b/src/shared/journal-util.c new file mode 100644 index 0000000000..8479221a44 --- /dev/null +++ b/src/shared/journal-util.c @@ -0,0 +1,151 @@ +/*** + This file is part of systemd. + + Copyright 2013 Zbigniew Jędrzejewski-Szmek + Copyright 2015 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "acl-util.h" +#include "fs-util.h" +#include "hashmap.h" +#include "journal-internal.h" +#include "journal-util.h" +#include "log.h" +#include "strv.h" +#include "user-util.h" + +static int access_check_var_log_journal(sd_journal *j) { +#ifdef HAVE_ACL + _cleanup_strv_free_ char **g = NULL; + const char* dir; +#endif + int r; + + assert(j); + + /* If we are root, we should have access, don't warn. */ + if (getuid() == 0) + return 0; + + /* If we are in the 'systemd-journal' group, we should have + * access too. */ + r = in_group("systemd-journal"); + if (r < 0) + return log_error_errno(r, "Failed to check if we are in the 'systemd-journal' group: %m"); + if (r > 0) + return 0; + +#ifdef HAVE_ACL + if (laccess("/run/log/journal", F_OK) >= 0) + dir = "/run/log/journal"; + else + dir = "/var/log/journal"; + + /* If we are in any of the groups listed in the journal ACLs, + * then all is good, too. Let's enumerate all groups from the + * default ACL of the directory, which generally should allow + * access to most journal files too. */ + r = acl_search_groups(dir, &g); + if (r < 0) + return log_error_errno(r, "Failed to search journal ACL: %m"); + if (r > 0) + return 0; + + /* Print a pretty list, if there were ACLs set. */ + if (!strv_isempty(g)) { + _cleanup_free_ char *s = NULL; + + /* Thre are groups in the ACL, let's list them */ + r = strv_extend(&g, "systemd-journal"); + if (r < 0) + return log_oom(); + + strv_sort(g); + strv_uniq(g); + + s = strv_join(g, "', '"); + if (!s) + return log_oom(); + + log_notice("Hint: You are currently not seeing messages from other users and the system.\n" + " Users in groups '%s' can see all messages.\n" + " Pass -q to turn off this notice.", s); + return 1; + } +#endif + + /* If no ACLs were found, print a short version of the message. */ + log_notice("Hint: You are currently not seeing messages from other users and the system.\n" + " Users in the 'systemd-journal' group can see all messages. Pass -q to\n" + " turn off this notice."); + + return 1; +} + +int journal_access_check_and_warn(sd_journal *j, bool quiet) { + Iterator it; + void *code; + char *path; + int r = 0; + + assert(j); + + if (hashmap_isempty(j->errors)) { + if (ordered_hashmap_isempty(j->files) && !quiet) + log_notice("No journal files were found."); + + return 0; + } + + if (hashmap_contains(j->errors, INT_TO_PTR(-EACCES))) { + if (!quiet) + (void) access_check_var_log_journal(j); + + if (ordered_hashmap_isempty(j->files)) + r = log_error_errno(EACCES, "No journal files were opened due to insufficient permissions."); + } + + HASHMAP_FOREACH_KEY(path, code, j->errors, it) { + int err; + + err = abs(PTR_TO_INT(code)); + + switch (err) { + case EACCES: + continue; + + case ENODATA: + log_warning_errno(err, "Journal file %s is truncated, ignoring file.", path); + break; + + case EPROTONOSUPPORT: + log_warning_errno(err, "Journal file %1$s uses an unsupported feature, ignoring file.\n" + "Use SYSTEMD_LOG_LEVEL=debug journalctl --file=%1$s to see the details.", + path); + break; + + case EBADMSG: + log_warning_errno(err, "Journal file %s corrupted, ignoring file.", path); + break; + + default: + log_warning_errno(err, "An error was encountered while opening journal file or directory %s, ignoring file: %m", path); + break; + } + } + + return r; +} diff --git a/src/shared/journal-util.h b/src/shared/journal-util.h new file mode 100644 index 0000000000..499e6c62ec --- /dev/null +++ b/src/shared/journal-util.h @@ -0,0 +1,25 @@ +/*** + This file is part of systemd. + + Copyright 2013 Zbigniew Jędrzejewski-Szmek + Copyright 2015 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "sd-journal.h" + +int journal_access_check_and_warn(sd_journal *j, bool quiet); -- cgit v1.2.3-54-g00ecf From b9aaa7f4801def92bb4753a780fe1c5302c4de16 Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Sun, 26 Feb 2017 18:07:39 -0500 Subject: coredumpctl: print a hint when no journal files are found [guest@fedora ~]$ coredumpctl No coredumps found. [guest@fedora ~]$ ./coredumpctl Hint: You are currently not seeing messages from other users and the system. Users in groups 'adm', 'systemd-journal', 'wheel' can see all messages. Pass -q to turn off this notice. No coredumps found. Fixes #1733. --- man/coredumpctl.xml | 8 ++++++++ src/coredump/coredumpctl.c | 24 ++++++++++++++++++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/man/coredumpctl.xml b/man/coredumpctl.xml index 2c657fed03..ca8156f77c 100644 --- a/man/coredumpctl.xml +++ b/man/coredumpctl.xml @@ -138,6 +138,14 @@ + + + + + Suppresses info messages about lack + of access to journal files and possible in-flight coredumps. + + diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c index 942c970d8a..93d5e1c9d1 100644 --- a/src/coredump/coredumpctl.c +++ b/src/coredump/coredumpctl.c @@ -36,6 +36,7 @@ #include "fileio.h" #include "fs-util.h" #include "journal-internal.h" +#include "journal-util.h" #include "log.h" #include "macro.h" #include "pager.h" @@ -67,6 +68,7 @@ static int arg_one = false; static FILE* arg_output = NULL; static bool arg_reverse = false; static char** arg_matches = NULL; +static bool arg_quiet = false; static int add_match(sd_journal *j, const char *match) { _cleanup_free_ char *p = NULL; @@ -136,6 +138,7 @@ static void help(void) { " -F --field=FIELD List all values a certain field takes\n" " -o --output=FILE Write output to FILE\n" " -D --directory=DIR Use journal files from directory\n\n" + " -q --quiet Do not show info messages and privilege warning\n" "Commands:\n" " list [MATCHES...] List available coredumps (default)\n" " info [MATCHES...] Show detailed information about one or more coredumps\n" @@ -164,13 +167,14 @@ static int parse_argv(int argc, char *argv[]) { { "reverse", no_argument, NULL, 'r' }, { "since", required_argument, NULL, 'S' }, { "until", required_argument, NULL, 'U' }, + { "quiet", no_argument, NULL, 'q' }, {} }; assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "ho:F:1D:S:U:r", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "ho:F:1D:rS:U:q", options, NULL)) >= 0) switch(c) { case 'h': arg_action = ACTION_NONE; @@ -233,6 +237,10 @@ static int parse_argv(int argc, char *argv[]) { arg_reverse = true; break; + case 'q': + arg_quiet = true; + break; + case '?': return -EINVAL; @@ -699,7 +707,8 @@ static int dump_list(sd_journal *j) { } if (!arg_field && n_found <= 0) { - log_notice("No coredumps found."); + if (!arg_quiet) + log_notice("No coredumps found."); return -ESRCH; } } @@ -861,8 +870,8 @@ static int dump_core(sd_journal* j) { return r; r = sd_journal_previous(j); - if (r > 0) - log_warning("More than one entry matches, ignoring rest."); + if (r > 0 && !arg_quiet) + log_notice("More than one entry matches, ignoring rest."); return 0; } @@ -956,6 +965,9 @@ static int check_units_active(void) { int c = 0, r; const char *id, *state, *substate; + if (arg_quiet) + return false; + r = sd_bus_default_system(&bus); if (r < 0) return log_error_errno(r, "Failed to acquire bus: %m"); @@ -1036,6 +1048,10 @@ int main(int argc, char *argv[]) { } } + r = journal_access_check_and_warn(j, arg_quiet); + if (r < 0) + goto end; + r = add_matches(j); if (r < 0) goto end; -- cgit v1.2.3-54-g00ecf From eb5877a024a6da2177bf309a97ebd3e0641d7f2c Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Tue, 28 Feb 2017 21:47:45 -0500 Subject: coredumpctl: avoid spurious warning about systemd-coredump@0.service Fixes #5477. --- src/coredump/coredumpctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c index 93d5e1c9d1..3e9a00bbcf 100644 --- a/src/coredump/coredumpctl.c +++ b/src/coredump/coredumpctl.c @@ -1003,7 +1003,7 @@ static int check_units_active(void) { reply, "(ssssssouso)", &id, NULL, NULL, &state, &substate, NULL, NULL, NULL, NULL, NULL)) > 0) { - bool found = !STR_IN_SET(state, "dead", "failed"); + bool found = !STR_IN_SET(state, "inactive", "dead", "failed"); log_debug("Unit %s is %s/%s, %scounting it.", id, state, substate, found ? "" : "not "); c += found; } -- cgit v1.2.3-54-g00ecf