#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"
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);
+}
#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"
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);
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,
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"),
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);