X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fjournal%2Fjournalctl.c;h=1a441dd0d68c17d934f49634195703eadb756d8b;hb=8d98da3f1107529d5ba49aea1fa285f7264b7cba;hp=8543adfb8a1d2d52ac65e72545fe505d552fce4d;hpb=478c82693c386e7a6e8e4b37cc99fb19b12e7186;p=elogind.git diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 8543adfb8..1a441dd0d 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -72,8 +71,10 @@ static bool arg_no_tail = false; static bool arg_quiet = false; static bool arg_merge = false; static bool arg_this_boot = false; +static bool arg_dmesg = false; static const char *arg_cursor = NULL; static const char *arg_directory = NULL; +static char **arg_file = NULL; static int arg_priorities = 0xFF; static const char *arg_verify_key = NULL; #ifdef HAVE_GCRYPT @@ -81,11 +82,13 @@ static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC; #endif static usec_t arg_since, arg_until; static bool arg_since_set = false, arg_until_set = false; -static const char *arg_unit = NULL; -static bool arg_unit_system; +static char **arg_system_units = NULL; +static char **arg_user_units = NULL; static const char *arg_field = NULL; static bool arg_catalog = false; static bool arg_reverse = false; +static int arg_journal_type = 0; +static const char *arg_root = NULL; static enum { ACTION_SHOW, @@ -104,10 +107,13 @@ static int help(void) { printf("%s [OPTIONS...] [MATCHES...]\n\n" "Query the journal.\n\n" "Flags:\n" + " --system Show only the system journal\n" + " --user Show only the user journal for current user\n" " --since=DATE Start showing entries newer or of the specified date\n" " --until=DATE Stop showing entries older or of the specified date\n" " -c --cursor=CURSOR Start showing entries from specified cursor\n" " -b --this-boot Show data only from current boot\n" + " -k --dmesg Show kmsg log from current boot\n" " -u --unit=UNIT Show data only from the specified unit\n" " --user-unit=UNIT Show data only from the specified user session unit\n" " -p --priority=RANGE Show only messages within the specified priority range\n" @@ -125,6 +131,8 @@ static int help(void) { " --no-pager Do not pipe output into a pager\n" " -m --merge Show entries from all available journals\n" " -D --directory=PATH Show journal files from directory\n" + " --file=PATH Show journal file\n" + " --root=ROOT Operate on catalog files underneath the root ROOT\n" #ifdef HAVE_GCRYPT " --interval=TIME Time interval for changing the FSS sealing key\n" " --verify-key=KEY Specify FSS verification key\n" @@ -155,9 +163,13 @@ static int parse_argv(int argc, char *argv[]) { ARG_NO_PAGER, ARG_NO_TAIL, ARG_NEW_ID128, + ARG_USER, + ARG_SYSTEM, + ARG_ROOT, ARG_HEADER, ARG_FULL, ARG_SETUP_KEYS, + ARG_FILE, ARG_INTERVAL, ARG_VERIFY, ARG_VERIFY_KEY, @@ -167,7 +179,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_USER_UNIT, ARG_LIST_CATALOG, ARG_DUMP_CATALOG, - ARG_UPDATE_CATALOG + ARG_UPDATE_CATALOG, }; static const struct option options[] = { @@ -185,7 +197,12 @@ static int parse_argv(int argc, char *argv[]) { { "quiet", no_argument, NULL, 'q' }, { "merge", no_argument, NULL, 'm' }, { "this-boot", no_argument, NULL, 'b' }, + { "dmesg", no_argument, NULL, 'k' }, + { "system", no_argument, NULL, ARG_SYSTEM }, + { "user", no_argument, NULL, ARG_USER }, { "directory", required_argument, NULL, 'D' }, + { "file", required_argument, NULL, ARG_FILE }, + { "root", required_argument, NULL, ARG_ROOT }, { "header", no_argument, NULL, ARG_HEADER }, { "priority", required_argument, NULL, 'p' }, { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS }, @@ -212,7 +229,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "hefo:an::qmbD:p:c:u:F:xr", options, NULL)) >= 0) { + while ((c = getopt_long(argc, argv, "hefo:an::qmbkD:p:c:u:F:xr", options, NULL)) >= 0) { switch (c) { @@ -314,10 +331,34 @@ static int parse_argv(int argc, char *argv[]) { arg_this_boot = true; break; + case 'k': + arg_this_boot = arg_dmesg = true; + break; + + case ARG_SYSTEM: + arg_journal_type |= SD_JOURNAL_SYSTEM; + break; + + case ARG_USER: + arg_journal_type |= SD_JOURNAL_CURRENT_USER; + break; + case 'D': arg_directory = optarg; break; + case ARG_FILE: + r = glob_extend(&arg_file, optarg); + if (r < 0) { + log_error("Failed to add paths: %s", strerror(-r)); + return r; + }; + break; + + case ARG_ROOT: + arg_root = optarg; + break; + case 'c': arg_cursor = optarg; break; @@ -347,7 +388,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_INTERVAL: - r = parse_usec(optarg, &arg_interval); + r = parse_sec(optarg, &arg_interval); if (r < 0 || arg_interval <= 0) { log_error("Failed to parse sealing key change interval: %s", optarg); return -EINVAL; @@ -430,13 +471,15 @@ static int parse_argv(int argc, char *argv[]) { break; case 'u': - arg_unit = optarg; - arg_unit_system = true; + r = strv_extend(&arg_system_units, optarg); + if (r < 0) + return log_oom(); break; case ARG_USER_UNIT: - arg_unit = optarg; - arg_unit_system = false; + r = strv_extend(&arg_user_units, optarg); + if (r < 0) + return log_oom(); break; case '?': @@ -475,6 +518,11 @@ static int parse_argv(int argc, char *argv[]) { if (arg_follow && !arg_no_tail && arg_lines < 0) arg_lines = 10; + if (arg_directory && arg_file) { + log_error("Please specify either -D/--directory= or --file=, not both."); + return -EINVAL; + } + if (arg_since_set && arg_until_set && arg_since > arg_until) { log_error("--since= must be before --until=."); return -EINVAL; @@ -535,7 +583,7 @@ static int add_matches(sd_journal *j, char **args) { if (streq(*i, "+")) r = sd_journal_add_disjunction(j); else if (path_is_absolute(*i)) { - char _cleanup_free_ *p, *t = NULL; + _cleanup_free_ char *p, *t = NULL; const char *path; struct stat st; @@ -554,7 +602,7 @@ static int add_matches(sd_journal *j, char **args) { else if (S_ISBLK(st.st_mode)) asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev)); else { - log_error("File is not a device node, regular file or is not executable: %s", *i); + log_error("File is neither a device node, nor regular file, nor executable: %s", *i); return -EINVAL; } @@ -575,48 +623,67 @@ static int add_matches(sd_journal *j, char **args) { } static int add_this_boot(sd_journal *j) { - char match[9+32+1] = "_BOOT_ID="; - sd_id128_t boot_id; - int r; + if (!arg_this_boot) + return 0; + return add_match_this_boot(j); +} + +static int add_dmesg(sd_journal *j) { + int r; assert(j); - if (!arg_this_boot) + if (!arg_dmesg) return 0; - r = sd_id128_get_boot(&boot_id); + r = sd_journal_add_match(j, "_TRANSPORT=kernel", strlen("_TRANSPORT=kernel")); if (r < 0) { - log_error("Failed to get boot id: %s", strerror(-r)); + log_error("Failed to add match: %s", strerror(-r)); return r; } - sd_id128_to_string(boot_id, match + 9); - r = sd_journal_add_match(j, match, strlen(match)); - if (r < 0) { - log_error("Failed to add match: %s", strerror(-r)); + r = sd_journal_add_conjunction(j); + if (r < 0) return r; - } return 0; } -static int add_unit(sd_journal *j) { +static int add_units(sd_journal *j) { _cleanup_free_ char *u = NULL; int r; + char **i; assert(j); - if (isempty(arg_unit)) - return 0; + STRV_FOREACH(i, arg_system_units) { + u = unit_name_mangle(*i); + if (!u) + return log_oom(); + r = add_matches_for_unit(j, u); + if (r < 0) + return r; + r = sd_journal_add_disjunction(j); + if (r < 0) + return r; + } - u = unit_name_mangle(arg_unit); - if (!u) - return log_oom(); + STRV_FOREACH(i, arg_user_units) { + u = unit_name_mangle(*i); + if (!u) + return log_oom(); - if (arg_unit_system) - r = add_matches_for_unit(j, u); - else r = add_matches_for_user_unit(j, u, getuid()); + if (r < 0) + return r; + + r = sd_journal_add_disjunction(j); + if (r < 0) + return r; + + } + + r = sd_journal_add_conjunction(j); if (r < 0) return r; @@ -626,7 +693,6 @@ static int add_unit(sd_journal *j) { static int add_priorities(sd_journal *j) { char match[] = "PRIORITY=0"; int i, r; - assert(j); if (arg_priorities == 0xFF) @@ -643,6 +709,10 @@ static int add_priorities(sd_journal *j) { } } + r = sd_journal_add_conjunction(j); + if (r < 0) + return r; + return 0; } @@ -796,12 +866,12 @@ static int setup_keys(void) { fprintf(stderr, ANSI_HIGHLIGHT_OFF "\n" "The sealing key is automatically changed every %s.\n", - format_timespan(tsb, sizeof(tsb), arg_interval)); + format_timespan(tsb, sizeof(tsb), arg_interval, 0)); hn = gethostname_malloc(); if (hn) { - hostname_cleanup(hn); + hostname_cleanup(hn, false); fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine)); } else fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine)); @@ -870,10 +940,10 @@ static int verify(sd_journal *j) { log_info("=> Validated from %s to %s, final %s entries not sealed.", format_timestamp(a, sizeof(a), first), format_timestamp(b, sizeof(b), validated), - format_timespan(c, sizeof(c), last > validated ? last - validated : 0)); + format_timespan(c, sizeof(c), last > validated ? last - validated : 0, 0)); } else if (last > 0) log_info("=> No sealing yet, %s of entries not sealed.", - format_timespan(c, sizeof(c), last - first)); + format_timespan(c, sizeof(c), last - first, 0)); else log_info("=> No sealing yet, no entries in file."); } @@ -883,68 +953,118 @@ static int verify(sd_journal *j) { return r; } -static int access_check(void) { - #ifdef HAVE_ACL - /* If /var/log/journal doesn't even exist, unprivileged users have no access at all */ - if (access("/var/log/journal", F_OK) < 0 && geteuid() != 0 && in_group("systemd-journal") <= 0) { - log_error("Unprivileged users can't see messages unless persistent log storage is enabled. Users in the group 'systemd-journal' can always see messages."); - return -EACCES; +static int access_check_var_log_journal(sd_journal *j) { + _cleanup_strv_free_ char **g = NULL; + bool have_access; + int r; + + assert(j); + + have_access = in_group("systemd-journal") > 0; + + if (!have_access) { + /* Let's enumerate all groups from the default ACL of + * the directory, which generally should allow access + * to most journal files too */ + r = search_acl_groups(&g, "/var/log/journal/", &have_access); + if (r < 0) + return r; } - /* If /var/log/journal exists, try to pring a nice notice if the user lacks access to it */ - if (!arg_quiet && geteuid() != 0) { - _cleanup_strv_free_ char **g = NULL; - bool have_access; - int r; + if (!have_access) { - have_access = in_group("systemd-journal") > 0; + if (strv_isempty(g)) + 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."); + else { + _cleanup_free_ char *s = NULL; - if (!have_access) { - /* Let's enumerate all groups from the default - * ACL of the directory, which generally - * should allow access to most journal - * files too */ - r = search_acl_groups(&g, "/var/log/journal/", &have_access); + r = strv_extend(&g, "systemd-journal"); if (r < 0) - return r; + 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 the groups '%s' can see all messages.\n" + " Pass -q to turn off this notice.", s); } + } - if (!have_access) { + return 0; +} +#endif - if (strv_isempty(g)) - log_notice("Hint: You are currently not seeing messages from other users and the system. Users in the group 'systemd-journal' can see all messages. Pass -q to turn this notice off."); - else { - _cleanup_free_ char *s = NULL; +static int access_check(sd_journal *j) { + Iterator it; + void *code; + int r = 0; - r = strv_extend(&g, "systemd-journal"); - if (r < 0) - return log_oom(); + assert(j); - strv_sort(g); - strv_uniq(g); + if (set_isempty(j->errors)) { + if (hashmap_isempty(j->files)) + log_notice("No journal files were found."); + return 0; + } - s = strv_join(g, "', '"); - if (!s) - return log_oom(); + if (set_contains(j->errors, INT_TO_PTR(-EACCES))) { +#ifdef HAVE_ACL + /* If /var/log/journal doesn't even exist, + * unprivileged users have no access at all */ + if (access("/var/log/journal", F_OK) < 0 && + geteuid() != 0 && + in_group("systemd-journal") <= 0) { + log_error("Unprivileged users cannot access messages, unless persistent log storage is\n" + "enabled. Users in the 'systemd-journal' group may always access messages."); + return -EACCES; + } - log_notice("Hint: You are currently not seeing messages from other users and the system. Users in the groups '%s' can see all messages. Pass -q to turn this notice off.", s); - } + /* If /var/log/journal exists, try to pring a nice + notice if the user lacks access to it */ + if (!arg_quiet && geteuid() != 0) { + r = access_check_var_log_journal(j); + if (r < 0) + return r; } - } #else - if (geteuid() != 0 && in_group("systemd-journal") <= 0) { - log_error("No access to messages. Only users in the group 'systemd-journal' can see messages."); - return -EACCES; - } + if (geteuid() != 0 && in_group("systemd-journal") <= 0) { + log_error("Unprivileged users cannot access messages. Users in the 'systemd-journal' group\n" + "group may access messages."); + return -EACCES; + } #endif - return 0; + if (hashmap_isempty(j->files)) { + log_error("No journal files were opened due to insufficient permissions."); + r = -EACCES; + } + } + + SET_FOREACH(code, j->errors, it) { + int err; + + err = -PTR_TO_INT(code); + assert(err > 0); + + if (err != EACCES) + log_warning("Error was encountered while opening journal files: %s", + strerror(err)); + } + + return r; } int main(int argc, char *argv[]) { int r; - sd_journal _cleanup_journal_close_ *j = NULL; + _cleanup_journal_close_ sd_journal*j = NULL; bool need_seek = false; sd_id128_t previous_boot_id; bool previous_boot_id_valid = false, first_line = true; @@ -970,36 +1090,58 @@ int main(int argc, char *argv[]) { goto finish; } - if (arg_action == ACTION_LIST_CATALOG || - arg_action == ACTION_DUMP_CATALOG) { - bool oneline = arg_action == ACTION_LIST_CATALOG; - if (optind < argc) - r = catalog_list_items(stdout, oneline, argv + optind); - else - r = catalog_list(stdout, oneline); - if (r < 0) - log_error("Failed to list catalog: %s", strerror(-r)); - goto finish; - } + if (arg_action == ACTION_UPDATE_CATALOG || + arg_action == ACTION_LIST_CATALOG || + arg_action == ACTION_DUMP_CATALOG) { + + const char* database = CATALOG_DATABASE; + _cleanup_free_ char *copy = NULL; + if (arg_root) { + copy = strjoin(arg_root, "/", CATALOG_DATABASE, NULL); + if (!copy) { + r = log_oom(); + goto finish; + } + path_kill_slashes(copy); + database = copy; + } + + if (arg_action == ACTION_UPDATE_CATALOG) { + r = catalog_update(database, arg_root, catalog_file_dirs); + if (r < 0) + log_error("Failed to list catalog: %s", strerror(-r)); + } else { + bool oneline = arg_action == ACTION_LIST_CATALOG; + + if (optind < argc) + r = catalog_list_items(stdout, database, + oneline, argv + optind); + else + r = catalog_list(stdout, database, oneline); + if (r < 0) + log_error("Failed to list catalog: %s", strerror(-r)); + } - if (arg_action == ACTION_UPDATE_CATALOG) { - r = catalog_update(); goto finish; } - r = access_check(); - if (r < 0) - return EXIT_FAILURE; - if (arg_directory) - r = sd_journal_open_directory(&j, arg_directory, 0); + r = sd_journal_open_directory(&j, arg_directory, arg_journal_type); + else if (arg_file) + r = sd_journal_open_files(&j, (const char**) arg_file, 0); else - r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY); + r = sd_journal_open(&j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type); if (r < 0) { - log_error("Failed to open journal: %s", strerror(-r)); + log_error("Failed to open %s: %s", + arg_directory ? arg_directory : arg_file ? "files" : "journal", + strerror(-r)); return EXIT_FAILURE; } + r = access_check(j); + if (r < 0) + return EXIT_FAILURE; + if (arg_action == ACTION_VERIFY) { r = verify(j); goto finish; @@ -1027,11 +1169,14 @@ int main(int argc, char *argv[]) { if (r < 0) return EXIT_FAILURE; - r = add_unit(j); + r = add_dmesg(j); if (r < 0) return EXIT_FAILURE; - r = add_matches(j, argv + optind); + r = add_units(j); + strv_free(arg_system_units); + strv_free(arg_user_units); + if (r < 0) return EXIT_FAILURE; @@ -1039,15 +1184,22 @@ int main(int argc, char *argv[]) { if (r < 0) return EXIT_FAILURE; - /* Opening the fd now means the first sd_journal_wait() will actually wait */ - r = sd_journal_get_fd(j); + r = add_matches(j, argv + optind); if (r < 0) return EXIT_FAILURE; + log_debug("Journal filter: %s", j->level0 ? journal_make_match_string(j) : "none"); + if (arg_field) { const void *data; size_t size; + r = sd_journal_set_data_threshold(j, 0); + if (r < 0) { + log_error("Failed to unset data size threshold"); + return EXIT_FAILURE; + } + r = sd_journal_query_unique(j, arg_field); if (r < 0) { log_error("Failed to query unique data objects: %s", strerror(-r)); @@ -1072,6 +1224,13 @@ int main(int argc, char *argv[]) { return EXIT_SUCCESS; } + /* Opening the fd now means the first sd_journal_wait() will actually wait */ + if (arg_follow) { + r = sd_journal_get_fd(j); + if (r < 0) + return EXIT_FAILURE; + } + if (arg_cursor) { r = sd_journal_seek_cursor(j, arg_cursor); if (r < 0) { @@ -1169,11 +1328,10 @@ int main(int argc, char *argv[]) { log_error("Failed to iterate through journal: %s", strerror(-r)); goto finish; } + if (r == 0) + break; } - if (r == 0) - break; - if (arg_until_set && !arg_reverse) { usec_t usec; @@ -1200,12 +1358,14 @@ int main(int argc, char *argv[]) { if (!arg_merge) { sd_id128_t boot_id; + const char *color_on = on_tty() ? ANSI_HIGHLIGHT_ON : "", + *color_off = on_tty() ? ANSI_HIGHLIGHT_OFF : ""; r = sd_journal_get_monotonic_usec(j, NULL, &boot_id); if (r >= 0) { if (previous_boot_id_valid && !sd_id128_equal(boot_id, previous_boot_id)) - printf(ANSI_HIGHLIGHT_ON "-- Reboot --" ANSI_HIGHLIGHT_OFF "\n"); + printf("%s-- Reboot --%s\n", color_on, color_off); previous_boot_id = boot_id; previous_boot_id_valid = true; @@ -1219,10 +1379,12 @@ int main(int argc, char *argv[]) { arg_catalog * OUTPUT_CATALOG; r = output_journal(stdout, j, arg_output, 0, flags); - if (r < 0 || ferror(stdout)) + need_seek = true; + if (r == -EADDRNOTAVAIL) + break; + else if (r < 0 || ferror(stdout)) goto finish; - need_seek = true; n_shown++; }