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-manager.c b/src/core/varlink-manager.c index 8f09205ddd8..9eeeed42ccf 100644 --- a/src/core/varlink-manager.c +++ b/src/core/varlink-manager.c @@ -2,15 +2,16 @@ #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 "dbus-job.h" #include "errno-util.h" #include "glyph-util.h" #include "json-util.h" @@ -304,40 +305,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, - 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)); - 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); @@ -365,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 */ @@ -380,33 +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) - r = varlink_manager_queue_job_one( - link, + if (r < 0) + error_id = SD_VARLINK_ERROR_PERMISSION_DENIED; + else + r = varlink_unit_queue_job_one( u, JOB_TRY_RESTART, JOB_FAIL, - &job_id); + /* reload_if_possible= */ !BIT_SET(u->markers, UNIT_MARKER_NEEDS_RESTART), + &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-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..7d91c8947f0 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,40 @@ 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( + Unit *u, + JobType type, + JobMode mode, + bool reload_if_possible, + uint32_t *ret_job_id, + sd_bus_error *reterr_bus_error) { + + int r; + + assert(u); + + 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_bus_error, &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..73de837940f 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( + Unit *u, + JobType type, + JobMode mode, + bool reload_if_possible, + uint32_t *ret_job_id, + sd_bus_error *reterr_bus_error); 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..cb304f22950 100644 --- a/src/shared/varlink-io.systemd.Manager.c +++ b/src/shared/varlink-io.systemd.Manager.c @@ -181,17 +181,20 @@ 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); -static SD_VARLINK_DEFINE_ERROR(OnlyByDependency); - -static SD_VARLINK_DEFINE_ERROR(BusShuttingDown); - SD_VARLINK_DEFINE_INTERFACE( io_systemd_Manager, "io.systemd.Manager", @@ -203,10 +206,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);