diff options
author | Lennart Poettering <lennart@poettering.net> | 2017-03-01 14:37:15 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-03-01 14:37:15 +0100 |
commit | ecadd9b3fd394fd5fdef87cf9f1fb764b0bb83f0 (patch) | |
tree | 677e04403d44556906c68ae8ceb154dcffff37b7 /src | |
parent | 92d6f2f34895a5f126493a2fd7ff8fb660a84a22 (diff) | |
parent | eb5877a024a6da2177bf309a97ebd3e0641d7f2c (diff) |
Merge pull request #5458 from keszybz/coredump
Fix for coredump crash
Diffstat (limited to 'src')
-rw-r--r-- | src/coredump/coredump.c | 224 | ||||
-rw-r--r-- | src/coredump/coredumpctl.c | 73 | ||||
-rw-r--r-- | src/journal/journalctl.c | 128 | ||||
-rw-r--r-- | src/shared/journal-util.c | 151 | ||||
-rw-r--r-- | src/shared/journal-util.h | 25 |
5 files changed, 351 insertions, 250 deletions
diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index 0fa830c33e..270af630cf 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, @@ -88,6 +102,7 @@ enum { CONTEXT_RLIMIT, CONTEXT_COMM, CONTEXT_EXE, + CONTEXT_UNIT, _CONTEXT_MAX }; @@ -310,7 +325,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 *ret_truncated) { _cleanup_free_ char *fn = NULL, *tmp = NULL; _cleanup_close_ int fd = -1; @@ -358,7 +374,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) + } + *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, @@ -677,6 +695,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, @@ -687,18 +707,22 @@ 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, journald_crash; 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); + 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); /* 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; @@ -737,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 @@ -750,12 +776,27 @@ 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); + 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) { @@ -784,17 +825,17 @@ 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] = { + 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; @@ -802,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; @@ -847,7 +891,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; } @@ -911,7 +955,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; } @@ -1009,33 +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; - 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); - 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; @@ -1053,10 +1070,9 @@ 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( - const char *context[_CONTEXT_MAX], +static int gather_pid_metadata( + 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 +1080,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,44 +1090,34 @@ 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"); - 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(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); @@ -1132,11 +1137,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,18 +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)); - if (comm_ret) { - *comm_ret = comm; - comm = NULL; - } - - return 0; /* == 0 means: we successfully acquired all metadata */ + return 0; /* we successfully acquired all metadata */ } static int process_kernel(int argc, char* argv[]) { - const char *context[_CONTEXT_MAX]; - struct iovec iovec[28]; + char* context[_CONTEXT_MAX] = {}; + struct iovec iovec[28 + SUBMIT_COREDUMP_FIELDS]; size_t i, n_iovec, n_to_free = 0; int r; @@ -1228,21 +1228,16 @@ 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(context, argv + 1 + CONTEXT_COMM, iovec, &n_to_free); if (r < 0) goto finish; - if (r > 0) { - /* This was a special crash, and has already been processed. */ - r = 0; - goto finish; - } n_iovec = n_to_free; @@ -1253,18 +1248,30 @@ 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 */ + free(context[CONTEXT_COMM]); + free(context[CONTEXT_EXE]); + free(context[CONTEXT_UNIT]); + 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; @@ -1279,19 +1286,20 @@ 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 */ + 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 + CONTEXT_COMM + 2, &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) { @@ -1317,7 +1325,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) { @@ -1344,6 +1353,11 @@ 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 */ + free(context[CONTEXT_COMM]); + free(context[CONTEXT_EXE]); + free(context[CONTEXT_UNIT]); + return r; } diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c index 4ac98d8163..3e9a00bbcf 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; @@ -343,7 +351,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 +373,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 +389,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 +414,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 +437,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 +463,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 +582,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 @@ -681,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; } } @@ -843,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; } @@ -936,7 +963,10 @@ 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; + + if (arg_quiet) + return false; r = sd_bus_default_system(&bus); if (r < 0) @@ -960,7 +990,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)); @@ -971,11 +1001,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, "inactive", "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); @@ -1017,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; 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 <http://www.gnu.org/licenses/>. +***/ + +#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 <http://www.gnu.org/licenses/>. +***/ + +#include <stdbool.h> + +#include "sd-journal.h" + +int journal_access_check_and_warn(sd_journal *j, bool quiet); |