journalctl: add new varlink read service to get entries

We already have some varlink support for the journal to perform
some actions like `Rotate`. It would be nice to be able to query
the journal via varlink too so this commit adds a new varlinkctl
based journal service that exposes a single GetEntries() call
to retrieve journal entries. Basic filtering is supported and
we can expand the API as needed.

This is a separate `io.systemd.JournalControl` [1] service from the
existing `io.systemd.Journald` to decouple read and write (thanks
to Lennart for suggesting this).

This also extracts some shared helper so that we do not duplicate
code when generating the json or when adding the filters.

[1] The name mirrors the bootctl->io.systemd.BootControl naming.
This commit is contained in:
Michael Vogt
2026-02-21 13:02:38 +01:00
parent bd7ba0a645
commit a109189fab
23 changed files with 480 additions and 110 deletions
+1
View File
@@ -895,6 +895,7 @@ foreach option : ['adm-gid',
'video-gid',
'wheel-gid',
'systemd-journal-gid',
'systemd-journal-uid',
'systemd-network-uid',
'systemd-resolve-uid',
'systemd-timesync-uid']
+2
View File
@@ -328,6 +328,8 @@ option('wheel-gid', type : 'integer', value : 0,
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,
+1 -97
View File
@@ -9,7 +9,6 @@
#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"
@@ -73,112 +72,17 @@ static int add_dmesg(sd_journal *j) {
}
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) {
+102
View File
@@ -177,6 +177,108 @@ 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;
+2
View File
@@ -3,6 +3,7 @@
#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(). */
@@ -31,5 +32,6 @@ 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);
+114
View File
@@ -0,0 +1,114 @@
/* 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;
}
+6
View File
@@ -0,0 +1,6 @@
/* 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);
+40
View File
@@ -4,6 +4,7 @@
#include <locale.h>
#include "sd-journal.h"
#include "sd-varlink.h"
#include "build.h"
#include "dissect-image.h"
@@ -17,6 +18,7 @@
#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"
@@ -35,6 +37,8 @@
#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)
@@ -107,6 +111,7 @@ 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;
STATIC_DESTRUCTOR_REGISTER(arg_cursor, freep);
STATIC_DESTRUCTOR_REGISTER(arg_cursor_file, freep);
@@ -324,6 +329,29 @@ static int help(void) {
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 {
@@ -453,6 +481,15 @@ static int parse_argv(int argc, char *argv[]) {
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) {
@@ -1094,6 +1131,9 @@ static int run(int argc, char *argv[]) {
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();
-2
View File
@@ -1,7 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "sd-event.h"
#include "journald-manager.h"
#include "journald-sync.h"
#include "journald-varlink.h"
+1
View File
@@ -41,6 +41,7 @@ journalctl_sources = files(
'journalctl-show.c',
'journalctl-util.c',
'journalctl-varlink.c',
'journalctl-varlink-server.c',
)
if get_option('link-journalctl-shared')
+24 -10
View File
@@ -1193,16 +1193,7 @@ static int update_json_data_split(
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;
@@ -1217,6 +1208,7 @@ static int output_json(
int r;
assert(j);
assert(ret);
(void) sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
@@ -1323,6 +1315,28 @@ static int output_json(
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),
+2
View File
@@ -81,6 +81,8 @@ void json_escape(
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,
+1
View File
@@ -205,6 +205,7 @@ shared_sources = files(
'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',
@@ -0,0 +1,30 @@
/* 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);
@@ -0,0 +1,6 @@
/* 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;
+2 -1
View File
@@ -5,4 +5,5 @@
# 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"
+59
View File
@@ -0,0 +1,59 @@
#!/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 )
+5
View File
@@ -439,6 +439,11 @@ units = [
'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',
+23
View File
@@ -0,0 +1,23 @@
# 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
+19
View File
@@ -0,0 +1,19 @@
# 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
+5
View File
@@ -56,6 +56,11 @@ units = [
},
{ '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' },
+20
View File
@@ -0,0 +1,20 @@
# 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
+15
View File
@@ -0,0 +1,15 @@
# 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