core: introduce io.systemd.Job interface with List, Cancel, and ClearAll methods
Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,8 +2,12 @@
|
||||
|
||||
#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"
|
||||
@@ -47,3 +51,202 @@ int job_build_json(sd_json_variant **ret, const char *name, void *userdata) {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -3,4 +3,10 @@
|
||||
|
||||
#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);
|
||||
|
||||
@@ -14,11 +14,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"
|
||||
@@ -398,6 +400,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);
|
||||
@@ -406,6 +409,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,
|
||||
|
||||
@@ -58,10 +58,37 @@ SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
Job,
|
||||
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"),
|
||||
@@ -69,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);
|
||||
|
||||
Reference in New Issue
Block a user