]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
journalctl-vl-server: allow querying units for specific uid in system scope 40794/head
authorMike Yuan <me@yhndnzj.com>
Mon, 23 Feb 2026 07:04:17 +0000 (08:04 +0100)
committerMike Yuan <me@yhndnzj.com>
Mon, 23 Feb 2026 09:43:00 +0000 (10:43 +0100)
This also moves journal_add_unit_matches() back to
journalctl-filter.[ch], as I see more appropriate.

14 files changed:
src/basic/basic-forward.h
src/journal/journalctl-filter.c
src/journal/journalctl-filter.h
src/journal/journalctl-util.c
src/journal/journalctl-util.h
src/journal/journalctl-varlink-server.c
src/journal/journalctl.c
src/journal/journalctl.h
src/shared/logs-show.c
src/shared/logs-show.h
src/shared/shared-forward.h
src/shared/varlink-io.systemd.JournalAccess.c
units/systemd-journalctl@.service
units/user/systemd-journalctl@.service

index ef616dc7a95b5203f59a5e40a3da3552cde755d9..1ca9ecfeff43e399b2d422ed847f451140e3b868 100644 (file)
@@ -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;
 
index 308f7354770bd69876f4f2bb71f4a4e2c9637fbe..0430f24ce9e462082dc8cb526a5a9608afbc7a3a 100644 (file)
@@ -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) {
index dc9474c0f043aa3c710bf04daebef5a70fd5e6ab..95b4aeaf0470b64887e6cc909ae95c7f9c9a80cc 100644 (file)
@@ -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);
index 68761c83c9cb652d4a4a5891585c4f129e233d86..aedb2da20dc4465f61521f7ca2de234ea55b3704 100644 (file)
@@ -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;
index 0b4993053a569b0414d07d5a323f7f93e899a506..f237aab2b03751b16662031c474cdac732378fcd 100644 (file)
@@ -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);
index 5957af499bf94f21ca5733b2a485f75ca2bd953b..f44e2a807cfcb107a200d53fbea38ccbf0326eb4 100644 (file)
@@ -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)
index 82bf6b88944ab4b60a12bd0795e927cd210f0752..b25827961f22c33fa5bc3d8a2103e794f2663154 100644 (file)
@@ -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;
         }
 
index 79ffe034649ad95aad381f15b92229964945164e..090e3382496cab370de65662e2fd5cbc63dccda6 100644 (file)
@@ -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;
index 9770c36a53164610a1dee6e54049bd9305ea47ce..3666c1639845c049137f3c2ef52d39011583bf37 100644 (file)
@@ -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;
 }
index e2886d69cd1d664f660e661ee80544138bda4011..5108941f798cbc30dc9c780b2ba5aeedc3e1c149 100644 (file)
@@ -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(
index 09ce806ad2b7fad673199848004721561b96cb5c..e07b25c056aac9e20ffc17dd4bebcb4e996773f0 100644 (file)
@@ -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;
index 178db1489cfb73d09a31310fdd512aa4031f90fd..b057067079f6470041d0fdf88f0da611ae6f19cd 100644 (file)
@@ -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."),
index a816284cecf3b3a408c0dd8a6398edb2660fd53f..b6111124efc97d16eff9f7d3e5eec0a6684444ef 100644 (file)
@@ -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
index 04749fdb584f832f8aafc3d571d8a43e5ff9001c..8449baa4c58937f5d939827bc88f3f1247c4ce98 100644 (file)
@@ -12,4 +12,4 @@ Description=Journal Log Access Service
 Documentation=man:journalctl(1)
 
 [Service]
-ExecStart=journalctl
+ExecStart=journalctl --user