mirror of
https://github.com/systemd/systemd.git
synced 2025-01-25 10:04:04 +03:00
Merge pull request #20813 from unusual-thoughts/exittype_v2
Reintroduce ExitType
This commit is contained in:
commit
e2de2d28f4
@ -309,6 +309,7 @@ Most service unit settings are available for transient units.
|
|||||||
✓ ExecStartPre=
|
✓ ExecStartPre=
|
||||||
✓ ExecStop=
|
✓ ExecStop=
|
||||||
✓ ExecStopPost=
|
✓ ExecStopPost=
|
||||||
|
✓ ExitType=
|
||||||
✓ FileDescriptorStoreMax=
|
✓ FileDescriptorStoreMax=
|
||||||
✓ GuessMainPID=
|
✓ GuessMainPID=
|
||||||
✓ NonBlocking=
|
✓ NonBlocking=
|
||||||
|
@ -2304,6 +2304,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
|||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||||
readonly s Type = '...';
|
readonly s Type = '...';
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||||
|
readonly s ExitType = '...';
|
||||||
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||||
readonly s Restart = '...';
|
readonly s Restart = '...';
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||||
readonly s PIDFile = '...';
|
readonly s PIDFile = '...';
|
||||||
@ -2898,6 +2900,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
|||||||
|
|
||||||
<!--property Type is not documented!-->
|
<!--property Type is not documented!-->
|
||||||
|
|
||||||
|
<!--property ExitType is not documented!-->
|
||||||
|
|
||||||
<!--property Restart is not documented!-->
|
<!--property Restart is not documented!-->
|
||||||
|
|
||||||
<!--property PIDFile 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="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="Restart"/>
|
||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="PIDFile"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="PIDFile"/>
|
||||||
|
@ -255,6 +255,31 @@
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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>
|
<varlistentry>
|
||||||
<term><varname>RemainAfterExit=</varname></term>
|
<term><varname>RemainAfterExit=</varname></term>
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ _systemd_run() {
|
|||||||
-p|--property)
|
-p|--property)
|
||||||
local comps='CPUAccounting= MemoryAccounting= BlockIOAccounting= SendSIGHUP=
|
local comps='CPUAccounting= MemoryAccounting= BlockIOAccounting= SendSIGHUP=
|
||||||
SendSIGKILL= MemoryLimit= CPUShares= BlockIOWeight= User= Group=
|
SendSIGKILL= MemoryLimit= CPUShares= BlockIOWeight= User= Group=
|
||||||
DevicePolicy= KillMode= DeviceAllow= BlockIOReadBandwidth=
|
DevicePolicy= KillMode= ExitType= DeviceAllow= BlockIOReadBandwidth=
|
||||||
BlockIOWriteBandwidth= BlockIODeviceWeight= Nice= Environment=
|
BlockIOWriteBandwidth= BlockIODeviceWeight= Nice= Environment=
|
||||||
KillSignal= RestartKillSignal= FinalKillSignal= LimitCPU= LimitFSIZE= LimitDATA=
|
KillSignal= RestartKillSignal= FinalKillSignal= LimitCPU= LimitFSIZE= LimitDATA=
|
||||||
LimitSTACK= LimitCORE= LimitRSS= LimitNOFILE= LimitAS= LimitNPROC=
|
LimitSTACK= LimitCORE= LimitRSS= LimitNOFILE= LimitAS= LimitNPROC=
|
||||||
|
@ -45,7 +45,7 @@ _arguments \
|
|||||||
{-p+,--property=}'[Set unit property]:NAME=VALUE:(( \
|
{-p+,--property=}'[Set unit property]:NAME=VALUE:(( \
|
||||||
CPUAccounting= MemoryAccounting= BlockIOAccounting= SendSIGHUP= \
|
CPUAccounting= MemoryAccounting= BlockIOAccounting= SendSIGHUP= \
|
||||||
SendSIGKILL= MemoryLimit= CPUShares= BlockIOWeight= User= Group= \
|
SendSIGKILL= MemoryLimit= CPUShares= BlockIOWeight= User= Group= \
|
||||||
DevicePolicy= KillMode= DeviceAllow= BlockIOReadBandwidth= \
|
DevicePolicy= KillMode= ExitType= DeviceAllow= BlockIOReadBandwidth= \
|
||||||
BlockIOWriteBandwidth= BlockIODeviceWeight= Nice= Environment= \
|
BlockIOWriteBandwidth= BlockIODeviceWeight= Nice= Environment= \
|
||||||
KillSignal= RestartKillSignal= FinalKillSignal= LimitCPU= LimitFSIZE= LimitDATA= \
|
KillSignal= RestartKillSignal= FinalKillSignal= LimitCPU= LimitFSIZE= LimitDATA= \
|
||||||
LimitSTACK= LimitCORE= LimitRSS= LimitNOFILE= LimitAS= LimitNPROC= \
|
LimitSTACK= LimitCORE= LimitRSS= LimitNOFILE= LimitAS= LimitNPROC= \
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "unit.h"
|
#include "unit.h"
|
||||||
|
|
||||||
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, service_type, ServiceType);
|
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_result, service_result, ServiceResult);
|
||||||
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_restart, service_restart, ServiceRestart);
|
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);
|
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[] = {
|
const sd_bus_vtable bus_service_vtable[] = {
|
||||||
SD_BUS_VTABLE_START(0),
|
SD_BUS_VTABLE_START(0),
|
||||||
SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Service, type), SD_BUS_VTABLE_PROPERTY_CONST),
|
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("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("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),
|
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(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_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(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_PARSE(oom_policy, OOMPolicy, oom_policy_from_string);
|
||||||
static BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(bus_name, sd_bus_service_name_is_valid);
|
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"))
|
if (streq(name, "Type"))
|
||||||
return bus_set_transient_service_type(u, name, &s->type, message, flags, error);
|
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"))
|
if (streq(name, "OOMPolicy"))
|
||||||
return bus_set_transient_oom_policy(u, name, &s->oom_policy, message, flags, error);
|
return bus_set_transient_oom_policy(u, name, &s->oom_policy, message, flags, error);
|
||||||
|
|
||||||
|
@ -394,6 +394,7 @@ Service.StartLimitAction, config_parse_emergency_action,
|
|||||||
Service.FailureAction, config_parse_emergency_action, 0, offsetof(Unit, failure_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.RebootArgument, config_parse_unit_string_printf, 0, offsetof(Unit, reboot_arg)
|
||||||
Service.Type, config_parse_service_type, 0, offsetof(Service, type)
|
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.Restart, config_parse_service_restart, 0, offsetof(Service, restart)
|
||||||
Service.PermissionsStartOnly, config_parse_bool, 0, offsetof(Service, permissions_start_only)
|
Service.PermissionsStartOnly, config_parse_bool, 0, offsetof(Service, permissions_start_only)
|
||||||
Service.RootDirectoryStartOnly, config_parse_bool, 0, offsetof(Service, root_directory_start_only)
|
Service.RootDirectoryStartOnly, config_parse_bool, 0, offsetof(Service, root_directory_start_only)
|
||||||
|
@ -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_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_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_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_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_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");
|
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_unit_deps, "UNIT [...]" },
|
||||||
{ config_parse_exec, "PATH [ARGUMENT [...]]" },
|
{ config_parse_exec, "PATH [ARGUMENT [...]]" },
|
||||||
{ config_parse_service_type, "SERVICETYPE" },
|
{ config_parse_service_type, "SERVICETYPE" },
|
||||||
|
{ config_parse_service_exit_type, "SERVICEEXITTYPE" },
|
||||||
{ config_parse_service_restart, "SERVICERESTART" },
|
{ config_parse_service_restart, "SERVICERESTART" },
|
||||||
{ config_parse_service_timeout_failure_mode, "TIMEOUTMODE" },
|
{ config_parse_service_timeout_failure_mode, "TIMEOUTMODE" },
|
||||||
{ config_parse_kill_mode, "KILLMODE" },
|
{ config_parse_kill_mode, "KILLMODE" },
|
||||||
|
@ -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_abort);
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_service_timeout_failure_mode);
|
CONFIG_PARSER_PROTOTYPE(config_parse_service_timeout_failure_mode);
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_service_type);
|
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_service_restart);
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_socket_bindtodevice);
|
CONFIG_PARSER_PROTOTYPE(config_parse_socket_bindtodevice);
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_exec_output);
|
CONFIG_PARSER_PROTOTYPE(config_parse_exec_output);
|
||||||
|
@ -602,6 +602,9 @@ static int service_verify(Service *s) {
|
|||||||
if (s->type == SERVICE_ONESHOT && !exit_status_set_is_empty(&s->restart_force_status))
|
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.");
|
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)
|
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.");
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (s->exit_type == SERVICE_EXIT_CGROUP && main_pid_good(s) <= 0)
|
||||||
|
service_enter_start_post(s);
|
||||||
|
|
||||||
_fallthrough_;
|
_fallthrough_;
|
||||||
case SERVICE_START_POST:
|
case SERVICE_START_POST:
|
||||||
if (s->pid_file_pathspec &&
|
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);
|
service_run_next_main(s);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
/* The service exited, so the service is officially gone. */
|
|
||||||
s->main_command = NULL;
|
s->main_command = NULL;
|
||||||
|
|
||||||
switch (s->state) {
|
/* 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_START_POST:
|
||||||
case SERVICE_RELOAD:
|
case SERVICE_RELOAD:
|
||||||
/* If neither main nor control processes are running then
|
/* If neither main nor control processes are running then
|
||||||
* the current state can never exit cleanly, hence immediately
|
* the current state can never exit cleanly, hence immediately
|
||||||
* terminate the service. */
|
* terminate the service. */
|
||||||
if (control_pid_good(s) <= 0)
|
if (control_pid_good(s) <= 0)
|
||||||
service_enter_stop(s, f);
|
service_enter_stop(s, f);
|
||||||
|
|
||||||
/* Otherwise need to wait until the operation is done. */
|
/* Otherwise 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;
|
break;
|
||||||
} else if (s->type == SERVICE_NOTIFY) {
|
|
||||||
/* Only enter running through a notification, so that the
|
case SERVICE_STOP:
|
||||||
* SERVICE_START state signifies that no ready notification
|
/* Need to wait until the operation is done. */
|
||||||
* 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;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
_fallthrough_;
|
||||||
|
case SERVICE_RUNNING:
|
||||||
|
service_enter_running(s, f);
|
||||||
|
break;
|
||||||
|
|
||||||
|
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 there is still a control process, wait for that first */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SERVICE_STOP_POST:
|
||||||
|
|
||||||
|
if (control_pid_good(s) <= 0)
|
||||||
|
service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert_not_reached();
|
||||||
}
|
}
|
||||||
|
|
||||||
_fallthrough_;
|
|
||||||
case SERVICE_RUNNING:
|
|
||||||
service_enter_running(s, f);
|
|
||||||
break;
|
|
||||||
|
|
||||||
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 there is still a control process, wait for that first */
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SERVICE_STOP_POST:
|
|
||||||
|
|
||||||
if (control_pid_good(s) <= 0)
|
|
||||||
service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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);
|
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] = {
|
static const char* const service_exec_command_table[_SERVICE_EXEC_COMMAND_MAX] = {
|
||||||
[SERVICE_EXEC_CONDITION] = "ExecCondition",
|
[SERVICE_EXEC_CONDITION] = "ExecCondition",
|
||||||
[SERVICE_EXEC_START_PRE] = "ExecStartPre",
|
[SERVICE_EXEC_START_PRE] = "ExecStartPre",
|
||||||
|
@ -35,6 +35,13 @@ typedef enum ServiceType {
|
|||||||
_SERVICE_TYPE_INVALID = -EINVAL,
|
_SERVICE_TYPE_INVALID = -EINVAL,
|
||||||
} ServiceType;
|
} 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 {
|
typedef enum ServiceExecCommand {
|
||||||
SERVICE_EXEC_CONDITION,
|
SERVICE_EXEC_CONDITION,
|
||||||
SERVICE_EXEC_START_PRE,
|
SERVICE_EXEC_START_PRE,
|
||||||
@ -97,6 +104,7 @@ struct Service {
|
|||||||
Unit meta;
|
Unit meta;
|
||||||
|
|
||||||
ServiceType type;
|
ServiceType type;
|
||||||
|
ServiceExitType exit_type;
|
||||||
ServiceRestart restart;
|
ServiceRestart restart;
|
||||||
ExitStatusSet restart_prevent_status;
|
ExitStatusSet restart_prevent_status;
|
||||||
ExitStatusSet restart_force_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_;
|
const char* service_type_to_string(ServiceType i) _const_;
|
||||||
ServiceType service_type_from_string(const char *s) _pure_;
|
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_;
|
const char* service_exec_command_to_string(ServiceExecCommand i) _const_;
|
||||||
ServiceExecCommand service_exec_command_from_string(const char *s) _pure_;
|
ServiceExecCommand service_exec_command_from_string(const char *s) _pure_;
|
||||||
|
|
||||||
|
@ -2132,6 +2132,7 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con
|
|||||||
|
|
||||||
if (STR_IN_SET(field, "PIDFile",
|
if (STR_IN_SET(field, "PIDFile",
|
||||||
"Type",
|
"Type",
|
||||||
|
"ExitType",
|
||||||
"Restart",
|
"Restart",
|
||||||
"BusName",
|
"BusName",
|
||||||
"NotifyAccess",
|
"NotifyAccess",
|
||||||
|
@ -597,6 +597,7 @@ int xdg_autostart_service_generate_unit(
|
|||||||
fprintf(f,
|
fprintf(f,
|
||||||
"\n[Service]\n"
|
"\n[Service]\n"
|
||||||
"Type=exec\n"
|
"Type=exec\n"
|
||||||
|
"ExitType=cgroup\n"
|
||||||
"ExecStart=:%s\n"
|
"ExecStart=:%s\n"
|
||||||
"Restart=no\n"
|
"Restart=no\n"
|
||||||
"TimeoutSec=5s\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
|
9
test/TEST-56-EXIT-TYPE/test.sh
Executable file
9
test/TEST-56-EXIT-TYPE/test.sh
Executable file
@ -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 "$@"
|
@ -161,6 +161,7 @@ ExecStartPost=
|
|||||||
ExecStartPre=
|
ExecStartPre=
|
||||||
ExecStop=
|
ExecStop=
|
||||||
ExecStopPost=
|
ExecStopPost=
|
||||||
|
ExitType=
|
||||||
ExtensionImages=
|
ExtensionImages=
|
||||||
FailureAction=
|
FailureAction=
|
||||||
FileDescriptorStoreMax=
|
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
|
79
test/units/testsuite-56.sh
Executable file
79
test/units/testsuite-56.sh
Executable file
@ -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
|
Loading…
x
Reference in New Issue
Block a user