From 339fa01d21ce8c73c9e6abafbcdf9416d6e15598 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Fri, 16 Jan 2026 21:56:30 +0100 Subject: [PATCH 1/4] core/varlink-manager: fix reload unit marker handling If both restart and reload markers are set, the former takes precedence. --- src/core/varlink-manager.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/varlink-manager.c b/src/core/varlink-manager.c index 8f09205ddd8..f1b6f8c0e64 100644 --- a/src/core/varlink-manager.c +++ b/src/core/varlink-manager.c @@ -309,13 +309,14 @@ static int varlink_manager_queue_job_one( Unit *u, JobType type, JobMode mode, + bool reload_if_possible, uint32_t *ret_job_id) { int r; assert(u); - r = unit_queue_job_check_and_mangle_type(u, &type, /* reload_if_possible= */ BIT_SET(u->markers, UNIT_MARKER_NEEDS_RELOAD)); + r = unit_queue_job_check_and_mangle_type(u, &type, reload_if_possible); if (r == -ENOENT) return varlink_error_no_such_unit(link, "name"); if (r == -ELIBEXEC) @@ -386,6 +387,7 @@ int vl_method_enqueue_marked_jobs_manager(sd_varlink *link, sd_json_variant *par u, JOB_TRY_RESTART, JOB_FAIL, + /* reload_if_possible= */ !BIT_SET(u->markers, UNIT_MARKER_NEEDS_RESTART), &job_id); if (ERRNO_IS_NEG_RESOURCE(r)) return r; From 7228da61b9bb894839664ce733a0bec450fbdec2 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Fri, 16 Jan 2026 21:00:30 +0100 Subject: [PATCH 2/4] core/varlink-manager: move varlink_unit_queue_job_one() to varlink-unit It's quite likely that we'll introduce StartUnit() and alike for varlink in the future, so the job enqueuing interface should be generic. On top of that, the errors really belong to Unit rather than Manager. --- src/core/varlink-manager.c | 38 +----------------- src/core/varlink-manager.h | 2 - src/core/varlink-unit.c | 51 +++++++++++++++++++++---- src/core/varlink-unit.h | 13 ++++++- src/shared/varlink-io.systemd.Manager.c | 8 ---- src/shared/varlink-io.systemd.Unit.c | 17 ++++++--- 6 files changed, 69 insertions(+), 60 deletions(-) diff --git a/src/core/varlink-manager.c b/src/core/varlink-manager.c index f1b6f8c0e64..25140bf465c 100644 --- a/src/core/varlink-manager.c +++ b/src/core/varlink-manager.c @@ -10,7 +10,6 @@ #include "build.h" #include "bus-polkit.h" #include "confidential-virt.h" -#include "dbus-job.h" #include "errno-util.h" #include "glyph-util.h" #include "json-util.h" @@ -304,41 +303,6 @@ int vl_method_reexecute_manager(sd_varlink *link, sd_json_variant *parameters, s return 1; } -static int varlink_manager_queue_job_one( - sd_varlink *link, - Unit *u, - JobType type, - JobMode mode, - bool reload_if_possible, - uint32_t *ret_job_id) { - - int r; - - assert(u); - - r = unit_queue_job_check_and_mangle_type(u, &type, reload_if_possible); - if (r == -ENOENT) - return varlink_error_no_such_unit(link, "name"); - if (r == -ELIBEXEC) - return sd_varlink_errorb(link, VARLINK_ERROR_MANAGER_ONLY_BY_DEPENDENCY); - if (r == -ESHUTDOWN) - return sd_varlink_errorb(link, VARLINK_ERROR_MANAGER_BUS_SHUTTING_DOWN); - if (r < 0) - return r; - - Job *j; - r = manager_add_job(u->manager, type, u, mode, /* reterr_error= */ NULL, &j); - if (r < 0) - return r; - - /* Before we send the method reply, force out the announcement JobNew for this job */ - bus_job_send_pending_change_signal(j, /* including_new= */ true); - - if (ret_job_id) - *ret_job_id = j->id; - - return 0; -} int vl_method_enqueue_marked_jobs_manager(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { Manager *manager = ASSERT_PTR(userdata); @@ -382,7 +346,7 @@ int vl_method_enqueue_marked_jobs_manager(sd_varlink *link, sd_json_variant *par r = mac_selinux_unit_access_check_varlink(u, link, job_type_to_access_method(JOB_TRY_RESTART)); if (r >= 0) - r = varlink_manager_queue_job_one( + r = varlink_unit_queue_job_one( link, u, JOB_TRY_RESTART, diff --git a/src/core/varlink-manager.h b/src/core/varlink-manager.h index c84c652e554..e5111eb58dc 100644 --- a/src/core/varlink-manager.h +++ b/src/core/varlink-manager.h @@ -4,8 +4,6 @@ #include "core-forward.h" #define VARLINK_ERROR_MANAGER_RATE_LIMIT_REACHED "io.systemd.Manager.RateLimitReached" -#define VARLINK_ERROR_MANAGER_ONLY_BY_DEPENDENCY "io.systemd.Manager.OnlyByDependency" -#define VARLINK_ERROR_MANAGER_BUS_SHUTTING_DOWN "io.systemd.Manager.BusShuttingDown" int vl_method_describe_manager(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); int vl_method_reexecute_manager(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); diff --git a/src/core/varlink-unit.c b/src/core/varlink-unit.c index 2d3f02d4c0b..210added603 100644 --- a/src/core/varlink-unit.c +++ b/src/core/varlink-unit.c @@ -5,6 +5,7 @@ #include "bitfield.h" #include "cgroup.h" #include "condition.h" +#include "dbus-job.h" #include "execute.h" #include "format-util.h" #include "install.h" @@ -382,13 +383,6 @@ static void unit_lookup_parameters_done(UnitLookupParameters *p) { pidref_done(&p->pidref); } -int varlink_error_no_such_unit(sd_varlink *v, const char *name) { - return sd_varlink_errorbo( - ASSERT_PTR(v), - VARLINK_ERROR_UNIT_NO_SUCH_UNIT, - JSON_BUILD_PAIR_STRING_NON_EMPTY("parameter", name)); -} - static int varlink_error_conflict_lookup_parameters(sd_varlink *v, const UnitLookupParameters *p) { log_debug_errno( ESRCH, @@ -511,3 +505,46 @@ int vl_method_list_units(sd_varlink *link, sd_json_variant *parameters, sd_varli return sd_varlink_error(link, "io.systemd.Manager.NoSuchUnit", NULL); } + +int varlink_unit_queue_job_one( + sd_varlink *link, + Unit *u, + JobType type, + JobMode mode, + bool reload_if_possible, + uint32_t *ret_job_id) { + + int r; + + assert(u); + + r = unit_queue_job_check_and_mangle_type(u, &type, reload_if_possible); + if (r == -ENOENT) + return varlink_error_no_such_unit(link, "name"); + if (r == -ELIBEXEC) + return sd_varlink_error(link, VARLINK_ERROR_UNIT_ONLY_BY_DEPENDENCY); + if (r == -ESHUTDOWN) + return sd_varlink_error(link, VARLINK_ERROR_UNIT_DBUS_SHUTTING_DOWN); + if (r < 0) + return r; + + Job *j; + r = manager_add_job(u->manager, type, u, mode, /* reterr_error= */ NULL, &j); + if (r < 0) + return r; + + /* Before we send the method reply, force out the announcement JobNew for this job */ + bus_job_send_pending_change_signal(j, /* including_new= */ true); + + if (ret_job_id) + *ret_job_id = j->id; + + return 0; +} + +int varlink_error_no_such_unit(sd_varlink *v, const char *name) { + return sd_varlink_errorbo( + ASSERT_PTR(v), + VARLINK_ERROR_UNIT_NO_SUCH_UNIT, + JSON_BUILD_PAIR_STRING_NON_EMPTY("parameter", name)); +} diff --git a/src/core/varlink-unit.h b/src/core/varlink-unit.h index e60d5a5b9bc..0913d8f0092 100644 --- a/src/core/varlink-unit.h +++ b/src/core/varlink-unit.h @@ -4,6 +4,17 @@ #include "core-forward.h" #define VARLINK_ERROR_UNIT_NO_SUCH_UNIT "io.systemd.Unit.NoSuchUnit" +#define VARLINK_ERROR_UNIT_ONLY_BY_DEPENDENCY "io.systemd.Unit.OnlyByDependency" +#define VARLINK_ERROR_UNIT_DBUS_SHUTTING_DOWN "io.systemd.Unit.DBusShuttingDown" + +int vl_method_list_units(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); + +int varlink_unit_queue_job_one( + sd_varlink *link, + Unit *u, + JobType type, + JobMode mode, + bool reload_if_possible, + uint32_t *ret_job_id); int varlink_error_no_such_unit(sd_varlink *v, const char *name); -int vl_method_list_units(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); diff --git a/src/shared/varlink-io.systemd.Manager.c b/src/shared/varlink-io.systemd.Manager.c index ecae5a0924b..79c5c7e7168 100644 --- a/src/shared/varlink-io.systemd.Manager.c +++ b/src/shared/varlink-io.systemd.Manager.c @@ -188,10 +188,6 @@ static SD_VARLINK_DEFINE_METHOD( static SD_VARLINK_DEFINE_ERROR(RateLimitReached); -static SD_VARLINK_DEFINE_ERROR(OnlyByDependency); - -static SD_VARLINK_DEFINE_ERROR(BusShuttingDown); - SD_VARLINK_DEFINE_INTERFACE( io_systemd_Manager, "io.systemd.Manager", @@ -203,10 +199,6 @@ SD_VARLINK_DEFINE_INTERFACE( SD_VARLINK_SYMBOL_COMMENT("Enqueue all marked jobs"), &vl_method_EnqueueMarkedJobs, &vl_error_RateLimitReached, - SD_VARLINK_SYMBOL_COMMENT("Unit operation may be requested by dependency only"), - &vl_error_OnlyByDependency, - SD_VARLINK_SYMBOL_COMMENT("Operation refused, the bus is shutting down"), - &vl_error_BusShuttingDown, &vl_type_ManagerContext, &vl_type_ManagerRuntime, &vl_type_Timestamp, diff --git a/src/shared/varlink-io.systemd.Unit.c b/src/shared/varlink-io.systemd.Unit.c index f8e255dd5b9..19d384dd913 100644 --- a/src/shared/varlink-io.systemd.Unit.c +++ b/src/shared/varlink-io.systemd.Unit.c @@ -1003,10 +1003,6 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE( SD_VARLINK_FIELD_COMMENT("The cgroup runtime of the unit"), SD_VARLINK_DEFINE_FIELD_BY_TYPE(CGroup, CGroupRuntime, SD_VARLINK_NULLABLE)); -static SD_VARLINK_DEFINE_ERROR( - NoSuchUnit, - SD_VARLINK_DEFINE_FIELD(parameter, SD_VARLINK_STRING, SD_VARLINK_NULLABLE)); - static SD_VARLINK_DEFINE_METHOD_FULL( List, SD_VARLINK_SUPPORTS_MORE, @@ -1023,6 +1019,13 @@ static SD_VARLINK_DEFINE_METHOD_FULL( SD_VARLINK_FIELD_COMMENT("Runtime information of the unit"), SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(runtime, UnitRuntime, 0)); +static SD_VARLINK_DEFINE_ERROR( + NoSuchUnit, + SD_VARLINK_DEFINE_FIELD(parameter, SD_VARLINK_STRING, SD_VARLINK_NULLABLE)); + +static SD_VARLINK_DEFINE_ERROR(OnlyByDependency); +static SD_VARLINK_DEFINE_ERROR(DBusShuttingDown); + SD_VARLINK_DEFINE_INTERFACE( io_systemd_Unit, "io.systemd.Unit", @@ -1087,4 +1090,8 @@ SD_VARLINK_DEFINE_INTERFACE( /* Errors */ SD_VARLINK_SYMBOL_COMMENT("No matching unit found"), - &vl_error_NoSuchUnit); + &vl_error_NoSuchUnit, + SD_VARLINK_SYMBOL_COMMENT("Job for the unit may only be enqueued by dependencies"), + &vl_error_OnlyByDependency, + SD_VARLINK_SYMBOL_COMMENT("A unit that requires D-Bus cannot be started as D-Bus is shutting down"), + &vl_error_DBusShuttingDown); From 927b861addc8162959da6ce782ba8c8d8522ab1a Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Sat, 17 Jan 2026 15:27:24 +0100 Subject: [PATCH 3/4] core/unit: make unit_queue_job_check_and_mangle_type() report bus error Our internal logic speaks dbus errors, and that's not changing anytime soon. Bus errors carried more comprehensive error message hence let's always return a sd_bus_error on failure, and introduce varlink_error_id_from_bus_error() for translation. --- src/core/dbus-unit.c | 18 +++++------------- src/core/unit.c | 21 +++++++++++---------- src/core/unit.h | 2 +- src/core/varlink-common.c | 24 ++++++++++++++++++++++++ src/core/varlink-common.h | 2 ++ src/core/varlink-unit.c | 16 ++++++++-------- 6 files changed, 51 insertions(+), 32 deletions(-) diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 18a95955bc9..2c61ddeb80c 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -1962,19 +1962,11 @@ int bus_unit_queue_job_one( assert(u); - r = unit_queue_job_check_and_mangle_type(u, &type, /* reload_if_possible= */ FLAGS_SET(flags, BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE)); - if (r == -ENOENT) - return sd_bus_error_setf(reterr_error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id); - if (r == -ELIBEXEC) - return sd_bus_error_setf(reterr_error, - BUS_ERROR_ONLY_BY_DEPENDENCY, - "Operation refused, unit %s may be requested by dependency only (it is configured to refuse manual start/stop).", - u->id); - if (r == -ESHUTDOWN) - return sd_bus_error_setf(reterr_error, - BUS_ERROR_SHUTTING_DOWN, - "Operation for unit %s refused, D-Bus is shutting down.", - u->id); + r = unit_queue_job_check_and_mangle_type( + u, + &type, + /* reload_if_possible= */ FLAGS_SET(flags, BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE), + reterr_error); if (r < 0) return r; diff --git a/src/core/unit.c b/src/core/unit.c index 96f56a28e04..e172a9e38c0 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -7005,13 +7005,8 @@ UnitDependency unit_mount_dependency_type_to_dependency_type(UnitMountDependency int unit_queue_job_check_and_mangle_type( Unit *u, JobType *type, /* input and output */ - bool reload_if_possible) { - - /* Returns: - * - * -ENOENT → Unit not loaded - * -ELIBEXEC → Unit can only be activated via dependency, not directly - * -ESHUTDOWN → System bus is shutting down */ + bool reload_if_possible, + sd_bus_error *reterr_error) { JobType t; @@ -7030,13 +7025,16 @@ int unit_queue_job_check_and_mangle_type( /* Our transaction logic allows units not properly loaded to be stopped. But if already dead * let's return clear error to caller. */ if (t == JOB_STOP && UNIT_IS_LOAD_ERROR(u->load_state) && unit_active_state(u) == UNIT_INACTIVE) - return -ENOENT; + return sd_bus_error_setf(reterr_error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id); if ((t == JOB_START && u->refuse_manual_start) || (t == JOB_STOP && u->refuse_manual_stop) || (IN_SET(t, JOB_RESTART, JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop)) || (t == JOB_RELOAD_OR_START && job_type_collapse(t, u) == JOB_START && u->refuse_manual_start)) - return -ELIBEXEC; + return sd_bus_error_setf(reterr_error, + BUS_ERROR_ONLY_BY_DEPENDENCY, + "Operation refused, unit %s may be requested by dependency only (it is configured to refuse manual start/stop).", + u->id); /* dbus-broker issues StartUnit for activation requests, and Type=dbus services automatically * gain dependency on dbus.socket. Therefore, if dbus has a pending stop job, the new start @@ -7050,7 +7048,10 @@ int unit_queue_job_check_and_mangle_type( FOREACH_STRING(dbus_unit, SPECIAL_DBUS_SOCKET, SPECIAL_DBUS_SERVICE) { Unit *dbus = manager_get_unit(u->manager, dbus_unit); if (dbus && unit_stop_pending(dbus)) - return -ESHUTDOWN; + return sd_bus_error_setf(reterr_error, + BUS_ERROR_SHUTTING_DOWN, + "Operation for unit %s refused, D-Bus is shutting down.", + u->id); } *type = t; diff --git a/src/core/unit.h b/src/core/unit.h index feaed9125ad..55a51a6f0e0 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -1092,7 +1092,7 @@ UnitDependency unit_mount_dependency_type_to_dependency_type(UnitMountDependency DECLARE_STRING_TABLE_LOOKUP(oom_policy, OOMPolicy); -int unit_queue_job_check_and_mangle_type(Unit *u, JobType *type, bool reload_if_possible); +int unit_queue_job_check_and_mangle_type(Unit *u, JobType *type, bool reload_if_possible, sd_bus_error *reterr_error); /* Macros which append UNIT= or USER_UNIT= to the message */ diff --git a/src/core/varlink-common.c b/src/core/varlink-common.c index babed68378f..e388b163392 100644 --- a/src/core/varlink-common.c +++ b/src/core/varlink-common.c @@ -1,9 +1,33 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "sd-bus.h" + +#include "bus-common-errors.h" #include "cpu-set-util.h" #include "json-util.h" #include "rlimit-util.h" #include "varlink-common.h" +#include "varlink-unit.h" + +const char* varlink_error_id_from_bus_error(const sd_bus_error *e) { + static const struct { + const char *bus_error; + const char *varlink_error; + } map[] = { + { BUS_ERROR_NO_SUCH_UNIT, VARLINK_ERROR_UNIT_NO_SUCH_UNIT }, + { BUS_ERROR_ONLY_BY_DEPENDENCY, VARLINK_ERROR_UNIT_ONLY_BY_DEPENDENCY }, + { BUS_ERROR_SHUTTING_DOWN, VARLINK_ERROR_UNIT_DBUS_SHUTTING_DOWN }, + }; + + if (!sd_bus_error_is_set(e)) + return NULL; + + FOREACH_ELEMENT(i, map) + if (sd_bus_error_has_name(e, i->bus_error)) + return i->varlink_error; + + return NULL; +} int rlimit_build_json(sd_json_variant **ret, const char *name, void *userdata) { const struct rlimit *rl = userdata; diff --git a/src/core/varlink-common.h b/src/core/varlink-common.h index 5918f9ac68f..82d1458dd96 100644 --- a/src/core/varlink-common.h +++ b/src/core/varlink-common.h @@ -6,3 +6,5 @@ int rlimit_build_json(sd_json_variant **ret, const char *name, void *userdata); int rlimit_table_build_json(sd_json_variant **ret, const char *name, void *userdata); int cpuset_build_json(sd_json_variant **ret, const char *name, void *userdata); + +const char* varlink_error_id_from_bus_error(const sd_bus_error *e); diff --git a/src/core/varlink-unit.c b/src/core/varlink-unit.c index 210added603..8e061c0d50f 100644 --- a/src/core/varlink-unit.c +++ b/src/core/varlink-unit.c @@ -514,19 +514,19 @@ int varlink_unit_queue_job_one( bool reload_if_possible, uint32_t *ret_job_id) { + _cleanup_(sd_bus_error_free) sd_bus_error bus_error = SD_BUS_ERROR_NULL; int r; assert(u); - r = unit_queue_job_check_and_mangle_type(u, &type, reload_if_possible); - if (r == -ENOENT) - return varlink_error_no_such_unit(link, "name"); - if (r == -ELIBEXEC) - return sd_varlink_error(link, VARLINK_ERROR_UNIT_ONLY_BY_DEPENDENCY); - if (r == -ESHUTDOWN) - return sd_varlink_error(link, VARLINK_ERROR_UNIT_DBUS_SHUTTING_DOWN); - if (r < 0) + r = unit_queue_job_check_and_mangle_type(u, &type, reload_if_possible, &bus_error); + if (r < 0) { + const char *error_id = varlink_error_id_from_bus_error(&bus_error); + if (error_id) + return sd_varlink_error(link, error_id); + return r; + } Job *j; r = manager_add_job(u->manager, type, u, mode, /* reterr_error= */ NULL, &j); From f7823fdae085e303baf1233d6d5c5056014bd6e8 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Fri, 16 Jan 2026 21:58:28 +0100 Subject: [PATCH 4/4] core/varlink-manager: report individual job enqueue result if client sets 'more' One nice property varlink has is that we can nicely report result of individual operations on each unit, through the 'more' mechanism. Hence do so for the EnqueueMarkedJobs() method. Also, apply 502d6f4bc9b96009627f923dcc0ab53e5a181d78 for varlink method too. While at it, use lowerCamelCase for method reply fields, as requested in https://github.com/systemd/systemd/pull/40365#discussion_r2702344239. --- src/core/varlink-manager.c | 52 ++++++++++++++++--------- src/core/varlink-unit.c | 16 +++----- src/core/varlink-unit.h | 4 +- src/shared/varlink-io.systemd.Manager.c | 13 +++++-- 4 files changed, 50 insertions(+), 35 deletions(-) diff --git a/src/core/varlink-manager.c b/src/core/varlink-manager.c index 25140bf465c..9eeeed42ccf 100644 --- a/src/core/varlink-manager.c +++ b/src/core/varlink-manager.c @@ -2,12 +2,14 @@ #include +#include "sd-bus.h" #include "sd-varlink.h" #include "alloc-util.h" #include "architecture.h" #include "bitfield.h" #include "build.h" +#include "bus-error.h" #include "bus-polkit.h" #include "confidential-virt.h" #include "errno-util.h" @@ -330,11 +332,12 @@ int vl_method_enqueue_marked_jobs_manager(sd_varlink *link, sd_json_variant *par log_info("Queuing reload/restart jobs for marked units%s", glyph(GLYPH_ELLIPSIS)); - _cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL, *reply = NULL; Unit *u; char *k; int ret = 0; HASHMAP_FOREACH_KEY(u, k, manager->units) { + _cleanup_(sd_bus_error_free) sd_bus_error bus_error = SD_BUS_ERROR_NULL; + const char *error_id = NULL; uint32_t job_id = 0; /* silence 'maybe-uninitialized' compiler warning */ /* ignore aliases */ @@ -345,34 +348,45 @@ int vl_method_enqueue_marked_jobs_manager(sd_varlink *link, sd_json_variant *par continue; r = mac_selinux_unit_access_check_varlink(u, link, job_type_to_access_method(JOB_TRY_RESTART)); - if (r >= 0) + if (r < 0) + error_id = SD_VARLINK_ERROR_PERMISSION_DENIED; + else r = varlink_unit_queue_job_one( - link, u, JOB_TRY_RESTART, JOB_FAIL, /* reload_if_possible= */ !BIT_SET(u->markers, UNIT_MARKER_NEEDS_RESTART), - &job_id); + &job_id, + &bus_error); if (ERRNO_IS_NEG_RESOURCE(r)) return r; - RET_GATHER(ret, r); - if (r >= 0) { - r = sd_json_variant_append_arrayb(&array, SD_JSON_BUILD_UNSIGNED(job_id)); - if (r < 0) - return r; - } + if (r < 0) + RET_GATHER(ret, log_unit_warning_errno(u, r, "Failed to enqueue marked job: %s", + bus_error_message(&bus_error, r))); + + if (!FLAGS_SET(flags, SD_VARLINK_METHOD_MORE)) + continue; + + if (r < 0) { + if (!error_id) + error_id = varlink_error_id_from_bus_error(&bus_error); + + const char *error_msg = bus_error.message ?: error_id ? NULL : STRERROR(r); + + r = sd_varlink_notifybo(link, + SD_JSON_BUILD_PAIR_STRING("unitID", u->id), + JSON_BUILD_PAIR_STRING_NON_EMPTY("error", error_id), + JSON_BUILD_PAIR_STRING_NON_EMPTY("errorMessage", error_msg)); + } else + r = sd_varlink_notifybo(link, + SD_JSON_BUILD_PAIR_STRING("unitID", u->id), + SD_JSON_BUILD_PAIR_INTEGER("jobID", job_id)); + if (r < 0) + return r; } if (ret < 0) return ret; - /* Return parameter is not nullable, build empty array if there's nothing to return */ - if (array) - r = sd_json_buildo(&reply, SD_JSON_BUILD_PAIR_VARIANT("JobIDs", array)); - else - r = sd_json_buildo(&reply, SD_JSON_BUILD_PAIR_EMPTY_ARRAY("JobIDs")); - if (r < 0) - return r; - - return sd_varlink_reply(link, reply); + return sd_varlink_reply(link, NULL); } diff --git a/src/core/varlink-unit.c b/src/core/varlink-unit.c index 8e061c0d50f..7d91c8947f0 100644 --- a/src/core/varlink-unit.c +++ b/src/core/varlink-unit.c @@ -507,29 +507,23 @@ int vl_method_list_units(sd_varlink *link, sd_json_variant *parameters, sd_varli } int varlink_unit_queue_job_one( - sd_varlink *link, Unit *u, JobType type, JobMode mode, bool reload_if_possible, - uint32_t *ret_job_id) { + uint32_t *ret_job_id, + sd_bus_error *reterr_bus_error) { - _cleanup_(sd_bus_error_free) sd_bus_error bus_error = SD_BUS_ERROR_NULL; int r; assert(u); - r = unit_queue_job_check_and_mangle_type(u, &type, reload_if_possible, &bus_error); - if (r < 0) { - const char *error_id = varlink_error_id_from_bus_error(&bus_error); - if (error_id) - return sd_varlink_error(link, error_id); - + r = unit_queue_job_check_and_mangle_type(u, &type, reload_if_possible, reterr_bus_error); + if (r < 0) return r; - } Job *j; - r = manager_add_job(u->manager, type, u, mode, /* reterr_error= */ NULL, &j); + r = manager_add_job(u->manager, type, u, mode, reterr_bus_error, &j); if (r < 0) return r; diff --git a/src/core/varlink-unit.h b/src/core/varlink-unit.h index 0913d8f0092..73de837940f 100644 --- a/src/core/varlink-unit.h +++ b/src/core/varlink-unit.h @@ -10,11 +10,11 @@ int vl_method_list_units(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); int varlink_unit_queue_job_one( - sd_varlink *link, Unit *u, JobType type, JobMode mode, bool reload_if_possible, - uint32_t *ret_job_id); + uint32_t *ret_job_id, + sd_bus_error *reterr_bus_error); int varlink_error_no_such_unit(sd_varlink *v, const char *name); diff --git a/src/shared/varlink-io.systemd.Manager.c b/src/shared/varlink-io.systemd.Manager.c index 79c5c7e7168..cb304f22950 100644 --- a/src/shared/varlink-io.systemd.Manager.c +++ b/src/shared/varlink-io.systemd.Manager.c @@ -181,10 +181,17 @@ static SD_VARLINK_DEFINE_METHOD( static SD_VARLINK_DEFINE_METHOD( Reload); -static SD_VARLINK_DEFINE_METHOD( +static SD_VARLINK_DEFINE_METHOD_FULL( EnqueueMarkedJobs, - SD_VARLINK_FIELD_COMMENT("IDs of enqueued jobs"), - SD_VARLINK_DEFINE_OUTPUT(JobIDs, SD_VARLINK_INT, SD_VARLINK_ARRAY)); + SD_VARLINK_SUPPORTS_MORE, + SD_VARLINK_FIELD_COMMENT("Enqueued unit ID"), + SD_VARLINK_DEFINE_OUTPUT(unitID, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("ID of enqueued job (if successful)"), + SD_VARLINK_DEFINE_OUTPUT(jobID, SD_VARLINK_INT, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("Varlink error ID (on failure)"), + SD_VARLINK_DEFINE_OUTPUT(error, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("Job enqueue error message (on failure)"), + SD_VARLINK_DEFINE_OUTPUT(errorMessage, SD_VARLINK_STRING, SD_VARLINK_NULLABLE)); static SD_VARLINK_DEFINE_ERROR(RateLimitReached);