core: introduce varlink io.systemd.Job interface (#42104)
Methods: - `io.systemd.Job.List` — list all queued jobs or look up by `id`/`unit` name, with streaming support. Uses context/runtime split: `JobContext` (Unit, JobType) and `JobRuntime` (Id, State, Result, ActivationDetails). Follows the same SELinux and parameter-conflict patterns as `io.systemd.Unit.List`. - `io.systemd.Job.Cancel` — cancel a specific job by ID, with SELinux and polkit authorization. - `io.systemd.Job.ClearAll` — cancel all pending jobs, with SELinux and polkit authorization.
This commit is contained in:
@@ -71,6 +71,7 @@ libcore_sources = files(
|
||||
'varlink-common.c',
|
||||
'varlink-dynamic-user.c',
|
||||
'varlink-execute.c',
|
||||
'varlink-job.c',
|
||||
'varlink-kill.c',
|
||||
'varlink-manager.c',
|
||||
'varlink-metrics.c',
|
||||
|
||||
@@ -0,0 +1,252 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "sd-varlink.h"
|
||||
|
||||
#include "bus-polkit.h"
|
||||
#include "job.h"
|
||||
#include "json-util.h"
|
||||
#include "locale-util.h"
|
||||
#include "manager.h"
|
||||
#include "selinux-access.h"
|
||||
#include "strv.h"
|
||||
#include "unit.h"
|
||||
#include "varlink-job.h"
|
||||
|
||||
static 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;
|
||||
Job *j = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
r = activation_details_append_pair(j->activation_details, &pairs);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to get activation details: %m");
|
||||
if (r == 0) {
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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(sd_json_variant **ret, const char *name, void *userdata) {
|
||||
Job *j = ASSERT_PTR(userdata);
|
||||
|
||||
/* "Unit" is omitted in StartTransient streaming notifications where the caller already knows the unit. */
|
||||
return sd_json_buildo(
|
||||
ASSERT_PTR(ret),
|
||||
SD_JSON_BUILD_PAIR_INTEGER("Id", j->id),
|
||||
JSON_BUILD_PAIR_STRING_NON_EMPTY("Unit", j->unit ? j->unit->id : NULL),
|
||||
JSON_BUILD_PAIR_ENUM("JobType", job_type_to_string(j->type)),
|
||||
JSON_BUILD_PAIR_ENUM("State", job_state_to_string(j->state)),
|
||||
JSON_BUILD_PAIR_ENUM_NON_EMPTY("Result", job_result_to_string(j->result)),
|
||||
JSON_BUILD_PAIR_CALLBACK_NON_NULL("ActivationDetails", activation_details_build_json, j));
|
||||
}
|
||||
|
||||
static int varlink_error_no_such_job(sd_varlink *link, const char *name) {
|
||||
return sd_varlink_errorbo(
|
||||
ASSERT_PTR(link),
|
||||
VARLINK_ERROR_JOB_NO_SUCH_JOB,
|
||||
JSON_BUILD_PAIR_STRING_NON_EMPTY("parameter", name));
|
||||
}
|
||||
|
||||
static int list_job_one(sd_varlink *link, Job *job) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(job);
|
||||
|
||||
r = job_build_json(&v, /* name= */ NULL, job);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_varlink_reply(link, v);
|
||||
}
|
||||
|
||||
static int list_job_one_with_selinux_access_check(sd_varlink *link, Job *job) {
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(job);
|
||||
assert(job->unit);
|
||||
|
||||
r = mac_selinux_unit_access_check_varlink(job->unit, link, "status");
|
||||
if (r < 0)
|
||||
/* If mac_selinux_unit_access_check_varlink() returned an error,
|
||||
* it means that SELinux enforce is on. It also does all the logging(). */
|
||||
return sd_varlink_error(link, SD_VARLINK_ERROR_PERMISSION_DENIED, NULL);
|
||||
|
||||
return list_job_one(link, job);
|
||||
}
|
||||
|
||||
typedef struct JobLookupParameters {
|
||||
uint32_t id;
|
||||
const char *unit;
|
||||
} JobLookupParameters;
|
||||
|
||||
static int lookup_job_by_parameters(
|
||||
sd_varlink *link,
|
||||
Manager *manager,
|
||||
JobLookupParameters *p,
|
||||
Job **ret) {
|
||||
|
||||
/* The function can return ret=NULL if no lookup parameters provided */
|
||||
Job *job = NULL;
|
||||
|
||||
assert(link);
|
||||
assert(manager);
|
||||
assert(p);
|
||||
assert(ret);
|
||||
|
||||
if (p->id > 0) {
|
||||
job = manager_get_job(manager, p->id);
|
||||
if (!job)
|
||||
return varlink_error_no_such_job(link, "id");
|
||||
}
|
||||
|
||||
if (p->unit) {
|
||||
Unit *u = manager_get_unit(manager, p->unit);
|
||||
if (!u || !u->job)
|
||||
return varlink_error_no_such_job(link, "unit");
|
||||
if (job && u->job != job) {
|
||||
log_debug("Job lookup by parameters id=%u unit='%s' resulted in different jobs.", p->id, p->unit);
|
||||
return varlink_error_no_such_job(link, /* name= */ NULL);
|
||||
}
|
||||
|
||||
job = u->job;
|
||||
}
|
||||
|
||||
*ret = job;
|
||||
return !!job;
|
||||
}
|
||||
|
||||
int vl_method_list_jobs(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "id", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_job_id, offsetof(JobLookupParameters, id), 0 },
|
||||
{ "unit", SD_JSON_VARIANT_STRING, json_dispatch_const_unit_name, offsetof(JobLookupParameters, unit), 0 },
|
||||
{}
|
||||
};
|
||||
|
||||
Manager *manager = ASSERT_PTR(userdata);
|
||||
JobLookupParameters p = {};
|
||||
Job *job;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(parameters);
|
||||
|
||||
r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
r = lookup_job_by_parameters(link, manager, &p, &job);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
return list_job_one_with_selinux_access_check(link, job);
|
||||
|
||||
/* List all jobs */
|
||||
if (!FLAGS_SET(flags, SD_VARLINK_METHOD_MORE))
|
||||
return sd_varlink_error(link, SD_VARLINK_ERROR_EXPECTED_MORE, NULL);
|
||||
|
||||
r = sd_varlink_set_sentinel(link, VARLINK_ERROR_JOB_NO_SUCH_JOB);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
HASHMAP_FOREACH(job, manager->jobs) {
|
||||
r = mac_selinux_unit_access_check_varlink(job->unit, link, "status");
|
||||
if (r < 0)
|
||||
continue;
|
||||
|
||||
r = list_job_one(link, job);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vl_method_cancel_job(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "id", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_job_id, 0, SD_JSON_MANDATORY },
|
||||
{}
|
||||
};
|
||||
|
||||
Manager *manager = ASSERT_PTR(userdata);
|
||||
uint32_t id = 0;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(parameters);
|
||||
|
||||
r = sd_varlink_dispatch(link, parameters, dispatch_table, &id);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
Job *j = manager_get_job(manager, id);
|
||||
if (!j)
|
||||
return varlink_error_no_such_job(link, "id");
|
||||
|
||||
r = mac_selinux_unit_access_check_varlink(j->unit, link, "stop");
|
||||
if (r < 0)
|
||||
return sd_varlink_error(link, SD_VARLINK_ERROR_PERMISSION_DENIED, NULL);
|
||||
|
||||
r = varlink_verify_polkit_async(
|
||||
link,
|
||||
manager->system_bus,
|
||||
"org.freedesktop.systemd1.manage-units",
|
||||
(const char**) STRV_MAKE(
|
||||
"unit", j->unit ? j->unit->id : NULL,
|
||||
"verb", "cancel",
|
||||
"polkit.message", N_("Authentication is required to cancel job for unit '$(unit)'."),
|
||||
"polkit.gettext_domain", GETTEXT_PACKAGE),
|
||||
&manager->polkit_registry);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
job_finish_and_invalidate(j, JOB_CANCELED, /* recursive= */ true, /* already= */ false);
|
||||
|
||||
return sd_varlink_reply(link, NULL);
|
||||
}
|
||||
|
||||
int vl_method_clear_all_jobs(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||
Manager *manager = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(parameters);
|
||||
|
||||
r = sd_varlink_dispatch(link, parameters, /* dispatch_table= */ NULL, /* userdata= */ NULL);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
r = mac_selinux_access_check_varlink(link, "reload");
|
||||
if (r < 0)
|
||||
return sd_varlink_error(link, SD_VARLINK_ERROR_PERMISSION_DENIED, NULL);
|
||||
|
||||
r = varlink_verify_polkit_async(
|
||||
link,
|
||||
manager->system_bus,
|
||||
"org.freedesktop.systemd1.manage-units",
|
||||
(const char**) STRV_MAKE(
|
||||
"verb", "clear-jobs",
|
||||
"polkit.message", N_("Authentication is required to clear all pending jobs."),
|
||||
"polkit.gettext_domain", GETTEXT_PACKAGE),
|
||||
&manager->polkit_registry);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
manager_clear_jobs(manager);
|
||||
|
||||
return sd_varlink_reply(link, NULL);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "core-forward.h"
|
||||
|
||||
#define VARLINK_ERROR_JOB_NO_SUCH_JOB "io.systemd.Job.NoSuchJob"
|
||||
|
||||
int job_build_json(sd_json_variant **ret, const char *name, void *userdata);
|
||||
|
||||
int vl_method_list_jobs(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata);
|
||||
int vl_method_cancel_job(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata);
|
||||
int vl_method_clear_all_jobs(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata);
|
||||
+1
-12
@@ -28,6 +28,7 @@
|
||||
#include "varlink-cgroup.h"
|
||||
#include "varlink-common.h"
|
||||
#include "varlink-execute.h"
|
||||
#include "varlink-job.h"
|
||||
#include "varlink-kill.h"
|
||||
#include "varlink-mount.h"
|
||||
#include "varlink-path.h"
|
||||
@@ -618,18 +619,6 @@ void varlink_unit_send_change_signal(Unit *u) {
|
||||
SD_JSON_BUILD_PAIR_CALLBACK("runtime", unit_runtime_build_json, u));
|
||||
}
|
||||
|
||||
static int job_build_json(sd_json_variant **ret, const char *name, void *userdata) {
|
||||
Job *j = ASSERT_PTR(userdata);
|
||||
|
||||
/* Note that "Result" is suppressed until the job reaches JOB_FINISHED. */
|
||||
return sd_json_buildo(
|
||||
ASSERT_PTR(ret),
|
||||
SD_JSON_BUILD_PAIR_INTEGER("Id", j->id),
|
||||
JSON_BUILD_PAIR_ENUM("JobType", job_type_to_string(j->type)),
|
||||
JSON_BUILD_PAIR_ENUM("State", job_state_to_string(j->state)),
|
||||
JSON_BUILD_PAIR_STRING_NON_EMPTY_UNDERSCORIFY("Result", job_result_to_string(j->result)));
|
||||
}
|
||||
|
||||
void varlink_job_send_change_signal(Job *j) {
|
||||
assert(j);
|
||||
|
||||
|
||||
@@ -15,11 +15,13 @@
|
||||
#include "unit.h"
|
||||
#include "varlink.h"
|
||||
#include "varlink-dynamic-user.h"
|
||||
#include "varlink-io.systemd.Job.h"
|
||||
#include "varlink-io.systemd.ManagedOOM.h"
|
||||
#include "varlink-io.systemd.Manager.h"
|
||||
#include "varlink-io.systemd.Unit.h"
|
||||
#include "varlink-io.systemd.UserDatabase.h"
|
||||
#include "varlink-io.systemd.service.h"
|
||||
#include "varlink-job.h"
|
||||
#include "varlink-manager.h"
|
||||
#include "varlink-metrics.h"
|
||||
#include "varlink-serialize.h"
|
||||
@@ -411,6 +413,7 @@ int manager_setup_varlink_server(Manager *m) {
|
||||
|
||||
r = sd_varlink_server_add_interface_many(
|
||||
s,
|
||||
&vl_interface_io_systemd_Job,
|
||||
&vl_interface_io_systemd_Manager,
|
||||
&vl_interface_io_systemd_Unit,
|
||||
&vl_interface_io_systemd_service);
|
||||
@@ -419,6 +422,9 @@ int manager_setup_varlink_server(Manager *m) {
|
||||
|
||||
r = sd_varlink_server_bind_method_many(
|
||||
s,
|
||||
"io.systemd.Job.List", vl_method_list_jobs,
|
||||
"io.systemd.Job.Cancel", vl_method_cancel_job,
|
||||
"io.systemd.Job.ClearAll", vl_method_clear_all_jobs,
|
||||
"io.systemd.Manager.Describe", vl_method_describe_manager,
|
||||
"io.systemd.Manager.Reexecute", vl_method_reexecute_manager,
|
||||
"io.systemd.Manager.Reload", vl_method_reload_manager,
|
||||
|
||||
@@ -796,6 +796,27 @@ int json_dispatch_access_mode(const char *name, sd_json_variant *variant, sd_jso
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_dispatch_job_id(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
uint32_t *id = ASSERT_PTR(userdata);
|
||||
uint32_t k;
|
||||
int r;
|
||||
|
||||
if (sd_json_variant_is_null(variant)) {
|
||||
*id = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = sd_json_dispatch_uint32(name, variant, flags, &k);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (k == 0)
|
||||
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid job ID.", strna(name));
|
||||
|
||||
*id = k;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_variant_compare(sd_json_variant *a, sd_json_variant *b) {
|
||||
int r;
|
||||
|
||||
|
||||
@@ -132,6 +132,7 @@ int json_dispatch_ifindex(const char *name, sd_json_variant *variant, sd_json_di
|
||||
int json_dispatch_log_level(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
int json_dispatch_strv_environment(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
int json_dispatch_access_mode(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
int json_dispatch_job_id(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
|
||||
static inline int json_variant_unbase64_iovec(sd_json_variant *v, struct iovec *ret) {
|
||||
return sd_json_variant_unbase64(v, ret ? &ret->iov_base : NULL, ret ? &ret->iov_len : NULL);
|
||||
@@ -267,6 +268,7 @@ enum {
|
||||
#define JSON_BUILD_PAIR_PIDREF(name, p) SD_JSON_BUILD_PAIR(name, JSON_BUILD_PIDREF(p))
|
||||
#define JSON_BUILD_PAIR_DEVNUM(name, d) SD_JSON_BUILD_PAIR(name, JSON_BUILD_DEVNUM(d))
|
||||
#define JSON_BUILD_PAIR_ENUM(name, s) SD_JSON_BUILD_PAIR(name, JSON_BUILD_STRING_UNDERSCORIFY(s))
|
||||
#define JSON_BUILD_PAIR_ENUM_NON_EMPTY(name, s) JSON_BUILD_PAIR_STRING_NON_EMPTY_UNDERSCORIFY(name, s)
|
||||
#define JSON_BUILD_PAIR_YES_NO(name, b) SD_JSON_BUILD_PAIR(name, SD_JSON_BUILD_STRING(yes_no(b)))
|
||||
|
||||
#define JSON_BUILD_PAIR_CONDITION_UNSIGNED(condition, name, value) \
|
||||
|
||||
@@ -1632,6 +1632,69 @@ TEST(access_mode) {
|
||||
&mm), ERANGE);
|
||||
}
|
||||
|
||||
TEST(job_id) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||
|
||||
ASSERT_OK(sd_json_parse("{\"a\": 1, \"b\": 4294967295, \"c\": null}",
|
||||
/* flags= */ 0,
|
||||
&v,
|
||||
/* reterr_line= */ NULL,
|
||||
/* reterr_column= */ NULL));
|
||||
|
||||
struct {
|
||||
uint32_t a, b, c;
|
||||
} data = { 99, 99, 99 };
|
||||
|
||||
ASSERT_OK(sd_json_dispatch(
|
||||
v,
|
||||
(const sd_json_dispatch_field[]) {
|
||||
{ "a", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_job_id, voffsetof(data, a), 0 },
|
||||
{ "b", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_job_id, voffsetof(data, b), 0 },
|
||||
{ "c", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_job_id, voffsetof(data, c), 0 },
|
||||
{},
|
||||
},
|
||||
/* flags= */ 0,
|
||||
&data));
|
||||
|
||||
ASSERT_EQ(data.a, UINT32_C(1));
|
||||
ASSERT_EQ(data.b, UINT32_MAX);
|
||||
ASSERT_EQ(data.c, UINT32_C(0));
|
||||
|
||||
/* Zero is not a valid job ID */
|
||||
sd_json_variant_unrefp(&v);
|
||||
ASSERT_OK(sd_json_parse("{\"a\": 0}",
|
||||
/* flags= */ 0,
|
||||
&v,
|
||||
/* reterr_line= */ NULL,
|
||||
/* reterr_column= */ NULL));
|
||||
|
||||
ASSERT_ERROR(sd_json_dispatch(
|
||||
v,
|
||||
(const sd_json_dispatch_field[]) {
|
||||
{ "a", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_job_id, voffsetof(data, a), 0 },
|
||||
{},
|
||||
},
|
||||
/* flags= */ 0,
|
||||
&data), EINVAL);
|
||||
|
||||
/* Negative values are not valid */
|
||||
sd_json_variant_unrefp(&v);
|
||||
ASSERT_OK(sd_json_parse("{\"a\": -1}",
|
||||
/* flags= */ 0,
|
||||
&v,
|
||||
/* reterr_line= */ NULL,
|
||||
/* reterr_column= */ NULL));
|
||||
|
||||
ASSERT_ERROR(sd_json_dispatch(
|
||||
v,
|
||||
(const sd_json_dispatch_field[]) {
|
||||
{ "a", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_job_id, voffsetof(data, a), 0 },
|
||||
{},
|
||||
},
|
||||
/* flags= */ 0,
|
||||
&data), EINVAL);
|
||||
}
|
||||
|
||||
static void test_json_variant_compare_one(const char *a, const char *b, int expected) {
|
||||
int r;
|
||||
|
||||
|
||||
@@ -39,22 +39,56 @@ SD_VARLINK_DEFINE_ENUM_TYPE(
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(frozen),
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(concurrency));
|
||||
|
||||
/* Field names match the D-Bus Job properties (Id, JobType, State) */
|
||||
/* Field names match the D-Bus Job properties (Id, Unit, JobType, State) */
|
||||
#define VARLINK_DEFINE_JOB_FIELDS(direction) \
|
||||
SD_VARLINK_FIELD_COMMENT("The numeric job ID"), \
|
||||
SD_VARLINK_DEFINE_##direction(Id, SD_VARLINK_INT, 0), \
|
||||
SD_VARLINK_FIELD_COMMENT("The unit name this job operates on"), \
|
||||
SD_VARLINK_DEFINE_##direction(Unit, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), \
|
||||
SD_VARLINK_FIELD_COMMENT("The job type"), \
|
||||
SD_VARLINK_DEFINE_##direction##_BY_TYPE(JobType, JobType, 0), \
|
||||
SD_VARLINK_FIELD_COMMENT("Current job state. 'finished' indicates the job has completed; in that case Result is also set."), \
|
||||
SD_VARLINK_DEFINE_##direction##_BY_TYPE(State, JobState, SD_VARLINK_NULLABLE), \
|
||||
SD_VARLINK_FIELD_COMMENT("Job result. Only set once the job has reached the 'finished' state."), \
|
||||
SD_VARLINK_DEFINE_##direction##_BY_TYPE(Result, JobResult, SD_VARLINK_NULLABLE), \
|
||||
SD_VARLINK_FIELD_COMMENT("Activation details describing what triggered this job, as key-value pairs"), \
|
||||
SD_VARLINK_DEFINE_##direction(ActivationDetails, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_MAP)
|
||||
|
||||
SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
Job,
|
||||
SD_VARLINK_FIELD_COMMENT("The numeric job ID"),
|
||||
SD_VARLINK_DEFINE_FIELD(Id, SD_VARLINK_INT, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("The job type"),
|
||||
SD_VARLINK_DEFINE_FIELD_BY_TYPE(JobType, JobType, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Current job state. 'finished' indicates the job has completed; in that case Result is also set."),
|
||||
SD_VARLINK_DEFINE_FIELD_BY_TYPE(State, JobState, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Job result. Only set once the job has reached the 'finished' state."),
|
||||
SD_VARLINK_DEFINE_FIELD_BY_TYPE(Result, JobResult, SD_VARLINK_NULLABLE));
|
||||
VARLINK_DEFINE_JOB_FIELDS(FIELD));
|
||||
|
||||
static SD_VARLINK_DEFINE_ERROR(
|
||||
NoSuchJob,
|
||||
SD_VARLINK_DEFINE_FIELD(parameter, SD_VARLINK_STRING, SD_VARLINK_NULLABLE));
|
||||
|
||||
static SD_VARLINK_DEFINE_METHOD_FULL(
|
||||
List,
|
||||
SD_VARLINK_SUPPORTS_MORE,
|
||||
SD_VARLINK_FIELD_COMMENT("If non-null, filter by job ID"),
|
||||
SD_VARLINK_DEFINE_INPUT(id, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("If non-null, filter by unit name"),
|
||||
SD_VARLINK_DEFINE_INPUT(unit, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_JOB_FIELDS(OUTPUT));
|
||||
|
||||
static SD_VARLINK_DEFINE_METHOD(
|
||||
Cancel,
|
||||
SD_VARLINK_FIELD_COMMENT("The job ID to cancel"),
|
||||
SD_VARLINK_DEFINE_INPUT(id, SD_VARLINK_INT, 0));
|
||||
|
||||
static SD_VARLINK_DEFINE_METHOD(
|
||||
ClearAll);
|
||||
|
||||
SD_VARLINK_DEFINE_INTERFACE(
|
||||
io_systemd_Job,
|
||||
"io.systemd.Job",
|
||||
SD_VARLINK_INTERFACE_COMMENT("Job-related types for the systemd service manager."),
|
||||
SD_VARLINK_INTERFACE_COMMENT("Job management interface for the systemd service manager."),
|
||||
SD_VARLINK_SYMBOL_COMMENT("List queued jobs"),
|
||||
&vl_method_List,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Cancel a specific job"),
|
||||
&vl_method_Cancel,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Cancel all pending jobs"),
|
||||
&vl_method_ClearAll,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Job type"),
|
||||
&vl_type_JobType,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Job state"),
|
||||
@@ -62,4 +96,6 @@ SD_VARLINK_DEFINE_INTERFACE(
|
||||
SD_VARLINK_SYMBOL_COMMENT("Job result"),
|
||||
&vl_type_JobResult,
|
||||
SD_VARLINK_SYMBOL_COMMENT("A job object"),
|
||||
&vl_type_Job);
|
||||
&vl_type_Job,
|
||||
SD_VARLINK_SYMBOL_COMMENT("The specified job does not exist"),
|
||||
&vl_error_NoSuchJob);
|
||||
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
# test io.systemd.Job
|
||||
varlinkctl introspect /run/systemd/io.systemd.Manager io.systemd.Job
|
||||
|
||||
# List with no jobs pending — should return empty with --more
|
||||
varlinkctl --more call /run/systemd/io.systemd.Manager io.systemd.Job.List '{}' --graceful=io.systemd.Job.NoSuchJob
|
||||
|
||||
# Without --more and no filter, must fail (streaming required)
|
||||
(! varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Job.List '{}')
|
||||
|
||||
# Error cases: non-existent job ID, non-existent unit
|
||||
(! varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Job.List '{"id": 999999}')
|
||||
(! varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Job.List '{"unit": "non-existent.service"}')
|
||||
|
||||
# Invalid inputs
|
||||
(! varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Job.List '{"id": 0}')
|
||||
(! varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Job.List '{"unit": ""}')
|
||||
|
||||
at_exit() {
|
||||
systemctl stop varlink-test-job.service 2>/dev/null || true
|
||||
rm -f /run/systemd/system/varlink-test-job.service
|
||||
systemctl daemon-reload
|
||||
}
|
||||
trap at_exit EXIT
|
||||
|
||||
# Create a job by starting a slow service, then test List/Cancel
|
||||
cat >/run/systemd/system/varlink-test-job.service <<UNIT
|
||||
[Service]
|
||||
Type=notify
|
||||
ExecStart=sleep infinity
|
||||
UNIT
|
||||
systemctl daemon-reload
|
||||
|
||||
# Start asynchronously to create a job
|
||||
systemctl start --no-block varlink-test-job.service
|
||||
|
||||
# The job should now be visible
|
||||
job_id=$(varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Job.List '{"unit": "varlink-test-job.service"}' | jq -r '.Id')
|
||||
test "$job_id" -gt 0
|
||||
|
||||
# Verify job fields
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Job.List '{"unit": "varlink-test-job.service"}' | jq -e '.Unit == "varlink-test-job.service"'
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Job.List '{"unit": "varlink-test-job.service"}' | jq -e '.JobType == "start"'
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Job.List '{"unit": "varlink-test-job.service"}' | jq -e '.State'
|
||||
|
||||
# Lookup by job ID should return the same job
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Job.List "{\"id\": $job_id}" | jq -e ".Unit == \"varlink-test-job.service\""
|
||||
|
||||
# Lookup by both id and unit (matching) should succeed
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Job.List "{\"id\": $job_id, \"unit\": \"varlink-test-job.service\"}" | jq -e ".Id == $job_id"
|
||||
|
||||
# Lookup by both id and unit (conflicting) should fail
|
||||
(! varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Job.List "{\"id\": $((job_id + 1)), \"unit\": \"varlink-test-job.service\"}")
|
||||
|
||||
# The job should appear in the full listing
|
||||
varlinkctl --more call /run/systemd/io.systemd.Manager io.systemd.Job.List '{}' | grep "varlink-test-job.service"
|
||||
|
||||
# Cancel the job
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Job.Cancel "{\"id\": $job_id}"
|
||||
|
||||
# The job should no longer exist
|
||||
(! varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Job.List '{"unit": "varlink-test-job.service"}')
|
||||
|
||||
# Cancel with non-existent job ID should fail
|
||||
(! varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Job.Cancel '{"id": 999999}')
|
||||
|
||||
# Test ClearAll: create another job, then clear all
|
||||
systemctl start --no-block varlink-test-job.service
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Job.List '{"unit": "varlink-test-job.service"}' | jq -e '.Id'
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Job.ClearAll '{}'
|
||||
|
||||
# No jobs should remain for our test unit
|
||||
(! varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Job.List '{"unit": "varlink-test-job.service"}')
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
# test io.systemd.Metrics
|
||||
varlinkctl info /run/systemd/report/io.systemd.Manager
|
||||
|
||||
varlinkctl list-methods /run/systemd/report/io.systemd.Manager
|
||||
varlinkctl list-methods -j /run/systemd/report/io.systemd.Manager io.systemd.Metrics | jq .
|
||||
|
||||
varlinkctl introspect /run/systemd/report/io.systemd.Manager
|
||||
varlinkctl introspect -j /run/systemd/report/io.systemd.Manager io.systemd.Metrics | jq .
|
||||
|
||||
varlinkctl --more call /run/systemd/report/io.systemd.Manager io.systemd.Metrics.List '{}'
|
||||
varlinkctl --more call /run/systemd/report/io.systemd.Manager io.systemd.Metrics.Describe '{}'
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
# test io.systemd.Network
|
||||
varlinkctl info /run/systemd/netif/io.systemd.Network
|
||||
varlinkctl introspect /run/systemd/netif/io.systemd.Network io.systemd.Network
|
||||
varlinkctl call /run/systemd/netif/io.systemd.Network io.systemd.Network.Describe '{}'
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
# test io.systemd.Unit
|
||||
varlinkctl info /run/systemd/io.systemd.Manager
|
||||
varlinkctl introspect /run/systemd/io.systemd.Manager io.systemd.Unit
|
||||
varlinkctl --more call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{}'
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{"name": "multi-user.target"}'
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{"pid": {"pid": 1}}'
|
||||
(! varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{}' |& grep "called without 'more' flag" >/dev/null)
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{"name": "init.scope", "pid": {"pid": 1}}'
|
||||
(! varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{"name": ""}')
|
||||
(! varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{"name": "non-existent.service"}')
|
||||
(! varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{"pid": {"pid": -1}}' )
|
||||
(! varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{"name": "multi-user.target", "pid": {"pid": 1}}')
|
||||
set +o pipefail
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties '{"runtime": true, "name": "non-existent.service", "properties": {"Markers": ["needs-restart"]}}' |& grep "io.systemd.Unit.NoSuchUnit"
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties '{"runtime": true, "name": "systemd-journald.service", "properties": {"LoadState": "foobar"}}' |& grep "io.systemd.Unit.PropertyNotSupported"
|
||||
set -o pipefail
|
||||
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{"cgroup": "/init.scope"}'
|
||||
invocation_id="$(systemctl show -P InvocationID systemd-journald.service)"
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "{\"invocationID\": \"$invocation_id\"}"
|
||||
# test for KillContext
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{"pid": {"pid": 0}}' | jq -e '.context.Kill'
|
||||
# test for AutomountContext/Runtime
|
||||
automount_id=$(varlinkctl call --collect /run/systemd/io.systemd.Manager io.systemd.Unit.List '{}' | jq -r '.[] | select(.context.Type == "automount" and .runtime.LoadState == "loaded") .context.ID // empty' | tail -n 1)
|
||||
test -n "$automount_id"
|
||||
# Use jq to JSON-encode the unit name as it may contain backslash escapes (e.g. \x2d) that
|
||||
# are not valid JSON escape sequences and would be rejected by varlinkctl's JSON parser.
|
||||
automount_params=$(jq -cn --arg name "$automount_id" '{name: $name}')
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$automount_params" | jq -e '.context.Automount'
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$automount_params" | jq -e '.runtime.Automount'
|
||||
# test for MountContext/Runtime
|
||||
mount_id=$(varlinkctl call --collect /run/systemd/io.systemd.Manager io.systemd.Unit.List '{}' | jq -r '.[] | select(.context.Type == "mount" and .runtime.LoadState == "loaded") .context.ID // empty' | tail -n 1)
|
||||
test -n "$mount_id"
|
||||
mount_params=$(jq -cn --arg name "$mount_id" '{name: $name}')
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$mount_params" | jq -e '.context.Mount'
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$mount_params" | jq -e '.runtime.Mount'
|
||||
# test for PathContext/Runtime
|
||||
path_id=$(varlinkctl call --collect /run/systemd/io.systemd.Manager io.systemd.Unit.List '{}' | jq -r '.[] | select(.context.Type == "path" and .runtime.LoadState == "loaded") .context.ID // empty' | tail -n 1)
|
||||
test -n "$path_id"
|
||||
path_params=$(jq -cn --arg name "$path_id" '{name: $name}')
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$path_params" | jq -e '.context.Path'
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$path_params" | jq -e '.runtime.Path'
|
||||
# test for ServiceContext/Runtime
|
||||
service_id=$(varlinkctl call --collect /run/systemd/io.systemd.Manager io.systemd.Unit.List '{}' | jq -r '.[] | select(.context.Type == "service" and .runtime.LoadState == "loaded") .context.ID // empty' | tail -n 1)
|
||||
test -n "$service_id"
|
||||
service_params=$(jq -cn --arg name "$service_id" '{name: $name}')
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$service_params" | jq -e '.context.Service'
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$service_params" | jq -e '.runtime.Service'
|
||||
# test for ScopeContext/Runtime
|
||||
scope_id=$(varlinkctl call --collect /run/systemd/io.systemd.Manager io.systemd.Unit.List '{}' | jq -r '.[] | select(.context.Type == "scope" and .runtime.LoadState == "loaded") .context.ID // empty' | tail -n 1)
|
||||
test -n "$scope_id"
|
||||
scope_params=$(jq -cn --arg name "$scope_id" '{name: $name}')
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$scope_params" | jq -e '.context.Scope'
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$scope_params" | jq -e '.runtime.Scope'
|
||||
# test for SocketContext/Runtime
|
||||
socket_id=$(varlinkctl call --collect /run/systemd/io.systemd.Manager io.systemd.Unit.List '{}' | jq -r '.[] | select(.context.Type == "socket" and .runtime.LoadState == "loaded") .context.ID // empty' | tail -n 1)
|
||||
test -n "$socket_id"
|
||||
socket_params=$(jq -cn --arg name "$socket_id" '{name: $name}')
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$socket_params" | jq -e '.context.Socket'
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$socket_params" | jq -e '.runtime.Socket'
|
||||
# test for SwapContext/Runtime (swap units may not be present on all systems)
|
||||
swap_id=$(varlinkctl call --collect /run/systemd/io.systemd.Manager io.systemd.Unit.List '{}' | jq -r '.[] | select(.context.Type == "swap" and .runtime.LoadState == "loaded") .context.ID // empty' | tail -n 1)
|
||||
if test -n "$swap_id"; then
|
||||
swap_params=$(jq -cn --arg name "$swap_id" '{name: $name}')
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$swap_params" | jq -e '.context.Swap'
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$swap_params" | jq -e '.runtime.Swap'
|
||||
fi
|
||||
# test for TimerContext/Runtime
|
||||
timer_id=$(varlinkctl call --collect /run/systemd/io.systemd.Manager io.systemd.Unit.List '{}' | jq -r '.[] | select(.context.Type == "timer" and .runtime.LoadState == "loaded") .context.ID // empty' | tail -n 1)
|
||||
test -n "$timer_id"
|
||||
timer_params=$(jq -cn --arg name "$timer_id" '{name: $name}')
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$timer_params" | jq -e '.context.Timer'
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$timer_params" | jq -e '.runtime.Timer'
|
||||
|
||||
# test io.systemd.Unit in user manager
|
||||
testuser_uid=$(id -u testuser)
|
||||
systemd-run --wait --pipe --user --machine testuser@ \
|
||||
varlinkctl --more call "/run/user/$testuser_uid/systemd/io.systemd.Manager" io.systemd.Unit.List '{}'
|
||||
@@ -204,97 +204,14 @@ varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Manager.Describe '{}'
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Manager.Reload '{}'
|
||||
# This will disconnect and fail, as the manager reexec and drops connections
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Manager.Reexecute '{}' ||:
|
||||
|
||||
# test io.systemd.Network
|
||||
varlinkctl info /run/systemd/netif/io.systemd.Network
|
||||
varlinkctl introspect /run/systemd/netif/io.systemd.Network io.systemd.Network
|
||||
varlinkctl call /run/systemd/netif/io.systemd.Network io.systemd.Network.Describe '{}'
|
||||
|
||||
# test io.systemd.Unit
|
||||
varlinkctl info /run/systemd/io.systemd.Manager
|
||||
varlinkctl introspect /run/systemd/io.systemd.Manager io.systemd.Unit
|
||||
varlinkctl --more call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{}'
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{"name": "multi-user.target"}'
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{"pid": {"pid": 1}}'
|
||||
(! varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{}' |& grep "called without 'more' flag" >/dev/null)
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{"name": "init.scope", "pid": {"pid": 1}}'
|
||||
(! varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{"name": ""}')
|
||||
(! varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{"name": "non-existent.service"}')
|
||||
(! varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{"pid": {"pid": -1}}' )
|
||||
(! varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{"name": "multi-user.target", "pid": {"pid": 1}}')
|
||||
set +o pipefail
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties '{"runtime": true, "name": "non-existent.service", "properties": {"Markers": ["needs-restart"]}}' |& grep "io.systemd.Unit.NoSuchUnit"
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties '{"runtime": true, "name": "systemd-journald.service", "properties": {"LoadState": "foobar"}}' |& grep "io.systemd.Unit.PropertyNotSupported"
|
||||
set -o pipefail
|
||||
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{"cgroup": "/init.scope"}'
|
||||
invocation_id="$(systemctl show -P InvocationID systemd-journald.service)"
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "{\"invocationID\": \"$invocation_id\"}"
|
||||
# test for KillContext
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{"pid": {"pid": 0}}' | jq -e '.context.Kill'
|
||||
# test for AutomountContext/Runtime
|
||||
automount_id=$(varlinkctl call --collect /run/systemd/io.systemd.Manager io.systemd.Unit.List '{}' | jq -r '.[] | select(.context.Type == "automount" and .runtime.LoadState == "loaded") .context.ID // empty' | tail -n 1)
|
||||
test -n "$automount_id"
|
||||
# Use jq to JSON-encode the unit name as it may contain backslash escapes (e.g. \x2d) that
|
||||
# are not valid JSON escape sequences and would be rejected by varlinkctl's JSON parser.
|
||||
automount_params=$(jq -cn --arg name "$automount_id" '{name: $name}')
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$automount_params" | jq -e '.context.Automount'
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$automount_params" | jq -e '.runtime.Automount'
|
||||
# test for MountContext/Runtime
|
||||
mount_id=$(varlinkctl call --collect /run/systemd/io.systemd.Manager io.systemd.Unit.List '{}' | jq -r '.[] | select(.context.Type == "mount" and .runtime.LoadState == "loaded") .context.ID // empty' | tail -n 1)
|
||||
test -n "$mount_id"
|
||||
mount_params=$(jq -cn --arg name "$mount_id" '{name: $name}')
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$mount_params" | jq -e '.context.Mount'
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$mount_params" | jq -e '.runtime.Mount'
|
||||
# test for PathContext/Runtime
|
||||
path_id=$(varlinkctl call --collect /run/systemd/io.systemd.Manager io.systemd.Unit.List '{}' | jq -r '.[] | select(.context.Type == "path" and .runtime.LoadState == "loaded") .context.ID // empty' | tail -n 1)
|
||||
test -n "$path_id"
|
||||
path_params=$(jq -cn --arg name "$path_id" '{name: $name}')
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$path_params" | jq -e '.context.Path'
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$path_params" | jq -e '.runtime.Path'
|
||||
# test for ServiceContext/Runtime
|
||||
service_id=$(varlinkctl call --collect /run/systemd/io.systemd.Manager io.systemd.Unit.List '{}' | jq -r '.[] | select(.context.Type == "service" and .runtime.LoadState == "loaded") .context.ID // empty' | tail -n 1)
|
||||
test -n "$service_id"
|
||||
service_params=$(jq -cn --arg name "$service_id" '{name: $name}')
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$service_params" | jq -e '.context.Service'
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$service_params" | jq -e '.runtime.Service'
|
||||
# test for ScopeContext/Runtime
|
||||
scope_id=$(varlinkctl call --collect /run/systemd/io.systemd.Manager io.systemd.Unit.List '{}' | jq -r '.[] | select(.context.Type == "scope" and .runtime.LoadState == "loaded") .context.ID // empty' | tail -n 1)
|
||||
test -n "$scope_id"
|
||||
scope_params=$(jq -cn --arg name "$scope_id" '{name: $name}')
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$scope_params" | jq -e '.context.Scope'
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$scope_params" | jq -e '.runtime.Scope'
|
||||
# test for SocketContext/Runtime
|
||||
socket_id=$(varlinkctl call --collect /run/systemd/io.systemd.Manager io.systemd.Unit.List '{}' | jq -r '.[] | select(.context.Type == "socket" and .runtime.LoadState == "loaded") .context.ID // empty' | tail -n 1)
|
||||
test -n "$socket_id"
|
||||
socket_params=$(jq -cn --arg name "$socket_id" '{name: $name}')
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$socket_params" | jq -e '.context.Socket'
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$socket_params" | jq -e '.runtime.Socket'
|
||||
# test for SwapContext/Runtime (swap units may not be present on all systems)
|
||||
swap_id=$(varlinkctl call --collect /run/systemd/io.systemd.Manager io.systemd.Unit.List '{}' | jq -r '.[] | select(.context.Type == "swap" and .runtime.LoadState == "loaded") .context.ID // empty' | tail -n 1)
|
||||
if test -n "$swap_id"; then
|
||||
swap_params=$(jq -cn --arg name "$swap_id" '{name: $name}')
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$swap_params" | jq -e '.context.Swap'
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$swap_params" | jq -e '.runtime.Swap'
|
||||
fi
|
||||
# test for TimerContext/Runtime
|
||||
timer_id=$(varlinkctl call --collect /run/systemd/io.systemd.Manager io.systemd.Unit.List '{}' | jq -r '.[] | select(.context.Type == "timer" and .runtime.LoadState == "loaded") .context.ID // empty' | tail -n 1)
|
||||
test -n "$timer_id"
|
||||
timer_params=$(jq -cn --arg name "$timer_id" '{name: $name}')
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$timer_params" | jq -e '.context.Timer'
|
||||
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "$timer_params" | jq -e '.runtime.Timer'
|
||||
|
||||
# test io.systemd.Metrics
|
||||
varlinkctl info /run/systemd/report/io.systemd.Manager
|
||||
|
||||
varlinkctl list-methods /run/systemd/report/io.systemd.Manager
|
||||
varlinkctl list-methods -j /run/systemd/report/io.systemd.Manager io.systemd.Metrics | jq .
|
||||
|
||||
varlinkctl introspect /run/systemd/report/io.systemd.Manager
|
||||
varlinkctl introspect -j /run/systemd/report/io.systemd.Manager io.systemd.Metrics | jq .
|
||||
|
||||
varlinkctl --more call /run/systemd/report/io.systemd.Manager io.systemd.Metrics.List {}
|
||||
varlinkctl --more call /run/systemd/report/io.systemd.Manager io.systemd.Metrics.Describe {}
|
||||
# Wait for the manager to finish re-exec before proceeding — the user manager
|
||||
# tests below use systemd-run which requires a functional PID 1.
|
||||
for _ in {1..10}; do
|
||||
if systemctl is-system-running 2>/dev/null | grep -qE 'running|degraded'; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# test io.systemd.Manager in user manager
|
||||
testuser_uid=$(id -u testuser)
|
||||
@@ -305,10 +222,6 @@ systemd-run --wait --pipe --user --machine testuser@ \
|
||||
systemd-run --wait --pipe --user --machine testuser@ \
|
||||
varlinkctl call "/run/user/$testuser_uid/systemd/io.systemd.Manager" io.systemd.Manager.Describe '{}'
|
||||
|
||||
# test io.systemd.Unit in user manager
|
||||
systemd-run --wait --pipe --user --machine testuser@ \
|
||||
varlinkctl --more call "/run/user/$testuser_uid/systemd/io.systemd.Manager" io.systemd.Unit.List '{}'
|
||||
|
||||
# test --upgrade (protocol upgrade)
|
||||
# The basic --upgrade proxy test is covered by the "varlinkctl serve" tests below (which use
|
||||
# serve+rev/gunzip as the server). The tests here exercise features that need the Python
|
||||
|
||||
Reference in New Issue
Block a user