X Tutup
Skip to content

Commit e2de2d2

Browse files
authored
Merge pull request systemd#20813 from unusual-thoughts/exittype_v2
Reintroduce ExitType
2 parents da845da + e83a422 commit e2de2d2

File tree

18 files changed

+227
-60
lines changed

18 files changed

+227
-60
lines changed

docs/TRANSIENT-SETTINGS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ Most service unit settings are available for transient units.
309309
✓ ExecStartPre=
310310
✓ ExecStop=
311311
✓ ExecStopPost=
312+
✓ ExitType=
312313
✓ FileDescriptorStoreMax=
313314
✓ GuessMainPID=
314315
✓ NonBlocking=

man/org.freedesktop.systemd1.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2304,6 +2304,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
23042304
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
23052305
readonly s Type = '...';
23062306
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
2307+
readonly s ExitType = '...';
2308+
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
23072309
readonly s Restart = '...';
23082310
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
23092311
readonly s PIDFile = '...';
@@ -2898,6 +2900,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
28982900

28992901
<!--property Type is not documented!-->
29002902

2903+
<!--property ExitType is not documented!-->
2904+
29012905
<!--property Restart is not documented!-->
29022906

29032907
<!--property PIDFile is not documented!-->
@@ -3428,6 +3432,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
34283432

34293433
<variablelist class="dbus-property" generated="True" extra-ref="Type"/>
34303434

3435+
<variablelist class="dbus-property" generated="True" extra-ref="ExitType"/>
3436+
34313437
<variablelist class="dbus-property" generated="True" extra-ref="Restart"/>
34323438

34333439
<variablelist class="dbus-property" generated="True" extra-ref="PIDFile"/>

man/systemd.service.xml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,31 @@
255255
</listitem>
256256
</varlistentry>
257257

258+
<varlistentry>
259+
<term><varname>ExitType=</varname></term>
260+
261+
<listitem>
262+
<para>Specifies when the manager should consider the service to be finished. One of <option>main</option> or
263+
<option>cgroup</option>:</para>
264+
265+
<itemizedlist>
266+
<listitem><para>If set to <option>main</option> (the default), the service manager
267+
will consider the unit stopped when the main process, which is determined according to the
268+
<varname>Type=</varname>, exits. Consequently, it cannot be used with
269+
<varname>Type=</varname><option>oneshot</option>.</para></listitem>
270+
271+
<listitem><para>If set to <option>cgroup</option>, the service will be considered running as long as at
272+
least one process in the cgroup has not exited.</para></listitem>
273+
</itemizedlist>
274+
275+
<para>It is generally recommended to use <varname>ExitType=</varname><option>main</option> when a service has
276+
a known forking model and a main process can reliably be determined. <varname>ExitType=</varname>
277+
<option>cgroup</option> is meant for applications whose forking model is not known ahead of time and which
278+
might not have a specific main process. It is well suited for transient or automatically generated services,
279+
such as graphical applications inside of a desktop environment.</para>
280+
</listitem>
281+
</varlistentry>
282+
258283
<varlistentry>
259284
<term><varname>RemainAfterExit=</varname></term>
260285

shell-completion/bash/systemd-run

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ _systemd_run() {
7878
-p|--property)
7979
local comps='CPUAccounting= MemoryAccounting= BlockIOAccounting= SendSIGHUP=
8080
SendSIGKILL= MemoryLimit= CPUShares= BlockIOWeight= User= Group=
81-
DevicePolicy= KillMode= DeviceAllow= BlockIOReadBandwidth=
81+
DevicePolicy= KillMode= ExitType= DeviceAllow= BlockIOReadBandwidth=
8282
BlockIOWriteBandwidth= BlockIODeviceWeight= Nice= Environment=
8383
KillSignal= RestartKillSignal= FinalKillSignal= LimitCPU= LimitFSIZE= LimitDATA=
8484
LimitSTACK= LimitCORE= LimitRSS= LimitNOFILE= LimitAS= LimitNPROC=

