From c87700a1335f489be31cd3549927da68b5638819 Mon Sep 17 00:00:00 2001 From: Anita Zhang Date: Wed, 19 Sep 2018 12:03:01 -0700 Subject: [PATCH] Make Watchdog Signal Configurable Allows configuring the watchdog signal (with a default of SIGABRT). This allows an alternative to SIGABRT when coredumps are not desirable. Appropriate references to SIGABRT or aborting were renamed to reflect more liberal watchdog signals. Closes #8658 --- docs/TRANSIENT-SETTINGS.md | 1 + man/systemd.kill.xml | 8 +++++ man/systemd.service.xml | 3 +- src/basic/unit-def.c | 2 +- src/basic/unit-def.h | 2 +- src/core/dbus-kill.c | 5 +++ src/core/kill.c | 1 + src/core/kill.h | 1 + src/core/load-fragment-gperf.gperf.m4 | 3 +- src/core/service.c | 46 +++++++++++++-------------- src/core/unit.c | 4 +-- src/core/unit.h | 2 +- src/shared/bus-unit-util.c | 2 +- test/fuzz-corpus/unit-file/directives.service | 1 + 14 files changed, 50 insertions(+), 31 deletions(-) diff --git a/docs/TRANSIENT-SETTINGS.md b/docs/TRANSIENT-SETTINGS.md index bb13cfd..1b9a240 100644 --- a/docs/TRANSIENT-SETTINGS.md +++ b/docs/TRANSIENT-SETTINGS.md @@ -257,6 +257,7 @@ All process killing settings are available for transient units: ✓ KillMode= ✓ KillSignal= ✓ FinalKillSignal= +✓ WatchdogSignal= ``` ## Service Unit Settings diff --git a/man/systemd.kill.xml b/man/systemd.kill.xml index 1a42906..9b264ec 100644 --- a/man/systemd.kill.xml +++ b/man/systemd.kill.xml @@ -160,6 +160,14 @@ + + WatchdogSignal= + Specifies which signal to use to terminate the + service when the watchdog timeout expires (enabled through + WatchdogSec=). Defaults to SIGABRT. + + + diff --git a/man/systemd.service.xml b/man/systemd.service.xml index 0cd5385..beb4420 100644 --- a/man/systemd.service.xml +++ b/man/systemd.service.xml @@ -599,7 +599,8 @@ "keep-alive ping"). If the time between two such calls is larger than the configured time, then the service is placed in a failed state and it will be terminated with - SIGABRT. By setting + SIGABRT (or the signal specified by + WatchdogSignal=). By setting Restart= to , , or , the service will be automatically diff --git a/src/basic/unit-def.c b/src/basic/unit-def.c index ac6a9b3..245daab 100644 --- a/src/basic/unit-def.c +++ b/src/basic/unit-def.c @@ -169,7 +169,7 @@ static const char* const service_state_table[_SERVICE_STATE_MAX] = { [SERVICE_EXITED] = "exited", [SERVICE_RELOAD] = "reload", [SERVICE_STOP] = "stop", - [SERVICE_STOP_SIGABRT] = "stop-sigabrt", + [SERVICE_STOP_WATCHDOG] = "stop-watchdog", [SERVICE_STOP_SIGTERM] = "stop-sigterm", [SERVICE_STOP_SIGKILL] = "stop-sigkill", [SERVICE_STOP_POST] = "stop-post", diff --git a/src/basic/unit-def.h b/src/basic/unit-def.h index d7e2d74..85f3e42 100644 --- a/src/basic/unit-def.h +++ b/src/basic/unit-def.h @@ -108,7 +108,7 @@ typedef enum ServiceState { SERVICE_EXITED, /* Nothing is running anymore, but RemainAfterExit is true hence this is OK */ SERVICE_RELOAD, SERVICE_STOP, /* No STOP_PRE state, instead just register multiple STOP executables */ - SERVICE_STOP_SIGABRT, /* Watchdog timeout */ + SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, diff --git a/src/core/dbus-kill.c b/src/core/dbus-kill.c index 3e2a769..e2b3a0d 100644 --- a/src/core/dbus-kill.c +++ b/src/core/dbus-kill.c @@ -15,12 +15,14 @@ const sd_bus_vtable bus_kill_vtable[] = { 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), + SD_BUS_PROPERTY("WatchdogSignal", "i", bus_property_get_int, offsetof(KillContext, watchdog_signal), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_VTABLE_END }; 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(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); int bus_kill_context_set_transient_property( Unit *u, @@ -52,5 +54,8 @@ int bus_kill_context_set_transient_property( if (streq(name, "FinalKillSignal")) return bus_set_transient_final_kill_signal(u, name, &c->final_kill_signal, message, flags, error); + if (streq(name, "WatchdogSignal")) + return bus_set_transient_watchdog_signal(u, name, &c->watchdog_signal, message, flags, error); + return 0; } diff --git a/src/core/kill.c b/src/core/kill.c index 73fa556..6fe96cf 100644 --- a/src/core/kill.c +++ b/src/core/kill.c @@ -12,6 +12,7 @@ void kill_context_init(KillContext *c) { c->final_kill_signal = SIGKILL; c->send_sigkill = true; c->send_sighup = false; + c->watchdog_signal = SIGABRT; } void kill_context_dump(KillContext *c, FILE *f, const char *prefix) { diff --git a/src/core/kill.h b/src/core/kill.h index f4e312d..f3915be 100644 --- a/src/core/kill.h +++ b/src/core/kill.h @@ -24,6 +24,7 @@ struct KillContext { int final_kill_signal; bool send_sigkill; bool send_sighup; + int watchdog_signal; }; typedef enum KillWho { diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index ad9c458..7410d47 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -152,7 +152,8 @@ 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.FinalKillSignal, config_parse_signal, 0, offsetof($1, kill_context.final_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 m4_define(`CGROUP_CONTEXT_CONFIG_ITEMS', `$1.Slice, config_parse_unit_slice, 0, 0 diff --git a/src/core/service.c b/src/core/service.c index 722a9e9..778bce8 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -48,7 +48,7 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = { [SERVICE_EXITED] = UNIT_ACTIVE, [SERVICE_RELOAD] = UNIT_RELOADING, [SERVICE_STOP] = UNIT_DEACTIVATING, - [SERVICE_STOP_SIGABRT] = UNIT_DEACTIVATING, + [SERVICE_STOP_WATCHDOG] = UNIT_DEACTIVATING, [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING, [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING, [SERVICE_STOP_POST] = UNIT_DEACTIVATING, @@ -69,7 +69,7 @@ static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] = [SERVICE_EXITED] = UNIT_ACTIVE, [SERVICE_RELOAD] = UNIT_RELOADING, [SERVICE_STOP] = UNIT_DEACTIVATING, - [SERVICE_STOP_SIGABRT] = UNIT_DEACTIVATING, + [SERVICE_STOP_WATCHDOG] = UNIT_DEACTIVATING, [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING, [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING, [SERVICE_STOP_POST] = UNIT_DEACTIVATING, @@ -1031,7 +1031,7 @@ static void service_set_state(Service *s, ServiceState state) { SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD, - SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, + SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL, SERVICE_AUTO_RESTART)) s->timer_event_source = sd_event_source_unref(s->timer_event_source); @@ -1039,7 +1039,7 @@ static void service_set_state(Service *s, ServiceState state) { if (!IN_SET(state, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD, - SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, + SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) { service_unwatch_main_pid(s); s->main_command = NULL; @@ -1048,7 +1048,7 @@ static void service_set_state(Service *s, ServiceState state) { if (!IN_SET(state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD, - SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, + SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) { service_unwatch_control_pid(s); s->control_command = NULL; @@ -1063,7 +1063,7 @@ static void service_set_state(Service *s, ServiceState state) { if (!IN_SET(state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD, - SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, + SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL) && !(state == SERVICE_DEAD && UNIT(s)->job)) service_close_socket_fd(s); @@ -1102,7 +1102,7 @@ static usec_t service_coldplug_timeout(Service *s) { return usec_add(UNIT(s)->active_enter_timestamp.monotonic, s->runtime_max_usec); case SERVICE_STOP: - case SERVICE_STOP_SIGABRT: + case SERVICE_STOP_WATCHDOG: case SERVICE_STOP_SIGTERM: case SERVICE_STOP_SIGKILL: case SERVICE_STOP_POST: @@ -1137,7 +1137,7 @@ static int service_coldplug(Unit *u) { (IN_SET(s->deserialized_state, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD, - SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, + SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))) { r = unit_watch_pid(UNIT(s), s->main_pid); if (r < 0) @@ -1149,7 +1149,7 @@ static int service_coldplug(Unit *u) { IN_SET(s->deserialized_state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD, - SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, + SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) { r = unit_watch_pid(UNIT(s), s->control_pid); if (r < 0) @@ -1780,8 +1780,8 @@ fail: static int state_to_kill_operation(ServiceState state) { switch (state) { - case SERVICE_STOP_SIGABRT: - return KILL_ABORT; + case SERVICE_STOP_WATCHDOG: + return KILL_WATCHDOG; case SERVICE_STOP_SIGTERM: case SERVICE_FINAL_SIGTERM: @@ -1827,9 +1827,9 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f goto fail; service_set_state(s, state); - } else if (IN_SET(state, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM) && s->kill_context.send_sigkill) + } else if (IN_SET(state, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM) && s->kill_context.send_sigkill) service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_SUCCESS); - else if (IN_SET(state, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL)) + else if (IN_SET(state, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL)) service_enter_stop_post(s, SERVICE_SUCCESS); else if (state == SERVICE_FINAL_SIGTERM && s->kill_context.send_sigkill) service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_SUCCESS); @@ -1841,7 +1841,7 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f fail: log_unit_warning_errno(UNIT(s), r, "Failed to kill processes: %m"); - if (IN_SET(state, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL)) + if (IN_SET(state, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL)) service_enter_stop_post(s, SERVICE_FAILURE_RESOURCES); else service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true); @@ -2291,7 +2291,7 @@ static int service_start(Unit *u) { /* We cannot fulfill this request right now, try again later * please! */ if (IN_SET(s->state, - SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, + SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) return -EAGAIN; @@ -2361,7 +2361,7 @@ static int service_stop(Unit *u) { /* Already on it */ if (IN_SET(s->state, - SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, + SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) return 0; @@ -3130,7 +3130,7 @@ static void service_notify_cgroup_empty_event(Unit *u) { service_enter_running(s, SERVICE_SUCCESS); break; - case SERVICE_STOP_SIGABRT: + case SERVICE_STOP_WATCHDOG: case SERVICE_STOP_SIGTERM: case SERVICE_STOP_SIGKILL: @@ -3274,7 +3274,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { service_enter_running(s, f); break; - case SERVICE_STOP_SIGABRT: + case SERVICE_STOP_WATCHDOG: case SERVICE_STOP_SIGTERM: case SERVICE_STOP_SIGKILL: @@ -3409,7 +3409,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { service_enter_signal(s, SERVICE_STOP_SIGTERM, f); break; - case SERVICE_STOP_SIGABRT: + case SERVICE_STOP_WATCHDOG: case SERVICE_STOP_SIGTERM: case SERVICE_STOP_SIGKILL: if (main_pid_good(s) <= 0) @@ -3480,8 +3480,8 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT); break; - case SERVICE_STOP_SIGABRT: - log_unit_warning(UNIT(s), "State 'stop-sigabrt' timed out. Terminating."); + case SERVICE_STOP_WATCHDOG: + log_unit_warning(UNIT(s), "State 'stop-watchdog' timed out. Terminating."); service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT); break; @@ -3560,7 +3560,7 @@ static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void log_unit_error(UNIT(s), "Watchdog timeout (limit %s)!", format_timespan(t, sizeof(t), watchdog_usec, 1)); - service_enter_signal(s, SERVICE_STOP_SIGABRT, SERVICE_FAILURE_WATCHDOG); + service_enter_signal(s, SERVICE_STOP_WATCHDOG, SERVICE_FAILURE_WATCHDOG); } else log_unit_warning(UNIT(s), "Watchdog disabled! Ignoring watchdog timeout (limit %s)!", format_timespan(t, sizeof(t), watchdog_usec, 1)); @@ -3967,7 +3967,7 @@ static bool service_needs_console(Unit *u) { SERVICE_RUNNING, SERVICE_RELOAD, SERVICE_STOP, - SERVICE_STOP_SIGABRT, + SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, diff --git a/src/core/unit.c b/src/core/unit.c index 6ca8f6a..5817fc1 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -4509,8 +4509,8 @@ static int operation_to_signal(KillContext *c, KillOperation k) { case KILL_KILL: return c->final_kill_signal; - case KILL_ABORT: - return SIGABRT; + case KILL_WATCHDOG: + return c->watchdog_signal; default: assert_not_reached("KillOperation unknown"); diff --git a/src/core/unit.h b/src/core/unit.h index 3770c01..861a2c1 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -19,7 +19,7 @@ typedef enum KillOperation { KILL_TERMINATE, KILL_TERMINATE_AND_LOG, KILL_KILL, - KILL_ABORT, + KILL_WATCHDOG, _KILL_OPERATION_MAX, _KILL_OPERATION_INVALID = -1 } KillOperation; diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 4becc8c..134ad63 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -1222,7 +1222,7 @@ static int bus_append_kill_property(sd_bus_message *m, const char *field, const return bus_append_parse_boolean(m, field, eq); - if (STR_IN_SET(field, "KillSignal", "FinalKillSignal")) + if (STR_IN_SET(field, "KillSignal", "FinalKillSignal", "WatchdogSignal")) return bus_append_signal_from_string(m, field, eq); diff --git a/test/fuzz-corpus/unit-file/directives.service b/test/fuzz-corpus/unit-file/directives.service index c2334d3..b9c1b46 100644 --- a/test/fuzz-corpus/unit-file/directives.service +++ b/test/fuzz-corpus/unit-file/directives.service @@ -769,6 +769,7 @@ KeyringMode= KillExcludeUsers= KillOnlyUsers= KillSignal= +WatchdogSignal= KillUserProcesses= LOCATION= LidSwitchIgnoreInhibited= -- 2.7.4