From 0a0403e41cf11188eccde35e3c4b99017f507b06 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Wed, 10 May 2023 13:54:15 +0800 Subject: [PATCH] core: only refuse Type=dbus service enqueue if dbus has pending stop job Follow-up for #27579 In #27579 we refused all StartUnit requests for Type=dbus units if dbus is not running, which means if dbus is manually stopped, user can't use systemctl to start Type=dbus units again, which is incorrect. The only culprit that leads to the cancellation of the whole transaction mentioned in #26799 is job type conflict on dbus. In other words, if the stop job is already running, or dbus is already inactive, everything should be fine. So let's relax the restriction and only refuse job enqueuing if dbus has a *pending* stop job. To summarize, the case we want to avoid is: 1. dbus has a pending stop job 2. StartUnit/ActivationRequest is received 3. Type=dbus service gets started, which has Requires=dbus.socket 4. dbus is pulled in again, resulting in job type conflict What we can support is: 1. dbus is already stopping (in UNIT_DEACTIVATING) 2. StartUnit is received (possibly through systemctl, i.e. on private bus) 3. Type=dbus service gets started, and will wait for running jobs for dbus to complete 4. dbus is started again, thus the Type=dbus service Fixes #27588 --- src/core/dbus-unit.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 5b89c76586652..ccec771f26345 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -1877,11 +1877,24 @@ int bus_unit_queue_job( (type == JOB_RELOAD_OR_START && job_type_collapse(type, u) == JOB_START && u->refuse_manual_start)) return sd_bus_error_setf(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, so let's apply the same check - * used in signal_activation_request(). */ - if (type == JOB_START && u->type == UNIT_SERVICE && - SERVICE(u)->type == SERVICE_DBUS && !manager_dbus_is_running(u->manager)) - return sd_bus_error_set(error, BUS_ERROR_SHUTTING_DOWN, "Refusing activation, D-Bus is not running."); + if (type == JOB_START && u->type == UNIT_SERVICE && SERVICE(u)->type == SERVICE_DBUS) { + /* 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 + * job that pulls in dbus again would result in job type conflict. */ + + bool refuse = false; + + FOREACH_STRING(dbus_unit, SPECIAL_DBUS_SOCKET, SPECIAL_DBUS_SERVICE) { + Unit *dbus; + + dbus = manager_get_unit(u->manager, dbus_unit); + if (dbus && unit_stop_pending(dbus)) + refuse = true; + } + + if (refuse) + return sd_bus_error_set(error, BUS_ERROR_SHUTTING_DOWN, "Refusing activation, D-Bus is shutting down."); + } r = sd_bus_message_new_method_return(message, &reply); if (r < 0)