shell-completion/zsh/_systemd-run

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ _arguments \
4545
{-p+,--property=}'[Set unit property]:NAME=VALUE:(( \
4646
CPUAccounting= MemoryAccounting= BlockIOAccounting= SendSIGHUP= \
4747
SendSIGKILL= MemoryLimit= CPUShares= BlockIOWeight= User= Group= \
48-
DevicePolicy= KillMode= DeviceAllow= BlockIOReadBandwidth= \
48+
DevicePolicy= KillMode= ExitType= DeviceAllow= BlockIOReadBandwidth= \
4949
BlockIOWriteBandwidth= BlockIODeviceWeight= Nice= Environment= \
5050
KillSignal= RestartKillSignal= FinalKillSignal= LimitCPU= LimitFSIZE= LimitDATA= \
5151
LimitSTACK= LimitCORE= LimitRSS= LimitNOFILE= LimitAS= LimitNPROC= \

src/core/dbus-service.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "unit.h"
2828

2929
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, service_type, ServiceType);
30+
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exit_type, service_exit_type, ServiceExitType);
3031
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, service_result, ServiceResult);
3132
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_restart, service_restart, ServiceRestart);
3233
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
192193
const sd_bus_vtable bus_service_vtable[] = {
193194
SD_BUS_VTABLE_START(0),
194195
SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Service, type), SD_BUS_VTABLE_PROPERTY_CONST),
196+
SD_BUS_PROPERTY("ExitType", "s", property_get_exit_type, offsetof(Service, exit_type), SD_BUS_VTABLE_PROPERTY_CONST),
195197
SD_BUS_PROPERTY("Restart", "s", property_get_restart, offsetof(Service, restart), SD_BUS_VTABLE_PROPERTY_CONST),
196198
SD_BUS_PROPERTY("PIDFile", "s", NULL, offsetof(Service, pid_file), SD_BUS_VTABLE_PROPERTY_CONST),
197199
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(
378380
}
379381
static BUS_DEFINE_SET_TRANSIENT_PARSE(notify_access, NotifyAccess, notify_access_from_string);
380382
static BUS_DEFINE_SET_TRANSIENT_PARSE(service_type, ServiceType, service_type_from_string);
383+
static BUS_DEFINE_SET_TRANSIENT_PARSE(service_exit_type, ServiceExitType, service_exit_type_from_string);
381384
static BUS_DEFINE_SET_TRANSIENT_PARSE(service_restart, ServiceRestart, service_restart_from_string);
382385
static BUS_DEFINE_SET_TRANSIENT_PARSE(oom_policy, OOMPolicy, oom_policy_from_string);
383386
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(
415418
if (streq(name, "Type"))
416419
return bus_set_transient_service_type(u, name, &s->type, message, flags, error);
417420

421+
if (streq(name, "ExitType"))
422+
return bus_set_transient_service_exit_type(u, name, &s->exit_type, message, flags, error);
423+
418424
if (streq(name, "OOMPolicy"))
419425
return bus_set_transient_oom_policy(u, name, &s->oom_policy, message, flags, error);
420426

src/core/load-fragment-gperf.gperf.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,7 @@ Service.StartLimitAction, config_parse_emergency_action,
394394
Service.FailureAction, config_parse_emergency_action, 0, offsetof(Unit, failure_action)
395395
Service.RebootArgument, config_parse_unit_string_printf, 0, offsetof(Unit, reboot_arg)
396396
Service.Type, config_parse_service_type, 0, offsetof(Service, type)
397+
Service.ExitType, config_parse_service_exit_type, 0, offsetof(Service, exit_type)
397398
Service.Restart, config_parse_service_restart, 0, offsetof(Service, restart)
398399
Service.PermissionsStartOnly, config_parse_bool, 0, offsetof(Service, permissions_start_only)
399400
Service.RootDirectoryStartOnly, config_parse_bool, 0, offsetof(Service, root_directory_start_only)

src/core/load-fragment.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_home, protect_home, ProtectHome, "
136136
DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_system, protect_system, ProtectSystem, "Failed to parse protect system value");
137137
DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode, exec_preserve_mode, ExecPreserveMode, "Failed to parse runtime directory preserve mode");
138138
DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
139+
DEFINE_CONFIG_PARSE_ENUM(config_parse_service_exit_type, service_exit_type, ServiceExitType, "Failed to parse service exit type");
139140
DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
140141
DEFINE_CONFIG_PARSE_ENUM(config_parse_service_timeout_failure_mode, service_timeout_failure_mode, ServiceTimeoutFailureMode, "Failed to parse timeout failure mode");
141142
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) {
61946195
{ config_parse_unit_deps, "UNIT [...]" },
61956196
{ config_parse_exec, "PATH [ARGUMENT [...]]" },
61966197
{ config_parse_service_type, "SERVICETYPE" },
6198+
{ config_parse_service_exit_type, "SERVICEEXITTYPE" },
61976199
{ config_parse_service_restart, "SERVICERESTART" },
61986200
{ config_parse_service_timeout_failure_mode, "TIMEOUTMODE" },
61996201
{ config_parse_kill_mode, "KILLMODE" },

src/core/load-fragment.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_service_timeout);
3737
CONFIG_PARSER_PROTOTYPE(config_parse_service_timeout_abort);
3838
CONFIG_PARSER_PROTOTYPE(config_parse_service_timeout_failure_mode);
3939
CONFIG_PARSER_PROTOTYPE(config_parse_service_type);
40+
CONFIG_PARSER_PROTOTYPE(config_parse_service_exit_type);
4041
CONFIG_PARSER_PROTOTYPE(config_parse_service_restart);
4142
CONFIG_PARSER_PROTOTYPE(config_parse_socket_bindtodevice);
4243
CONFIG_PARSER_PROTOTYPE(config_parse_exec_output);

