diff options
author | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2021-11-08 15:06:37 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-11-08 15:06:37 +0100 |
commit | e2de2d28f40ff5dded8104044792fb58cdd0afa6 (patch) | |
tree | 28111f462ae3e0ff6db56edf145e617c7708a5c3 | |
parent | da845dabf576f73f76af2704254e0ce7d8167356 (diff) | |
parent | e83a422797b4f2d0e3b16646937d930e163c56ea (diff) | |
download | systemd-e2de2d28f40ff5dded8104044792fb58cdd0afa6.tar.gz |
Merge pull request #20813 from unusual-thoughts/exittype_v2
Reintroduce ExitType
-rw-r--r-- | docs/TRANSIENT-SETTINGS.md | 1 | ||||
-rw-r--r-- | man/org.freedesktop.systemd1.xml | 6 | ||||
-rw-r--r-- | man/systemd.service.xml | 25 | ||||
-rw-r--r-- | shell-completion/bash/systemd-run | 2 | ||||
-rw-r--r-- | shell-completion/zsh/_systemd-run | 2 | ||||
-rw-r--r-- | src/core/dbus-service.c | 6 | ||||
-rw-r--r-- | src/core/load-fragment-gperf.gperf.in | 1 | ||||
-rw-r--r-- | src/core/load-fragment.c | 2 | ||||
-rw-r--r-- | src/core/load-fragment.h | 1 | ||||
-rw-r--r-- | src/core/service.c | 132 | ||||
-rw-r--r-- | src/core/service.h | 11 | ||||
-rw-r--r-- | src/shared/bus-unit-util.c | 1 | ||||
-rw-r--r-- | src/xdg-autostart-generator/xdg-autostart-service.c | 1 | ||||
l--------- | test/TEST-56-EXIT-TYPE/Makefile | 1 | ||||
-rwxr-xr-x | test/TEST-56-EXIT-TYPE/test.sh | 9 | ||||
-rw-r--r-- | test/fuzz/fuzz-unit-file/directives.service | 1 | ||||
-rw-r--r-- | test/units/testsuite-56.service | 6 | ||||
-rwxr-xr-x | test/units/testsuite-56.sh | 79 |
18 files changed, 227 insertions, 60 deletions
diff --git a/docs/TRANSIENT-SETTINGS.md b/docs/TRANSIENT-SETTINGS.md index b90aaa702e..7bab5ec154 100644 --- a/docs/TRANSIENT-SETTINGS.md +++ b/docs/TRANSIENT-SETTINGS.md @@ -309,6 +309,7 @@ Most service unit settings are available for transient units. ✓ ExecStartPre= ✓ ExecStop= ✓ ExecStopPost= +✓ ExitType= ✓ FileDescriptorStoreMax= ✓ GuessMainPID= ✓ NonBlocking= diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml index 93f2c90280..73283de721 100644 --- a/man/org.freedesktop.systemd1.xml +++ b/man/org.freedesktop.systemd1.xml @@ -2304,6 +2304,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s Type = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s ExitType = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s Restart = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s PIDFile = '...'; @@ -2898,6 +2900,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { <!--property Type is not documented!--> + <!--property ExitType is not documented!--> + <!--property Restart is not documented!--> <!--property PIDFile is not documented!--> @@ -3428,6 +3432,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { <variablelist class="dbus-property" generated="True" extra-ref="Type"/> + <variablelist class="dbus-property" generated="True" extra-ref="ExitType"/> + <variablelist class="dbus-property" generated="True" extra-ref="Restart"/> <variablelist class="dbus-property" generated="True" extra-ref="PIDFile"/> diff --git a/man/systemd.service.xml b/man/systemd.service.xml index 5042066d0d..95cb0aca3d 100644 --- a/man/systemd.service.xml +++ b/man/systemd.service.xml @@ -256,6 +256,31 @@ </varlistentry> <varlistentry> + <term><varname>ExitType=</varname></term> + + <listitem> + <para>Specifies when the manager should consider the service to be finished. One of <option>main</option> or + <option>cgroup</option>:</para> + + <itemizedlist> + <listitem><para>If set to <option>main</option> (the default), the service manager + will consider the unit stopped when the main process, which is determined according to the + <varname>Type=</varname>, exits. Consequently, it cannot be used with + <varname>Type=</varname><option>oneshot</option>.</para></listitem> + + <listitem><para>If set to <option>cgroup</option>, the service will be considered running as long as at + least one process in the cgroup has not exited.</para></listitem> + </itemizedlist> + + <para>It is generally recommended to use <varname>ExitType=</varname><option>main</option> when a service has + a known forking model and a main process can reliably be determined. <varname>ExitType=</varname> + <option>cgroup</option> is meant for applications whose forking model is not known ahead of time and which + might not have a specific main process. It is well suited for transient or automatically generated services, + such as graphical applications inside of a desktop environment.</para> + </listitem> + </varlistentry> + + <varlistentry> <term><varname>RemainAfterExit=</varname></term> <listitem><para>Takes a boolean value that specifies whether diff --git a/shell-completion/bash/systemd-run b/shell-completion/bash/systemd-run index 76b9700f79..c5db8b77bd 100644 --- a/shell-completion/bash/systemd-run +++ b/shell-completion/bash/systemd-run @@ -78,7 +78,7 @@ _systemd_run() { -p|--property) local comps='CPUAccounting= MemoryAccounting= BlockIOAccounting= SendSIGHUP= SendSIGKILL= MemoryLimit= CPUShares= BlockIOWeight= User= Group= - DevicePolicy= KillMode= DeviceAllow= BlockIOReadBandwidth= + DevicePolicy= KillMode= ExitType= DeviceAllow= BlockIOReadBandwidth= BlockIOWriteBandwidth= BlockIODeviceWeight= Nice= Environment= KillSignal= RestartKillSignal= FinalKillSignal= LimitCPU= LimitFSIZE= LimitDATA= LimitSTACK= LimitCORE= LimitRSS= LimitNOFILE= LimitAS= LimitNPROC= diff --git a/shell-completion/zsh/_systemd-run b/shell-completion/zsh/_systemd-run index 934834b94b..7568ed4840 100644 --- a/shell-completion/zsh/_systemd-run +++ b/shell-completion/zsh/_systemd-run @@ -45,7 +45,7 @@ _arguments \ {-p+,--property=}'[Set unit property]:NAME=VALUE:(( \ CPUAccounting= MemoryAccounting= BlockIOAccounting= SendSIGHUP= \ SendSIGKILL= MemoryLimit= CPUShares= BlockIOWeight= User= Group= \ - DevicePolicy= KillMode= DeviceAllow= BlockIOReadBandwidth= \ + DevicePolicy= KillMode= ExitType= DeviceAllow= BlockIOReadBandwidth= \ BlockIOWriteBandwidth= BlockIODeviceWeight= Nice= Environment= \ KillSignal= RestartKillSignal= FinalKillSignal= LimitCPU= LimitFSIZE= LimitDATA= \ LimitSTACK= LimitCORE= LimitRSS= LimitNOFILE= LimitAS= LimitNPROC= \ diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c index f42d97afac..e90fe4f596 100644 --- a/src/core/dbus-service.c +++ b/src/core/dbus-service.c @@ -27,6 +27,7 @@ #include "unit.h" static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, service_type, ServiceType); +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exit_type, service_exit_type, ServiceExitType); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, service_result, ServiceResult); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_restart, service_restart, ServiceRestart); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_notify_access, notify_access, NotifyAccess); @@ -192,6 +193,7 @@ int bus_service_method_mount_image(sd_bus_message *message, void *userdata, sd_b const sd_bus_vtable bus_service_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Service, type), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ExitType", "s", property_get_exit_type, offsetof(Service, exit_type), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Restart", "s", property_get_restart, offsetof(Service, restart), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("PIDFile", "s", NULL, offsetof(Service, pid_file), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("NotifyAccess", "s", property_get_notify_access, offsetof(Service, notify_access), SD_BUS_VTABLE_PROPERTY_CONST), @@ -378,6 +380,7 @@ static int bus_set_transient_std_fd( } static BUS_DEFINE_SET_TRANSIENT_PARSE(notify_access, NotifyAccess, notify_access_from_string); static BUS_DEFINE_SET_TRANSIENT_PARSE(service_type, ServiceType, service_type_from_string); +static BUS_DEFINE_SET_TRANSIENT_PARSE(service_exit_type, ServiceExitType, service_exit_type_from_string); static BUS_DEFINE_SET_TRANSIENT_PARSE(service_restart, ServiceRestart, service_restart_from_string); static BUS_DEFINE_SET_TRANSIENT_PARSE(oom_policy, OOMPolicy, oom_policy_from_string); static BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(bus_name, sd_bus_service_name_is_valid); @@ -415,6 +418,9 @@ static int bus_service_set_transient_property( if (streq(name, "Type")) return bus_set_transient_service_type(u, name, &s->type, message, flags, error); + if (streq(name, "ExitType")) + return bus_set_transient_service_exit_type(u, name, &s->exit_type, message, flags, error); + if (streq(name, "OOMPolicy")) return bus_set_transient_oom_policy(u, name, &s->oom_policy, message, flags, error); diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in index 5315d2da88..71a5904a70 100644 --- a/src/core/load-fragment-gperf.gperf.in +++ b/src/core/load-fragment-gperf.gperf.in @@ -394,6 +394,7 @@ Service.StartLimitAction, config_parse_emergency_action, Service.FailureAction, config_parse_emergency_action, 0, offsetof(Unit, failure_action) Service.RebootArgument, config_parse_unit_string_printf, 0, offsetof(Unit, reboot_arg) Service.Type, config_parse_service_type, 0, offsetof(Service, type) +Service.ExitType, config_parse_service_exit_type, 0, offsetof(Service, exit_type) Service.Restart, config_parse_service_restart, 0, offsetof(Service, restart) Service.PermissionsStartOnly, config_parse_bool, 0, offsetof(Service, permissions_start_only) Service.RootDirectoryStartOnly, config_parse_bool, 0, offsetof(Service, root_directory_start_only) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 8cf821cd22..7ef6bcc41d 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -136,6 +136,7 @@ DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_home, protect_home, ProtectHome, " DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_system, protect_system, ProtectSystem, "Failed to parse protect system value"); DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode, exec_preserve_mode, ExecPreserveMode, "Failed to parse runtime directory preserve mode"); DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_service_exit_type, service_exit_type, ServiceExitType, "Failed to parse service exit type"); DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier"); DEFINE_CONFIG_PARSE_ENUM(config_parse_service_timeout_failure_mode, service_timeout_failure_mode, ServiceTimeoutFailureMode, "Failed to parse timeout failure mode"); DEFINE_CONFIG_PARSE_ENUM(config_parse_socket_bind, socket_address_bind_ipv6_only_or_bool, SocketAddressBindIPv6Only, "Failed to parse bind IPv6 only value"); @@ -6194,6 +6195,7 @@ void unit_dump_config_items(FILE *f) { { config_parse_unit_deps, "UNIT [...]" }, { config_parse_exec, "PATH [ARGUMENT [...]]" }, { config_parse_service_type, "SERVICETYPE" }, + { config_parse_service_exit_type, "SERVICEEXITTYPE" }, { config_parse_service_restart, "SERVICERESTART" }, { config_parse_service_timeout_failure_mode, "TIMEOUTMODE" }, { config_parse_kill_mode, "KILLMODE" }, diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index ab2a0393fc..26b8de28f7 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -37,6 +37,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_service_timeout); CONFIG_PARSER_PROTOTYPE(config_parse_service_timeout_abort); CONFIG_PARSER_PROTOTYPE(config_parse_service_timeout_failure_mode); CONFIG_PARSER_PROTOTYPE(config_parse_service_type); +CONFIG_PARSER_PROTOTYPE(config_parse_service_exit_type); CONFIG_PARSER_PROTOTYPE(config_parse_service_restart); CONFIG_PARSER_PROTOTYPE(config_parse_socket_bindtodevice); CONFIG_PARSER_PROTOTYPE(config_parse_exec_output); diff --git a/src/core/service.c b/src/core/service.c index 4b99311e5a..83cbc9f489 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -602,6 +602,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."); @@ -3289,6 +3292,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 && @@ -3477,79 +3483,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(); + default: + assert_not_reached(); + } } } @@ -4513,6 +4522,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", diff --git a/src/core/service.h b/src/core/service.h index eaad95df6d..70ce70fba5 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -35,6 +35,13 @@ typedef enum ServiceType { _SERVICE_TYPE_INVALID = -EINVAL, } ServiceType; +typedef enum ServiceExitType { + SERVICE_EXIT_MAIN, /* we consider the main PID when deciding if the service exited */ + SERVICE_EXIT_CGROUP, /* we wait for the last process in the cgroup to exit */ + _SERVICE_EXIT_TYPE_MAX, + _SERVICE_EXIT_TYPE_INVALID = -EINVAL, +} ServiceExitType; + typedef enum ServiceExecCommand { SERVICE_EXEC_CONDITION, SERVICE_EXEC_START_PRE, @@ -97,6 +104,7 @@ struct Service { Unit meta; ServiceType type; + ServiceExitType exit_type; ServiceRestart restart; ExitStatusSet restart_prevent_status; ExitStatusSet restart_force_status; @@ -227,6 +235,9 @@ ServiceRestart service_restart_from_string(const char *s) _pure_; const char* service_type_to_string(ServiceType i) _const_; ServiceType service_type_from_string(const char *s) _pure_; +const char* service_exit_type_to_string(ServiceExitType i) _const_; +ServiceExitType service_exit_type_from_string(const char *s) _pure_; + const char* service_exec_command_to_string(ServiceExecCommand i) _const_; ServiceExecCommand service_exec_command_from_string(const char *s) _pure_; diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index d1068440f7..bbf7499e57 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -2132,6 +2132,7 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con if (STR_IN_SET(field, "PIDFile", "Type", + "ExitType", "Restart", "BusName", "NotifyAccess", diff --git a/src/xdg-autostart-generator/xdg-autostart-service.c b/src/xdg-autostart-generator/xdg-autostart-service.c index 501cdca0b6..241a5b3cfd 100644 --- a/src/xdg-autostart-generator/xdg-autostart-service.c +++ b/src/xdg-autostart-generator/xdg-autostart-service.c @@ -597,6 +597,7 @@ int xdg_autostart_service_generate_unit( fprintf(f, "\n[Service]\n" "Type=exec\n" + "ExitType=cgroup\n" "ExecStart=:%s\n" "Restart=no\n" "TimeoutSec=5s\n" diff --git a/test/TEST-56-EXIT-TYPE/Makefile b/test/TEST-56-EXIT-TYPE/Makefile new file mode 120000 index 0000000000..e9f93b1104 --- /dev/null +++ b/test/TEST-56-EXIT-TYPE/Makefile @@ -0,0 +1 @@ +../TEST-01-BASIC/Makefile
\ No newline at end of file diff --git a/test/TEST-56-EXIT-TYPE/test.sh b/test/TEST-56-EXIT-TYPE/test.sh new file mode 100755 index 0000000000..0f84dca1ba --- /dev/null +++ b/test/TEST-56-EXIT-TYPE/test.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -e + +TEST_DESCRIPTION="test ExitType=cgroup" + +# shellcheck source=test/test-functions +. "${TEST_BASE_DIR:?}/test-functions" + +do_test "$@" diff --git a/test/fuzz/fuzz-unit-file/directives.service b/test/fuzz/fuzz-unit-file/directives.service index d2e2a120a0..176c825cab 100644 --- a/test/fuzz/fuzz-unit-file/directives.service +++ b/test/fuzz/fuzz-unit-file/directives.service @@ -161,6 +161,7 @@ ExecStartPost= ExecStartPre= ExecStop= ExecStopPost= +ExitType= ExtensionImages= FailureAction= FileDescriptorStoreMax= diff --git a/test/units/testsuite-56.service b/test/units/testsuite-56.service new file mode 100644 index 0000000000..d8ad589ca0 --- /dev/null +++ b/test/units/testsuite-56.service @@ -0,0 +1,6 @@ +[Unit] +Description=TEST-56-EXIT-TYPE + +[Service] +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-56.sh b/test/units/testsuite-56.sh new file mode 100755 index 0000000000..b167320615 --- /dev/null +++ b/test/units/testsuite-56.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +set -eux + +systemd-analyze log-level debug + +# Multiple level process tree, parent process stays up +cat >/tmp/test56-exit-cgroup.sh <<EOF +#!/usr/bin/env bash +set -eux + +# process tree: systemd -> sleep +sleep infinity & +disown + +# process tree: systemd -> bash -> bash -> sleep +((sleep infinity); true) & + +systemd-notify --ready + +# process tree: systemd -> bash -> sleep +sleep infinity +EOF +chmod +x /tmp/test56-exit-cgroup.sh + +# service should be stopped cleanly +systemd-run --wait --unit=one -p Type=notify -p ExitType=cgroup \ + -p ExecStartPost='bash -c "systemctl stop one &"' \ + /tmp/test56-exit-cgroup.sh + +# same thing with a truthy exec condition +systemd-run --wait --unit=two -p Type=notify -p ExitType=cgroup \ + -p ExecCondition=true \ + -p ExecStartPost='bash -c "systemctl stop two &"' \ + /tmp/test56-exit-cgroup.sh + +# false exec condition: systemd-run should exit immediately with status code: 1 +systemd-run --wait --unit=three -p Type=notify -p ExitType=cgroup \ + -p ExecCondition=false \ + /tmp/test56-exit-cgroup.sh \ + && { echo 'unexpected success'; exit 1; } + +# service should exit uncleanly (main process exits with SIGKILL) +systemd-run --wait --unit=four -p Type=notify -p ExitType=cgroup \ + -p ExecStartPost='bash -c "systemctl kill --signal 9 four &"' \ + /tmp/test56-exit-cgroup.sh \ + && { echo 'unexpected success'; exit 1; } + + +# Multiple level process tree, parent process exits quickly +cat >/tmp/test56-exit-cgroup-parentless.sh <<EOF +#!/usr/bin/env bash +set -eux + +# process tree: systemd -> sleep +sleep infinity & + +# process tree: systemd -> bash -> sleep +((sleep infinity); true) & + +systemd-notify --ready +EOF +chmod +x /tmp/test56-exit-cgroup-parentless.sh + +# service should be stopped cleanly +systemd-run --wait --unit=five -p Type=notify -p ExitType=cgroup \ + -p ExecStartPost='bash -c "systemctl stop five &"' \ + /tmp/test56-exit-cgroup-parentless.sh + +# service should still exit cleanly despite SIGKILL (the main process already exited cleanly) +systemd-run --wait --unit=six -p Type=notify -p ExitType=cgroup \ + -p ExecStartPost='bash -c "systemctl kill --signal 9 six &"' \ + /tmp/test56-exit-cgroup-parentless.sh + + +systemd-analyze log-level info + +echo OK >/testok + +exit 0 |