From 43ce15ac307fe87785f82391936842dd4f1402f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kl=C3=B6tzke?= Date: Thu, 11 Jan 2018 10:42:27 +0100 Subject: [PATCH 1/4] bus-util: add bool property setter --- src/shared/bus-util.c | 19 +++++++++++++++++++ src/shared/bus-util.h | 1 + 2 files changed, 20 insertions(+) diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index b6677e27f6..6b177afb8c 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -1338,6 +1338,25 @@ int bus_property_get_bool( return sd_bus_message_append_basic(reply, 'b', &b); } +int bus_property_set_bool( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *value, + void *userdata, + sd_bus_error *error) { + + int b, r; + + r = sd_bus_message_read(value, "b", &b); + if (r < 0) + return r; + + *(bool *) userdata = !!b; + return 0; +} + int bus_property_get_id128( sd_bus *bus, const char *path, diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h index cbd22a6cd6..969a444d83 100644 --- a/src/shared/bus-util.h +++ b/src/shared/bus-util.h @@ -80,6 +80,7 @@ int bus_print_property(const char *name, sd_bus_message *property, bool value, b int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool value, bool all); int bus_property_get_bool(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); +int bus_property_set_bool(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *value, void *userdata, sd_bus_error *error); int bus_property_get_id128(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); #define bus_property_get_usec ((sd_bus_property_get_t) NULL) From 2a12e32efa8dadf7c67ef4e3ea2139734eb94566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kl=C3=B6tzke?= Date: Mon, 20 Mar 2017 13:10:43 +0100 Subject: [PATCH 2/4] pid1: add option to disable service watchdogs Add a "systemd.service_watchdogs=" option to the command line which disables all service runtime watchdogs and emergency actions. --- man/kernel-command-line.xml | 1 + man/systemd.xml | 22 ++++++++++++++++++++++ src/core/dbus-manager.c | 1 + src/core/emergency-action.c | 5 +++++ src/core/main.c | 21 ++++++++++++++++++++- src/core/manager.c | 10 ++++++++++ src/core/manager.h | 1 + src/core/service.c | 10 +++++++--- 8 files changed, 67 insertions(+), 4 deletions(-) diff --git a/man/kernel-command-line.xml b/man/kernel-command-line.xml index 422c0607da..be55f14e4f 100644 --- a/man/kernel-command-line.xml +++ b/man/kernel-command-line.xml @@ -85,6 +85,7 @@ systemd.crash_shell systemd.crash_reboot systemd.confirm_spawn + systemd.service_watchdogs systemd.show_status systemd.log_target= systemd.log_level= diff --git a/man/systemd.xml b/man/systemd.xml index 1c644d128e..ad2c1e4f0b 100644 --- a/man/systemd.xml +++ b/man/systemd.xml @@ -273,6 +273,15 @@ to all zeros. + + + + Globally enable/disable all service watchdog timeouts and emergency + actions. This setting may also be specified during boot, on the kernel + command line via the systemd.service_watchdogs= + option, see below. Defaults to enabled. + + @@ -964,6 +973,19 @@ + + systemd.service_watchdogs= + + Takes a boolean argument. If disabled, all service runtime + watchdogs () and emergency actions (e.g. + or ) are + ignored by the system manager (PID 1); see + systemd.service5. + Defaults to enabled, i.e. watchdogs and failure actions are processed + normally. The hardware watchdog is not affected by this + option. + + systemd.show_status diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 721cad454d..4fe374867c 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -2416,6 +2416,7 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_PROPERTY("DefaultStandardError", "s", bus_property_get_exec_output, offsetof(Manager, default_std_output), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_WRITABLE_PROPERTY("RuntimeWatchdogUSec", "t", bus_property_get_usec, property_set_runtime_watchdog, offsetof(Manager, runtime_watchdog), 0), SD_BUS_WRITABLE_PROPERTY("ShutdownWatchdogUSec", "t", bus_property_get_usec, bus_property_set_usec, offsetof(Manager, shutdown_watchdog), 0), + SD_BUS_WRITABLE_PROPERTY("ServiceWatchdogs", "b", bus_property_get_bool, bus_property_set_bool, offsetof(Manager, service_watchdogs), 0), SD_BUS_PROPERTY("ControlGroup", "s", NULL, offsetof(Manager, cgroup_root), 0), SD_BUS_PROPERTY("SystemState", "s", property_get_system_state, 0, 0), SD_BUS_PROPERTY("ExitCode", "y", bus_property_get_unsigned, offsetof(Manager, return_value), 0), diff --git a/src/core/emergency-action.c b/src/core/emergency-action.c index 308608e426..decfacd600 100644 --- a/src/core/emergency-action.c +++ b/src/core/emergency-action.c @@ -49,6 +49,11 @@ int emergency_action( if (action == EMERGENCY_ACTION_NONE) return -ECANCELED; + if (!m->service_watchdogs) { + log_warning("Watchdog disabled! Not acting on: %s", reason); + return -ECANCELED; + } + if (!MANAGER_IS_SYSTEM(m)) { /* Downgrade all options to simply exiting if we run * in user mode */ diff --git a/src/core/main.c b/src/core/main.c index a088913f43..7d20f4e67b 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -112,6 +112,7 @@ static char *arg_confirm_spawn = NULL; static ShowStatus arg_show_status = _SHOW_STATUS_UNSET; static bool arg_switched_root = false; static bool arg_no_pager = false; +static bool arg_service_watchdogs = true; static char ***arg_join_controllers = NULL; static ExecOutput arg_default_std_output = EXEC_OUTPUT_JOURNAL; static ExecOutput arg_default_std_error = EXEC_OUTPUT_INHERIT; @@ -396,6 +397,14 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat arg_confirm_spawn = s; } + } else if (proc_cmdline_key_streq(key, "systemd.service_watchdogs")) { + + r = value ? parse_boolean(value) : true; + if (r < 0) + log_warning("Failed to parse service watchdog switch %s. Ignoring.", value); + else + arg_service_watchdogs = r; + } else if (proc_cmdline_key_streq(key, "systemd.show_status")) { if (value) { @@ -855,6 +864,7 @@ static void set_manager_settings(Manager *m) { assert(m); m->confirm_spawn = arg_confirm_spawn; + m->service_watchdogs = arg_service_watchdogs; m->runtime_watchdog = arg_runtime_watchdog; m->shutdown_watchdog = arg_shutdown_watchdog; m->cad_burst_action = arg_cad_burst_action; @@ -886,7 +896,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_SWITCHED_ROOT, ARG_DEFAULT_STD_OUTPUT, ARG_DEFAULT_STD_ERROR, - ARG_MACHINE_ID + ARG_MACHINE_ID, + ARG_SERVICE_WATCHDOGS, }; static const struct option options[] = { @@ -913,6 +924,7 @@ static int parse_argv(int argc, char *argv[]) { { "default-standard-output", required_argument, NULL, ARG_DEFAULT_STD_OUTPUT, }, { "default-standard-error", required_argument, NULL, ARG_DEFAULT_STD_ERROR, }, { "machine-id", required_argument, NULL, ARG_MACHINE_ID }, + { "service-watchdogs", required_argument, NULL, ARG_SERVICE_WATCHDOGS }, {} }; @@ -1067,6 +1079,13 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(r, "Failed to parse confirm spawn option: %m"); break; + case ARG_SERVICE_WATCHDOGS: + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse service watchdogs boolean: %s", optarg); + arg_service_watchdogs = r; + break; + case ARG_SHOW_STATUS: if (optarg) { r = parse_show_status(optarg, &arg_show_status); diff --git a/src/core/manager.c b/src/core/manager.c index ee9ff15076..a6037c335d 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -2659,6 +2659,7 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) { fprintf(f, "taint-usr=%s\n", yes_no(m->taint_usr)); fprintf(f, "ready-sent=%s\n", yes_no(m->ready_sent)); fprintf(f, "taint-logged=%s\n", yes_no(m->taint_logged)); + fprintf(f, "service-watchdogs=%s\n", yes_no(m->service_watchdogs)); for (q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) { /* The userspace and finish timestamps only apply to the host system, hence only serialize them there */ @@ -2830,6 +2831,15 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { else m->taint_logged = m->taint_logged || b; + } else if ((val = startswith(l, "service-watchdogs="))) { + int b; + + b = parse_boolean(val); + if (b < 0) + log_notice("Failed to parse service-watchdogs flag %s", val); + else + m->service_watchdogs = b; + } else if (startswith(l, "env=")) { r = deserialize_environment(&m->environment, l); if (r == -ENOMEM) diff --git a/src/core/manager.h b/src/core/manager.h index 1531374d1c..b01edea1b5 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -277,6 +277,7 @@ struct Manager { ShowStatus show_status; char *confirm_spawn; bool no_console_output; + bool service_watchdogs; ExecOutput default_std_output, default_std_error; diff --git a/src/core/service.c b/src/core/service.c index 44d9ca0a32..969e9b4ffc 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -3419,10 +3419,14 @@ static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void watchdog_usec = service_get_watchdog_usec(s); - log_unit_error(UNIT(s), "Watchdog timeout (limit %s)!", - format_timespan(t, sizeof(t), watchdog_usec, 1)); + if (UNIT(s)->manager->service_watchdogs) { + 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_SIGABRT, SERVICE_FAILURE_WATCHDOG); + } else + log_unit_warning(UNIT(s), "Watchdog disabled! Ignoring watchdog timeout (limit %s)!", + format_timespan(t, sizeof(t), watchdog_usec, 1)); return 0; } From 889d695d6c2772618a1018ee2f7e24021bcf9f57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kl=C3=B6tzke?= Date: Thu, 11 Jan 2018 10:44:38 +0100 Subject: [PATCH 3/4] systemd-analyze: add service-watchdogs verb New debug verb that enables or disables the service runtime watchdogs and emergency actions during runtime. This is the systemd-analyze version of the systemd.service_watchdogs command line option. --- man/systemd-analyze.xml | 18 ++++++++++-- src/analyze/analyze.c | 63 ++++++++++++++++++++++++++++++++--------- 2 files changed, 65 insertions(+), 16 deletions(-) diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml index 876f96d559..7e545316d8 100644 --- a/man/systemd-analyze.xml +++ b/man/systemd-analyze.xml @@ -53,7 +53,7 @@ systemd-analyze - Analyze system boot-up performance + Analyze and debug system manager @@ -131,6 +131,12 @@ calendar SPECS + + systemd-analyze + OPTIONS + service-watchdogs + STATE + @@ -139,7 +145,8 @@ systemd-analyze may be used to determine system boot-up performance statistics and retrieve other state and tracing information from the system and service manager, and to - verify the correctness of unit files. + verify the correctness of unit files. It is also used to access + special functions useful for advanced system manager debugging. systemd-analyze time prints the time spent in the kernel before userspace has been reached, the time @@ -232,6 +239,13 @@ syntax described in systemd.time7. + systemd-analyze service-watchdogs + STATE globally enables or disables the service + runtime watchdogs () and emergency actions (e.g. + or ); see + systemd.service5. + The hardware watchdog is not affected by this setting. + If no command is passed, systemd-analyze time is implied. diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index dce6dd61dd..461104458b 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -1521,6 +1521,39 @@ static int test_calendar(int argc, char *argv[], void *userdata) { return ret; } +static int service_watchdogs(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + int b, r; + + assert(argc == 2); + assert(argv); + + b = parse_boolean(argv[1]); + if (b < 0) { + log_error("Failed to parse service-watchdogs argument."); + return -EINVAL; + } + + r = acquire_bus(false, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); + + r = sd_bus_set_property( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ServiceWatchdogs", + &error, + "b", + b); + if (r < 0) + return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r)); + + return 0; +} + static int do_verify(int argc, char *argv[], void *userdata) { return verify_units(strv_skip(argv, 1), arg_user ? UNIT_FILE_USER : UNIT_FILE_SYSTEM, @@ -1563,6 +1596,7 @@ static int help(int argc, char *argv[], void *userdata) { " syscall-filter [NAME...] Print list of syscalls in seccomp filter\n" " verify FILE... Check unit files for correctness\n" " calendar SPEC... Validate repetitive calendar time events\n" + " service-watchdogs on/off Enable/disable service watchdogs\n" , program_invocation_short_name); /* When updating this list, including descriptions, apply @@ -1708,20 +1742,21 @@ static int parse_argv(int argc, char *argv[]) { int main(int argc, char *argv[]) { static const Verb verbs[] = { - { "help", VERB_ANY, VERB_ANY, 0, help }, - { "time", VERB_ANY, 1, VERB_DEFAULT, analyze_time }, - { "blame", VERB_ANY, 1, 0, analyze_blame }, - { "critical-chain", VERB_ANY, VERB_ANY, 0, analyze_critical_chain }, - { "plot", VERB_ANY, 1, 0, analyze_plot }, - { "dot", VERB_ANY, VERB_ANY, 0, dot }, - { "set-log-level", 2, 2, 0, set_log_level }, - { "get-log-level", VERB_ANY, 1, 0, get_log_level }, - { "set-log-target", 2, 2, 0, set_log_target }, - { "get-log-target", VERB_ANY, 1, 0, get_log_target }, - { "dump", VERB_ANY, 1, 0, dump }, - { "syscall-filter", VERB_ANY, VERB_ANY, 0, dump_syscall_filters }, - { "verify", 2, VERB_ANY, 0, do_verify }, - { "calendar", 2, VERB_ANY, 0, test_calendar }, + { "help", VERB_ANY, VERB_ANY, 0, help }, + { "time", VERB_ANY, 1, VERB_DEFAULT, analyze_time }, + { "blame", VERB_ANY, 1, 0, analyze_blame }, + { "critical-chain", VERB_ANY, VERB_ANY, 0, analyze_critical_chain }, + { "plot", VERB_ANY, 1, 0, analyze_plot }, + { "dot", VERB_ANY, VERB_ANY, 0, dot }, + { "set-log-level", 2, 2, 0, set_log_level }, + { "get-log-level", VERB_ANY, 1, 0, get_log_level }, + { "set-log-target", 2, 2, 0, set_log_target }, + { "get-log-target", VERB_ANY, 1, 0, get_log_target }, + { "dump", VERB_ANY, 1, 0, dump }, + { "syscall-filter", VERB_ANY, VERB_ANY, 0, dump_syscall_filters }, + { "verify", 2, VERB_ANY, 0, do_verify }, + { "calendar", 2, VERB_ANY, 0, test_calendar }, + { "service-watchdogs", 2, 2, 0, service_watchdogs }, {} }; From 21a6abdf0ea9ce1fa5b6edf3b9314347cc0dbca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kl=C3=B6tzke?= Date: Mon, 22 Jan 2018 21:39:57 +0100 Subject: [PATCH 4/4] bash-completion: systemd-analyze: add service-watchdogs verb --- shell-completion/bash/systemd-analyze | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/shell-completion/bash/systemd-analyze b/shell-completion/bash/systemd-analyze index 34d274bb4f..171802e885 100644 --- a/shell-completion/bash/systemd-analyze +++ b/shell-completion/bash/systemd-analyze @@ -49,6 +49,7 @@ _systemd_analyze() { [LOG_TARGET]='set-log-target' [VERIFY]='verify' [SECCOMP_FILTER]='syscall-filter' + [SERVICE_WATCHDOGS]='service-watchdogs' ) _init_completion || return @@ -124,6 +125,13 @@ _systemd_analyze() { compopt -o filenames fi + elif __contains_word "$verb" ${VERBS[SERVICE_WATCHDOGS]}; then + if [[ $cur = -* ]]; then + comps='--help --version --system --user' + else + comps='on off' + fi + fi COMPREPLY=( $(compgen -W '$comps' -- "$cur") )