From e8823b5e352711c68ab3282697a6ab1cfb06d25a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 11 Dec 2024 09:05:34 +0900 Subject: [PATCH 1/3] journalctl: make --invocation and --list-invocations accept unit name without suffix Fixes #35538. --- src/journal/journalctl-util.c | 21 +++++++++++++++++---- test/units/TEST-04-JOURNAL.invocation.sh | 9 ++++++++- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/journal/journalctl-util.c b/src/journal/journalctl-util.c index 1e663b0c8ce..58257abc323 100644 --- a/src/journal/journalctl-util.c +++ b/src/journal/journalctl-util.c @@ -10,6 +10,7 @@ #include "rlimit-util.h" #include "strv.h" #include "terminal-util.h" +#include "unit-name.h" char* format_timestamp_maybe_utc(char *buf, size_t l, usec_t t) { assert(buf); @@ -113,6 +114,7 @@ int journal_acquire_boot(sd_journal *j) { int acquire_unit(const char *option_name, const char **ret_unit, LogIdType *ret_type) { size_t n; + int r; assert(option_name); assert(ret_unit); @@ -128,15 +130,26 @@ int acquire_unit(const char *option_name, const char **ret_unit, LogIdType *ret_ "Using %s with multiple units is not supported.", option_name); + LogIdType type; + char **units; if (!strv_isempty(arg_system_units)) { - *ret_type = LOG_SYSTEM_UNIT_INVOCATION_ID; - *ret_unit = arg_system_units[0]; + type = LOG_SYSTEM_UNIT_INVOCATION_ID; + units = arg_system_units; } else { assert(!strv_isempty(arg_user_units)); - *ret_type = LOG_USER_UNIT_INVOCATION_ID; - *ret_unit = arg_user_units[0]; + type = LOG_USER_UNIT_INVOCATION_ID; + units = arg_user_units; } + _cleanup_free_ char *u = NULL; + r = unit_name_mangle(units[0], arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN, &u); + if (r < 0) + return log_error_errno(r, "Failed to mangle unit name '%s': %m", units[0]); + + free_and_replace(units[0], u); + + *ret_type = type; + *ret_unit = units[0]; return 0; } diff --git a/test/units/TEST-04-JOURNAL.invocation.sh b/test/units/TEST-04-JOURNAL.invocation.sh index e7a6f547b48..3ac931e2633 100755 --- a/test/units/TEST-04-JOURNAL.invocation.sh +++ b/test/units/TEST-04-JOURNAL.invocation.sh @@ -7,7 +7,8 @@ set -o pipefail # shellcheck source=test/units/util.sh . "$(dirname "$0")"/util.sh -SERVICE_NAME=invocation-id-test-"$RANDOM".service +SERVICE_NAME_SHORT=invocation-id-test-"$RANDOM" +SERVICE_NAME="$SERVICE_NAME_SHORT".service TMP_DIR=$(mktemp -d) @@ -26,6 +27,7 @@ done # systemd[1]: invocation-id-test-26448.service: Deactivated successfully. journalctl --sync +journalctl --list-invocation -u "$SERVICE_NAME_SHORT" | tee "$TMP_DIR"/short journalctl --list-invocation -u "$SERVICE_NAME" | tee "$TMP_DIR"/10 journalctl --list-invocation -u "$SERVICE_NAME" --reverse | tee "$TMP_DIR"/10-r journalctl --list-invocation -u "$SERVICE_NAME" -n +10 | tee "$TMP_DIR"/p10 @@ -44,6 +46,7 @@ journalctl --list-invocation -u "$SERVICE_NAME" -n +5 --reverse | tee "$TMP_DIR" [[ $(cat "$TMP_DIR"/p5 | wc -l) == 6 ]] [[ $(cat "$TMP_DIR"/p5-r | wc -l) == 6 ]] +diff "$TMP_DIR"/10 "$TMP_DIR"/short diff <(tail -n 10 "$TMP_DIR"/10 | tac) <(tail -n 10 "$TMP_DIR"/10-r) diff <(tail -n 5 "$TMP_DIR"/10) <(tail -n 5 "$TMP_DIR"/5) diff <(tail -n 5 "$TMP_DIR"/10 | tac) <(tail -n 5 "$TMP_DIR"/5-r) @@ -54,6 +57,8 @@ diff <(tail -n 10 "$TMP_DIR"/p10 | head -n 5 | tac) <(tail -n 5 "$TMP_DIR"/p5-r) tail -n 10 "$TMP_DIR"/10 | while read -r idx invocation _; do i="$(( idx + 10 ))" + assert_in "invocation ${i} ${invocation}" "$(journalctl --no-hostname -n 1 -t bash --invocation="${i}" -u "$SERVICE_NAME_SHORT")" + assert_in "invocation ${i} ${invocation}" "$(journalctl --no-hostname -n 1 -t bash --invocation="${idx}" -u "$SERVICE_NAME_SHORT")" assert_in "invocation ${i} ${invocation}" "$(journalctl --no-hostname -n 1 -t bash --invocation="${i}" -u "$SERVICE_NAME")" assert_in "invocation ${i} ${invocation}" "$(journalctl --no-hostname -n 1 -t bash --invocation="${idx}" -u "$SERVICE_NAME")" assert_in "invocation ${i} ${invocation}" "$(journalctl --no-hostname -n 1 -t bash --invocation="${invocation}")" @@ -62,6 +67,8 @@ tail -n 10 "$TMP_DIR"/10 | tail -n 10 "$TMP_DIR"/p10 | while read -r i invocation _; do idx="$(( i - 10 ))" + assert_in "invocation ${i} ${invocation}" "$(journalctl --no-hostname -n 1 -t bash --invocation="${i}" -u "$SERVICE_NAME_SHORT")" + assert_in "invocation ${i} ${invocation}" "$(journalctl --no-hostname -n 1 -t bash --invocation="${idx}" -u "$SERVICE_NAME_SHORT")" assert_in "invocation ${i} ${invocation}" "$(journalctl --no-hostname -n 1 -t bash --invocation="${i}" -u "$SERVICE_NAME")" assert_in "invocation ${i} ${invocation}" "$(journalctl --no-hostname -n 1 -t bash --invocation="${idx}" -u "$SERVICE_NAME")" assert_in "invocation ${i} ${invocation}" "$(journalctl --no-hostname -n 1 -t bash --invocation="${invocation}")" From 48b22321af9ed0336716dbb4d4d095e18d7eebec Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 11 Dec 2024 09:04:06 +0900 Subject: [PATCH 2/3] journalctl: move get_possible_units() to journalctl-util.c No functional change. Preparation for the next commit. --- src/journal/journalctl-filter.c | 72 +-------------------------------- src/journal/journalctl-util.c | 51 +++++++++++++++++++++++ src/journal/journalctl-util.h | 24 +++++++++++ 3 files changed, 77 insertions(+), 70 deletions(-) diff --git a/src/journal/journalctl-filter.c b/src/journal/journalctl-filter.c index 1c6348574c5..b171cf1f903 100644 --- a/src/journal/journalctl-filter.c +++ b/src/journal/journalctl-filter.c @@ -12,7 +12,6 @@ #include "journalctl-util.h" #include "logs-show.h" #include "missing_sched.h" -#include "nulstr-util.h" #include "path-util.h" #include "unit-name.h" @@ -65,73 +64,6 @@ static int add_dmesg(sd_journal *j) { return sd_journal_add_conjunction(j); } -static int get_possible_units( - sd_journal *j, - const char *fields, - char **patterns, - Set **ret) { - - _cleanup_set_free_ Set *found = NULL; - int r; - - assert(j); - assert(fields); - assert(ret); - - NULSTR_FOREACH(field, fields) { - const void *data; - size_t size; - - r = sd_journal_query_unique(j, field); - if (r < 0) - return r; - - SD_JOURNAL_FOREACH_UNIQUE(j, data, size) { - _cleanup_free_ char *u = NULL; - char *eq; - - eq = memchr(data, '=', size); - if (eq) { - size -= eq - (char*) data + 1; - data = ++eq; - } - - u = strndup(data, size); - if (!u) - return -ENOMEM; - - size_t i; - if (!strv_fnmatch_full(patterns, u, FNM_NOESCAPE, &i)) - continue; - - log_debug("Matched %s with pattern %s=%s", u, field, patterns[i]); - r = set_ensure_consume(&found, &string_hash_ops_free, TAKE_PTR(u)); - if (r < 0) - return r; - } - } - - *ret = TAKE_PTR(found); - return 0; -} - -/* This list is supposed to return the superset of unit names - * possibly matched by rules added with add_matches_for_unit... */ -#define SYSTEM_UNITS \ - "_SYSTEMD_UNIT\0" \ - "COREDUMP_UNIT\0" \ - "UNIT\0" \ - "OBJECT_SYSTEMD_UNIT\0" \ - "_SYSTEMD_SLICE\0" - -/* ... and add_matches_for_user_unit */ -#define USER_UNITS \ - "_SYSTEMD_USER_UNIT\0" \ - "USER_UNIT\0" \ - "COREDUMP_USER_UNIT\0" \ - "OBJECT_SYSTEMD_USER_UNIT\0" \ - "_SYSTEMD_USER_SLICE\0" - static int add_units(sd_journal *j) { _cleanup_strv_free_ char **patterns = NULL; bool added = false; @@ -175,7 +107,7 @@ static int add_units(sd_journal *j) { _cleanup_set_free_ Set *units = NULL; char *u; - r = get_possible_units(j, SYSTEM_UNITS, patterns, &units); + r = get_possible_units(j, SYSTEM_UNITS_FULL, patterns, &units); if (r < 0) return r; @@ -218,7 +150,7 @@ static int add_units(sd_journal *j) { _cleanup_set_free_ Set *units = NULL; char *u; - r = get_possible_units(j, USER_UNITS, patterns, &units); + r = get_possible_units(j, USER_UNITS_FULL, patterns, &units); if (r < 0) return r; diff --git a/src/journal/journalctl-util.c b/src/journal/journalctl-util.c index 58257abc323..82ddb64f2b3 100644 --- a/src/journal/journalctl-util.c +++ b/src/journal/journalctl-util.c @@ -7,6 +7,7 @@ #include "journalctl.h" #include "journalctl-util.h" #include "logs-show.h" +#include "nulstr-util.h" #include "rlimit-util.h" #include "strv.h" #include "terminal-util.h" @@ -112,6 +113,56 @@ int journal_acquire_boot(sd_journal *j) { return 1; } +int get_possible_units( + sd_journal *j, + const char *fields, + char * const *patterns, + Set **ret) { + + _cleanup_set_free_ Set *found = NULL; + int r; + + assert(j); + assert(fields); + assert(ret); + + NULSTR_FOREACH(field, fields) { + const void *data; + size_t size; + + r = sd_journal_query_unique(j, field); + if (r < 0) + return r; + + SD_JOURNAL_FOREACH_UNIQUE(j, data, size) { + _cleanup_free_ char *u = NULL; + char *eq; + + eq = memchr(data, '=', size); + if (eq) { + size -= eq - (char*) data + 1; + data = ++eq; + } + + u = strndup(data, size); + if (!u) + return -ENOMEM; + + size_t i; + if (!strv_fnmatch_full(patterns, u, FNM_NOESCAPE, &i)) + continue; + + log_debug("Matched %s with pattern %s=%s", u, field, patterns[i]); + r = set_ensure_consume(&found, &string_hash_ops_free, TAKE_PTR(u)); + if (r < 0) + return r; + } + } + + *ret = TAKE_PTR(found); + return 0; +} + int acquire_unit(const char *option_name, const char **ret_unit, LogIdType *ret_type) { size_t n; int r; diff --git a/src/journal/journalctl-util.h b/src/journal/journalctl-util.h index 14e3d569a64..49a4d3e08b0 100644 --- a/src/journal/journalctl-util.h +++ b/src/journal/journalctl-util.h @@ -4,11 +4,35 @@ #include "sd-journal.h" #include "logs-show.h" +#include "set.h" #include "time-util.h" +/* The lists below are supposed to return the superset of unit names possibly matched by rules added with + * add_matches_for_unit() and add_matches_for_user_unit(). */ +#define SYSTEM_UNITS \ + "_SYSTEMD_UNIT\0" \ + "UNIT\0" \ + "OBJECT_SYSTEMD_UNIT\0" + +#define SYSTEM_UNITS_FULL \ + SYSTEM_UNITS \ + "COREDUMP_UNIT\0" \ + "_SYSTEMD_SLICE\0" + +#define USER_UNITS \ + "_SYSTEMD_USER_UNIT\0" \ + "USER_UNIT\0" \ + "OBJECT_SYSTEMD_USER_UNIT\0" + +#define USER_UNITS_FULL \ + USER_UNITS \ + "COREDUMP_USER_UNIT\0" \ + "_SYSTEMD_USER_SLICE\0" + 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 acquire_unit(const char *option_name, const char **ret_unit, LogIdType *ret_type); int journal_acquire_invocation(sd_journal *j); From 7bb1c8f2a30bf41c968059fab92c6296904910e2 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 11 Dec 2024 09:35:32 +0900 Subject: [PATCH 3/3] journalctl: make --invocation and --list-invocations accept unit name with glob Previously, journalctl -I -u GLOB was not supported, while journalctl -u GLOB works fine. Let's make them consistent. --- src/journal/journalctl-misc.c | 4 ++-- src/journal/journalctl-util.c | 28 ++++++++++++++++++++---- src/journal/journalctl-util.h | 2 +- test/units/TEST-04-JOURNAL.invocation.sh | 10 ++++++++- 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/journal/journalctl-misc.c b/src/journal/journalctl-misc.c index 3dea69d9fb3..46a70ae1ac5 100644 --- a/src/journal/journalctl-misc.c +++ b/src/journal/journalctl-misc.c @@ -246,11 +246,11 @@ int action_list_invocations(void) { assert(arg_action == ACTION_LIST_INVOCATIONS); - r = acquire_unit("--list-invocations", &unit, &type); + r = acquire_journal(&j); if (r < 0) return r; - r = acquire_journal(&j); + r = acquire_unit(j, "--list-invocations", &unit, &type); if (r < 0) return r; diff --git a/src/journal/journalctl-util.c b/src/journal/journalctl-util.c index 82ddb64f2b3..918e5713f6d 100644 --- a/src/journal/journalctl-util.c +++ b/src/journal/journalctl-util.c @@ -2,6 +2,7 @@ #include +#include "glob-util.h" #include "id128-util.h" #include "journal-util.h" #include "journalctl.h" @@ -163,10 +164,11 @@ int get_possible_units( return 0; } -int acquire_unit(const char *option_name, const char **ret_unit, LogIdType *ret_type) { +int acquire_unit(sd_journal *j, const char *option_name, const char **ret_unit, LogIdType *ret_type) { size_t n; int r; + assert(j); assert(option_name); assert(ret_unit); assert(ret_type); @@ -193,11 +195,29 @@ int acquire_unit(const char *option_name, const char **ret_unit, LogIdType *ret_ } _cleanup_free_ char *u = NULL; - r = unit_name_mangle(units[0], arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN, &u); + r = unit_name_mangle(units[0], UNIT_NAME_MANGLE_GLOB | (arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN), &u); if (r < 0) return log_error_errno(r, "Failed to mangle unit name '%s': %m", units[0]); - free_and_replace(units[0], u); + 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, + STRV_MAKE(u), &s); + if (r < 0) + return log_error_errno(r, "Failed to get matching unit '%s' from journal: %m", u); + if (set_isempty(s)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No matching unit found for '%s' in journal.", u); + if (set_size(s) > 1) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Multiple matching units found for '%s' in journal.", u); + + char *found = set_steal_first(s); + log_debug("Found matching unit '%s' for '%s'.", found, u); + + free_and_replace(units[0], found); + assert(set_isempty(s)); + } else + free_and_replace(units[0], u); *ret_type = type; *ret_unit = units[0]; @@ -225,7 +245,7 @@ int journal_acquire_invocation(sd_journal *j) { * system unit or user unit, and calling without unit name is allowed. Otherwise, a unit name must * be specified. */ if (arg_invocation_offset != 0 || sd_id128_is_null(arg_invocation_id)) { - r = acquire_unit("-I/--invocation= with an offset", &unit, &type); + r = acquire_unit(j, "-I/--invocation= with an offset", &unit, &type); if (r < 0) return r; } diff --git a/src/journal/journalctl-util.h b/src/journal/journalctl-util.h index 49a4d3e08b0..14cdfb51361 100644 --- a/src/journal/journalctl-util.h +++ b/src/journal/journalctl-util.h @@ -34,5 +34,5 @@ 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 acquire_unit(const char *option_name, const char **ret_unit, LogIdType *ret_type); +int acquire_unit(sd_journal *j, const char *option_name, const char **ret_unit, LogIdType *ret_type); int journal_acquire_invocation(sd_journal *j); diff --git a/test/units/TEST-04-JOURNAL.invocation.sh b/test/units/TEST-04-JOURNAL.invocation.sh index 3ac931e2633..74db1040fdf 100755 --- a/test/units/TEST-04-JOURNAL.invocation.sh +++ b/test/units/TEST-04-JOURNAL.invocation.sh @@ -7,8 +7,10 @@ set -o pipefail # shellcheck source=test/units/util.sh . "$(dirname "$0")"/util.sh -SERVICE_NAME_SHORT=invocation-id-test-"$RANDOM" +BASE=test-"$RANDOM" +SERVICE_NAME_SHORT=invocation-id-"$BASE" SERVICE_NAME="$SERVICE_NAME_SHORT".service +SERVICE_NAME_GLOB="invocation-*-${BASE}.service" TMP_DIR=$(mktemp -d) @@ -28,6 +30,7 @@ done journalctl --sync journalctl --list-invocation -u "$SERVICE_NAME_SHORT" | tee "$TMP_DIR"/short +journalctl --list-invocation -u "$SERVICE_NAME_GLOB" | tee "$TMP_DIR"/glob journalctl --list-invocation -u "$SERVICE_NAME" | tee "$TMP_DIR"/10 journalctl --list-invocation -u "$SERVICE_NAME" --reverse | tee "$TMP_DIR"/10-r journalctl --list-invocation -u "$SERVICE_NAME" -n +10 | tee "$TMP_DIR"/p10 @@ -47,6 +50,7 @@ journalctl --list-invocation -u "$SERVICE_NAME" -n +5 --reverse | tee "$TMP_DIR" [[ $(cat "$TMP_DIR"/p5-r | wc -l) == 6 ]] diff "$TMP_DIR"/10 "$TMP_DIR"/short +diff "$TMP_DIR"/10 "$TMP_DIR"/glob diff <(tail -n 10 "$TMP_DIR"/10 | tac) <(tail -n 10 "$TMP_DIR"/10-r) diff <(tail -n 5 "$TMP_DIR"/10) <(tail -n 5 "$TMP_DIR"/5) diff <(tail -n 5 "$TMP_DIR"/10 | tac) <(tail -n 5 "$TMP_DIR"/5-r) @@ -59,6 +63,8 @@ tail -n 10 "$TMP_DIR"/10 | i="$(( idx + 10 ))" assert_in "invocation ${i} ${invocation}" "$(journalctl --no-hostname -n 1 -t bash --invocation="${i}" -u "$SERVICE_NAME_SHORT")" assert_in "invocation ${i} ${invocation}" "$(journalctl --no-hostname -n 1 -t bash --invocation="${idx}" -u "$SERVICE_NAME_SHORT")" + assert_in "invocation ${i} ${invocation}" "$(journalctl --no-hostname -n 1 -t bash --invocation="${i}" -u "$SERVICE_NAME_GLOB")" + assert_in "invocation ${i} ${invocation}" "$(journalctl --no-hostname -n 1 -t bash --invocation="${idx}" -u "$SERVICE_NAME_GLOB")" assert_in "invocation ${i} ${invocation}" "$(journalctl --no-hostname -n 1 -t bash --invocation="${i}" -u "$SERVICE_NAME")" assert_in "invocation ${i} ${invocation}" "$(journalctl --no-hostname -n 1 -t bash --invocation="${idx}" -u "$SERVICE_NAME")" assert_in "invocation ${i} ${invocation}" "$(journalctl --no-hostname -n 1 -t bash --invocation="${invocation}")" @@ -69,6 +75,8 @@ tail -n 10 "$TMP_DIR"/p10 | idx="$(( i - 10 ))" assert_in "invocation ${i} ${invocation}" "$(journalctl --no-hostname -n 1 -t bash --invocation="${i}" -u "$SERVICE_NAME_SHORT")" assert_in "invocation ${i} ${invocation}" "$(journalctl --no-hostname -n 1 -t bash --invocation="${idx}" -u "$SERVICE_NAME_SHORT")" + assert_in "invocation ${i} ${invocation}" "$(journalctl --no-hostname -n 1 -t bash --invocation="${i}" -u "$SERVICE_NAME_GLOB")" + assert_in "invocation ${i} ${invocation}" "$(journalctl --no-hostname -n 1 -t bash --invocation="${idx}" -u "$SERVICE_NAME_GLOB")" assert_in "invocation ${i} ${invocation}" "$(journalctl --no-hostname -n 1 -t bash --invocation="${i}" -u "$SERVICE_NAME")" assert_in "invocation ${i} ${invocation}" "$(journalctl --no-hostname -n 1 -t bash --invocation="${idx}" -u "$SERVICE_NAME")" assert_in "invocation ${i} ${invocation}" "$(journalctl --no-hostname -n 1 -t bash --invocation="${invocation}")"