mirror of
https://github.com/systemd/systemd
synced 2026-02-25 16:54:44 +01:00
Compare commits
4 Commits
bd7ba0a645
...
3a7486c9fc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a7486c9fc | ||
|
|
05f5156ad1 | ||
|
|
3660f0e7da | ||
|
|
a109189fab |
1
TODO
1
TODO
@ -968,7 +968,6 @@ Features:
|
||||
* Varlinkification of the following command line tools, to open them up to
|
||||
other programs via IPC:
|
||||
- bootctl
|
||||
- journalctl (allowing journal read access via IPC)
|
||||
- coredumpcl
|
||||
- systemd-bless-boot
|
||||
- systemd-measure
|
||||
|
||||
@ -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']
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -34,6 +34,7 @@
|
||||
#include "strv.h"
|
||||
#include "transaction.h" /* IWYU pragma: keep */
|
||||
#include "unit-name.h"
|
||||
#include "user-util.h"
|
||||
#include "web-util.h"
|
||||
|
||||
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_collect_mode, collect_mode, CollectMode);
|
||||
@ -1649,6 +1650,9 @@ int bus_unit_method_attach_processes(sd_bus_message *message, void *userdata, sd
|
||||
if (r == 0)
|
||||
return sd_bus_error_setf(reterr_error, SD_BUS_ERROR_ACCESS_DENIED, "Process " PID_FMT " not owned by client's UID. Refusing.", pidref->pid);
|
||||
|
||||
if (!uid_is_valid(u->ref_uid)) /* process_is_owned_by_uid() requires a valid uid */
|
||||
return sd_bus_error_setf(reterr_error, SD_BUS_ERROR_ACCESS_DENIED, "Unit does not have a valid ref_uid, refusing to attach process " PID_FMT ".", pidref->pid);
|
||||
|
||||
r = process_is_owned_by_uid(pidref, u->ref_uid);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(reterr_error, r, "Failed to check if process " PID_FMT " is owned by target unit's UID: %m", pidref->pid);
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
src/journal/journalctl-varlink-server.c
Normal file
114
src/journal/journalctl-varlink-server.c
Normal 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
src/journal/journalctl-varlink-server.h
Normal file
6
src/journal/journalctl-varlink-server.h
Normal 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);
|
||||
@ -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();
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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')
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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',
|
||||
|
||||
30
src/shared/varlink-io.systemd.JournalAccess.c
Normal file
30
src/shared/varlink-io.systemd.JournalAccess.c
Normal file
@ -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);
|
||||
6
src/shared/varlink-io.systemd.JournalAccess.h
Normal file
6
src/shared/varlink-io.systemd.JournalAccess.h
Normal file
@ -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;
|
||||
@ -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
test/units/TEST-04-JOURNAL.journalctl-varlink.sh
Executable file
59
test/units/TEST-04-JOURNAL.journalctl-varlink.sh
Executable 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 )
|
||||
34
test/units/TEST-07-PID1.attach_processes.sh
Executable file
34
test/units/TEST-07-PID1.attach_processes.sh
Executable file
@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -eux
|
||||
|
||||
# Assert when calling AttachProcesses on a unit without ref_uid set
|
||||
|
||||
at_exit() {
|
||||
set +e
|
||||
|
||||
systemctl stop attach_and_barf.service
|
||||
rm -f /run/systemd/system/attach_and_barf.service
|
||||
systemctl daemon-reload
|
||||
}
|
||||
|
||||
trap at_exit EXIT
|
||||
|
||||
mkdir -p /run/systemd/system
|
||||
|
||||
cat >/run/systemd/system/attach_and_barf.service <<EOF
|
||||
[Service]
|
||||
Type=simple
|
||||
Delegate=yes
|
||||
ExecStart=sleep infinity
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl start attach_and_barf.service
|
||||
run0 -u testuser \
|
||||
busctl --system call \
|
||||
org.freedesktop.systemd1 \
|
||||
/org/freedesktop/systemd1 \
|
||||
org.freedesktop.systemd1.Manager \
|
||||
AttachProcessesToUnit \
|
||||
"ssau" "attach_and_barf.service" "" 1 0 |& grep -F "Access denied" &>/dev/null
|
||||
@ -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
units/systemd-journalctl.socket
Normal file
23
units/systemd-journalctl.socket
Normal 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
units/systemd-journalctl@.service
Normal file
19
units/systemd-journalctl@.service
Normal 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
|
||||
@ -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
units/user/systemd-journalctl.socket
Normal file
20
units/user/systemd-journalctl.socket
Normal 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
units/user/systemd-journalctl@.service
Normal file
15
units/user/systemd-journalctl@.service
Normal 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
|
||||
Loading…
x
Reference in New Issue
Block a user