summaryrefslogtreecommitdiff
path: root/src/core/service.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/service.c')
-rw-r--r--src/core/service.c132
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",