From 8a125ff7b3de71bf26270d151fbee64fd80052d3 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Mon, 14 Sep 2026 17:25:25 +0800 Subject: [PATCH] journalctl: Accept globs in --{exclude-,}identifier= --unit= has accepted globs for a long time, let's bring --identifier= up to parity. Closes: #20457 --- man/journalctl.xml | 8 ++++ src/journal/journalctl-filter.c | 69 ++++++++++++++++++++++++++++++--- src/journal/journalctl-util.c | 10 +++-- src/journal/journalctl-util.h | 2 +- 4 files changed, 79 insertions(+), 10 deletions(-) diff --git a/man/journalctl.xml b/man/journalctl.xml index 933d56a38e9..ff89fc50f8f 100644 --- a/man/journalctl.xml +++ b/man/journalctl.xml @@ -395,6 +395,10 @@ This parameter can be specified multiple times. + If the argument contains a shell-style glob, it is matched against the syslog identifiers + actually present in the journal, as for . If no glob matches anything, and + no exact identifier was given either, an error is returned. + @@ -407,6 +411,10 @@ This parameter can be specified multiple times. + If the argument contains a shell-style glob, it is matched against the syslog identifiers + actually present in the journal, as for . Patterns which do not match + anything are silently skipped. + diff --git a/src/journal/journalctl-filter.c b/src/journal/journalctl-filter.c index 01c82ca4059..87922d98e0f 100644 --- a/src/journal/journalctl-filter.c +++ b/src/journal/journalctl-filter.c @@ -133,7 +133,7 @@ int journal_add_unit_matches( if (!strv_isempty(patterns)) { _cleanup_set_free_ Set *units = NULL; - r = get_possible_units(j, SYSTEM_UNITS_FULL, patterns, &units); + r = get_possible_field_values(j, SYSTEM_UNITS_FULL, patterns, &units); if (r < 0) return r; @@ -176,7 +176,7 @@ int journal_add_unit_matches( if (!strv_isempty(patterns)) { _cleanup_set_free_ Set *units = NULL; - r = get_possible_units(j, USER_UNITS_FULL, patterns, &units); + r = get_possible_field_values(j, USER_UNITS_FULL, patterns, &units); if (r < 0) return r; @@ -217,6 +217,8 @@ static int add_units(sd_journal *j) { } static int add_syslog_identifier(sd_journal *j) { + _cleanup_strv_free_ char **patterns = NULL; + bool added = false; int r; assert(j); @@ -225,26 +227,83 @@ static int add_syslog_identifier(sd_journal *j) { return 0; STRV_FOREACH(i, arg_syslog_identifier) { + if (string_is_glob(*i)) { + r = strv_extend(&patterns, *i); + if (r < 0) + return r; + continue; + } + r = journal_add_match_pair(j, "SYSLOG_IDENTIFIER", *i); if (r < 0) return r; r = sd_journal_add_disjunction(j); if (r < 0) return r; + added = true; } + if (!strv_isempty(patterns)) { + _cleanup_set_free_ Set *identifiers = NULL; + + r = get_possible_field_values(j, "SYSLOG_IDENTIFIER\0", patterns, &identifiers); + if (r < 0) + return r; + + const char *id; + SET_FOREACH(id, identifiers) { + r = journal_add_match_pair(j, "SYSLOG_IDENTIFIER", id); + if (r < 0) + return r; + r = sd_journal_add_disjunction(j); + if (r < 0) + return r; + added = true; + } + } + + /* Complain if the user asked for identifiers but nothing whatsoever was found, since otherwise + * everything would be matched. Same as for --unit=. */ + if (!added) + return -ENODATA; + return sd_journal_add_conjunction(j); } static int add_exclude_identifier(sd_journal *j) { + _cleanup_strv_free_ char **patterns = NULL; _cleanup_set_free_ Set *excludes = NULL; int r; assert(j); - r = set_put_strdupv(&excludes, arg_exclude_identifier); - if (r < 0) - return r; + STRV_FOREACH(i, arg_exclude_identifier) { + if (string_is_glob(*i)) { + r = strv_extend(&patterns, *i); + if (r < 0) + return r; + continue; + } + + r = set_put_strdup(&excludes, *i); + if (r < 0) + return r; + } + + if (!strv_isempty(patterns)) { + _cleanup_set_free_ Set *found = NULL; + + r = get_possible_field_values(j, "SYSLOG_IDENTIFIER\0", patterns, &found); + if (r < 0) + return r; + + const char *id; + SET_FOREACH(id, found) { + r = set_put_strdup(&excludes, id); + if (r < 0) + return r; + } + } return set_free_and_replace(j->exclude_syslog_identifiers, excludes); } diff --git a/src/journal/journalctl-util.c b/src/journal/journalctl-util.c index aedb2da20dc..18d250a868e 100644 --- a/src/journal/journalctl-util.c +++ b/src/journal/journalctl-util.c @@ -128,7 +128,7 @@ int journal_acquire_boot(sd_journal *j) { return 1; } -int get_possible_units( +int get_possible_field_values( sd_journal *j, const char *fields, char * const *patterns, @@ -163,10 +163,12 @@ int get_possible_units( return -ENOMEM; size_t i; - if (!strv_fnmatch_full(patterns, u, FNM_NOESCAPE, &i)) + /* Unlike unit names, values such as SYSLOG_IDENTIFIER= may legitimately contain + * '*', '?' or '[', so an exact match must count too. */ + if (!strv_fnmatch_full(patterns, u, FNM_NOESCAPE, &i) && !strv_contains(patterns, u)) continue; - log_debug("Matched %s with pattern %s=%s", u, field, patterns[i]); + log_debug("Matched %s with pattern %s=%s", u, field, i == SIZE_MAX ? u : patterns[i]); r = set_ensure_consume(&found, &string_hash_ops_free, TAKE_PTR(u)); if (r < 0) return r; @@ -215,7 +217,7 @@ int acquire_unit(sd_journal *j, const char *option_name, const char **ret_unit, if (string_is_glob(u)) { _cleanup_set_free_ Set *s = NULL; - r = get_possible_units(j, type == LOG_SYSTEM_UNIT_INVOCATION_ID ? SYSTEM_UNITS : USER_UNITS, + r = get_possible_field_values(j, type == LOG_SYSTEM_UNIT_INVOCATION_ID ? SYSTEM_UNITS : USER_UNITS, STRV_MAKE(u), &s); if (r < 0) return log_error_errno(r, "Failed to get matching unit '%s' from journal: %m", u); diff --git a/src/journal/journalctl-util.h b/src/journal/journalctl-util.h index 4571c285408..07c7c17102b 100644 --- a/src/journal/journalctl-util.h +++ b/src/journal/journalctl-util.h @@ -30,6 +30,6 @@ char* format_timestamp_maybe_utc(char *buf, size_t l, usec_t t); int acquire_journal(sd_journal **ret); bool journal_boot_has_effect(sd_journal *j); int journal_acquire_boot(sd_journal *j); -int get_possible_units(sd_journal *j, const char *fields, char * const *patterns, Set **ret); +int get_possible_field_values(sd_journal *j, const char *fields, char * const *patterns, Set **ret); int acquire_unit(sd_journal *j, const char *option_name, const char **ret_unit, LogIdType *ret_type); int journal_acquire_invocation(sd_journal *j);