'video-gid',
'wheel-gid',
'systemd-journal-gid',
+ 'systemd-journal-uid',
'systemd-network-uid',
'systemd-resolve-uid',
'systemd-timesync-uid']
description : 'soft-static allocation for the "wheel" group')
option('systemd-journal-gid', type : 'integer', value : 0,
description : 'soft-static allocation for the systemd-journal group')
+option('systemd-journal-uid', type : 'integer', value : 0,
+ description : 'soft-static allocation for the systemd-journal user')
option('systemd-network-uid', type : 'integer', value : 0,
description : 'soft-static allocation for the systemd-network user')
option('systemd-resolve-uid', type : 'integer', value : 0,
#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"
}
static int add_units(sd_journal *j) {
- _cleanup_strv_free_ char **patterns = NULL;
- bool added = false;
MatchUnitFlag flags = MATCH_UNIT_ALL;
- int r;
assert(j);
- if (strv_isempty(arg_system_units) && strv_isempty(arg_user_units))
- return 0;
-
/* When --directory/-D, --root, --file/-i, or --machine/-M is specified, the opened journal file may
* be external, and the uid of the systemd-coredump user that generates the coredump entries may be
* different from the one in the current host. Let's relax the filter condition in such cases. */
if (arg_directory || arg_root || arg_file_stdin || arg_file || arg_machine)
flags &= ~MATCH_UNIT_COREDUMP_UID;
- STRV_FOREACH(i, arg_system_units) {
- _cleanup_free_ char *u = NULL;
-
- r = unit_name_mangle(*i, UNIT_NAME_MANGLE_GLOB | (arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN), &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;
- char *u;
-
- r = get_possible_units(j, SYSTEM_UNITS_FULL, patterns, &units);
- if (r < 0)
- return r;
-
- 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, arg_user_units) {
- _cleanup_free_ char *u = NULL;
-
- r = unit_name_mangle(*i, UNIT_NAME_MANGLE_GLOB | (arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN), &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;
- char *u;
-
- r = get_possible_units(j, USER_UNITS_FULL, patterns, &units);
- if (r < 0)
- return r;
-
- 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);
+ return journal_add_unit_matches(j, flags, arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN, arg_system_units, arg_user_units);
}
static int add_syslog_identifier(sd_journal *j) {
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;
#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(). */
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);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "sd-journal.h"
+#include "sd-varlink.h"
+
+#include "journal-internal.h"
+#include "journalctl-util.h"
+#include "journalctl-varlink-server.h"
+#include "json-util.h"
+#include "log.h"
+#include "logs-show.h"
+#include "output-mode.h"
+#include "strv.h"
+#include "varlink-util.h"
+
+typedef struct GetEntriesParameters {
+ char **units;
+ char **user_units;
+ const char *namespace;
+ 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);
+}
+
+int vl_method_get_entries(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+
+ static const sd_json_dispatch_field dispatch_table[] = {
+ { "units", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(GetEntriesParameters, units), 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 },
+ { "limit", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(GetEntriesParameters, limit), 0 },
+ {}
+ };
+
+ _cleanup_(get_entries_parameters_done) GetEntriesParameters p = {
+ .priority = -1,
+ };
+ _cleanup_(sd_journal_closep) sd_journal *j = NULL;
+ int r;
+
+ assert(link);
+ assert(FLAGS_SET(flags, SD_VARLINK_METHOD_MORE));
+
+ r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
+ if (r != 0)
+ return r;
+
+ /* 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 log_error_errno(r, "Failed to open journal: %m");
+
+ r = journal_add_unit_matches(j, MATCH_UNIT_ALL, /* mangle_flags= */ 0, p.units, p.user_units);
+ if (r == -ENODATA)
+ return sd_varlink_error(link, SD_VARLINK_ERROR_INVALID_PARAMETER, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add unit matches: %m");
+
+ if (p.priority >= 0) {
+ for (int i = 0; i <= p.priority; i++) {
+ r = journal_add_matchf(j, "PRIORITY=%d", i);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add priority match: %m");
+ }
+
+ r = sd_journal_add_conjunction(j);
+ if (r < 0)
+ return r;
+ }
+
+ /* this simulates "journalctl -n $p.limit" */
+ r = sd_journal_seek_tail(j);
+ if (r < 0)
+ return log_error_errno(r, "Failed to seek to tail: %m");
+
+ /* FIXME: this restriction should be removed eventually */
+ if (p.limit > 10000)
+ return sd_varlink_error_invalid_parameter_name(link, "limit");
+
+ uint64_t n = p.limit == 0 ? 100 : p.limit;
+
+ r = varlink_set_sentinel(link, "io.systemd.JournalAccess.NoEntries");
+ if (r < 0)
+ return r;
+
+ for (uint64_t i = 0; i < n; i++) {
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *entry = NULL;
+
+ r = sd_journal_previous(j);
+ if (r < 0)
+ return log_error_errno(r, "Failed to iterate journal: %m");
+ if (r == 0)
+ break;
+
+ r = journal_entry_to_json(j, OUTPUT_SHOW_ALL, /* output_fields= */ NULL, &entry);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue; /* skip corrupted entry */
+
+ r = sd_varlink_replybo(link, SD_JSON_BUILD_PAIR("entry", SD_JSON_BUILD_VARIANT(entry)));
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-varlink.h"
+
+int vl_method_get_entries(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata);
#include <locale.h>
#include "sd-journal.h"
+#include "sd-varlink.h"
#include "build.h"
#include "dissect-image.h"
#include "journalctl-misc.h"
#include "journalctl-show.h"
#include "journalctl-varlink.h"
+#include "journalctl-varlink-server.h"
#include "log.h"
#include "loop-util.h"
#include "main-func.h"
#include "strv.h"
#include "syslog-util.h"
#include "time-util.h"
+#include "varlink-io.systemd.JournalAccess.h"
+#include "varlink-util.h"
#define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
PatternCompileCase arg_case = PATTERN_COMPILE_CASE_AUTO;
ImagePolicy *arg_image_policy = NULL;
bool arg_synchronize_on_exit = false;
+static bool arg_varlink = false;
STATIC_DESTRUCTOR_REGISTER(arg_cursor, freep);
STATIC_DESTRUCTOR_REGISTER(arg_cursor_file, freep);
return 0;
}
+static int vl_server(void) {
+ _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *varlink_server = NULL;
+ int r;
+
+ r = varlink_server_new(&varlink_server, /* flags= */ 0, /* userdata= */ NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate Varlink server: %m");
+
+ r = sd_varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_JournalAccess);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add Varlink interface: %m");
+
+ r = sd_varlink_server_bind_method(varlink_server, "io.systemd.JournalAccess.GetEntries", vl_method_get_entries);
+ if (r < 0)
+ return log_error_errno(r, "Failed to bind Varlink method: %m");
+
+ r = sd_varlink_server_loop_auto(varlink_server);
+ if (r < 0)
+ return log_error_errno(r, "Failed to run Varlink event loop: %m");
+
+ return 0;
+}
+
static int parse_argv(int argc, char *argv[]) {
enum {
assert(argc >= 0);
assert(argv);
+ r = sd_varlink_invocation(SD_VARLINK_ALLOW_ACCEPT);
+ if (r < 0)
+ 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;
+ return 1;
+ }
+
while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:g:c:S:U:t:T:u:INF:xrM:i:W", options, NULL)) >= 0)
switch (c) {
if (r <= 0)
return r;
+ if (arg_varlink)
+ return vl_server();
+
r = strv_copy_unless_empty(strv_skip(argv, optind), &args);
if (r < 0)
return log_oom();
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#include "sd-event.h"
-
#include "journald-manager.h"
#include "journald-sync.h"
#include "journald-varlink.h"
'journalctl-show.c',
'journalctl-util.c',
'journalctl-varlink.c',
+ 'journalctl-varlink-server.c',
)
if get_option('link-journalctl-shared')
return update_json_data(h, flags, name, eq + 1, size - fieldlen - 1);
}
-static int output_json(
- FILE *f,
- sd_journal *j,
- OutputMode mode,
- unsigned n_columns,
- OutputFlags flags,
- const Set *output_fields,
- const size_t highlight[2],
- dual_timestamp *previous_display_ts, /* unused */
- sd_id128_t *previous_boot_id) { /* unused */
+int journal_entry_to_json(sd_journal *j, OutputFlags flags, const Set *output_fields, sd_json_variant **ret) {
char usecbuf[CONST_MAX(DECIMAL_STR_MAX(usec_t), DECIMAL_STR_MAX(uint64_t))];
_cleanup_(sd_json_variant_unrefp) sd_json_variant *object = NULL;
int r;
assert(j);
+ assert(ret);
(void) sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
if (r < 0)
return log_error_errno(r, "Failed to allocate JSON object: %m");
+ *ret = TAKE_PTR(object);
+ return 1;
+}
+
+static int output_json(
+ FILE *f,
+ sd_journal *j,
+ OutputMode mode,
+ unsigned n_columns,
+ OutputFlags flags,
+ const Set *output_fields,
+ const size_t highlight[2],
+ dual_timestamp *previous_display_ts, /* unused */
+ sd_id128_t *previous_boot_id) { /* unused */
+
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *object = NULL;
+ int r;
+
+ r = journal_entry_to_json(j, flags, output_fields, &object);
+ if (r <= 0)
+ return r;
+
return sd_json_variant_dump(object,
output_mode_to_json_format_flags(mode) |
(FLAGS_SET(flags, OUTPUT_COLOR) ? SD_JSON_FORMAT_COLOR : 0),
size_t l,
OutputFlags flags);
+int journal_entry_to_json(sd_journal *j, OutputFlags flags, const Set *output_fields, sd_json_variant **ret);
+
int discover_next_id(
sd_journal *j,
LogIdType type,
'varlink-io.systemd.Hostname.c',
'varlink-io.systemd.Import.c',
'varlink-io.systemd.Journal.c',
+ 'varlink-io.systemd.JournalAccess.c',
'varlink-io.systemd.Login.c',
'varlink-io.systemd.Machine.c',
'varlink-io.systemd.MachineImage.c',
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "varlink-io.systemd.JournalAccess.h"
+
+static SD_VARLINK_DEFINE_METHOD_FULL(
+ GetEntries,
+ 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("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."),
+ SD_VARLINK_DEFINE_INPUT(namespace, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("Filter output by message priorities or priority ranges (i.e. between 0/'emerg' and 7/'debug')"),
+ SD_VARLINK_DEFINE_INPUT(priority, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("Maximum number of entries to return. Defaults to 100, capped at 10000."),
+ SD_VARLINK_DEFINE_INPUT(limit, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("The journal entry in flat JSON format, matching journalctl --output=json."),
+ SD_VARLINK_DEFINE_OUTPUT(entry, SD_VARLINK_OBJECT, SD_VARLINK_NULLABLE));
+
+static SD_VARLINK_DEFINE_ERROR(NoEntries);
+
+SD_VARLINK_DEFINE_INTERFACE(
+ io_systemd_JournalAccess,
+ "io.systemd.JournalAccess",
+ SD_VARLINK_INTERFACE_COMMENT("Journal log read APIs"),
+ SD_VARLINK_SYMBOL_COMMENT("Retrieve journal log entries, optionally filtered by unit, priority, etc."),
+ &vl_method_GetEntries,
+ SD_VARLINK_SYMBOL_COMMENT("No journal entries matched the specified filters."),
+ &vl_error_NoEntries);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-varlink-idl.h"
+
+extern const sd_varlink_interface vl_interface_io_systemd_JournalAccess;
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
-g systemd-journal {{SYSTEMD_JOURNAL_GID}} -
+g systemd-journal {{SYSTEMD_JOURNAL_GID}} -
+u! systemd-journal {{SYSTEMD_JOURNAL_UID}} "systemd Journal"
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+VARLINK_SOCKET="/run/systemd/io.systemd.JournalAccess"
+
+# ensure the varlink basics work
+varlinkctl list-interfaces "$VARLINK_SOCKET" | grep io.systemd.JournalAccess
+varlinkctl introspect "$VARLINK_SOCKET" | grep "method GetEntries("
+
+# lets start with a basic log entry
+TAG="$(systemd-id128 new)"
+echo "varlink-test-message" | systemd-cat -t "$TAG"
+systemd-cat -t "$TAG" -p warning echo "varlink-test-warning"
+journalctl --sync
+
+# most basic call works
+varlinkctl call --more "$VARLINK_SOCKET" io.systemd.JournalAccess.GetEntries '{}' | jq --seq .
+# validate the JSON has some basic properties (similar to journalctls json output)
+varlinkctl call --more "$VARLINK_SOCKET" io.systemd.JournalAccess.GetEntries '{}' | jq --seq '.entry | {MESSAGE, PRIORITY, _UID}'
+
+# check that default limit works (100), we don't know how many entries we have so we just check
+# bounds
+ENTRIES=$(varlinkctl call --more "$VARLINK_SOCKET" io.systemd.JournalAccess.GetEntries '{}' | wc -l)
+test "$ENTRIES" -gt 0
+test "$ENTRIES" -le 100
+
+# check explicit limit
+ENTRIES=$(varlinkctl call --more "$VARLINK_SOCKET" io.systemd.JournalAccess.GetEntries '{"limit": 3}' | wc -l)
+test "$ENTRIES" -le 3
+
+# check unit filter: use transient units to get deterministic results
+UNIT_NAME_1="test-journalctl-varlink-1-$RANDOM.service"
+systemd-run --unit="$UNIT_NAME_1" --wait bash -c 'echo hello-from-varlink-test-1'
+UNIT_NAME_2="test-journalctl-varlink-2-$RANDOM.service"
+systemd-run --unit="$UNIT_NAME_2" --wait bash -c 'echo hello-from-varlink-test-2'
+journalctl --sync
+
+# single unit filter
+varlinkctl call --more "$VARLINK_SOCKET" io.systemd.JournalAccess.GetEntries "{\"units\": [\"$UNIT_NAME_1\"]}" | grep -q "hello-from-varlink-test-1"
+(! varlinkctl call --more "$VARLINK_SOCKET" io.systemd.JournalAccess.GetEntries "{\"units\": [\"$UNIT_NAME_1\"]}" | grep "hello-from-varlink-test-2")
+# multi unit filter
+varlinkctl call --more "$VARLINK_SOCKET" io.systemd.JournalAccess.GetEntries "{\"units\": [\"$UNIT_NAME_1\", \"$UNIT_NAME_2\"]}" | grep -q "hello-from-varlink-test-1"
+varlinkctl call --more "$VARLINK_SOCKET" io.systemd.JournalAccess.GetEntries "{\"units\": [\"$UNIT_NAME_1\", \"$UNIT_NAME_2\"]}" | grep -q "hello-from-varlink-test-2"
+
+# check priority filter: priority 4 (warning) should include our warning message
+varlinkctl call --more "$VARLINK_SOCKET" io.systemd.JournalAccess.GetEntries '{"priority": 4, "limit": 1000}' | grep -q "varlink-test-warning"
+# check priority filter: priority 3 (error) should NOT include our warning (priority 4)
+(! varlinkctl call --more "$VARLINK_SOCKET" io.systemd.JournalAccess.GetEntries '{"priority": 3, "limit": 1000}' | grep "varlink-test-warning")
+
+# invalid parameter: limit over 10000 should fail
+(! varlinkctl call --more "$VARLINK_SOCKET" io.systemd.JournalAccess.GetEntries '{"limit": 99999}')
+
+# invalid parameter: priority over 7 should fail
+(! varlinkctl call --more "$VARLINK_SOCKET" io.systemd.JournalAccess.GetEntries '{"priority": 99}')
+
+# NoEntries error is raised if there is no result
+(! varlinkctl call --more "$VARLINK_SOCKET" io.systemd.JournalAccess.GetEntries '{"units": ["nonexistent-unit-that-should-have-no-entries.service"]}' 2>&1 | grep io.systemd.JournalAccess.NoEntries )
'file' : 'systemd-journal-upload.service.in',
'conditions' : ['ENABLE_REMOTE', 'HAVE_LIBCURL'],
},
+ { 'file' : 'systemd-journalctl@.service' },
+ {
+ 'file' : 'systemd-journalctl.socket',
+ 'symlinks' : ['sockets.target.wants/'],
+ },
{ 'file' : 'systemd-journald-audit.socket' },
{
'file' : 'systemd-journald-dev-log.socket',
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Journal Log Access Socket
+Documentation=man:journalctl(1)
+DefaultDependencies=no
+Before=sockets.target
+
+[Socket]
+ListenStream=/run/systemd/io.systemd.JournalAccess
+Symlinks=/run/varlink/registry/io.systemd.JournalAccess
+FileDescriptorName=varlink
+SocketGroup=systemd-journal
+SocketMode=0660
+Accept=yes
+MaxConnectionsPerSource=16
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Journal Log Access Service
+Documentation=man:journalctl(1)
+DefaultDependencies=no
+Conflicts=shutdown.target
+Before=shutdown.target
+
+[Service]
+ExecStart=journalctl
+User=systemd-journal
},
{ 'file' : 'systemd-ask-password@.service' },
{ 'file' : 'systemd-exit.service' },
+ { 'file' : 'systemd-journalctl@.service' },
+ {
+ 'file' : 'systemd-journalctl.socket',
+ 'symlinks' : ['sockets.target.wants/'],
+ },
{ 'file' : 'systemd-tmpfiles-clean.service' },
{ 'file' : 'systemd-tmpfiles-clean.timer' },
{ 'file' : 'systemd-tmpfiles-setup.service' },
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Journal Log Access Socket
+Documentation=man:journalctl(1)
+
+[Socket]
+ListenStream=%t/systemd/io.systemd.JournalAccess
+Symlinks=%t/varlink/registry/io.systemd.JournalAccess
+FileDescriptorName=varlink
+SocketMode=0600
+Accept=yes
+MaxConnectionsPerSource=16
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Journal Log Access Service
+Documentation=man:journalctl(1)
+
+[Service]
+ExecStart=journalctl