diff --git a/man/systemd.kill.xml b/man/systemd.kill.xml index 2c6fea7493..6a1c67d406 100644 --- a/man/systemd.kill.xml +++ b/man/systemd.kill.xml @@ -85,32 +85,36 @@ unless it is empty. Processes will first be terminated via SIGTERM (unless the signal to send - is changed via KillSignal=). Optionally, this is immediately followed by a - SIGHUP (if enabled with SendSIGHUP=). If processes still - remain after the main process of a unit has exited or the delay configured via the - TimeoutStopSec= has passed, the termination request is repeated with the - SIGKILL signal or the signal specified via FinalKillSignal= - (unless this is disabled via the SendSIGKILL= option). See - kill2 for more - information. + is changed via KillSignal= or RestartKillSignal=). Optionally, + this is immediately followed by a SIGHUP (if enabled with + SendSIGHUP=). If processes still remain after the main process of a unit has + exited or the delay configured via the TimeoutStopSec= has passed, the termination + request is repeated with the SIGKILL signal or the signal specified via + FinalKillSignal= (unless this is disabled via the SendSIGKILL= + option). See kill2 + for more information. Defaults to . KillSignal= - Specifies which signal to use when killing a - service. This controls the signal that is sent as first step - of shutting down a unit (see above), and is usually followed - by SIGKILL (see above and below). For a - list of valid signals, see + Specifies which signal to use when stopping a service. This controls the signal that + is sent as first step of shutting down a unit (see above), and is usually followed by + SIGKILL (see above and below). For a list of valid signals, see signal7. - Defaults to SIGTERM. + Defaults to SIGTERM. - Note that, right after sending the signal specified in - this setting, systemd will always send - SIGCONT, to ensure that even suspended - tasks can be terminated cleanly. + Note that, right after sending the signal specified in this setting, systemd will always send + SIGCONT, to ensure that even suspended tasks can be terminated cleanly. + + + + + RestartKillSignal= + Specifies which signal to use when restarting a service. The same as + KillSignal= described above, with the exception that this setting is used in a + restart job. Not set by default, and the value of KillSignal= is used. diff --git a/man/systemd.network.xml b/man/systemd.network.xml index ac0f618643..657ba66245 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -876,8 +876,8 @@ lease expires. This is contrary to the DHCP specification, but may be the best choice if, e.g., the root filesystem relies on this connection. The setting dhcp implies dhcp-on-stop, and yes implies - dhcp and static. Defaults to - dhcp-on-stop. + dhcp and static. Defaults to no. + @@ -1568,7 +1568,7 @@ SendRelease= When true, the DHCPv4 client sends a DHCP release packet when it stops. - Defaults to false. + Defaults to true. diff --git a/man/systemd.service.xml b/man/systemd.service.xml index 14840c2e16..af14eedfcc 100644 --- a/man/systemd.service.xml +++ b/man/systemd.service.xml @@ -477,26 +477,24 @@ ExecStop= - Commands to execute to stop the service - started via ExecStart=. This argument takes - multiple command lines, following the same scheme as described - for ExecStart= above. Use of this setting - is optional. After the commands configured in this option are - run, it is implied that the service is stopped, and any processes - remaining for it are terminated - according to the KillMode= setting (see + Commands to execute to stop the service started via + ExecStart=. This argument takes multiple command lines, following the same scheme + as described for ExecStart= above. Use of this setting is optional. After the + commands configured in this option are run, it is implied that the service is stopped, and any + processes remaining for it are terminated according to the KillMode= setting (see systemd.kill5). - If this option is not specified, the process is terminated by - sending the signal specified in KillSignal= - when service stop is requested. Specifier and environment - variable substitution is supported (including + If this option is not specified, the process is terminated by sending the signal specified in + KillSignal= or RestartKillSignal= when service stop is + requested. Specifier and environment variable substitution is supported (including $MAINPID, see above). - Note that it is usually not sufficient to specify a command for this setting that only asks the service - to terminate (for example, by queuing some form of termination signal for it), but does not wait for it to do - so. Since the remaining processes of the services are killed according to KillMode= and - KillSignal= as described above immediately after the command exited, this may not result in - a clean stop. The specified command should hence be a synchronous operation, not an asynchronous one. + Note that it is usually not sufficient to specify a command for this setting that only asks the + service to terminate (for example, by sending some form of termination signal to it), but does not + wait for it to do so. Since the remaining processes of the services are killed according to + KillMode= and KillSignal= or + RestartKillSignal= as described above immediately after the command exited, this + may not result in a clean stop. The specified command should hence be a synchronous operation, not an + asynchronous one. Note that the commands specified in ExecStop= are only executed when the service started successfully first. They are not invoked if the service was never started at all, or in case its 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-job.h b/src/core/dbus-job.h index c9f6fc7187..380e117fca 100644 --- a/src/core/dbus-job.h +++ b/src/core/dbus-job.h @@ -4,7 +4,7 @@ #include "sd-bus.h" #include "sd-bus-vtable.h" -#include "job.h" +#include "unit.h" extern const sd_bus_vtable bus_job_vtable[]; 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/dbus-unit.h b/src/core/dbus-unit.h index 24abf3c088..91711311a7 100644 --- a/src/core/dbus-unit.h +++ b/src/core/dbus-unit.h @@ -4,7 +4,6 @@ #include "sd-bus.h" #include "sd-bus-vtable.h" -#include "job.h" #include "unit.h" extern const sd_bus_vtable bus_unit_vtable[]; diff --git a/src/core/kill.c b/src/core/kill.c index 6fe96cfc07..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", + "%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)); @@ -35,20 +38,20 @@ void kill_context_dump(KillContext *c, FILE *f, const char *prefix) { static const char* const kill_mode_table[_KILL_MODE_MAX] = { [KILL_CONTROL_GROUP] = "control-group", - [KILL_PROCESS] = "process", - [KILL_MIXED] = "mixed", - [KILL_NONE] = "none" + [KILL_PROCESS] = "process", + [KILL_MIXED] = "mixed", + [KILL_NONE] = "none", }; DEFINE_STRING_TABLE_LOOKUP(kill_mode, KillMode); static const char* const kill_who_table[_KILL_WHO_MAX] = { - [KILL_MAIN] = "main", - [KILL_CONTROL] = "control", - [KILL_ALL] = "all", - [KILL_MAIN_FAIL] = "main-fail", + [KILL_MAIN] = "main", + [KILL_CONTROL] = "control", + [KILL_ALL] = "all", + [KILL_MAIN_FAIL] = "main-fail", [KILL_CONTROL_FAIL] = "control-fail", - [KILL_ALL_FAIL] = "all-fail" + [KILL_ALL_FAIL] = "all-fail", }; DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho); 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/load-fragment.h b/src/core/load-fragment.h index e29d58f706..f369c4dc4f 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -43,8 +43,6 @@ CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_sched_prio); CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_affinity); CONFIG_PARSER_PROTOTYPE(config_parse_exec_secure_bits); CONFIG_PARSER_PROTOTYPE(config_parse_capability_set); -CONFIG_PARSER_PROTOTYPE(config_parse_kill_signal); -CONFIG_PARSER_PROTOTYPE(config_parse_final_kill_signal); CONFIG_PARSER_PROTOTYPE(config_parse_exec_mount_flags); CONFIG_PARSER_PROTOTYPE(config_parse_timer); CONFIG_PARSER_PROTOTYPE(config_parse_trigger_unit); 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 71befcddc8..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); @@ -2245,7 +2249,7 @@ static void service_enter_restart(Service *s) { assert(s); - if (UNIT(s)->job && UNIT(s)->job->type == JOB_STOP) { + if (unit_has_job_type(UNIT(s), JOB_STOP)) { /* Don't restart things if we are going down anyway */ log_unit_info(UNIT(s), "Stop job pending for unit, delaying automatic restart."); 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 b9fb7dbc0e..0d93b94b81 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -4022,7 +4022,7 @@ bool unit_stop_pending(Unit *u) { * different from unit_inactive_or_pending() which checks both * the current state and for a queued job. */ - return u->job && u->job->type == JOB_STOP; + return unit_has_job_type(u, JOB_STOP); } bool unit_inactive_or_pending(Unit *u) { @@ -4057,12 +4057,7 @@ bool unit_active_or_pending(Unit *u) { bool unit_will_restart_default(Unit *u) { assert(u); - if (!u->job) - return false; - if (u->job->type == JOB_START) - return true; - - return false; + return unit_has_job_type(u, JOB_START); } bool unit_will_restart(Unit *u) { @@ -4691,19 +4686,26 @@ static int log_kill(pid_t pid, int sig, void *userdata) { return 1; } -static int operation_to_signal(KillContext *c, KillOperation k) { +static int operation_to_signal(const KillContext *c, KillOperation k, bool *noteworthy) { assert(c); switch (k) { case KILL_TERMINATE: case KILL_TERMINATE_AND_LOG: + *noteworthy = false; return c->kill_signal; + case KILL_RESTART: + *noteworthy = false; + return restart_kill_signal(c); + case KILL_KILL: + *noteworthy = true; return c->final_kill_signal; case KILL_WATCHDOG: + *noteworthy = true; return c->watchdog_signal; default: @@ -4732,16 +4734,16 @@ int unit_kill_context( if (c->kill_mode == KILL_NONE) return 0; - sig = operation_to_signal(c, k); + bool noteworthy; + sig = operation_to_signal(c, k, ¬eworthy); + if (noteworthy) + log_func = log_kill; send_sighup = c->send_sighup && IN_SET(k, KILL_TERMINATE, KILL_TERMINATE_AND_LOG) && sig != SIGHUP; - if (k != KILL_TERMINATE || IN_SET(sig, SIGKILL, SIGABRT)) - log_func = log_kill; - if (main_pid > 0) { if (log_func) log_func(main_pid, sig, u); diff --git a/src/core/unit.h b/src/core/unit.h index 3b0042a9eb..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, @@ -842,6 +843,10 @@ const char *unit_label_path(Unit *u); int unit_pid_attachable(Unit *unit, pid_t pid, sd_bus_error *error); +static inline bool unit_has_job_type(Unit *u, JobType type) { + return u && u->job && u->job->type == type; +} + /* unit_log_skip is for cases like ExecCondition= where a unit is considered "done" * after some execution, rather than succeeded or failed. */ void unit_log_skip(Unit *u, const char *result); diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 2d511f7feb..02f3569edc 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -870,41 +870,6 @@ static int client_send_discover(sd_dhcp_client *client) { return 0; } -static int client_send_release(sd_dhcp_client *client) { - _cleanup_free_ DHCPPacket *release = NULL; - size_t optoffset, optlen; - int r; - - assert(client); - assert(!IN_SET(client->state, DHCP_STATE_STOPPED)); - - r = client_message_init(client, &release, DHCP_RELEASE, - &optlen, &optoffset); - if (r < 0) - return r; - - /* Fill up release IP and MAC */ - release->dhcp.ciaddr = client->lease->address; - memcpy(&release->dhcp.chaddr, &client->mac_addr, client->mac_addr_len); - - r = dhcp_option_append(&release->dhcp, optlen, &optoffset, 0, - SD_DHCP_OPTION_END, 0, NULL); - if (r < 0) - return r; - - r = dhcp_network_send_udp_socket(client->fd, - client->lease->server_address, - DHCP_PORT_SERVER, - &release->dhcp, - sizeof(DHCPMessage) + optoffset); - if (r < 0) - return r; - - log_dhcp_client(client, "RELEASE"); - - return 0; -} - static int client_send_request(sd_dhcp_client *client) { _cleanup_free_ DHCPPacket *request = NULL; size_t optoffset, optlen; @@ -1929,8 +1894,35 @@ int sd_dhcp_client_start(sd_dhcp_client *client) { int sd_dhcp_client_send_release(sd_dhcp_client *client) { assert_return(client, -EINVAL); + assert_return(client->state != DHCP_STATE_STOPPED, -ESTALE); + assert_return(client->lease, -EUNATCH); - client_send_release(client); + _cleanup_free_ DHCPPacket *release = NULL; + size_t optoffset, optlen; + int r; + + r = client_message_init(client, &release, DHCP_RELEASE, &optlen, &optoffset); + if (r < 0) + return r; + + /* Fill up release IP and MAC */ + release->dhcp.ciaddr = client->lease->address; + memcpy(&release->dhcp.chaddr, &client->mac_addr, client->mac_addr_len); + + r = dhcp_option_append(&release->dhcp, optlen, &optoffset, 0, + SD_DHCP_OPTION_END, 0, NULL); + if (r < 0) + return r; + + r = dhcp_network_send_udp_socket(client->fd, + client->lease->server_address, + DHCP_PORT_SERVER, + &release->dhcp, + sizeof(DHCPMessage) + optoffset); + if (r < 0) + return r; + + log_dhcp_client(client, "RELEASE"); return 0; } diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 78cd241140..db762163a8 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -864,10 +864,10 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { return 0; } - if (link->network->dhcp_send_release) - (void) sd_dhcp_client_send_release(client); - if (link->dhcp_lease) { + if (link->network->dhcp_send_release) + (void) sd_dhcp_client_send_release(client); + r = dhcp_lease_lost(link); if (r < 0) { link_enter_failed(link); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 4c0daa4802..a23bddde9b 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -770,8 +770,12 @@ int link_stop_clients(Link *link, bool may_keep_dhcp) { dhcp4_release_old_lease(link); - if (link->dhcp_client && (!may_keep_dhcp || !link->network || - !FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP_ON_STOP))) { + bool keep_dhcp = may_keep_dhcp && + link->network && + (link->manager->restarting || + FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP_ON_STOP)); + + if (link->dhcp_client && !keep_dhcp) { k = sd_dhcp_client_stop(link->dhcp_client); if (k < 0) r = log_link_warning_errno(link, k, "Could not stop DHCPv4 client: %m"); diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index e0c095d022..546bb2375c 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -30,6 +30,7 @@ #include "ordered-set.h" #include "path-util.h" #include "set.h" +#include "signal-util.h" #include "strv.h" #include "sysctl-util.h" #include "tmpfile-util.h" @@ -1561,6 +1562,28 @@ static int manager_dirty_handler(sd_event_source *s, void *userdata) { return 1; } +static int signal_terminate_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + Manager *m = userdata; + + assert(m); + m->restarting = false; + + log_debug("Terminate operation initiated."); + + return sd_event_exit(sd_event_source_get_event(s), 0); +} + +static int signal_restart_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + Manager *m = userdata; + + assert(m); + m->restarting = true; + + log_debug("Restart operation initiated."); + + return sd_event_exit(sd_event_source_get_event(s), 0); +} + int manager_new(Manager **ret) { _cleanup_(manager_freep) Manager *m = NULL; int r; @@ -1581,9 +1604,12 @@ int manager_new(Manager **ret) { if (r < 0) return r; + assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, SIGUSR2, -1) >= 0); + (void) sd_event_set_watchdog(m->event, true); - (void) sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL); - (void) sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL); + (void) sd_event_add_signal(m->event, NULL, SIGTERM, signal_terminate_callback, m); + (void) sd_event_add_signal(m->event, NULL, SIGINT, signal_terminate_callback, m); + (void) sd_event_add_signal(m->event, NULL, SIGUSR2, signal_restart_callback, m); r = sd_event_add_post(m->event, NULL, manager_dirty_handler, m); if (r < 0) diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index f5166dad12..7b5557f5bf 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -29,6 +29,7 @@ struct Manager { bool enumerating:1; bool dirty:1; + bool restarting:1; Set *dirty_links; diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 23a21d8c9e..3ea76a034a 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -268,13 +268,11 @@ int network_verify(Network *network) { /* CriticalConnection=yes also preserve foreign static configurations. */ network->keep_configuration = KEEP_CONFIGURATION_YES; else - /* For backward compatibility, we do not release DHCP addresses on manager stop. */ - network->keep_configuration = KEEP_CONFIGURATION_DHCP_ON_STOP; + network->keep_configuration = KEEP_CONFIGURATION_NO; } if (network->keep_configuration < 0) - /* For backward compatibility, we do not release DHCP addresses on manager stop. */ - network->keep_configuration = KEEP_CONFIGURATION_DHCP_ON_STOP; + network->keep_configuration = KEEP_CONFIGURATION_NO; LIST_FOREACH_SAFE(addresses, address, address_next, network->static_addresses) if (address_section_verify(address) < 0) @@ -373,6 +371,7 @@ int network_load_one(Manager *manager, const char *filename) { .dhcp_use_routes = true, /* NOTE: this var might be overwritten by network_apply_anonymize_if_set */ .dhcp_send_hostname = true, + .dhcp_send_release = true, /* To enable/disable RFC7844 Anonymity Profiles */ .dhcp_anonymize = false, .dhcp_route_metric = DHCP_ROUTE_METRIC, 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/src/test/test-engine.c b/src/test/test-engine.c index 633cc42534..1064811497 100644 --- a/src/test/test-engine.c +++ b/src/test/test-engine.c @@ -106,9 +106,9 @@ int main(int argc, char *argv[]) { printf("Test11: (Start/stop job ordering, execution cycle)\n"); assert_se(manager_add_job(m, JOB_START, i, JOB_FAIL, NULL, NULL, &j) == 0); - assert_se(a->job && a->job->type == JOB_STOP); - assert_se(d->job && d->job->type == JOB_STOP); - assert_se(b->job && b->job->type == JOB_START); + assert_se(unit_has_job_type(a, JOB_STOP)); + assert_se(unit_has_job_type(d, JOB_STOP)); + assert_se(unit_has_job_type(b, JOB_START)); manager_dump_jobs(m, stdout, "\t"); printf("Load6:\n"); diff --git a/src/test/test-job-type.c b/src/test/test-job-type.c index d51e0d94fd..33a95c6b52 100644 --- a/src/test/test-job-type.c +++ b/src/test/test-job-type.c @@ -2,7 +2,6 @@ #include -#include "job.h" #include "service.h" #include "unit.h" 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= diff --git a/units/systemd-networkd.service.in b/units/systemd-networkd.service.in index 9ea3bb914e..5c6275e5b3 100644 --- a/units/systemd-networkd.service.in +++ b/units/systemd-networkd.service.in @@ -42,6 +42,7 @@ SystemCallArchitectures=native SystemCallErrorNumber=EPERM SystemCallFilter=@system-service Type=notify +RestartKillSignal=SIGUSR2 User=systemd-network WatchdogSec=3min