summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
authorJan Klötzke <Jan.Kloetzke@preh.de>2017-11-29 07:43:44 +0100
committerLennart Poettering <lennart@poettering.net>2019-04-12 17:32:52 +0200
commitdc653bf487bae9d1ddf794442bf4176fee173b41 (patch)
treeec2ca709c4d1bdf14dee5252807c8c7e3cb359f0 /src/core
parentcode style format: clang-format applied to src/a*/* (diff)
downloadsystemd-dc653bf487bae9d1ddf794442bf4176fee173b41.tar.xz
systemd-dc653bf487bae9d1ddf794442bf4176fee173b41.zip
service: handle abort stops with dedicated timeout
When shooting down a service with SIGABRT the user might want to have a much longer stop timeout than on regular stops/shutdowns. Especially in the face of short stop timeouts the time might not be sufficient to write huge core dumps before the service is killed. This commit adds a dedicated (Default)TimeoutAbortSec= timer that is used when stopping a service via SIGABRT. In all other cases the existing TimeoutStopSec= is used. The timer value is unset by default to skip the special handling and use TimeoutStopSec= for state 'stop-watchdog' to keep the old behaviour. If the service is in state 'stop-watchdog' and the service should be stopped explicitly we still go to 'stop-sigterm' and re-apply the usual TimeoutStopSec= timeout.
Diffstat (limited to 'src/core')
-rw-r--r--src/core/dbus-manager.c22
-rw-r--r--src/core/dbus-service.c22
-rw-r--r--src/core/load-fragment-gperf.gperf.m41
-rw-r--r--src/core/load-fragment.c36
-rw-r--r--src/core/load-fragment.h1
-rw-r--r--src/core/main.c39
-rw-r--r--src/core/manager.h6
-rw-r--r--src/core/service.c19
-rw-r--r--src/core/service.h6
-rw-r--r--src/core/system.conf.in1
-rw-r--r--src/core/user.conf1
11 files changed, 149 insertions, 5 deletions
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index 9c0b994cc3..7ca3b815ef 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -287,6 +287,27 @@ static int property_set_runtime_watchdog(
return watchdog_set_timeout(t);
}
+static int property_get_default_timeout_abort_usec(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Manager *m = userdata;
+ usec_t t;
+
+ assert(bus);
+ assert(reply);
+ assert(m);
+
+ t = manager_default_timeout_abort_usec(m);
+
+ return sd_bus_message_append(reply, "t", t);
+}
+
static int bus_get_unit_by_name(Manager *m, sd_bus_message *message, const char *name, Unit **ret_unit, sd_bus_error *error) {
Unit *u;
int r;
@@ -2410,6 +2431,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_PROPERTY("DefaultTimerAccuracyUSec", "t", bus_property_get_usec, offsetof(Manager, default_timer_accuracy_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultTimeoutStartUSec", "t", bus_property_get_usec, offsetof(Manager, default_timeout_start_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultTimeoutStopUSec", "t", bus_property_get_usec, offsetof(Manager, default_timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultTimeoutAbortUSec", "t", property_get_default_timeout_abort_usec, 0, 0),
SD_BUS_PROPERTY("DefaultRestartUSec", "t", bus_property_get_usec, offsetof(Manager, default_restart_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultStartLimitIntervalUSec", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST),
/* The following two items are obsolete alias */
diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c
index 4e6709c42e..4fba8ae62f 100644
--- a/src/core/dbus-service.c
+++ b/src/core/dbus-service.c
@@ -29,6 +29,27 @@ static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_restart, service_restart, Servi
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_notify_access, notify_access, NotifyAccess);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_emergency_action, emergency_action, EmergencyAction);
+static int property_get_timeout_abort_usec(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Service *s = userdata;
+ usec_t t;
+
+ assert(bus);
+ assert(reply);
+ assert(s);
+
+ t = service_timeout_abort_usec(s);
+
+ return sd_bus_message_append(reply, "t", t);
+}
+
static int property_get_exit_status_set(
sd_bus *bus,
const char *path,
@@ -103,6 +124,7 @@ const sd_bus_vtable bus_service_vtable[] = {
SD_BUS_PROPERTY("RestartUSec", "t", bus_property_get_usec, offsetof(Service, restart_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("TimeoutStartUSec", "t", bus_property_get_usec, offsetof(Service, timeout_start_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("TimeoutStopUSec", "t", bus_property_get_usec, offsetof(Service, timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("TimeoutAbortUSec", "t", property_get_timeout_abort_usec, 0, 0),
SD_BUS_PROPERTY("RuntimeMaxUSec", "t", bus_property_get_usec, offsetof(Service, runtime_max_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("WatchdogUSec", "t", bus_property_get_usec, offsetof(Service, watchdog_usec), SD_BUS_VTABLE_PROPERTY_CONST),
BUS_PROPERTY_DUAL_TIMESTAMP("WatchdogTimestamp", offsetof(Service, watchdog_timestamp), 0),
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 2a09d8e588..13e9bbbfb1 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -308,6 +308,7 @@ Service.RestartSec, config_parse_sec, 0,
Service.TimeoutSec, config_parse_service_timeout, 0, 0
Service.TimeoutStartSec, config_parse_service_timeout, 0, 0
Service.TimeoutStopSec, config_parse_sec_fix_0, 0, offsetof(Service, timeout_stop_usec)
+Service.TimeoutAbortSec, config_parse_service_timeout_abort, 0, 0
Service.RuntimeMaxSec, config_parse_sec, 0, offsetof(Service, runtime_max_usec)
Service.WatchdogSec, config_parse_sec, 0, offsetof(Service, watchdog_usec)
m4_dnl The following five only exist for compatibility, they moved into Unit, see above
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index faa694d90c..e7a1f4206b 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -1894,6 +1894,42 @@ int config_parse_service_timeout(
return 0;
}
+int config_parse_service_timeout_abort(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Service *s = userdata;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(s);
+
+ rvalue += strspn(rvalue, WHITESPACE);
+ if (isempty(rvalue)) {
+ s->timeout_abort_set = false;
+ return 0;
+ }
+
+ r = parse_sec(rvalue, &s->timeout_abort_usec);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse TimeoutAbortSec= setting, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ s->timeout_abort_set = true;
+ return 0;
+}
+
int config_parse_sec_fix_0(
const char *unit,
const char *filename,
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index 95d06320c0..0891f36760 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -24,6 +24,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_exec_nice);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_oom_score_adjust);
CONFIG_PARSER_PROTOTYPE(config_parse_exec);
CONFIG_PARSER_PROTOTYPE(config_parse_service_timeout);
+CONFIG_PARSER_PROTOTYPE(config_parse_service_timeout_abort);
CONFIG_PARSER_PROTOTYPE(config_parse_service_type);
CONFIG_PARSER_PROTOTYPE(config_parse_service_restart);
CONFIG_PARSER_PROTOTYPE(config_parse_socket_bindtodevice);
diff --git a/src/core/main.c b/src/core/main.c
index 3c4b5202df..f80f32183d 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -112,6 +112,8 @@ static ExecOutput arg_default_std_error = EXEC_OUTPUT_INHERIT;
static usec_t arg_default_restart_usec = DEFAULT_RESTART_USEC;
static usec_t arg_default_timeout_start_usec = DEFAULT_TIMEOUT_USEC;
static usec_t arg_default_timeout_stop_usec = DEFAULT_TIMEOUT_USEC;
+static usec_t arg_default_timeout_abort_usec = DEFAULT_TIMEOUT_USEC;
+static bool arg_default_timeout_abort_set = false;
static usec_t arg_default_start_limit_interval = DEFAULT_START_LIMIT_INTERVAL;
static unsigned arg_default_start_limit_burst = DEFAULT_START_LIMIT_BURST;
static usec_t arg_runtime_watchdog = 0;
@@ -668,6 +670,40 @@ static int config_parse_crash_chvt(
return 0;
}
+static int config_parse_timeout_abort(
+ const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ rvalue += strspn(rvalue, WHITESPACE);
+ if (isempty(rvalue)) {
+ arg_default_timeout_abort_set = false;
+ return 0;
+ }
+
+ r = parse_sec(rvalue, &arg_default_timeout_abort_usec);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse DefaultTimeoutAbortSec= setting, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ arg_default_timeout_abort_set = true;
+ return 0;
+}
+
static int parse_config_file(void) {
const ConfigTableItem items[] = {
@@ -697,6 +733,7 @@ static int parse_config_file(void) {
{ "Manager", "DefaultStandardError", config_parse_output_restricted,0, &arg_default_std_error },
{ "Manager", "DefaultTimeoutStartSec", config_parse_sec, 0, &arg_default_timeout_start_usec },
{ "Manager", "DefaultTimeoutStopSec", config_parse_sec, 0, &arg_default_timeout_stop_usec },
+ { "Manager", "DefaultTimeoutAbortSec", config_parse_timeout_abort, 0, NULL },
{ "Manager", "DefaultRestartSec", config_parse_sec, 0, &arg_default_restart_usec },
{ "Manager", "DefaultStartLimitInterval", config_parse_sec, 0, &arg_default_start_limit_interval }, /* obsolete alias */
{ "Manager", "DefaultStartLimitIntervalSec",config_parse_sec, 0, &arg_default_start_limit_interval },
@@ -765,6 +802,8 @@ static void set_manager_defaults(Manager *m) {
m->default_std_error = arg_default_std_error;
m->default_timeout_start_usec = arg_default_timeout_start_usec;
m->default_timeout_stop_usec = arg_default_timeout_stop_usec;
+ m->default_timeout_abort_usec = arg_default_timeout_abort_usec;
+ m->default_timeout_abort_set = arg_default_timeout_abort_set;
m->default_restart_usec = arg_default_restart_usec;
m->default_start_limit_interval = arg_default_start_limit_interval;
m->default_start_limit_burst = arg_default_start_limit_burst;
diff --git a/src/core/manager.h b/src/core/manager.h
index b5def59ed0..4ab4231b3b 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -330,6 +330,8 @@ struct Manager {
ExecOutput default_std_output, default_std_error;
usec_t default_restart_usec, default_timeout_start_usec, default_timeout_stop_usec;
+ usec_t default_timeout_abort_usec;
+ bool default_timeout_abort_set;
usec_t default_start_limit_interval;
unsigned default_start_limit_burst;
@@ -417,6 +419,10 @@ struct Manager {
bool honor_device_enumeration;
};
+static inline usec_t manager_default_timeout_abort_usec(Manager *m) {
+ return m->default_timeout_abort_set ? m->default_timeout_abort_usec : m->default_timeout_stop_usec;
+}
+
#define MANAGER_IS_SYSTEM(m) ((m)->unit_file_scope == UNIT_FILE_SYSTEM)
#define MANAGER_IS_USER(m) ((m)->unit_file_scope != UNIT_FILE_SYSTEM)
diff --git a/src/core/service.c b/src/core/service.c
index 53cead772a..0efd861888 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -99,6 +99,8 @@ static void service_init(Unit *u) {
s->timeout_start_usec = u->manager->default_timeout_start_usec;
s->timeout_stop_usec = u->manager->default_timeout_stop_usec;
+ s->timeout_abort_usec = u->manager->default_timeout_abort_usec;
+ s->timeout_abort_set = u->manager->default_timeout_abort_set;
s->restart_usec = u->manager->default_restart_usec;
s->runtime_max_usec = USEC_INFINITY;
s->type = _SERVICE_TYPE_INVALID;
@@ -789,7 +791,7 @@ static int service_load(Unit *u) {
static void service_dump(Unit *u, FILE *f, const char *prefix) {
char buf_restart[FORMAT_TIMESPAN_MAX], buf_start[FORMAT_TIMESPAN_MAX], buf_stop[FORMAT_TIMESPAN_MAX];
- char buf_runtime[FORMAT_TIMESPAN_MAX], buf_watchdog[FORMAT_TIMESPAN_MAX];
+ char buf_runtime[FORMAT_TIMESPAN_MAX], buf_watchdog[FORMAT_TIMESPAN_MAX], buf_abort[FORMAT_TIMESPAN_MAX];
ServiceExecCommand c;
Service *s = SERVICE(u);
const char *prefix2;
@@ -860,11 +862,15 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
"%sRestartSec: %s\n"
"%sTimeoutStartSec: %s\n"
"%sTimeoutStopSec: %s\n"
+ "%sTimeoutAbortSec: %s\n"
"%sRuntimeMaxSec: %s\n"
"%sWatchdogSec: %s\n",
prefix, format_timespan(buf_restart, sizeof(buf_restart), s->restart_usec, USEC_PER_SEC),
prefix, format_timespan(buf_start, sizeof(buf_start), s->timeout_start_usec, USEC_PER_SEC),
prefix, format_timespan(buf_stop, sizeof(buf_stop), s->timeout_stop_usec, USEC_PER_SEC),
+ prefix, s->timeout_abort_set
+ ? format_timespan(buf_abort, sizeof(buf_abort), s->timeout_abort_usec, USEC_PER_SEC)
+ : "",
prefix, format_timespan(buf_runtime, sizeof(buf_runtime), s->runtime_max_usec, USEC_PER_SEC),
prefix, format_timespan(buf_watchdog, sizeof(buf_watchdog), s->watchdog_usec, USEC_PER_SEC));
@@ -1132,7 +1138,6 @@ 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_WATCHDOG:
case SERVICE_STOP_SIGTERM:
case SERVICE_STOP_SIGKILL:
case SERVICE_STOP_POST:
@@ -1140,6 +1145,9 @@ static usec_t service_coldplug_timeout(Service *s) {
case SERVICE_FINAL_SIGKILL:
return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->timeout_stop_usec);
+ case SERVICE_STOP_WATCHDOG:
+ return usec_add(UNIT(s)->state_change_timestamp.monotonic, service_timeout_abort_usec(s));
+
case SERVICE_AUTO_RESTART:
return usec_add(UNIT(s)->inactive_enter_timestamp.monotonic, s->restart_usec);
@@ -1857,7 +1865,8 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f
goto fail;
if (r > 0) {
- r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_stop_usec));
+ r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC),
+ state == SERVICE_STOP_WATCHDOG ? service_timeout_abort_usec(s) : s->timeout_stop_usec));
if (r < 0)
goto fail;
@@ -2428,7 +2437,7 @@ static int service_stop(Unit *u) {
/* Already on it */
if (IN_SET(s->state,
- SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
+ SERVICE_STOP, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))
return 0;
@@ -2440,7 +2449,7 @@ static int service_stop(Unit *u) {
/* If there's already something running we go directly into
* kill mode. */
- if (IN_SET(s->state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD)) {
+ if (IN_SET(s->state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD, SERVICE_STOP_WATCHDOG)) {
service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_SUCCESS);
return 0;
}
diff --git a/src/core/service.h b/src/core/service.h
index 5cc946acaf..7f1dfe0a8a 100644
--- a/src/core/service.h
+++ b/src/core/service.h
@@ -97,6 +97,8 @@ struct Service {
usec_t restart_usec;
usec_t timeout_start_usec;
usec_t timeout_stop_usec;
+ usec_t timeout_abort_usec;
+ usec_t timeout_abort_set;
usec_t runtime_max_usec;
dual_timestamp watchdog_timestamp;
@@ -189,6 +191,10 @@ struct Service {
OOMPolicy oom_policy;
};
+static inline usec_t service_timeout_abort_usec(Service *s) {
+ return s->timeout_abort_set ? s->timeout_abort_usec : s->timeout_stop_usec;
+}
+
extern const UnitVTable service_vtable;
int service_set_socket_fd(Service *s, int fd, struct Socket *socket, bool selinux_context_net);
diff --git a/src/core/system.conf.in b/src/core/system.conf.in
index 0a58737b82..548e6dfb8c 100644
--- a/src/core/system.conf.in
+++ b/src/core/system.conf.in
@@ -35,6 +35,7 @@
#DefaultStandardError=inherit
#DefaultTimeoutStartSec=90s
#DefaultTimeoutStopSec=90s
+#DefaultTimeoutAbortSec=
#DefaultRestartSec=100ms
#DefaultStartLimitIntervalSec=10s
#DefaultStartLimitBurst=5
diff --git a/src/core/user.conf b/src/core/user.conf
index b427f1ef6d..0b52f733ef 100644
--- a/src/core/user.conf
+++ b/src/core/user.conf
@@ -22,6 +22,7 @@
#DefaultStandardError=inherit
#DefaultTimeoutStartSec=90s
#DefaultTimeoutStopSec=90s
+#DefaultTimeoutAbortSec=
#DefaultRestartSec=100ms
#DefaultStartLimitIntervalSec=10s
#DefaultStartLimitBurst=5