[SERVICE_FAILED_BEFORE_AUTO_RESTART] = "failed-before-auto-restart",
[SERVICE_DEAD_RESOURCES_PINNED] = "dead-resources-pinned",
[SERVICE_AUTO_RESTART] = "auto-restart",
+ [SERVICE_AUTO_RESTART_QUEUED] = "auto-restart-queued",
[SERVICE_CLEANING] = "cleaning",
};
SERVICE_FAILED_BEFORE_AUTO_RESTART,
SERVICE_DEAD_RESOURCES_PINNED, /* Like SERVICE_DEAD, but with pinned resources */
SERVICE_AUTO_RESTART,
+ SERVICE_AUTO_RESTART_QUEUED,
SERVICE_CLEANING,
_SERVICE_STATE_MAX,
_SERVICE_STATE_INVALID = -EINVAL,
[JOB_IGNORE_DEPENDENCIES] = "ignore-dependencies",
[JOB_IGNORE_REQUIREMENTS] = "ignore-requirements",
[JOB_TRIGGERING] = "triggering",
+ [JOB_RESTART_DEPENDENCIES] = "restart-dependencies",
};
DEFINE_STRING_TABLE_LOOKUP(job_mode, JobMode);
JOB_IGNORE_DEPENDENCIES, /* Ignore both requirement and ordering dependencies */
JOB_IGNORE_REQUIREMENTS, /* Ignore requirement dependencies */
JOB_TRIGGERING, /* Adds TRIGGERED_BY dependencies to the same transaction */
+ JOB_RESTART_DEPENDENCIES,/* A "start" job for the specified unit becomes "restart" for depending units */
_JOB_MODE_MAX,
_JOB_MODE_INVALID = -EINVAL,
};
if (mode == JOB_TRIGGERING && type != JOB_STOP)
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "--job-mode=triggering is only valid for stop.");
+ if (mode == JOB_RESTART_DEPENDENCIES && type != JOB_START)
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "--job-mode=restart-dependencies is only valid for start.");
+
log_unit_debug(unit, "Trying to enqueue job %s/%s/%s", unit->id, job_type_to_string(type), job_mode_to_string(mode));
type = job_type_collapse(type, unit);
/* by= */ NULL,
TRANSACTION_MATTERS |
(IN_SET(mode, JOB_IGNORE_DEPENDENCIES, JOB_IGNORE_REQUIREMENTS) ? TRANSACTION_IGNORE_REQUIREMENTS : 0) |
- (mode == JOB_IGNORE_DEPENDENCIES ? TRANSACTION_IGNORE_ORDER : 0),
+ (mode == JOB_IGNORE_DEPENDENCIES ? TRANSACTION_IGNORE_ORDER : 0) |
+ (mode == JOB_RESTART_DEPENDENCIES ? TRANSACTION_PROPAGATE_START_AS_RESTART : 0),
error);
if (r < 0)
return r;
[SERVICE_FAILED_BEFORE_AUTO_RESTART] = UNIT_FAILED,
[SERVICE_DEAD_RESOURCES_PINNED] = UNIT_INACTIVE,
[SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
+ [SERVICE_AUTO_RESTART_QUEUED] = UNIT_ACTIVATING,
[SERVICE_CLEANING] = UNIT_MAINTENANCE,
};
[SERVICE_FAILED_BEFORE_AUTO_RESTART] = UNIT_FAILED,
[SERVICE_DEAD_RESOURCES_PINNED] = UNIT_INACTIVE,
[SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
+ [SERVICE_AUTO_RESTART_QUEUED] = UNIT_ACTIVATING,
[SERVICE_CLEANING] = UNIT_MAINTENANCE,
};
if (IN_SET(state,
SERVICE_DEAD, SERVICE_FAILED,
- SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART, SERVICE_AUTO_RESTART,
+ SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART, SERVICE_AUTO_RESTART, SERVICE_AUTO_RESTART_QUEUED,
SERVICE_DEAD_RESOURCES_PINNED)) {
unit_unwatch_all_pids(UNIT(s));
unit_dequeue_rewatch_pids(UNIT(s));
if (!IN_SET(s->deserialized_state,
SERVICE_DEAD, SERVICE_FAILED,
- SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART, SERVICE_AUTO_RESTART,
+ SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART, SERVICE_AUTO_RESTART, SERVICE_AUTO_RESTART_QUEUED,
SERVICE_CLEANING,
SERVICE_DEAD_RESOURCES_PINNED)) {
(void) unit_enqueue_rewatch_pids(u);
assert(s);
- if (IN_SET(s->state, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART, SERVICE_AUTO_RESTART))
+ if (IN_SET(s->state, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART, SERVICE_AUTO_RESTART, SERVICE_AUTO_RESTART_QUEUED))
return true;
return unit_will_restart_default(u);
return;
}
- /* Any units that are bound to this service must also be
- * restarted. We use JOB_RESTART (instead of the more obvious
- * JOB_START) here so that those dependency jobs will be added
- * as well. */
- r = manager_add_job(UNIT(s)->manager, JOB_RESTART, UNIT(s), JOB_REPLACE, NULL, &error, NULL);
+ /* Any units that are bound to this service must also be restarted. We use JOB_START for ourselves
+ * but then set JOB_RESTART_DEPENDENCIES which will enqueue JOB_RESTART for those dependency jobs. */
+ r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(s), JOB_RESTART_DEPENDENCIES, NULL, &error, NULL);
if (r < 0)
goto fail;
"Scheduled restart job, restart counter is at %u.", s->n_restarts),
"N_RESTARTS=%u", s->n_restarts);
+ service_set_state(s, SERVICE_AUTO_RESTART_QUEUED);
+
/* Notify clients about changed restart counter */
unit_add_to_dbus_queue(UNIT(s));
-
- /* Note that we stay in the SERVICE_AUTO_RESTART state here, it will be canceled as part of the
- * service_stop() call that is executed as part of JOB_RESTART. */
return;
fail:
if (IN_SET(s->state, SERVICE_AUTO_RESTART, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART))
return -EAGAIN;
- assert(IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_DEAD_RESOURCES_PINNED));
+ assert(IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_DEAD_RESOURCES_PINNED, SERVICE_AUTO_RESTART_QUEUED));
r = unit_acquire_invocation_id(u);
if (r < 0)
return 0;
case SERVICE_AUTO_RESTART:
- /* A restart will be scheduled or is in progress. */
+ case SERVICE_AUTO_RESTART_QUEUED:
+ /* Give up on the auto restart */
service_set_state(s, service_determine_dead_state(s));
return 0;
/* If the cgroup empty notification comes when the unit is not active, we must have failed to clean
* up the cgroup earlier and should do it now. */
case SERVICE_AUTO_RESTART:
+ case SERVICE_AUTO_RESTART_QUEUED:
unit_prune_cgroup(u);
break;
}
}
- if (IN_SET(type, JOB_STOP, JOB_RESTART)) {
- _cleanup_set_free_ Set *propagated_restart = NULL;
- /* We propagate STOP as STOP, but RESTART only as TRY_RESTART, in order not to start
- * dependencies that are not around. */
+ _cleanup_set_free_ Set *propagated_restart = NULL;
- if (type == JOB_RESTART)
- UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PROPAGATE_RESTART) {
- JobType nt;
+ if (type == JOB_RESTART || (type == JOB_START && FLAGS_SET(flags, TRANSACTION_PROPAGATE_START_AS_RESTART))) {
- r = set_ensure_put(&propagated_restart, NULL, dep);
- if (r < 0)
- return r;
+ /* We propagate RESTART only as TRY_RESTART, in order not to start dependencies that
+ * are not around. */
+
+ UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PROPAGATE_RESTART) {
+ JobType nt;
- nt = job_type_collapse(JOB_TRY_RESTART, dep);
- if (nt == JOB_NOP)
- continue;
+ r = set_ensure_put(&propagated_restart, NULL, dep);
+ if (r < 0)
+ return r;
- r = transaction_add_job_and_dependencies(tr, nt, dep, ret, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER), e);
- if (r < 0) {
- if (r != -EBADR) /* job type not applicable */
- return r;
+ nt = job_type_collapse(JOB_TRY_RESTART, dep);
+ if (nt == JOB_NOP)
+ continue;
- sd_bus_error_free(e);
- }
+ r = transaction_add_job_and_dependencies(tr, nt, dep, ret, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER), e);
+ if (r < 0) {
+ if (r != -EBADR) /* job type not applicable */
+ return r;
+
+ sd_bus_error_free(e);
}
+ }
+ }
+ if (type == JOB_STOP) {
/* The 'stop' part of a restart job is also propagated to units with
* UNIT_ATOM_PROPAGATE_STOP */
+
UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PROPAGATE_STOP) {
/* Units experienced restart propagation are skipped */
if (set_contains(propagated_restart, dep))
DEFINE_TRIVIAL_CLEANUP_FUNC(Transaction*, transaction_abort_and_free);
typedef enum TransactionAddFlags {
- TRANSACTION_MATTERS = 1 << 0,
- TRANSACTION_CONFLICTS = 1 << 1,
- TRANSACTION_IGNORE_REQUIREMENTS = 1 << 2,
- TRANSACTION_IGNORE_ORDER = 1 << 3,
+ TRANSACTION_MATTERS = 1 << 0,
+ TRANSACTION_CONFLICTS = 1 << 1,
+ TRANSACTION_IGNORE_REQUIREMENTS = 1 << 2,
+ TRANSACTION_IGNORE_ORDER = 1 << 3,
+
+ /* Propagate a START job to other units like a RESTART */
+ TRANSACTION_PROPAGATE_START_AS_RESTART = 1 << 4,
} TransactionAddFlags;
void transaction_add_propagate_reload_jobs(