Compare commits

...

9 Commits

Author SHA1 Message Date
Daan De Meyer 252b0f4dee
Merge 45ce3cf8e7 into 5261c521e3 2024-11-08 14:03:31 +01:00
Yu Watanabe 5261c521e3 mount-util: make path_get_mount_info() work arbitrary inode
Follow-up for d49d95df0a.
Replaces 9a032ec55a.
Fixes #35075.
2024-11-08 13:25:17 +01:00
Franck Bui 514d9e1665 test: install integration-test-setup.sh in testdata/
integration-test-setup.sh is an auxiliary script that tests rely on at
runtime. As such, install the script in testdata/.

Follow-up for af153e36ae.
2024-11-08 12:37:40 +01:00
Lennart Poettering b480a4c15e update TODO 2024-11-08 10:10:11 +01:00
Lennart Poettering af3baf174a fs-util: add comment about XO_NOCOW 2024-11-08 09:21:25 +01:00
Ryan Wilson d8091e1281 Fix PrivatePIDs=yes integration test for kernels with no /proc/scsi 2024-11-08 13:38:35 +09:00
Daan De Meyer 45ce3cf8e7 WIP 2024-11-05 20:24:39 +01:00
Daan De Meyer ad4ad82924 core: Expose Job object information via varlink
Let's extend pid1's varlink interface and add a DescribeJobs method to
get the job information as a streaming list of JSON objects.
2024-11-05 20:24:39 +01:00
Daan De Meyer 8c1bff449c core: Expose Manager object information via varlink
Let's extend pid1's varlink interface and add a Describe method to
get the global Manager object information as a JSON object.

Because the new varlink interface should be available on both the
user managers and the system manager, we also make the necessary
changes to expose a varlink server on user managers.
2024-11-05 20:24:39 +01:00
26 changed files with 3799 additions and 116 deletions

25
TODO
View File

@ -129,6 +129,10 @@ Deprecations and removals:
Features:
* format-table: introduce new cell type for strings with ansi sequences in
them. display them in regular output mode (via strip_tab_ansi()), but
suppress them in json mode.
* machined: when registering a machine, also take a relative cgroup path,
relative to the machine's unit. This is useful when registering unpriv
machines, as they might sit down the cgroup tree, below a cgroup delegation
@ -217,12 +221,8 @@ Features:
services where mount propagation from the root fs is off, an still have
confext/sysext propagated in.
* support F_DUDFD_QUERY for comparing fds in same_fd (requires kernel 6.10)
* generic interface for varlink for setting log level and stuff that all our daemons can implement
* use pty ioctl to get peer wherever possible (TIOCGPTPEER)
* maybe teach repart.d/ dropins a new setting MakeMountNodes= or so, which is
just like MakeDirectories=, but uses an access mode of 0000 and sets the +i
chattr bit. This is useful as protection against early uses of /var/ or /tmp/
@ -253,8 +253,6 @@ Features:
* initrd: when transitioning from initrd to host, validate that
/lib/modules/`uname -r` exists, refuse otherwise
* tmpfiles: add "owning" flag for lines that limits effect of --purge
* signed bpf loading: to address need for signature verification for bpf
programs when they are loaded, and given the bpf folks don't think this is
realistic in kernel space, maybe add small daemon that facilitates this
@ -458,9 +456,6 @@ Features:
* introduce mntid_t, and make it 64bit, as apparently the kernel switched to
64bit mount ids
* use udev rule networkd ownership property to take ownership of network
interfaces nspawn creates
* mountfsd/nsresourced
- userdb: maybe allow callers to map one uid to their own uid
- bpflsm: allow writes if resulting UID on disk would be userns' owner UID
@ -647,6 +642,7 @@ Features:
- openpt_allocate_in_namespace()
- unit_attach_pid_to_cgroup_via_bus()
- cg_attach() requires new kernel feature
- journald's process cache
* ddi must be listed as block device fstype
@ -1470,9 +1466,6 @@ Features:
* in sd-id128: also parse UUIDs in RFC4122 URN syntax (i.e. chop off urn:uuid: prefix)
* DynamicUser= + StateDirectory= → use uid mapping mounts, too, in order to
make dirs appear under right UID.
* systemd-sysext: optionally, run it in initrd already, before transitioning
into host, to open up possibility for services shipped like that.
@ -1644,14 +1637,6 @@ Features:
* maybe add kernel cmdline params: to force random seed crediting
* introduce a new per-process uuid, similar to the boot id, the machine id, the
invocation id, that is derived from process creds, specifically a hashed
combination of AT_RANDOM + getpid() + the starttime from
/proc/self/status. Then add these ids implicitly when logging. Deriving this
uuid from these three things has the benefit that it can be derived easily
from /proc/$PID/ in a stable, and unique way that changes on both fork() and
exec().
* let's not GC a unit while its ratelimits are still pending
* when killing due to service watchdog timeout maybe detect whether target

View File

@ -1,6 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "macro.h"
#include "version.h"
extern const char* const systemd_features;
#define PROJECT_VERSION_STR STRINGIFY(PROJECT_VERSION)
int version(void);

View File

