static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
[SERVICE_DEAD] = UNIT_INACTIVE,
+ [SERVICE_CONDITION] = UNIT_ACTIVATING,
[SERVICE_START_PRE] = UNIT_ACTIVATING,
[SERVICE_START] = UNIT_ACTIVATING,
[SERVICE_START_POST] = UNIT_ACTIVATING,
* consider idle jobs active as soon as we start working on them */
static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] = {
[SERVICE_DEAD] = UNIT_INACTIVE,
+ [SERVICE_CONDITION] = UNIT_ACTIVE,
[SERVICE_START_PRE] = UNIT_ACTIVE,
[SERVICE_START] = UNIT_ACTIVE,
[SERVICE_START_POST] = UNIT_ACTIVE,
service_unwatch_pid_file(s);
if (!IN_SET(state,
- SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
+ SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
SERVICE_RUNNING,
SERVICE_RELOAD,
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
}
if (!IN_SET(state,
- SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
+ SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
SERVICE_RELOAD,
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
}
if (!IN_SET(state,
- SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
+ SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
SERVICE_RUNNING, SERVICE_RELOAD,
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL) &&
unit_notify(UNIT(s), table[old_state], table[state],
(s->reload_result == SERVICE_SUCCESS ? 0 : UNIT_NOTIFY_RELOAD_FAILURE) |
- (s->will_auto_restart ? UNIT_NOTIFY_WILL_AUTO_RESTART : 0));
+ (s->will_auto_restart ? UNIT_NOTIFY_WILL_AUTO_RESTART : 0) |
+ (s->result == SERVICE_SKIP_CONDITION ? UNIT_NOTIFY_SKIP_CONDITION : 0));
}
static usec_t service_coldplug_timeout(Service *s) {
switch (s->deserialized_state) {
+ case SERVICE_CONDITION:
case SERVICE_START_PRE:
case SERVICE_START:
case SERVICE_START_POST:
if (s->control_pid > 0 &&
pid_is_unwaited(s->control_pid) &&
IN_SET(s->deserialized_state,
- SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
+ SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
SERVICE_RELOAD,
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
}
static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) {
+ ServiceState end_state;
int r;
assert(s);
if (s->result == SERVICE_SUCCESS)
s->result = f;
- unit_log_result(UNIT(s), s->result == SERVICE_SUCCESS, service_result_to_string(s->result));
+ if (s->result == SERVICE_SUCCESS) {
+ unit_log_success(UNIT(s));
+ end_state = SERVICE_DEAD;
+ } else if (s->result == SERVICE_SKIP_CONDITION) {
+ unit_log_skip(UNIT(s), service_result_to_string(s->result));
+ end_state = SERVICE_DEAD;
+ } else {
+ unit_log_failure(UNIT(s), service_result_to_string(s->result));
+ end_state = SERVICE_FAILED;
+ }
if (allow_restart && service_shall_restart(s))
s->will_auto_restart = true;
* SERVICE_FAILED/SERVICE_DEAD before entering into SERVICE_AUTO_RESTART. */
s->n_keep_fd_store ++;
- service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD);
+ service_set_state(s, end_state);
if (s->will_auto_restart) {
s->will_auto_restart = false;
service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true);
}
+static void service_enter_condition(Service *s) {
+ int r;
+
+ assert(s);
+
+ service_unwatch_control_pid(s);
+
+ s->control_command = s->exec_command[SERVICE_EXEC_CONDITION];
+ if (s->control_command) {
+
+ r = service_adverse_to_leftover_processes(s);
+ if (r < 0)
+ goto fail;
+
+ s->control_command_id = SERVICE_EXEC_CONDITION;
+
+ r = service_spawn(s,
+ s->control_command,
+ s->timeout_start_usec,
+ EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_APPLY_TTY_STDIN,
+ &s->control_pid);
+
+ if (r < 0)
+ goto fail;
+
+ service_set_state(s, SERVICE_CONDITION);
+ } else
+ service_enter_start_pre(s);
+
+ return;
+
+fail:
+ log_unit_warning_errno(UNIT(s), r, "Failed to run 'exec-condition' task: %m");
+ service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true);
+}
+
static void service_enter_restart(Service *s) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
s->control_command = s->control_command->command_next;
service_unwatch_control_pid(s);
- if (IN_SET(s->state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD))
+ if (IN_SET(s->state, SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD))
timeout = s->timeout_start_usec;
else
timeout = s->timeout_stop_usec;
s->control_command,
timeout,
EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|
- (IN_SET(s->control_command_id, SERVICE_EXEC_START_PRE, SERVICE_EXEC_STOP_POST) ? EXEC_APPLY_TTY_STDIN : 0)|
+ (IN_SET(s->control_command_id, SERVICE_EXEC_CONDITION, SERVICE_EXEC_START_PRE, SERVICE_EXEC_STOP_POST) ? EXEC_APPLY_TTY_STDIN : 0)|
(IN_SET(s->control_command_id, SERVICE_EXEC_STOP, SERVICE_EXEC_STOP_POST) ? EXEC_SETENV_RESULT : 0)|
(IN_SET(s->control_command_id, SERVICE_EXEC_START_POST, SERVICE_EXEC_RELOAD, SERVICE_EXEC_STOP, SERVICE_EXEC_STOP_POST) ? EXEC_CONTROL_CGROUP : 0),
&s->control_pid);
fail:
log_unit_warning_errno(UNIT(s), r, "Failed to run next control task: %m");
- if (IN_SET(s->state, SERVICE_START_PRE, SERVICE_START_POST, SERVICE_STOP))
+ if (IN_SET(s->state, SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START_POST, SERVICE_STOP))
service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES);
else if (s->state == SERVICE_STOP_POST)
service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true);
return -EAGAIN;
/* Already on it! */
- if (IN_SET(s->state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST))
+ if (IN_SET(s->state, SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST))
return 0;
/* A service that will be restarted must be stopped first to
u->reset_accounting = true;
- service_enter_start_pre(s);
+ service_enter_condition(s);
return 1;
}
/* If there's already something running we go directly into
* kill mode. */
- if (IN_SET(s->state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD, SERVICE_STOP_WATCHDOG)) {
+ if (IN_SET(s->state, SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD, SERVICE_STOP_WATCHDOG)) {
service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_SUCCESS);
return 0;
}
switch (s->state) {
+ case SERVICE_CONDITION:
case SERVICE_START_PRE:
case SERVICE_START:
case SERVICE_START_POST:
} else if (s->control_pid == pid) {
s->control_pid = 0;
+ /* ExecCondition= calls that exit with (0, 254] should invoke skip-like behavior instead of failing */
+ if (f == SERVICE_FAILURE_EXIT_CODE && s->state == SERVICE_CONDITION && status < 255)
+ f = SERVICE_SKIP_CONDITION;
+
if (s->control_command) {
exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status);
switch (s->state) {
+ case SERVICE_CONDITION:
+ if (f == SERVICE_SUCCESS)
+ service_enter_start_pre(s);
+ else
+ service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
+ break;
+
case SERVICE_START_PRE:
if (f == SERVICE_SUCCESS)
service_enter_start(s);
switch (s->state) {
+ case SERVICE_CONDITION:
case SERVICE_START_PRE:
case SERVICE_START:
- log_unit_warning(UNIT(s), "%s operation timed out. Terminating.", s->state == SERVICE_START ? "Start" : "Start-pre");
+ log_unit_warning(UNIT(s), "%s operation timed out. Terminating.", service_state_to_string(s->state));
service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT);
break;
return false;
return IN_SET(s->state,
+ SERVICE_CONDITION,
SERVICE_START_PRE,
SERVICE_START,
SERVICE_START_POST,
DEFINE_STRING_TABLE_LOOKUP(service_type, ServiceType);
static const char* const service_exec_command_table[_SERVICE_EXEC_COMMAND_MAX] = {
+ [SERVICE_EXEC_CONDITION] = "ExecCondition",
[SERVICE_EXEC_START_PRE] = "ExecStartPre",
[SERVICE_EXEC_START] = "ExecStart",
[SERVICE_EXEC_START_POST] = "ExecStartPost",
[SERVICE_FAILURE_WATCHDOG] = "watchdog",
[SERVICE_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
[SERVICE_FAILURE_OOM_KILL] = "oom-kill",
+ [SERVICE_SKIP_CONDITION] = "exec-condition",
};
DEFINE_STRING_TABLE_LOOKUP(service_result, ServiceResult);
.finished_start_job = {
[JOB_DONE] = "Started %s.",
[JOB_FAILED] = "Failed to start %s.",
+ [JOB_SKIPPED] = "Skipped %s.",
},
.finished_stop_job = {
[JOB_DONE] = "Stopped %s.",