src/core/service.c

Lines changed: 74 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,9 @@ static int service_verify(Service *s) {
602602
if (s->type == SERVICE_ONESHOT && !exit_status_set_is_empty(&s->restart_force_status))
603603
return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Service has RestartForceStatus= set, which isn't allowed for Type=oneshot services. Refusing.");
604604

605+
if (s->type == SERVICE_ONESHOT && s->exit_type == SERVICE_EXIT_CGROUP)
606+
return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Service has ExitType=cgroup set, which isn't allowed for Type=oneshot services. Refusing.");
607+
605608
if (s->type == SERVICE_DBUS && !s->bus_name)
606609
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.");
607610

@@ -3289,6 +3292,9 @@ static void service_notify_cgroup_empty_event(Unit *u) {
32893292
break;
32903293
}
32913294

3295+
if (s->exit_type == SERVICE_EXIT_CGROUP && main_pid_good(s) <= 0)
3296+
service_enter_start_post(s);
3297+
32923298
_fallthrough_;
32933299
case SERVICE_START_POST:
32943300
if (s->pid_file_pathspec &&
@@ -3477,79 +3483,82 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
34773483
service_run_next_main(s);
34783484

34793485
} else {
3480-
3481-
/* The service exited, so the service is officially gone. */
34823486
s->main_command = NULL;
34833487

3484-
switch (s->state) {
3485-
3486-
case SERVICE_START_POST:
3487-
case SERVICE_RELOAD:
3488-
/* If neither main nor control processes are running then
3489-
* the current state can never exit cleanly, hence immediately
3490-
* terminate the service. */
3491-
if (control_pid_good(s) <= 0)
3492-
service_enter_stop(s, f);
3488+
/* Services with ExitType=cgroup do not act on main PID exiting,
3489+
* unless the cgroup is already empty */
3490+
if (s->exit_type == SERVICE_EXIT_MAIN || cgroup_good(s) <= 0) {
3491+
/* The service exited, so the service is officially gone. */
3492+
switch (s->state) {
3493+
3494+
case SERVICE_START_POST:
3495+
case SERVICE_RELOAD:
3496+
/* If neither main nor control processes are running then
3497+
* the current state can never exit cleanly, hence immediately
3498+
* terminate the service. */
3499+
if (control_pid_good(s) <= 0)
3500+
service_enter_stop(s, f);
3501+
3502+
/* Otherwise need to wait until the operation is done. */
3503+
break;
34933504

3494-
/* Otherwise need to wait until the operation is done. */
3495-
break;
3505+
case SERVICE_STOP:
3506+
/* Need to wait until the operation is done. */
3507+
break;
34963508

3497-
case SERVICE_STOP:
3498-
/* Need to wait until the operation is done. */
3499-
break;
3509+
case SERVICE_START:
3510+
if (s->type == SERVICE_ONESHOT) {
3511+
/* This was our main goal, so let's go on */
3512+
if (f == SERVICE_SUCCESS)
3513+
service_enter_start_post(s);
3514+
else
3515+
service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
3516+
break;
3517+
} else if (s->type == SERVICE_NOTIFY) {
3518+
/* Only enter running through a notification, so that the
3519+
* SERVICE_START state signifies that no ready notification
3520+
* has been received */
3521+
if (f != SERVICE_SUCCESS)
3522+
service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
3523+
else if (!s->remain_after_exit || s->notify_access == NOTIFY_MAIN)
3524+
/* The service has never been and will never be active */
3525+
service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_PROTOCOL);
3526+
break;
3527+
}
35003528

3501-
case SERVICE_START:
3502-
if (s->type == SERVICE_ONESHOT) {
3503-
/* This was our main goal, so let's go on */
3504-
if (f == SERVICE_SUCCESS)
3505-
service_enter_start_post(s);
3506-
else
3507-
service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
3508-
break;
3509-
} else if (s->type == SERVICE_NOTIFY) {
3510-
/* Only enter running through a notification, so that the
3511-
* SERVICE_START state signifies that no ready notification
3512-
* has been received */
3513-
if (f != SERVICE_SUCCESS)
3514-
service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
3515-
else if (!s->remain_after_exit || s->notify_access == NOTIFY_MAIN)
3516-
/* The service has never been and will never be active */
3517-
service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_PROTOCOL);
3529+
_fallthrough_;
3530+
case SERVICE_RUNNING:
3531+
service_enter_running(s, f);
35183532
break;
3519-
}
3520-
3521-
_fallthrough_;
3522-
case SERVICE_RUNNING:
3523-
service_enter_running(s, f);
3524-
break;
35253533

3526-
case SERVICE_STOP_WATCHDOG:
3527-
case SERVICE_STOP_SIGTERM:
3528-
case SERVICE_STOP_SIGKILL:
3534+
case SERVICE_STOP_WATCHDOG:
3535+
case SERVICE_STOP_SIGTERM:
3536+
case SERVICE_STOP_SIGKILL:
35293537

3530-
if (control_pid_good(s) <= 0)
3531-
service_enter_stop_post(s, f);
3538+
if (control_pid_good(s) <= 0)
3539+
service_enter_stop_post(s, f);
35323540

3533-
/* If there is still a control process, wait for that first */
3534-
break;
3541+
/* If there is still a control process, wait for that first */
3542+
break;
35353543

3536-
case SERVICE_STOP_POST:
3544+
case SERVICE_STOP_POST:
35373545

3538-
if (control_pid_good(s) <= 0)
3539-
service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
3546+
if (control_pid_good(s) <= 0)
3547+
service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
35403548

3541-
break;
3549+
break;
35423550

3543-
case SERVICE_FINAL_WATCHDOG:
3544-
case SERVICE_FINAL_SIGTERM:
3545-
case SERVICE_FINAL_SIGKILL:
3551+
case SERVICE_FINAL_WATCHDOG:
3552+
case SERVICE_FINAL_SIGTERM:
3553+
case SERVICE_FINAL_SIGKILL:
35463554

3547-
if (control_pid_good(s) <= 0)
3548-
service_enter_dead(s, f, true);
3549-
break;
3555+
if (control_pid_good(s) <= 0)
3556+
service_enter_dead(s, f, true);
3557+
break;
35503558

3551-
default:
3552-
assert_not_reached();
3559+
default:
3560+
assert_not_reached();
3561+
}
35533562
}
35543563
}
35553564

@@ -4513,6 +4522,13 @@ static const char* const service_type_table[_SERVICE_TYPE_MAX] = {
45134522

45144523
DEFINE_STRING_TABLE_LOOKUP(service_type, ServiceType);
45154524

4525+
static const char* const service_exit_type_table[_SERVICE_EXIT_TYPE_MAX] = {
4526+
[SERVICE_EXIT_MAIN] = "main",
4527+
[SERVICE_EXIT_CGROUP] = "cgroup",
4528+
};
4529+
4530+
DEFINE_STRING_TABLE_LOOKUP(service_exit_type, ServiceExitType);
4531+
45164532
static const char* const service_exec_command_table[_SERVICE_EXEC_COMMAND_MAX] = {
45174533
[SERVICE_EXEC_CONDITION] = "ExecCondition",
45184534
[SERVICE_EXEC_START_PRE] = "ExecStartPre",

0 commit comments

Comments
 (0)
X Tutup