mirror of
https://github.com/systemd/systemd.git
synced 2025-01-11 09:18:07 +03:00
Introduce ExitType
This commit is contained in:
parent
82a335db83
commit
cb0e818f7c
Notes:
Zbigniew Jędrzejewski-Szmek
2021-04-22 09:22:27 +02:00
description from the pull request: This PR introduces `ExitType=main|cgroup` for services. Similar to how `Type` specifies the launch of a service, `ExitType` is concerned with how systemd determines that a service exited. - If set to `main` (the current behavior), the service manager will consider the unit stopped when the main process exits. - The `cgroup` exit type is meant for applications whose forking model is not known ahead of time and which might not have a specific main process. The service will stay running as long as at least one process in the cgroup is running. The exit status of the service is that of the last process in the cgroup to exit. This is intended for transient or automatically generated services, such as graphical applications inside of a desktop environment. Motivation for this is #16805. `xdg-autostart-generator` was amended to use `ExitType=cgroup`, which fixes https://bugs.kde.org/show_bug.cgi?id=433299, a bug where some generated autostart app services that forked not launching (systemd kills the entire cgroup after the main process exited).
docs
man
shell-completion
src
core
shared
xdg-autostart-generator
test
TEST-56-EXIT-TYPE
fuzz/fuzz-unit-file
units
@ -303,6 +303,7 @@ Most service unit settings are available for transient units.
|
||||
✓ ExecStartPre=
|
||||
✓ ExecStop=
|
||||
✓ ExecStopPost=
|
||||
✓ ExitType=
|
||||
✓ FileDescriptorStoreMax=
|
||||
✓ GuessMainPID=
|
||||
✓ NonBlocking=
|
||||
|
@ -2250,6 +2250,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 = '...';
|
||||
@ -2808,6 +2810,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!-->
|
||||
@ -3320,6 +3324,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"/>
|
||||
|
@ -255,6 +255,31 @@
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>ExitType=</varname></term>
|
||||
|
||||
<listitem>
|
||||
<para>Configures the process exit type for this service unit. 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 `Type`, exits.
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>The <option>cgroup</option> exit type is meant for applications whose forking model is not
|
||||
known ahead of time and which might not have a specific main process. The service will stay running as long
|
||||
as at least one process in the cgroup is running. The exit status of the service is that of the last
|
||||
process in the cgroup to exit.</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 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>
|
||||
|
||||
|
@ -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=
|
||||
|
@ -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= \
|
||||
|
@ -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),
|
||||
@ -377,6 +379,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);
|
||||
@ -414,6 +417,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);
|
||||
|
||||
|
@ -359,6 +359,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)
|
||||
|
@ -130,6 +130,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");
|
||||
@ -5748,6 +5749,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" },
|
||||
|
@ -32,6 +32,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);
|
||||
|
@ -1621,18 +1621,25 @@ static int control_pid_good(Service *s) {
|
||||
return s->control_pid > 0;
|
||||
}
|
||||
|
||||
static int cgroup_good(Service *s) {
|
||||
int r;
|
||||
|
||||
static int cgroup_empty(Service *s) {
|
||||
assert(s);
|
||||
|
||||
/* Returns 0 if the cgroup is empty or doesn't exist, > 0 if it is exists and is populated, < 0 if we can't
|
||||
* figure it out */
|
||||
/* Returns 0 if there is no cgroup, > 0 if is empty or doesn't exist, < 0 if we can't figure it out */
|
||||
|
||||
if (!UNIT(s)->cgroup_path)
|
||||
return 0;
|
||||
|
||||
r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, UNIT(s)->cgroup_path);
|
||||
return cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, UNIT(s)->cgroup_path);
|
||||
}
|
||||
|
||||
|
||||
static int cgroup_good(Service *s) {
|
||||
int r;
|
||||
|
||||
/* Returns 0 if the cgroup is empty or doesn't exist, > 0 if it is exists and is populated, < 0 if we can't
|
||||
* figure it out */
|
||||
|
||||
r = cgroup_empty(s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -3398,7 +3405,14 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
||||
else
|
||||
assert_not_reached("Unknown code");
|
||||
|
||||
if (s->main_pid == pid) {
|
||||
/* Services with ExitType=cgroup ignore the main PID for purposes of exit status */
|
||||
if (s->exit_type == SERVICE_EXIT_CGROUP && s->main_pid == pid) {
|
||||
service_unwatch_main_pid(s);
|
||||
s->main_pid_known = false;
|
||||
}
|
||||
|
||||
if ((s->exit_type == SERVICE_EXIT_MAIN && s->main_pid == pid) ||
|
||||
(s->exit_type == SERVICE_EXIT_CGROUP && cgroup_empty(s) && !control_pid_good(s))) {
|
||||
/* Forking services may occasionally move to a new PID.
|
||||
* As long as they update the PID file before exiting the old
|
||||
* PID, they're fine. */
|
||||
@ -3431,7 +3445,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
||||
|
||||
unit_log_process_exit(
|
||||
u,
|
||||
"Main process",
|
||||
s->exit_type == SERVICE_EXIT_CGROUP ? "Last process" : "Main process",
|
||||
service_exec_command_to_string(SERVICE_EXEC_START),
|
||||
f == SERVICE_SUCCESS,
|
||||
code, status);
|
||||
@ -4448,6 +4462,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",
|
||||
|
@ -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;
|
||||
@ -226,6 +234,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_;
|
||||
|
||||
|
@ -1947,6 +1947,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",
|
||||
|
@ -599,6 +599,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"
|
||||
|
1
test/TEST-56-EXIT-TYPE/Makefile
Symbolic link
1
test/TEST-56-EXIT-TYPE/Makefile
Symbolic link
@ -0,0 +1 @@
|
||||
../TEST-01-BASIC/Makefile
|
6
test/TEST-56-EXIT-TYPE/test.sh
Executable file
6
test/TEST-56-EXIT-TYPE/test.sh
Executable file
@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
TEST_DESCRIPTION="test ExitType=cgroup"
|
||||
. $TEST_BASE_DIR/test-functions
|
||||
|
||||
do_test "$@" 56
|
@ -94,6 +94,7 @@ ExecStartPre=
|
||||
ExecStop=
|
||||
ExecStopPost=
|
||||
ExecStopPre=
|
||||
ExitType=
|
||||
FailureAction=
|
||||
FileDescriptorName=
|
||||
FileDescriptorStoreMax=
|
||||
|
6
test/units/testsuite-56.service
Normal file
6
test/units/testsuite-56.service
Normal file
@ -0,0 +1,6 @@
|
||||
[Unit]
|
||||
Description=TEST-56-EXIT-TYPE
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
|
||||
Type=oneshot
|
94
test/units/testsuite-56.sh
Executable file
94
test/units/testsuite-56.sh
Executable file
@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env bash
|
||||
set -ex
|
||||
|
||||
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) &
|
||||
|
||||
# process tree: systemd -> bash -> sleep
|
||||
sleep infinity
|
||||
EOF
|
||||
chmod +x /tmp/test56-exit-cgroup.sh
|
||||
|
||||
# service should be stopped cleanly
|
||||
(sleep 1; systemctl stop one) &
|
||||
systemd-run --wait --unit=one -p ExitType=cgroup /tmp/test56-exit-cgroup.sh
|
||||
|
||||
# same thing with a truthy exec condition
|
||||
(sleep 1; systemctl stop two) &
|
||||
systemd-run --wait --unit=two -p ExitType=cgroup -p ExecCondition=true /tmp/test56-exit-cgroup.sh
|
||||
|
||||
# false exec condition: systemd-run should exit immediately with status code: 1
|
||||
! systemd-run --wait --unit=three -p ExitType=cgroup -p ExecCondition=false /tmp/test56-exit-cgroup.sh
|
||||
|
||||
# service should exit uncleanly
|
||||
(sleep 1; systemctl kill --signal 9 four) &
|
||||
! systemd-run --wait --unit=four -p ExitType=cgroup /tmp/test56-exit-cgroup.sh
|
||||
|
||||
|
||||
# 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) &
|
||||
EOF
|
||||
chmod +x /tmp/test56-exit-cgroup-parentless.sh
|
||||
|
||||
# service should be stopped cleanly
|
||||
(sleep 1; systemctl stop five) &
|
||||
systemd-run --wait --unit=five -p ExitType=cgroup /tmp/test56-exit-cgroup-parentless.sh
|
||||
|
||||
# service should exit uncleanly
|
||||
(sleep 1; systemctl kill --signal 9 six) &
|
||||
! systemd-run --wait --unit=six -p ExitType=cgroup /tmp/test56-exit-cgroup-parentless.sh
|
||||
|
||||
|
||||
# Multiple level process tree, parent process exits uncleanly but last process exits cleanly
|
||||
cat >/tmp/test56-exit-cgroup-clean.sh <<EOF
|
||||
#!/usr/bin/env bash
|
||||
set -eux
|
||||
|
||||
# process tree: systemd -> bash -> sleep
|
||||
(sleep 1; true) &
|
||||
|
||||
exit 255
|
||||
EOF
|
||||
chmod +x /tmp/test56-exit-cgroup-clean.sh
|
||||
|
||||
# service should exit cleanly and be garbage-collected
|
||||
systemd-run --wait --unit=seven -p ExitType=cgroup /tmp/test56-exit-cgroup-clean.sh
|
||||
|
||||
|
||||
# Multiple level process tree, parent process exits cleanly but last process exits uncleanly
|
||||
cat >/tmp/test56-exit-cgroup-unclean.sh <<EOF
|
||||
#!/usr/bin/env bash
|
||||
set -eux
|
||||
|
||||
# process tree: systemd -> bash -> sleep
|
||||
(sleep 1; exit 255) &
|
||||
EOF
|
||||
chmod +x /tmp/test56-exit-cgroup-unclean.sh
|
||||
|
||||
# service should exit uncleanly after 1 second
|
||||
! systemd-run --wait --unit=eight -p ExitType=cgroup /tmp/test56-exit-cgroup-unclean.sh
|
||||
|
||||
systemd-analyze log-level info
|
||||
|
||||
echo OK > /testok
|
||||
|
||||
exit 0
|
Loading…
Reference in New Issue
Block a user