diff options
Diffstat (limited to 'src/core/service.c')
-rw-r--r-- | src/core/service.c | 132 |
1 files changed, 74 insertions, 58 deletions
diff --git a/src/core/service.c b/src/core/service.c index 701c145565..9e1602950d 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -588,6 +588,9 @@ static int service_verify(Service *s) { if (s->type == SERVICE_ONESHOT && !exit_status_set_is_empty(&s->restart_force_status)) return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Service has RestartForceStatus= set, which isn't allowed for Type=oneshot services. Refusing."); + if (s->type == SERVICE_ONESHOT && s->exit_type == SERVICE_EXIT_CGROUP) + return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Service has ExitType=cgroup set, which isn't allowed for Type=oneshot services. Refusing."); + if (s->type == SERVICE_DBUS && !s->bus_name) return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Service is of type D-Bus but no D-Bus service name has been specified. Refusing."); @@ -3273,6 +3276,9 @@ static void service_notify_cgroup_empty_event(Unit *u) { break; } + if (s->exit_type == SERVICE_EXIT_CGROUP && main_pid_good(s) <= 0) + service_enter_start_post(s); + _fallthrough_; case SERVICE_START_POST: if (s->pid_file_pathspec && @@ -3461,79 +3467,82 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { service_run_next_main(s); } else { - - /* The service exited, so the service is officially gone. */ s->main_command = NULL; - switch (s->state) { - - case SERVICE_START_POST: - case SERVICE_RELOAD: - /* If neither main nor control processes are running then - * the current state can never exit cleanly, hence immediately - * terminate the service. */ - if (control_pid_good(s) <= 0) - service_enter_stop(s, f); + /* Services with ExitType=cgroup do not act on main PID exiting, + * unless the cgroup is already empty */ + if (s->exit_type == SERVICE_EXIT_MAIN || cgroup_good(s) <= 0) { + /* The service exited, so the service is officially gone. */ + switch (s->state) { + + case SERVICE_START_POST: + case SERVICE_RELOAD: + /* If neither main nor control processes are running then + * the current state can never exit cleanly, hence immediately + * terminate the service. */ + if (control_pid_good(s) <= 0) + service_enter_stop(s, f); + + /* Otherwise need to wait until the operation is done. */ + break; - /* Otherwise need to wait until the operation is done. */ - break; + case SERVICE_STOP: + /* Need to wait until the operation is done. */ + break; - case SERVICE_STOP: - /* Need to wait until the operation is done. */ - break; + case SERVICE_START: + if (s->type == SERVICE_ONESHOT) { + /* This was our main goal, so let's go on */ + if (f == SERVICE_SUCCESS) + service_enter_start_post(s); + else + service_enter_signal(s, SERVICE_STOP_SIGTERM, f); + break; + } else if (s->type == SERVICE_NOTIFY) { + /* Only enter running through a notification, so that the + * SERVICE_START state signifies that no ready notification + * has been received */ + if (f != SERVICE_SUCCESS) + service_enter_signal(s, SERVICE_STOP_SIGTERM, f); + else if (!s->remain_after_exit || s->notify_access == NOTIFY_MAIN) + /* The service has never been and will never be active */ + service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_PROTOCOL); + break; + } - case SERVICE_START: - if (s->type == SERVICE_ONESHOT) { - /* This was our main goal, so let's go on */ - if (f == SERVICE_SUCCESS) - service_enter_start_post(s); - else - service_enter_signal(s, SERVICE_STOP_SIGTERM, f); - break; - } else if (s->type == SERVICE_NOTIFY) { - /* Only enter running through a notification, so that the - * SERVICE_START state signifies that no ready notification - * has been received */ - if (f != SERVICE_SUCCESS) - service_enter_signal(s, SERVICE_STOP_SIGTERM, f); - else if (!s->remain_after_exit || s->notify_access == NOTIFY_MAIN) - /* The service has never been and will never be active */ - service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_PROTOCOL); + _fallthrough_; + case SERVICE_RUNNING: + service_enter_running(s, f); break; - } - _fallthrough_; - case SERVICE_RUNNING: - service_enter_running(s, f); - break; + case SERVICE_STOP_WATCHDOG: + case SERVICE_STOP_SIGTERM: + case SERVICE_STOP_SIGKILL: - case SERVICE_STOP_WATCHDOG: - case SERVICE_STOP_SIGTERM: - case SERVICE_STOP_SIGKILL: + if (control_pid_good(s) <= 0) + service_enter_stop_post(s, f); - if (control_pid_good(s) <= 0) - service_enter_stop_post(s, f); + /* If there is still a control process, wait for that first */ + break; - /* If there is still a control process, wait for that first */ - break; + case SERVICE_STOP_POST: - case SERVICE_STOP_POST: + if (control_pid_good(s) <= 0) + service_enter_signal(s, SERVICE_FINAL_SIGTERM, f); - if (control_pid_good(s) <= 0) - service_enter_signal(s, SERVICE_FINAL_SIGTERM, f); + break; - break; + case SERVICE_FINAL_WATCHDOG: + case SERVICE_FINAL_SIGTERM: + case SERVICE_FINAL_SIGKILL: - case SERVICE_FINAL_WATCHDOG: - case SERVICE_FINAL_SIGTERM: - case SERVICE_FINAL_SIGKILL: - - if (control_pid_good(s) <= 0) - service_enter_dead(s, f, true); - break; + if (control_pid_good(s) <= 0) + service_enter_dead(s, f, true); + break; - default: - assert_not_reached("Uh, main process died at wrong time."); + default: + assert_not_reached("Process died at the wrong time"); + } } } @@ -4491,6 +4500,13 @@ static const char* const service_type_table[_SERVICE_TYPE_MAX] = { DEFINE_STRING_TABLE_LOOKUP(service_type, ServiceType); +static const char* const service_exit_type_table[_SERVICE_EXIT_TYPE_MAX] = { + [SERVICE_EXIT_MAIN] = "main", + [SERVICE_EXIT_CGROUP] = "cgroup", +}; + +DEFINE_STRING_TABLE_LOOKUP(service_exit_type, ServiceExitType); + static const char* const service_exec_command_table[_SERVICE_EXEC_COMMAND_MAX] = { [SERVICE_EXEC_CONDITION] = "ExecCondition", [SERVICE_EXEC_START_PRE] = "ExecStartPre", |