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-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/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/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") )
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 },
{}
};
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;
}
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)