diff --git a/shell-completion/bash/systemd-run b/shell-completion/bash/systemd-run index 4670973032..707b038d9a 100644 --- a/shell-completion/bash/systemd-run +++ b/shell-completion/bash/systemd-run @@ -80,7 +80,7 @@ _systemd_run() { SendSIGKILL= MemoryLimit= CPUShares= BlockIOWeight= User= Group= DevicePolicy= KillMode= DeviceAllow= BlockIOReadBandwidth= BlockIOWriteBandwidth= BlockIODeviceWeight= Nice= Environment= - KillSignal= FinalKillSignal= LimitCPU= LimitFSIZE= LimitDATA= + KillSignal= RestartKillSignal= FinalKillSignal= LimitCPU= LimitFSIZE= LimitDATA= LimitSTACK= LimitCORE= LimitRSS= LimitNOFILE= LimitAS= LimitNPROC= LimitMEMLOCK= LimitLOCKS= LimitSIGPENDING= LimitMSGQUEUE= LimitNICE= LimitRTPRIO= LimitRTTIME= PrivateTmp= PrivateDevices= diff --git a/shell-completion/zsh/_systemd-run b/shell-completion/zsh/_systemd-run index 6a703a075b..ca0faa1484 100644 --- a/shell-completion/zsh/_systemd-run +++ b/shell-completion/zsh/_systemd-run @@ -35,7 +35,7 @@ _arguments \ SendSIGKILL= MemoryLimit= CPUShares= BlockIOWeight= User= Group= \ DevicePolicy= KillMode= DeviceAllow= BlockIOReadBandwidth= \ BlockIOWriteBandwidth= BlockIODeviceWeight= Nice= Environment= \ - KillSignal= FinalKillSignal= LimitCPU= LimitFSIZE= LimitDATA= \ + KillSignal= RestartKillSignal= FinalKillSignal= LimitCPU= LimitFSIZE= LimitDATA= \ LimitSTACK= LimitCORE= LimitRSS= LimitNOFILE= LimitAS= LimitNPROC= \ LimitMEMLOCK= LimitLOCKS= LimitSIGPENDING= LimitMSGQUEUE= \ LimitNICE= LimitRTPRIO= LimitRTTIME= PrivateTmp= PrivateDevices= \ diff --git a/src/core/dbus-kill.c b/src/core/dbus-kill.c index e2b3a0d517..30597e86f0 100644 --- a/src/core/dbus-kill.c +++ b/src/core/dbus-kill.c @@ -8,10 +8,28 @@ static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_kill_mode, kill_mode, KillMode); +static int property_get_restart_kill_signal( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + KillContext *c = userdata; + int s; + + assert(c); + + s = restart_kill_signal(c); + return sd_bus_message_append_basic(reply, 'i', &s); +} + const sd_bus_vtable bus_kill_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("KillMode", "s", property_get_kill_mode, offsetof(KillContext, kill_mode), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("KillSignal", "i", bus_property_get_int, offsetof(KillContext, kill_signal), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RestartKillSignal", "i", property_get_restart_kill_signal, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("FinalKillSignal", "i", bus_property_get_int, offsetof(KillContext, final_kill_signal), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SendSIGKILL", "b", bus_property_get_bool, offsetof(KillContext, send_sigkill), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SendSIGHUP", "b", bus_property_get_bool, offsetof(KillContext, send_sighup), SD_BUS_VTABLE_PROPERTY_CONST), @@ -21,6 +39,7 @@ const sd_bus_vtable bus_kill_vtable[] = { static BUS_DEFINE_SET_TRANSIENT_PARSE(kill_mode, KillMode, kill_mode_from_string); static BUS_DEFINE_SET_TRANSIENT_TO_STRING(kill_signal, "i", int32_t, int, "%" PRIi32, signal_to_string_with_check); +static BUS_DEFINE_SET_TRANSIENT_TO_STRING(restart_kill_signal, "i", int32_t, int, "%" PRIi32, signal_to_string_with_check); static BUS_DEFINE_SET_TRANSIENT_TO_STRING(final_kill_signal, "i", int32_t, int, "%" PRIi32, signal_to_string_with_check); static BUS_DEFINE_SET_TRANSIENT_TO_STRING(watchdog_signal, "i", int32_t, int, "%" PRIi32, signal_to_string_with_check); @@ -51,6 +70,9 @@ int bus_kill_context_set_transient_property( if (streq(name, "KillSignal")) return bus_set_transient_kill_signal(u, name, &c->kill_signal, message, flags, error); + if (streq(name, "RestartKillSignal")) + return bus_set_transient_restart_kill_signal(u, name, &c->restart_kill_signal, message, flags, error); + if (streq(name, "FinalKillSignal")) return bus_set_transient_final_kill_signal(u, name, &c->final_kill_signal, message, flags, error); diff --git a/src/core/kill.c b/src/core/kill.c index e7608ac81f..a9f468e29e 100644 --- a/src/core/kill.c +++ b/src/core/kill.c @@ -9,6 +9,7 @@ void kill_context_init(KillContext *c) { assert(c); c->kill_signal = SIGTERM; + /* restart_kill_signal is unset by default and we fall back to kill_signal */ c->final_kill_signal = SIGKILL; c->send_sigkill = true; c->send_sighup = false; @@ -23,11 +24,13 @@ void kill_context_dump(KillContext *c, FILE *f, const char *prefix) { fprintf(f, "%sKillMode: %s\n" "%sKillSignal: SIG%s\n" + "%sRestartKillSignal: SIG%s\n" "%sFinalKillSignal: SIG%s\n" "%sSendSIGKILL: %s\n" "%sSendSIGHUP: %s\n", prefix, kill_mode_to_string(c->kill_mode), prefix, signal_to_string(c->kill_signal), + prefix, signal_to_string(restart_kill_signal(c)), prefix, signal_to_string(c->final_kill_signal), prefix, yes_no(c->send_sigkill), prefix, yes_no(c->send_sighup)); diff --git a/src/core/kill.h b/src/core/kill.h index 43648b6d8a..1deb0aff9b 100644 --- a/src/core/kill.h +++ b/src/core/kill.h @@ -21,6 +21,7 @@ typedef enum KillMode { struct KillContext { KillMode kill_mode; int kill_signal; + int restart_kill_signal; int final_kill_signal; int watchdog_signal; bool send_sigkill; @@ -47,3 +48,9 @@ KillMode kill_mode_from_string(const char *s) _pure_; const char *kill_who_to_string(KillWho k) _const_; KillWho kill_who_from_string(const char *s) _pure_; + +static inline int restart_kill_signal(const KillContext *c) { + if (c->restart_kill_signal != 0) + return c->restart_kill_signal; + return c->kill_signal; +} diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 62dff99d86..d057c0d18b 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -161,6 +161,7 @@ m4_define(`KILL_CONTEXT_CONFIG_ITEMS', $1.SendSIGHUP, config_parse_bool, 0, offsetof($1, kill_context.send_sighup) $1.KillMode, config_parse_kill_mode, 0, offsetof($1, kill_context.kill_mode) $1.KillSignal, config_parse_signal, 0, offsetof($1, kill_context.kill_signal) +$1.RestartKillSignal, config_parse_signal, 0, offsetof($1, kill_context.restart_kill_signal) $1.FinalKillSignal, config_parse_signal, 0, offsetof($1, kill_context.final_kill_signal) $1.WatchdogSignal, config_parse_signal, 0, offsetof($1, kill_context.watchdog_signal)' )m4_dnl diff --git a/src/core/mount.c b/src/core/mount.c index fb3467c350..8a0c8be120 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -866,6 +866,8 @@ static int state_to_kill_operation(MountState state) { switch (state) { case MOUNT_REMOUNTING_SIGTERM: + return KILL_RESTART; + case MOUNT_UNMOUNTING_SIGTERM: return KILL_TERMINATE; diff --git a/src/core/service.c b/src/core/service.c index 8e313f8e90..1c677b4355 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1838,13 +1838,17 @@ fail: service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); } -static int state_to_kill_operation(ServiceState state) { +static int state_to_kill_operation(Service *s, ServiceState state) { switch (state) { case SERVICE_STOP_WATCHDOG: return KILL_WATCHDOG; case SERVICE_STOP_SIGTERM: + if (unit_has_job_type(UNIT(s), JOB_RESTART)) + return KILL_RESTART; + _fallthrough_; + case SERVICE_FINAL_SIGTERM: return KILL_TERMINATE; @@ -1875,7 +1879,7 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f r = unit_kill_context( UNIT(s), &s->kill_context, - state_to_kill_operation(state), + state_to_kill_operation(s, state), s->main_pid, s->control_pid, s->main_pid_alien); diff --git a/src/core/socket.c b/src/core/socket.c index d7ff7d1501..7696490f8a 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -2077,6 +2077,16 @@ fail: socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_RESOURCES); } +static int state_to_kill_operation(Socket *s, SocketState state) { + if (state == SOCKET_STOP_PRE_SIGTERM && unit_has_job_type(UNIT(s), JOB_RESTART)) + return KILL_RESTART; + + if (state == SOCKET_FINAL_SIGTERM) + return KILL_TERMINATE; + + return KILL_KILL; +} + static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) { int r; @@ -2088,8 +2098,7 @@ static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) { r = unit_kill_context( UNIT(s), &s->kill_context, - !IN_SET(state, SOCKET_STOP_PRE_SIGTERM, SOCKET_FINAL_SIGTERM) ? - KILL_KILL : KILL_TERMINATE, + state_to_kill_operation(s, state), -1, s->control_pid, false); diff --git a/src/core/swap.c b/src/core/swap.c index 6d0cd51750..0924b35050 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -712,21 +712,32 @@ static void swap_enter_dead_or_active(Swap *s, SwapResult f) { swap_enter_dead(s, f); } +static int state_to_kill_operation(Swap *s, SwapState state) { + if (state == SWAP_DEACTIVATING_SIGTERM) { + if (unit_has_job_type(UNIT(s), JOB_RESTART)) + return KILL_RESTART; + else + return KILL_TERMINATE; + } + + return KILL_KILL; +} + static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) { int r; - KillOperation kop; assert(s); if (s->result == SWAP_SUCCESS) s->result = f; - if (state == SWAP_DEACTIVATING_SIGTERM) - kop = KILL_TERMINATE; - else - kop = KILL_KILL; - r = unit_kill_context(UNIT(s), &s->kill_context, kop, -1, s->control_pid, false); + r = unit_kill_context(UNIT(s), + &s->kill_context, + state_to_kill_operation(s, state), + -1, + s->control_pid, + false); if (r < 0) goto fail; diff --git a/src/core/unit.c b/src/core/unit.c index 71fe7a6e23..494185a046 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -4695,6 +4695,9 @@ static int operation_to_signal(KillContext *c, KillOperation k) { case KILL_TERMINATE_AND_LOG: return c->kill_signal; + case KILL_RESTART: + return restart_kill_signal(c); + case KILL_KILL: return c->final_kill_signal; diff --git a/src/core/unit.h b/src/core/unit.h index 793ee638a6..96f718acdc 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -18,6 +18,7 @@ typedef struct UnitRef UnitRef; typedef enum KillOperation { KILL_TERMINATE, KILL_TERMINATE_AND_LOG, + KILL_RESTART, KILL_KILL, KILL_WATCHDOG, _KILL_OPERATION_MAX, diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 2cd5adfd30..0b0772c972 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -1355,15 +1355,12 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con static int bus_append_kill_property(sd_bus_message *m, const char *field, const char *eq) { if (streq(field, "KillMode")) - return bus_append_string(m, field, eq); if (STR_IN_SET(field, "SendSIGHUP", "SendSIGKILL")) - return bus_append_parse_boolean(m, field, eq); - if (STR_IN_SET(field, "KillSignal", "FinalKillSignal", "WatchdogSignal")) - + if (STR_IN_SET(field, "KillSignal", "RestartKillSignal", "FinalKillSignal", "WatchdogSignal")) return bus_append_signal_from_string(m, field, eq); return 0; diff --git a/test/TEST-23-TYPE-EXEC/testsuite.sh b/test/TEST-23-TYPE-EXEC/testsuite.sh index b161d97a7c..50d6754b96 100755 --- a/test/TEST-23-TYPE-EXEC/testsuite.sh +++ b/test/TEST-23-TYPE-EXEC/testsuite.sh @@ -19,6 +19,14 @@ systemd-run --unit=four -p Type=exec /bin/sleep infinity ! systemd-run --unit=five -p Type=exec -p User=idontexist /bin/sleep infinity ! systemd-run --unit=six -p Type=exec /tmp/brokenbinary +systemd-run --unit=seven -p KillSignal=SIGTERM -p RestartKillSignal=SIGINT -p Type=exec /bin/sleep infinity +# Both TERM and SIGINT happen to have the same number on all architectures +test $(systemctl show --value -p KillSignal seven.service) -eq 15 +test $(systemctl show --value -p RestartKillSignal seven.service) -eq 2 + +systemctl restart seven.service +systemctl stop seven.service + systemd-analyze log-level info echo OK > /testok diff --git a/test/fuzz/fuzz-unit-file/directives.service b/test/fuzz/fuzz-unit-file/directives.service index fe9d451b41..068f4398b9 100644 --- a/test/fuzz/fuzz-unit-file/directives.service +++ b/test/fuzz/fuzz-unit-file/directives.service @@ -185,6 +185,7 @@ RequiresMountsFor= Requisite= Restart= RestartForceExitStatus= +RestartKillSignal= RestartPreventExitStatus= RestartSec= ReusePort=