@ -1131,6 +1131,8 @@ int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_
* If O_CREAT is used with XO_LABEL, any created file will be immediately relabelled.
*
* If the path is specified NULL or empty, behaves like fd_reopen().
*
* If XO_NOCOW is specified will turn on the NOCOW btrfs flag on the file, if available.
*/
if (isempty(path)) {

View File

@ -3,13 +3,18 @@
#include "sd-varlink.h"
#include "core-varlink.h"
#include "format-util.h"
#include "json-util.h"
#include "manager-json.h"
#include "mkdir-label.h"
#include "strv.h"
#include "unit-json.h"
#include "user-util.h"
#include "varlink-internal.h"
#include "varlink-serialize.h"
#include "varlink-io.systemd.UserDatabase.h"
#include "varlink-io.systemd.ManagedOOM.h"
#include "varlink-io.systemd.Manager.h"
#include "varlink-util.h"
typedef struct LookupParameters {
@ -22,6 +27,11 @@ typedef struct LookupParameters {
const char *service;
} LookupParameters;
typedef struct DescribeUnitsParameters {
char **states;
char **patterns;
} DescribeUnitsParameters;
static const char* const managed_oom_mode_properties[] = {
"ManagedOOMSwap",
"ManagedOOMMemoryPressure",
@ -560,6 +570,139 @@ static int vl_method_get_memberships(sd_varlink *link, sd_json_variant *paramete
return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
}
static int vl_method_describe(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
Manager *manager = ASSERT_PTR(userdata);
int r;
assert(parameters);
if (sd_json_variant_elements(parameters) > 0)
return sd_varlink_error_invalid_parameter(link, parameters);
r = manager_build_json(manager, &v);
if (r < 0)
return log_error_errno(r, "Failed to build manager JSON data: %m");
return sd_varlink_reply(link, v);
}
static int vl_method_list_units(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
static const sd_json_dispatch_field dispatch_table[] = {
{ "states", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(DescribeUnitsParameters, states), SD_JSON_STRICT },
{ "patterns", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_const_string, offsetof(DescribeUnitsParameters, patterns), SD_JSON_STRICT },
{},
};
Manager *m = ASSERT_PTR(userdata);
DescribeUnitsParameters p = {};
const char *k;
Unit *u;
int r;
assert(parameters);
if (sd_json_variant_elements(parameters) > 0)
return sd_varlink_error_invalid_parameter(link, parameters);
if (!FLAGS_SET(flags, SD_VARLINK_METHOD_MORE))
return sd_varlink_error(link, SD_VARLINK_ERROR_EXPECTED_MORE, NULL);
r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
if (r != 0)
return r;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *previous = NULL;
HASHMAP_FOREACH_KEY(u, k, m->units) {
if (k != u->id)
continue;
if (unit_is_filtered(u, p.states, p.patterns))
continue;
if (previous) {
r = sd_varlink_notifybo(link, SD_JSON_BUILD_PAIR_VARIANT("unit", previous));
if (r < 0)
return r;
previous = sd_json_variant_unref(previous);
}
r = unit_build_json(u, &previous);
if (r < 0)
return log_error_errno(r, "Failed to build unit JSON data: %m");
}
if (!previous)
return sd_varlink_error(link, "io.systemd.Manager.NoSuchUnit", NULL);
return sd_varlink_replybo(link, SD_JSON_BUILD_PAIR_VARIANT("unit", previous));
}
static int vl_method_list_jobs(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
struct p {
uint32_t id;
} p = {
.id = 0,
};
static const sd_json_dispatch_field dispatch_table[] = {
{ "id", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint32, offsetof(struct p, id), 0 },
{},
};
Manager *m = ASSERT_PTR(userdata);
Job *j;
int r;
assert(link);
assert(parameters);
r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
if (r != 0)
return r;
if (p.id > 0) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
j = hashmap_get(m->jobs, UINT_TO_PTR(p.id));
if (!j)
return sd_varlink_error(link, "io.systemd.Manager.NoSuchJob", NULL);
r = job_build_json(j, &v);
if (r < 0)
return log_error_errno(r, "Failed to build job JSON data: %m");
return sd_varlink_reply(link, v);
}
if (!FLAGS_SET(flags, SD_VARLINK_METHOD_MORE))
return sd_varlink_error(link, SD_VARLINK_ERROR_EXPECTED_MORE, NULL);
_cleanup_(sd_json_variant_unrefp) sd_json_variant *previous = NULL;
HASHMAP_FOREACH(j, m->jobs) {
if (previous) {
r = sd_varlink_notify(link, previous);
if (r < 0)
return r;
previous = sd_json_variant_unref(previous);
}
r = job_build_json(j, &previous);
if (r < 0)
return log_error_errno(r, "Failed to build job JSON data: %m");
}
if (!previous)
return sd_varlink_error(link, "io.systemd.Manager.NoSuchJob", NULL);
return sd_varlink_reply(link, previous);
}
static void vl_disconnect(sd_varlink_server *s, sd_varlink *link, void *userdata) {
Manager *m = ASSERT_PTR(userdata);
@ -579,15 +722,29 @@ int manager_setup_varlink_server(Manager *m) {
if (m->varlink_server)
return 0;
if (!MANAGER_IS_SYSTEM(m))
return -EINVAL;
sd_varlink_server_flags_t flags = SD_VARLINK_SERVER_INHERIT_USERDATA;
if (MANAGER_IS_SYSTEM(m))
flags |= SD_VARLINK_SERVER_ACCOUNT_UID;
r = sd_varlink_server_new(&s, SD_VARLINK_SERVER_ACCOUNT_UID|SD_VARLINK_SERVER_INHERIT_USERDATA);
r = sd_varlink_server_new(&s, flags);
if (r < 0)
return log_debug_errno(r, "Failed to allocate varlink server object: %m");
sd_varlink_server_set_userdata(s, m);
r = sd_varlink_server_add_interface_many(s, &vl_interface_io_systemd_Manager);
if (r < 0)
return log_debug_errno(r, "Failed to add interfaces to varlink server: %m");
r = sd_varlink_server_bind_method_many(
s,
"io.systemd.Manager.Describe", vl_method_describe,
"io.systemd.Manager.ListUnits", vl_method_list_units,
"io.systemd.Manager.ListJobs", vl_method_list_jobs);
if (r < 0)
return log_debug_errno(r, "Failed to register varlink methods: %m");
if (MANAGER_IS_SYSTEM(m)) {
r = sd_varlink_server_add_interface_many(
s,
&vl_interface_io_systemd_UserDatabase,
@ -607,6 +764,7 @@ int manager_setup_varlink_server(Manager *m) {
r = sd_varlink_server_bind_disconnect(s, vl_disconnect);
if (r < 0)
return log_debug_errno(r, "Failed to register varlink disconnect handler: %m");
}
r = sd_varlink_server_attach_event(s, m->event, EVENT_PRIORITY_IPC);
if (r < 0)
@ -632,20 +790,13 @@ static int manager_varlink_init_system(Manager *m) {
if (!MANAGER_IS_TEST_RUN(m)) {
(void) mkdir_p_label("/run/systemd/userdb", 0755);
FOREACH_STRING(address, "/run/systemd/userdb/io.systemd.DynamicUser", VARLINK_ADDR_PATH_MANAGED_OOM_SYSTEM) {
if (!fresh) {
FOREACH_STRING(address,
"/run/systemd/userdb/io.systemd.DynamicUser",
VARLINK_ADDR_PATH_MANAGED_OOM_SYSTEM,
"/run/systemd/io.systemd.Manager") {
/* We might have got sockets through deserialization. Do not bind to them twice. */
bool found = false;
LIST_FOREACH(sockets, ss, m->varlink_server->sockets)
if (path_equal(ss->address, address)) {
found = true;
break;
}
if (found)
if (!fresh && varlink_server_contains_socket(m->varlink_server, address))
continue;
}
r = sd_varlink_server_listen_address(m->varlink_server, address, 0666);
if (r < 0)
@ -657,6 +808,9 @@ static int manager_varlink_init_system(Manager *m) {
}
static int manager_varlink_init_user(Manager *m) {
_cleanup_free_ char *address = NULL;
int r;
assert(m);
if (!MANAGER_IS_USER(m))
@ -665,6 +819,22 @@ static int manager_varlink_init_user(Manager *m) {
if (MANAGER_IS_TEST_RUN(m))
return 0;
r = manager_setup_varlink_server(m);
if (r < 0)
return log_error_errno(r, "Failed to set up varlink server: %m");
bool fresh = r > 0;
address = path_join(m->prefix[EXEC_DIRECTORY_RUNTIME], "systemd/io.systemd.Manager");
if (!address)
return -ENOMEM;
/* We might have got sockets through deserialization. Do not bind to them twice. */
if (fresh || !varlink_server_contains_socket(m->varlink_server, address)) {
r = sd_varlink_server_listen_address(m->varlink_server, address, 0666);
if (r < 0)
return log_error_errno(r, "Failed to bind to varlink socket '%s': %m", address);
}
return manager_varlink_managed_oom_connect(m);
}

View File

@ -14,6 +14,7 @@
#include "escape.h"
#include "fileio.h"
#include "job.h"
#include "json-util.h"
#include "log.h"
#include "macro.h"
#include "parse-util.h"
@ -1347,6 +1348,39 @@ int job_deserialize(Job *j, FILE *f) {
return 0;
}
int activation_details_build_json(sd_json_variant **ret, const char *name, void *userdata) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
_cleanup_strv_free_ char **pairs = NULL;
ActivationDetails *activation_details = userdata;
int r;
assert(ret);
r = activation_details_append_pair(activation_details, &pairs);
if (r < 0)
return r;
STRV_FOREACH_PAIR(key, value, pairs) {
r = sd_json_variant_set_field_string(&v, *key, *value);
if (r < 0)
return r;
}
*ret = TAKE_PTR(v);
return 0;
}
int job_build_json(Job *job, sd_json_variant **ret) {
assert(job);
return sd_json_buildo(ret,
SD_JSON_BUILD_PAIR_UNSIGNED("id", job->id),
SD_JSON_BUILD_PAIR_STRING("unit", job->unit->id),
SD_JSON_BUILD_PAIR_STRING("jobType", job_type_to_string(job->type)),
SD_JSON_BUILD_PAIR_STRING("state", job_state_to_string(job->state)),
JSON_BUILD_PAIR_CALLBACK_NON_NULL("activationDetails", activation_details_build_json, job->activation_details));
}
int job_coldplug(Job *j) {
int r;
usec_t timeout_time = USEC_INFINITY;

View File

@ -5,6 +5,7 @@
#include "sd-bus.h"
#include "sd-event.h"
#include "sd-json.h"
#include "list.h"
#include "unit-dependency-atom.h"
@ -185,6 +186,8 @@ void job_uninstall(Job *j);
void job_dump(Job *j, FILE *f, const char *prefix);
int job_serialize(Job *j, FILE *f);
int job_deserialize(Job *j, FILE *f);
int activation_details_build_json(sd_json_variant **ret, const char *name, void *userdata);
int job_build_json(Job *job, sd_json_variant **ret);
int job_coldplug(Job *j);
JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool conflicts);

181
src/core/manager-json.c Normal file
View File

@ -0,0 +1,181 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <sys/prctl.h>
#include "build.h"
#include "confidential-virt.h"
#include "json-util.h"
#include "manager-json.h"
#include "manager.h"
#include "rlimit-util.h"
#include "syslog-util.h"
#include "taint.h"
#include "version.h"
#include "virt.h"
#include "watchdog.h"
int rlimit_build_json(sd_json_variant **ret, const char *name, void *userdata) {
struct rlimit *rl = userdata, buf = {};
assert(name);
assert(ret);
if (rl)
buf = *rl;
else {
const char *p;
int z;
/* Skip over any prefix, such as "Default" */
assert_se(p = strstrafter(name, "Limit"));
z = rlimit_from_string(p);
assert(z >= 0);
(void) getrlimit(z, &buf);
}
if (buf.rlim_cur == RLIM_INFINITY && buf.rlim_max == RLIM_INFINITY)
return 0;
/* rlim_t might have different sizes, let's map RLIMIT_INFINITY to UINT64_MAX, so that it is the same
* on all archs */
return sd_json_buildo(ret,
JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("soft", buf.rlim_cur, RLIM_INFINITY),
JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("hard", buf.rlim_max, RLIM_INFINITY));
}
static int manager_context_build_json(sd_json_variant **ret, const char *name, void *userdata) {
Manager *m = ASSERT_PTR(userdata);
return sd_json_buildo(ASSERT_PTR(ret),
SD_JSON_BUILD_PAIR_STRING("Version", GIT_VERSION),
SD_JSON_BUILD_PAIR_STRING("Features", systemd_features),
SD_JSON_BUILD_PAIR_BOOLEAN("ShowStatus", manager_get_show_status_on(m)),
SD_JSON_BUILD_PAIR_STRV("UnitPath", m->lookup_paths.search_path),
SD_JSON_BUILD_PAIR_INTEGER("LogLevel", m->log_level_overridden ? m->original_log_level : log_get_max_level()),
SD_JSON_BUILD_PAIR_STRING("LogTarget", log_target_to_string(m->log_target_overridden ? m->original_log_target : log_get_target())),
JSON_BUILD_PAIR_STRV_NON_EMPTY("Environment", m->transient_environment),
SD_JSON_BUILD_PAIR_STRING("DefaultStandardOutput", exec_output_to_string(m->defaults.std_output)),
SD_JSON_BUILD_PAIR_STRING("DefaultStandardError", exec_output_to_string(m->defaults.std_error)),
JSON_BUILD_PAIR_FINITE_USEC("RuntimeWatchdogUSec", manager_get_watchdog(m, WATCHDOG_RUNTIME)),
JSON_BUILD_PAIR_FINITE_USEC("RuntimeWatchdogPreUSec", manager_get_watchdog(m, WATCHDOG_PRETIMEOUT)),
JSON_BUILD_PAIR_STRING_NON_EMPTY("RuntimeWatchdogPreGovernor", m->watchdog_pretimeout_governor),
JSON_BUILD_PAIR_FINITE_USEC("RebootWatchdogUSec", manager_get_watchdog(m, WATCHDOG_REBOOT)),
JSON_BUILD_PAIR_FINITE_USEC("KExecWatchdogUSec", manager_get_watchdog(m, WATCHDOG_KEXEC)),
SD_JSON_BUILD_PAIR_BOOLEAN("ServiceWatchdogs", m->service_watchdogs),
JSON_BUILD_PAIR_FINITE_USEC("DefaultTimerAccuracyUSec", m->defaults.timer_accuracy_usec),
JSON_BUILD_PAIR_FINITE_USEC("DefaultTimeoutStartUSec", m->defaults.timeout_start_usec),
JSON_BUILD_PAIR_FINITE_USEC("DefaultTimeoutStopUSec", m->defaults.timeout_stop_usec),
JSON_BUILD_PAIR_FINITE_USEC("DefaultTimeoutAbortUSec", manager_default_timeout_abort_usec(m)),
JSON_BUILD_PAIR_FINITE_USEC("DefaultDeviceTimeoutUSec", m->defaults.device_timeout_usec),
JSON_BUILD_PAIR_FINITE_USEC("DefaultRestartUSec", m->defaults.restart_usec),
JSON_BUILD_PAIR_RATELIMIT("DefaultStartLimit", &m->defaults.start_limit),
SD_JSON_BUILD_PAIR_BOOLEAN("DefaultCPUAccounting", m->defaults.cpu_accounting),
SD_JSON_BUILD_PAIR_BOOLEAN("DefaultBlockIOAccounting", m->defaults.blockio_accounting),
SD_JSON_BUILD_PAIR_BOOLEAN("DefaultIOAccounting", m->defaults.io_accounting),
SD_JSON_BUILD_PAIR_BOOLEAN("DefaultIPAccounting", m->defaults.ip_accounting),
SD_JSON_BUILD_PAIR_BOOLEAN("DefaultMemoryAccounting", m->defaults.memory_accounting),
SD_JSON_BUILD_PAIR_BOOLEAN("DefaultTasksAccounting", m->defaults.tasks_accounting),
JSON_BUILD_PAIR_CALLBACK_NON_NULL("DefaultLimitCPU", rlimit_build_json, m->defaults.rlimit[RLIMIT_CPU]),
JSON_BUILD_PAIR_CALLBACK_NON_NULL("DefaultLimitFSIZE", rlimit_build_json, m->defaults.rlimit[RLIMIT_FSIZE]),
JSON_BUILD_PAIR_CALLBACK_NON_NULL("DefaultLimitDATA", rlimit_build_json, m->defaults.rlimit[RLIMIT_DATA]),
JSON_BUILD_PAIR_CALLBACK_NON_NULL("DefaultLimitSTACK", rlimit_build_json, m->defaults.rlimit[RLIMIT_STACK]),
JSON_BUILD_PAIR_CALLBACK_NON_NULL("DefaultLimitCORE", rlimit_build_json, m->defaults.rlimit[RLIMIT_CORE]),
JSON_BUILD_PAIR_CALLBACK_NON_NULL("DefaultLimitRSS", rlimit_build_json, m->defaults.rlimit[RLIMIT_RSS]),
JSON_BUILD_PAIR_CALLBACK_NON_NULL("DefaultLimitNOFILE", rlimit_build_json, m->defaults.rlimit[RLIMIT_NOFILE]),
JSON_BUILD_PAIR_CALLBACK_NON_NULL("DefaultLimitAS", rlimit_build_json, m->defaults.rlimit[RLIMIT_AS]),
JSON_BUILD_PAIR_CALLBACK_NON_NULL("DefaultLimitNPROC", rlimit_build_json, m->defaults.rlimit[RLIMIT_NPROC]),
JSON_BUILD_PAIR_CALLBACK_NON_NULL("DefaultLimitMEMLOCK", rlimit_build_json, m->defaults.rlimit[RLIMIT_MEMLOCK]),
JSON_BUILD_PAIR_CALLBACK_NON_NULL("DefaultLimitLOCKS", rlimit_build_json, m->defaults.rlimit[RLIMIT_LOCKS]),
JSON_BUILD_PAIR_CALLBACK_NON_NULL("DefaultLimitSIGPENDING", rlimit_build_json, m->defaults.rlimit[RLIMIT_SIGPENDING]),
JSON_BUILD_PAIR_CALLBACK_NON_NULL("DefaultLimitMSGQUEUE", rlimit_build_json, m->defaults.rlimit[RLIMIT_MSGQUEUE]),
JSON_BUILD_PAIR_CALLBACK_NON_NULL("DefaultLimitNICE", rlimit_build_json, m->defaults.rlimit[RLIMIT_NICE]),
JSON_BUILD_PAIR_CALLBACK_NON_NULL("DefaultLimitRTPRIO", rlimit_build_json, m->defaults.rlimit[RLIMIT_RTPRIO]),
JSON_BUILD_PAIR_CALLBACK_NON_NULL("DefaultLimitRTTIME", rlimit_build_json, m->defaults.rlimit[RLIMIT_RTTIME]),
SD_JSON_BUILD_PAIR_UNSIGNED("DefaultTasksMax", cgroup_tasks_max_resolve(&m->defaults.tasks_max)),
JSON_BUILD_PAIR_FINITE_USEC("DefaultMemoryPressureThresholdUSec", m->defaults.memory_pressure_threshold_usec),
SD_JSON_BUILD_PAIR_STRING("DefaultMemoryPressureWatch", cgroup_pressure_watch_to_string(m->defaults.memory_pressure_watch)),
JSON_BUILD_PAIR_FINITE_USEC("TimerSlackNSec", (uint64_t) prctl(PR_GET_TIMERSLACK)),
SD_JSON_BUILD_PAIR_STRING("DefaultOOMPolicy", oom_policy_to_string(m->defaults.oom_policy)),
SD_JSON_BUILD_PAIR_INTEGER("DefaultOOMScoreAdjust", m->defaults.oom_score_adjust),
SD_JSON_BUILD_PAIR_STRING("CtrlAltDelBurstAction", emergency_action_to_string(m->cad_burst_action)));
}
static int manager_environment_build_json(sd_json_variant **ret, const char *name, void *userdata) {
_cleanup_strv_free_ char **l = NULL;
Manager *m = ASSERT_PTR(userdata);
int r;
assert(ret);
r = manager_get_effective_environment(m, &l);
if (r < 0)
return r;
if (strv_isempty(l))
return 0;
return sd_json_variant_new_array_strv(ret, l);
}
static int manager_runtime_build_json(sd_json_variant **ret, const char *name, void *userdata) {
Manager *m = ASSERT_PTR(userdata);
dual_timestamp watchdog_last_ping = {
.monotonic = watchdog_get_last_ping(CLOCK_MONOTONIC),
.realtime = watchdog_get_last_ping(CLOCK_REALTIME),
};
_cleanup_strv_free_ char **taints = NULL;
taints = taint_strv();
if (!taints)
return -ENOMEM;
return sd_json_buildo(ASSERT_PTR(ret),
SD_JSON_BUILD_PAIR_STRING("Architecture", architecture_to_string(uname_architecture())),
SD_JSON_BUILD_PAIR_STRING("Virtualization", virtualization_to_string(detect_virtualization())),
SD_JSON_BUILD_PAIR_STRING("ConfidentialVirtualization", confidential_virtualization_to_string(detect_confidential_virtualization())),
SD_JSON_BUILD_PAIR_STRV("Taints", taints),
JSON_BUILD_PAIR_STRING_NON_EMPTY("ConfirmSpawn", manager_get_confirm_spawn(m)),
JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("FirmwareTimestamp", &m->timestamps[MANAGER_TIMESTAMP_FIRMWARE]),
JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("LoaderTimestamp", &m->timestamps[MANAGER_TIMESTAMP_LOADER]),
JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("KernelTimestamp", &m->timestamps[MANAGER_TIMESTAMP_KERNEL]),
JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("InitRDTimestamp", &m->timestamps[MANAGER_TIMESTAMP_INITRD]),
JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("UserspaceTimestamp", &m->timestamps[MANAGER_TIMESTAMP_USERSPACE]),
JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("FinishTimestamp", &m->timestamps[MANAGER_TIMESTAMP_FINISH]),
JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("SecurityStartTimestamp", &m->timestamps[MANAGER_TIMESTAMP_SECURITY_START]),
JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("SecurityFinishTimestamp", &m->timestamps[MANAGER_TIMESTAMP_SECURITY_FINISH]),
JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("GeneratorsStartTimestamp", &m->timestamps[MANAGER_TIMESTAMP_GENERATORS_START]),
JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("GeneratorsFinishTimestamp", &m->timestamps[MANAGER_TIMESTAMP_GENERATORS_FINISH]),
JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("UnitsLoadStartTimestamp", &m->timestamps[MANAGER_TIMESTAMP_UNITS_LOAD_START]),
JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("UnitsLoadFinishTimestamp", &m->timestamps[MANAGER_TIMESTAMP_UNITS_LOAD_FINISH]),
JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("UnitsLoadTimestamp", &m->timestamps[MANAGER_TIMESTAMP_UNITS_LOAD]),
JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("InitRDSecurityStartTimestamp", &m->timestamps[MANAGER_TIMESTAMP_INITRD_SECURITY_START]),
JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("InitRDSecurityFinishTimestamp", &m->timestamps[MANAGER_TIMESTAMP_INITRD_SECURITY_FINISH]),
JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("InitRDGeneratorsStartTimestamp", &m->timestamps[MANAGER_TIMESTAMP_INITRD_GENERATORS_START]),
JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("InitRDGeneratorsFinishTimestamp", &m->timestamps[MANAGER_TIMESTAMP_INITRD_GENERATORS_FINISH]),
JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("InitRDUnitsLoadStartTimestamp", &m->timestamps[MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_START]),
JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("InitRDUnitsLoadFinishTimestamp", &m->timestamps[MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_FINISH]),
SD_JSON_BUILD_PAIR_CONDITION(m->log_level_overridden, "LogLevel", SD_JSON_BUILD_INTEGER(log_get_max_level())),
SD_JSON_BUILD_PAIR_CONDITION(m->log_target_overridden, "LogTarget", SD_JSON_BUILD_STRING(log_target_to_string(log_get_target()))),
SD_JSON_BUILD_PAIR_UNSIGNED("NNames", hashmap_size(m->units)),
SD_JSON_BUILD_PAIR_UNSIGNED("NFailedUnits", set_size(m->failed_units)),
SD_JSON_BUILD_PAIR_UNSIGNED("NJobs", hashmap_size(m->jobs)),
SD_JSON_BUILD_PAIR_UNSIGNED("NInstalledJobs", m->n_installed_jobs),
SD_JSON_BUILD_PAIR_UNSIGNED("NFailedJobs", m->n_failed_jobs),
SD_JSON_BUILD_PAIR_REAL("Progress", manager_get_progress(m)),
JSON_BUILD_PAIR_CALLBACK_NON_NULL("Environment", manager_environment_build_json, m),
JSON_BUILD_PAIR_STRING_NON_EMPTY("WatchdogDevice", watchdog_get_device()),
JSON_BUILD_PAIR_DUAL_TIMESTAMP_NON_NULL("WatchdogLastPingTimestamp", &watchdog_last_ping),
JSON_BUILD_PAIR_STRING_NON_EMPTY("ControlGroup", m->cgroup_root),
SD_JSON_BUILD_PAIR_STRING("SystemState", manager_state_to_string(manager_state(m))),
SD_JSON_BUILD_PAIR_UNSIGNED("ExitCode", m->return_value));
}
int manager_build_json(Manager *m, sd_json_variant **ret) {
assert(m);
return sd_json_buildo(ASSERT_PTR(ret),
SD_JSON_BUILD_PAIR_CALLBACK("Context", manager_context_build_json, m),
SD_JSON_BUILD_PAIR_CALLBACK("Runtime", manager_runtime_build_json, m));
}

10
src/core/manager-json.h Normal file
View File

@ -0,0 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "sd-json.h"
#include "manager.h"
int rlimit_build_json(sd_json_variant **ret, const char *name, void *userdata);
int environment_build_json(sd_json_variant **ret, const char *name, void *userdata);
int manager_build_json(Manager *manager, sd_json_variant **ret);

View File

@ -497,7 +497,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
if (r < 0)
return r;
} else if ((val = startswith(l, "varlink-server-socket-address="))) {
if (!m->varlink_server && MANAGER_IS_SYSTEM(m)) {
if (!m->varlink_server) {
r = manager_setup_varlink_server(m);
if (r < 0) {
log_warning_errno(r, "Failed to setup varlink server, ignoring: %m");

View File

@ -44,6 +44,7 @@ libcore_sources = files(
'load-dropin.c',
'load-fragment.c',
'manager-dump.c',
'manager-json.c',
'manager-serialize.c',
'manager.c',
'mount.c',
@ -62,6 +63,7 @@ libcore_sources = files(
'transaction.c',
'unit-dependency-atom.c',
'unit-printf.c',
'unit-json.c',
'unit-serialize.c',
'unit.c',
)

2063
src/core/unit-json.c Normal file

File diff suppressed because it is too large Load Diff

8
src/core/unit-json.h Normal file
View File

@ -0,0 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "sd-json.h"
#include "unit.h"
int unit_build_json(Unit *unit, sd_json_variant **ret);

View File

@ -185,6 +185,7 @@ shared_sources = files(
'varlink-io.systemd.Machine.c',
'varlink-io.systemd.MachineImage.c',
'varlink-io.systemd.ManagedOOM.c',
'varlink-io.systemd.Manager.c',
'varlink-io.systemd.MountFileSystem.c',
'varlink-io.systemd.NamespaceResource.c',
'varlink-io.systemd.Network.c',

View File

@ -1808,40 +1808,50 @@ char* umount_and_unlink_and_free(char *p) {
return mfree(p);
}
static int path_get_mount_info(
static int path_get_mount_info_at(
int dir_fd,
const char *path,
char **ret_fstype,
char **ret_options) {
_cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL;
_cleanup_free_ char *fstype = NULL, *options = NULL;
struct libmnt_fs *fs;
int r;
_cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL;
int r, mnt_id;
assert(path);
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
table = mnt_new_table();
if (!table)
return -ENOMEM;
r = mnt_table_parse_mtab(table, /* filename = */ NULL);
r = path_get_mnt_id_at(dir_fd, path, &mnt_id);
if (r < 0)
return r;
return log_debug_errno(r, "Failed to get mount ID: %m");
fs = mnt_table_find_mountpoint(table, path, MNT_ITER_FORWARD);
if (!fs)
return -EINVAL;
r = libmount_parse("/proc/self/mountinfo", NULL, &table, &iter);
if (r < 0)
return log_debug_errno(r, "Failed to parse /proc/self/mountinfo: %m");
for (;;) {
struct libmnt_fs *fs;
r = mnt_table_next_fs(table, iter, &fs);
if (r == 1)
break; /* EOF */
if (r < 0)
return log_debug_errno(r, "Failed to get next entry from /proc/self/mountinfo: %m");
if (mnt_fs_get_id(fs) != mnt_id)
continue;
_cleanup_free_ char *fstype = NULL, *options = NULL;
if (ret_fstype) {
fstype = strdup(strempty(mnt_fs_get_fstype(fs)));
if (!fstype)
return -ENOMEM;
return log_oom_debug();
}
if (ret_options) {
options = strdup(strempty(mnt_fs_get_options(fs)));
if (!options)
return -ENOMEM;
return log_oom_debug();
}
if (ret_fstype)
@ -1852,19 +1862,27 @@ static int path_get_mount_info(
return 0;
}
int path_is_network_fs_harder(const char *path) {
return log_debug_errno(SYNTHETIC_ERRNO(ESTALE), "Cannot find mount ID %i from /proc/self/mountinfo.", mnt_id);
}
int path_is_network_fs_harder_at(int dir_fd, const char *path) {
_cleanup_close_ int fd = -EBADF;
int r;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
fd = xopenat(dir_fd, path, O_PATH | O_CLOEXEC | O_NOFOLLOW);
if (fd < 0)
return fd;
r = fd_is_network_fs(fd);
if (r != 0)
return r;
_cleanup_free_ char *fstype = NULL, *options = NULL;
int r, ret;
assert(path);
ret = path_is_network_fs(path);
if (ret > 0)
return true;
r = path_get_mount_info(path, &fstype, &options);
r = path_get_mount_info_at(fd, /* path = */ NULL, &fstype, &options);
if (r < 0)
return RET_GATHER(ret, r);
return r;
if (fstype_is_network(fstype))
return true;

View File

@ -181,4 +181,7 @@ int mount_credentials_fs(const char *path, size_t size, bool ro);
int make_fsmount(int error_log_level, const char *what, const char *type, unsigned long flags, const char *options, int userns_fd);
int path_is_network_fs_harder(const char *path);
int path_is_network_fs_harder_at(int dir_fd, const char *path);
static inline int path_is_network_fs_harder(const char *path) {
return path_is_network_fs_harder_at(AT_FDCWD, path);
}

View File

@ -7,6 +7,7 @@
#include "namespace-util.h"
#include "nsflags.h"
#include "string-util.h"
#include "strv.h"
int namespace_flags_from_string(const char *name, unsigned long *ret) {
unsigned long flags = 0;
@ -42,19 +43,17 @@ int namespace_flags_from_string(const char *name, unsigned long *ret) {
}
int namespace_flags_to_string(unsigned long flags, char **ret) {
_cleanup_free_ char *s = NULL;
unsigned i;
_cleanup_strv_free_ char **l = NULL;
for (i = 0; namespace_info[i].proc_name; i++) {
if ((flags & namespace_info[i].clone_flag) != namespace_info[i].clone_flag)
continue;
if (!strextend_with_separator(&s, " ", namespace_info[i].proc_name))
l = namespace_flags_to_strv(flags);
if (!l)
return -ENOMEM;
}
*ret = TAKE_PTR(s);
char *s = strv_join(l, NULL);
if (!s)
return -ENOMEM;
*ret = s;
return 0;
}
@ -65,3 +64,18 @@ const char* namespace_single_flag_to_string(unsigned long flag) {
return NULL;
}
char** namespace_flags_to_strv(unsigned long flags) {
_cleanup_strv_free_ char **s = NULL;
unsigned i;
for (i = 0; namespace_info[i].proc_name; i++) {
if ((flags & namespace_info[i].clone_flag) != namespace_info[i].clone_flag)
continue;
if (strv_extend(&s, namespace_info[i].proc_name) < 0)
return NULL;
}
return s ? TAKE_PTR(s) : strv_new(NULL);
}

View File

@ -21,3 +21,5 @@
int namespace_flags_from_string(const char *name, unsigned long *ret);
int namespace_flags_to_string(unsigned long flags, char **ret);
const char* namespace_single_flag_to_string(unsigned long flag);
char** namespace_flags_to_strv(unsigned long flags);

File diff suppressed because it is too large Load Diff

View 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_Manager;

View File

@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "parse-util.h"
#include "path-util.h"
#include "varlink-internal.h"
#include "varlink-serialize.h"
@ -83,3 +84,14 @@ int varlink_server_deserialize_one(sd_varlink_server *s, const char *value, FDSe
LIST_PREPEND(sockets, s->sockets, TAKE_PTR(ss));
return 0;
}
int varlink_server_contains_socket(sd_varlink_server *s, const char *address) {
assert(s);
assert(address);
LIST_FOREACH(sockets, ss, s->sockets)
if (path_equal(ss->address, address))
return true;
return false;
}

View File

@ -9,3 +9,5 @@
int varlink_server_serialize(sd_varlink_server *s, FILE *f, FDSet *fds);
int varlink_server_deserialize_one(sd_varlink_server *s, const char *value, FDSet *fds);
int varlink_server_contains_socket(sd_varlink_server *s, const char *address);

View File

@ -538,9 +538,53 @@ TEST(bind_mount_submounts) {
}
TEST(path_is_network_fs_harder) {
ASSERT_OK_ZERO(path_is_network_fs_harder("/dev"));
ASSERT_OK_ZERO(path_is_network_fs_harder("/sys"));
ASSERT_OK_ZERO(path_is_network_fs_harder("/run"));
_cleanup_close_ int dir_fd = -EBADF;
int r;
ASSERT_OK(dir_fd = open("/", O_PATH | O_CLOEXEC));
FOREACH_STRING(s,
"/", "/dev/", "/proc/", "/run/", "/sys/", "/tmp/", "/usr/", "/var/tmp/",
"", ".", "../../../", "/this/path/should/not/exist/for/test-mount-util/") {
r = path_is_network_fs_harder(s);
log_debug("path_is_network_fs_harder(%s) → %i: %s", s, r, r < 0 ? STRERROR(r) : yes_no(r));
const char *q = path_startswith(s, "/") ?: s;
r = path_is_network_fs_harder_at(dir_fd, q);
log_debug("path_is_network_fs_harder_at(root, %s) → %i: %s", q, r, r < 0 ? STRERROR(r) : yes_no(r));
}
if (geteuid() != 0 || have_effective_cap(CAP_SYS_ADMIN) <= 0) {
(void) log_tests_skipped("not running privileged");
return;
}
_cleanup_(rm_rf_physical_and_freep) char *t = NULL;
assert_se(mkdtemp_malloc("/tmp/test-mount-util.path_is_network_fs_harder.XXXXXXX", &t) >= 0);
r = safe_fork("(make_mount-point)",
FORK_RESET_SIGNALS |
FORK_CLOSE_ALL_FDS |
FORK_DEATHSIG_SIGTERM |
FORK_WAIT |
FORK_REOPEN_LOG |
FORK_LOG |
FORK_NEW_MOUNTNS |
FORK_MOUNTNS_SLAVE,
NULL);
ASSERT_OK(r);
if (r == 0) {
ASSERT_OK(mount_nofollow_verbose(LOG_INFO, "tmpfs", t, "tmpfs", 0, NULL));
ASSERT_OK_ZERO(path_is_network_fs_harder(t));
ASSERT_OK_ERRNO(umount(t));
ASSERT_OK(mount_nofollow_verbose(LOG_INFO, "tmpfs", t, "tmpfs", 0, "x-systemd-growfs,x-systemd-automount"));
ASSERT_OK_ZERO(path_is_network_fs_harder(t));
ASSERT_OK_ERRNO(umount(t));
_exit(EXIT_SUCCESS);
}
}
DEFINE_TEST_MAIN(LOG_DEBUG);

View File

@ -142,11 +142,13 @@ endif
############################################################
if install_tests
foreach script : ['integration-test-setup.sh', 'run-unit-tests.py']
install_data(script,
install_data('run-unit-tests.py',
install_mode : 'rwxr-xr-x',
install_dir : testsdir)
endforeach
install_data('integration-test-setup.sh',
install_mode : 'rwxr-xr-x',
install_dir : testdata_dir)
endif
############################################################

View File

@ -7,9 +7,9 @@ Before=getty-pre.target
[Service]
ExecStartPre=rm -f /failed /testok
ExecStartPre=/usr/lib/systemd/tests/integration-test-setup.sh setup
ExecStartPre=/usr/lib/systemd/tests/testdata/integration-test-setup.sh setup
ExecStart=@command@
ExecStopPost=/usr/lib/systemd/tests/integration-test-setup.sh finalize
ExecStopPost=/usr/lib/systemd/tests/testdata/integration-test-setup.sh finalize
Type=oneshot
MemoryAccounting=@memory-accounting@
StateDirectory=%N

View File

@ -132,10 +132,12 @@ testcase_unpriv() {
return 0
fi
# The kernel has a restriction for unprivileged user namespaces where they cannot mount a less restrictive
# instance of /proc/. So if /proc/ is masked (e.g. /proc/kmsg is over-mounted with tmpfs as systemd-nspawn does),
# then mounting a new /proc/ will fail and we will still see the host's /proc/. Thus, to allow tests to run in
# a VM or nspawn, we mount a new proc on a temporary directory with no masking to bypass this kernel restriction.
# IMPORTANT: For /proc/ to be remounted in pid namespace within an unprivileged user namespace, there needs to
# be at least 1 unmasked procfs mount in ANY directory. Otherwise, if /proc/ is masked (e.g. /proc/scsi is
# over-mounted with tmpfs), then mounting a new /proc/ will fail.
#
# Thus, to guarantee PrivatePIDs=yes tests for unprivileged users pass, we mount a new procfs on a temporary
# directory with no masking. This will guarantee an unprivileged user can mount a new /proc/ successfully.
mkdir -p /tmp/TEST-07-PID1-private-pids-proc
mount -t proc proc /tmp/TEST-07-PID1-private-pids-proc
@ -146,7 +148,16 @@ testcase_unpriv() {
umount /tmp/TEST-07-PID1-private-pids-proc
rm -rf /tmp/TEST-07-PID1-private-pids-proc
# Now verify the behavior with masking - units should fail as PrivatePIDs=yes has no graceful fallback.
# Now we will mask /proc/ by mounting tmpfs over /proc/scsi. This will guarantee that mounting /proc/ will fail
# for unprivileged users when using PrivatePIDs=yes. Now units should fail as PrivatePIDs=yes has no graceful
# fallback.
#
# Note some kernels do not have /proc/scsi so we verify the directory exists prior to running the test.
if [ ! -d /proc/scsi ]; then
echo "/proc/scsi does not exist, skipping unprivileged PrivatePIDs=yes test with masked /proc/"
return 0
fi
if [[ "$HAS_EXISTING_SCSI_MOUNT" == "no" ]]; then
mount -t tmpfs tmpfs /proc/scsi
fi

View File

@ -3,6 +3,9 @@
set -eux
set -o pipefail
# shellcheck source=test/units/util.sh
. "$(dirname "$0")"/util.sh
# Unset $PAGER so we don't have to use --no-pager everywhere
export PAGER=
@ -160,3 +163,19 @@ done
varlinkctl info /run/systemd/io.systemd.Hostname
varlinkctl introspect /run/systemd/io.systemd.Hostname io.systemd.Hostname
varlinkctl call /run/systemd/io.systemd.Hostname io.systemd.Hostname.Describe '{}'
varlinkctl info /run/systemd/io.systemd.Manager
varlinkctl introspect /run/systemd/io.systemd.Manager io.systemd.Manager
for _ in $(seq 3); do
systemd-run --service-type=oneshot --no-block sleep infinity
done
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Manager.Describe '{}'
JOB="$(varlinkctl call --more /run/systemd/io.systemd.Manager io.systemd.Manager.ListJobs '{}' | jq --slurp .[0].id)"
assert_eq "$(varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Manager.ListJobs "{\"id\": $JOB}" | jq .id)" "$JOB"
systemctl start user@4711
varlinkctl info /run/user/4711/systemd/io.systemd.Manager
varlinkctl introspect /run/user/4711/systemd/io.systemd.Manager
varlinkctl call /run/user/4711/systemd/io.systemd.Manager io.systemd.Manager.Describe '{}'