Skip to content

Commit

Permalink
core: only refuse Type=dbus service enqueue if dbus has pending stop job
Browse files Browse the repository at this point in the history
Follow-up for systemd#27579

In systemd#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 systemd#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 systemd#27588
  • Loading branch information
YHNdnzj committed May 11, 2023
1 parent 1fd5ec5 commit 0a0403e
Showing 1 changed file with 18 additions and 5 deletions.
23 changes: 18 additions & 5 deletions src/core/dbus-unit.c
Expand Up @@ -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)
Expand Down

0 comments on commit 0a0403e

Please sign in to comment.