core: several follow-ups for varlink EnqueueMarkedJobs() method (#40365)

This commit is contained in:
Mike Yuan
2026-01-30 08:38:13 +01:00
committed by GitHub
11 changed files with 150 additions and 105 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 */

View File

@@ -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;

View File

@@ -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);

View File

@@ -2,15 +2,16 @@
#include <sys/prctl.h>
#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);
}

View File

@@ -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);

View File

@@ -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));
}

View File

@@ -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);

View File

@@ -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,

View File

@@ -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);