From: Mike Yuan Date: Mon, 23 Feb 2026 07:04:17 +0000 (+0100) Subject: journalctl-vl-server: allow querying units for specific uid in system scope X-Git-Tag: v260-rc1~39^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d8302c2fd92602eae780511037ca08ed8cb0667d;p=thirdparty%2Fsystemd.git journalctl-vl-server: allow querying units for specific uid in system scope This also moves journal_add_unit_matches() back to journalctl-filter.[ch], as I see more appropriate. --- diff --git a/src/basic/basic-forward.h b/src/basic/basic-forward.h index ef616dc7a95..1ca9ecfeff4 100644 --- a/src/basic/basic-forward.h +++ b/src/basic/basic-forward.h @@ -97,6 +97,7 @@ typedef enum RuntimeScope RuntimeScope; typedef enum TimestampStyle TimestampStyle; typedef enum UnitActiveState UnitActiveState; typedef enum UnitDependency UnitDependency; +typedef enum UnitNameMangle UnitNameMangle; typedef enum UnitType UnitType; typedef enum WaitFlags WaitFlags; diff --git a/src/journal/journalctl-filter.c b/src/journal/journalctl-filter.c index 308f7354770..0430f24ce9e 100644 --- a/src/journal/journalctl-filter.c +++ b/src/journal/journalctl-filter.c @@ -9,6 +9,7 @@ #include "chase.h" #include "devnum-util.h" #include "fileio.h" +#include "glob-util.h" #include "journal-internal.h" #include "journalctl.h" #include "journalctl-filter.h" @@ -71,6 +72,116 @@ static int add_dmesg(sd_journal *j) { return sd_journal_add_conjunction(j); } +int journal_add_unit_matches( + sd_journal *j, + MatchUnitFlag flags, + UnitNameMangle mangle_flags, + char * const *system_units, + uid_t uid, + char * const *user_units) { + + _cleanup_strv_free_ char **patterns = NULL; + bool added = false; + int r; + + assert(j); + + if (strv_isempty(system_units) && strv_isempty(user_units)) + return 0; + + STRV_FOREACH(i, system_units) { + _cleanup_free_ char *u = NULL; + + r = unit_name_mangle(*i, UNIT_NAME_MANGLE_GLOB | mangle_flags, &u); + if (r < 0) + return r; + + if (string_is_glob(u)) { + r = strv_consume(&patterns, TAKE_PTR(u)); + if (r < 0) + return r; + } else { + r = add_matches_for_unit_full(j, flags, u); + 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 *units = NULL; + + r = get_possible_units(j, SYSTEM_UNITS_FULL, patterns, &units); + if (r < 0) + return r; + + char *u; + SET_FOREACH(u, units) { + r = add_matches_for_unit_full(j, flags, u); + if (r < 0) + return r; + r = sd_journal_add_disjunction(j); + if (r < 0) + return r; + added = true; + } + } + + patterns = strv_free(patterns); + + STRV_FOREACH(i, user_units) { + _cleanup_free_ char *u = NULL; + + r = unit_name_mangle(*i, UNIT_NAME_MANGLE_GLOB | mangle_flags, &u); + if (r < 0) + return r; + + if (string_is_glob(u)) { + r = strv_consume(&patterns, TAKE_PTR(u)); + if (r < 0) + return r; + } else { + r = add_matches_for_user_unit_full(j, flags, uid, u); + 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 *units = NULL; + + r = get_possible_units(j, USER_UNITS_FULL, patterns, &units); + if (r < 0) + return r; + + char *u; + SET_FOREACH(u, units) { + r = add_matches_for_user_unit_full(j, flags, uid, u); + if (r < 0) + return r; + r = sd_journal_add_disjunction(j); + if (r < 0) + return r; + added = true; + } + } + + /* Complain if the user request matches but nothing whatsoever was found, since otherwise everything + * would be matched. */ + if (!added) + return -ENODATA; + + return sd_journal_add_conjunction(j); +} + static int add_units(sd_journal *j) { MatchUnitFlag flags = MATCH_UNIT_ALL; @@ -82,7 +193,9 @@ static int add_units(sd_journal *j) { if (arg_directory || arg_root || arg_file_stdin || arg_file || arg_machine) flags &= ~MATCH_UNIT_COREDUMP_UID; - return journal_add_unit_matches(j, flags, arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN, arg_system_units, arg_user_units); + return journal_add_unit_matches(j, flags, arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN, + arg_system_units, + UID_INVALID, arg_user_units); } static int add_syslog_identifier(sd_journal *j) { diff --git a/src/journal/journalctl-filter.h b/src/journal/journalctl-filter.h index dc9474c0f04..95b4aeaf047 100644 --- a/src/journal/journalctl-filter.h +++ b/src/journal/journalctl-filter.h @@ -3,4 +3,12 @@ #include "shared-forward.h" +int journal_add_unit_matches( + sd_journal *j, + MatchUnitFlag flags, + UnitNameMangle mangle_flags, + char * const *system_units, + uid_t uid, + char * const *user_units); + int add_filters(sd_journal *j, char **matches); diff --git a/src/journal/journalctl-util.c b/src/journal/journalctl-util.c index 68761c83c9c..aedb2da20dc 100644 --- a/src/journal/journalctl-util.c +++ b/src/journal/journalctl-util.c @@ -177,108 +177,6 @@ int get_possible_units( return 0; } -int journal_add_unit_matches(sd_journal *j, MatchUnitFlag flags, UnitNameMangle mangle_flags, char **system_units, char **user_units) { - _cleanup_strv_free_ char **patterns = NULL; - bool added = false; - int r; - - assert(j); - - if (strv_isempty(system_units) && strv_isempty(user_units)) - return 0; - - STRV_FOREACH(i, system_units) { - _cleanup_free_ char *u = NULL; - - r = unit_name_mangle(*i, UNIT_NAME_MANGLE_GLOB | mangle_flags, &u); - if (r < 0) - return r; - - if (string_is_glob(u)) { - r = strv_consume(&patterns, TAKE_PTR(u)); - if (r < 0) - return r; - } else { - r = add_matches_for_unit_full(j, flags, u); - 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 *units = NULL; - - r = get_possible_units(j, SYSTEM_UNITS_FULL, patterns, &units); - if (r < 0) - return r; - - char *u; - SET_FOREACH(u, units) { - r = add_matches_for_unit_full(j, flags, u); - if (r < 0) - return r; - r = sd_journal_add_disjunction(j); - if (r < 0) - return r; - added = true; - } - } - - patterns = strv_free(patterns); - - STRV_FOREACH(i, user_units) { - _cleanup_free_ char *u = NULL; - - r = unit_name_mangle(*i, UNIT_NAME_MANGLE_GLOB | mangle_flags, &u); - if (r < 0) - return r; - - if (string_is_glob(u)) { - r = strv_consume(&patterns, TAKE_PTR(u)); - if (r < 0) - return r; - } else { - r = add_matches_for_user_unit_full(j, flags, u); - 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 *units = NULL; - - r = get_possible_units(j, USER_UNITS_FULL, patterns, &units); - if (r < 0) - return r; - - char *u; - SET_FOREACH(u, units) { - r = add_matches_for_user_unit_full(j, flags, u); - if (r < 0) - return r; - r = sd_journal_add_disjunction(j); - if (r < 0) - return r; - added = true; - } - } - - /* Complain if the user request matches but nothing whatsoever was found, since otherwise everything - * would be matched. */ - if (!added) - return -ENODATA; - - return sd_journal_add_conjunction(j); -} - int acquire_unit(sd_journal *j, 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 0b4993053a5..f237aab2b03 100644 --- a/src/journal/journalctl-util.h +++ b/src/journal/journalctl-util.h @@ -3,7 +3,6 @@ #include "shared-forward.h" #include "logs-show.h" -#include "unit-name.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(). */ @@ -32,6 +31,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 journal_add_unit_matches(sd_journal *j, MatchUnitFlag flags, UnitNameMangle mangle_flags, char **system_units, char **user_units); 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/src/journal/journalctl-varlink-server.c b/src/journal/journalctl-varlink-server.c index 5957af499bf..f44e2a807cf 100644 --- a/src/journal/journalctl-varlink-server.c +++ b/src/journal/journalctl-varlink-server.c @@ -4,25 +4,30 @@ #include "sd-varlink.h" #include "journal-internal.h" -#include "journalctl-util.h" +#include "journalctl.h" +#include "journalctl-filter.h" #include "journalctl-varlink-server.h" #include "json-util.h" -#include "log.h" #include "logs-show.h" #include "output-mode.h" +#include "runtime-scope.h" #include "strv.h" +#include "unit-name.h" /* IWYU pragma: keep */ +#include "user-util.h" #include "varlink-util.h" typedef struct GetEntriesParameters { char **units; char **user_units; const char *namespace; + uid_t uid; int priority; uint64_t limit; } GetEntriesParameters; static void get_entries_parameters_done(GetEntriesParameters *p) { assert(p); + p->units = strv_free(p->units); p->user_units = strv_free(p->user_units); } @@ -31,6 +36,7 @@ int vl_method_get_entries(sd_varlink *link, sd_json_variant *parameters, sd_varl static const sd_json_dispatch_field dispatch_table[] = { { "units", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(GetEntriesParameters, units), 0 }, + { "uid", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, offsetof(GetEntriesParameters, uid), 0 }, { "userUnits", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(GetEntriesParameters, user_units), 0 }, { "namespace", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(GetEntriesParameters, namespace), 0 }, { "priority", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_log_level, offsetof(GetEntriesParameters, priority), 0 }, @@ -39,6 +45,7 @@ int vl_method_get_entries(sd_varlink *link, sd_json_variant *parameters, sd_varl }; _cleanup_(get_entries_parameters_done) GetEntriesParameters p = { + .uid = UID_INVALID, .priority = -1, }; _cleanup_(sd_journal_closep) sd_journal *j = NULL; @@ -51,13 +58,16 @@ int vl_method_get_entries(sd_varlink *link, sd_json_variant *parameters, sd_varl if (r != 0) return r; + if (arg_varlink_runtime_scope == RUNTIME_SCOPE_SYSTEM && p.user_units && !uid_is_valid(p.uid)) + return sd_varlink_error_invalid_parameter_name(link, "uid"); + /* systemd ships with sensible defaults for the system/user services and the socket permissions so we * do not need to do extra sd_varlink_get_peer_uid() or policykit checks here */ r = sd_journal_open_namespace(&j, p.namespace, SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_ASSUME_IMMUTABLE); if (r < 0) return r; - r = journal_add_unit_matches(j, MATCH_UNIT_ALL, /* mangle_flags= */ 0, p.units, p.user_units); + r = journal_add_unit_matches(j, MATCH_UNIT_ALL, /* mangle_flags= */ 0, p.units, p.uid, p.user_units); if (r == -ENODATA) return sd_varlink_error(link, "io.systemd.JournalAccess.NoMatches", NULL); if (r < 0) diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 82bf6b88944..b25827961f2 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -30,6 +30,7 @@ #include "parse-util.h" #include "pcre2-util.h" #include "pretty-print.h" +#include "runtime-scope.h" #include "set.h" #include "static-destruct.h" #include "string-table.h" @@ -111,7 +112,9 @@ pcre2_code *arg_compiled_pattern = NULL; PatternCompileCase arg_case = PATTERN_COMPILE_CASE_AUTO; ImagePolicy *arg_image_policy = NULL; bool arg_synchronize_on_exit = false; + static bool arg_varlink = false; +RuntimeScope arg_varlink_runtime_scope = _RUNTIME_SCOPE_INVALID; STATIC_DESTRUCTOR_REGISTER(arg_cursor, freep); STATIC_DESTRUCTOR_REGISTER(arg_cursor_file, freep); @@ -486,7 +489,35 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m"); if (r > 0) { arg_varlink = true; - arg_pager_flags |= PAGER_DISABLE; + + static const struct option varlink_options[] = { + { "system", no_argument, NULL, ARG_SYSTEM }, + { "user", no_argument, NULL, ARG_USER }, + {} + }; + + while ((c = getopt_long(argc, argv, "", varlink_options, NULL)) >= 0) + + switch (c) { + + case ARG_SYSTEM: + arg_varlink_runtime_scope = RUNTIME_SCOPE_SYSTEM; + break; + + case ARG_USER: + arg_varlink_runtime_scope = RUNTIME_SCOPE_USER; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached(); + } + + if (arg_varlink_runtime_scope < 0) + return log_error_errno(arg_varlink_runtime_scope, "Cannot run in Varlink mode with no runtime scope specified."); + return 1; } diff --git a/src/journal/journalctl.h b/src/journal/journalctl.h index 79ffe034649..090e3382496 100644 --- a/src/journal/journalctl.h +++ b/src/journal/journalctl.h @@ -94,3 +94,6 @@ extern bool arg_synchronize_on_exit; static inline bool arg_lines_needs_seek_end(void) { return arg_lines >= 0 && !arg_lines_oldest; } + +/* Only used for varlink server invocation */ +extern RuntimeScope arg_varlink_runtime_scope; diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index 9770c36a531..3666c163984 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -1767,13 +1767,15 @@ int add_matches_for_unit_full(sd_journal *j, MatchUnitFlag flags, const char *un return r; } -int add_matches_for_user_unit_full(sd_journal *j, MatchUnitFlag flags, const char *unit) { - uid_t uid = getuid(); +int add_matches_for_user_unit_full(sd_journal *j, MatchUnitFlag flags, uid_t uid, const char *unit) { int r; assert(j); assert(unit); + if (uid == UID_INVALID) + uid = getuid(); + (void) ( /* Look for messages from the user service itself */ (r = journal_add_match_pair(j, "_SYSTEMD_USER_UNIT", unit)) || @@ -2010,7 +2012,7 @@ static int set_matches_for_discover_id( return add_matches_for_unit_full(j, /* flags= */ 0, unit); if (type == LOG_USER_UNIT_INVOCATION_ID) - return add_matches_for_user_unit_full(j, /* flags= */ 0, unit); + return add_matches_for_user_unit_full(j, /* flags= */ 0, UID_INVALID, unit); return -EINVAL; } diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h index e2886d69cd1..5108941f798 100644 --- a/src/shared/logs-show.h +++ b/src/shared/logs-show.h @@ -57,9 +57,9 @@ int add_matches_for_unit_full(sd_journal *j, MatchUnitFlag flags, const char *un static inline int add_matches_for_unit(sd_journal *j, const char *unit) { return add_matches_for_unit_full(j, MATCH_UNIT_ALL, unit); } -int add_matches_for_user_unit_full(sd_journal *j, MatchUnitFlag flags, const char *unit); +int add_matches_for_user_unit_full(sd_journal *j, MatchUnitFlag flags, uid_t uid, const char *unit); static inline int add_matches_for_user_unit(sd_journal *j, const char *unit) { - return add_matches_for_user_unit_full(j, MATCH_UNIT_ALL, unit); + return add_matches_for_user_unit_full(j, MATCH_UNIT_ALL, UID_INVALID, unit); } int show_journal_by_unit( diff --git a/src/shared/shared-forward.h b/src/shared/shared-forward.h index 09ce806ad2b..e07b25c056a 100644 --- a/src/shared/shared-forward.h +++ b/src/shared/shared-forward.h @@ -26,6 +26,7 @@ typedef enum DnssecMode DnssecMode; typedef enum Fido2EnrollFlags Fido2EnrollFlags; typedef enum KeySourceType KeySourceType; typedef enum LabelFixFlags LabelFixFlags; +typedef enum MatchUnitFlag MatchUnitFlag; typedef enum MountInNamespaceFlags MountInNamespaceFlags; typedef enum NamePolicy NamePolicy; typedef enum OutputFlags OutputFlags; diff --git a/src/shared/varlink-io.systemd.JournalAccess.c b/src/shared/varlink-io.systemd.JournalAccess.c index 178db1489cf..b057067079f 100644 --- a/src/shared/varlink-io.systemd.JournalAccess.c +++ b/src/shared/varlink-io.systemd.JournalAccess.c @@ -7,6 +7,8 @@ static SD_VARLINK_DEFINE_METHOD_FULL( SD_VARLINK_REQUIRES_MORE, SD_VARLINK_FIELD_COMMENT("Show messages for the specified systemd units (e.g. ['foo.service'])."), SD_VARLINK_DEFINE_INPUT(units, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY), + SD_VARLINK_FIELD_COMMENT("UID to match user units for"), + SD_VARLINK_DEFINE_INPUT(uid, SD_VARLINK_INT, SD_VARLINK_NULLABLE), SD_VARLINK_FIELD_COMMENT("Show messages for the specified user units (e.g. ['foo.service'])."), SD_VARLINK_DEFINE_INPUT(userUnits, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY), SD_VARLINK_FIELD_COMMENT("If specified, shows the log data of the specified namespace, otherwise the default namespace."), diff --git a/units/systemd-journalctl@.service b/units/systemd-journalctl@.service index a816284cecf..b6111124efc 100644 --- a/units/systemd-journalctl@.service +++ b/units/systemd-journalctl@.service @@ -16,7 +16,7 @@ Before=shutdown.target RequiresMountsFor=/var/log/journal [Service] -ExecStart=journalctl +ExecStart=journalctl --system DynamicUser=yes User=systemd-journal-access SupplementaryGroups=systemd-journal diff --git a/units/user/systemd-journalctl@.service b/units/user/systemd-journalctl@.service index 04749fdb584..8449baa4c58 100644 --- a/units/user/systemd-journalctl@.service +++ b/units/user/systemd-journalctl@.service @@ -12,4 +12,4 @@ Description=Journal Log Access Service Documentation=man:journalctl(1) [Service] -ExecStart=journalctl +ExecStart=journalctl --user