core: rework unit timeout handling, and add new setting RuntimeMaxSec=

This clean-ups timeout handling in PID 1. Specifically, instead of storing 0 in internal timeout variables as
indication for a disabled timeout, use USEC_INFINITY which is in-line with how we do this in the rest of our code
(following the logic that 0 means "no", and USEC_INFINITY means "never").

This also replace all usec_t additions with invocations to usec_add(), so that USEC_INFINITY is properly propagated,
and sd-event considers it has indication for turning off the event source.

This also alters the deserialization of the units to restart timeouts from the time they were originally started from.
Before this patch timeouts would be restarted beginning with the time of the deserialization, which could lead to
artificially prolonged timeouts if a daemon reload took place.

Finally, a new RuntimeMaxSec= setting is introduced for service units, that specifies a maximum runtime after which a
specific service is forcibly terminated. This is useful to put time limits on time-intensive processing jobs.

This also simplifies the various xyz_spawn() calls of the various types in that explicit distruction of the timers is
removed, as that is done anyway by the state change handlers, and a state change is always done when the xyz_spawn()
calls fail.

Fixes: #2249
This commit is contained in:
Lennart Poettering 2016-02-01 21:48:10 +01:00
parent c5962fcff0
commit 36c16a7cdd
14 changed files with 213 additions and 197 deletions

View file

@ -112,29 +112,27 @@ static void busname_done(Unit *u) {
n->timer_event_source = sd_event_source_unref(n->timer_event_source);
}
static int busname_arm_timer(BusName *n) {
static int busname_arm_timer(BusName *n, usec_t usec) {
int r;
assert(n);
if (n->timeout_usec <= 0) {
n->timer_event_source = sd_event_source_unref(n->timer_event_source);
return 0;
}
if (n->timer_event_source) {
r = sd_event_source_set_time(n->timer_event_source, now(CLOCK_MONOTONIC) + n->timeout_usec);
r = sd_event_source_set_time(n->timer_event_source, usec);
if (r < 0)
return r;
return sd_event_source_set_enabled(n->timer_event_source, SD_EVENT_ONESHOT);
}
if (usec == USEC_INFINITY)
return 0;
r = sd_event_add_time(
UNIT(n)->manager->event,
&n->timer_event_source,
CLOCK_MONOTONIC,
now(CLOCK_MONOTONIC) + n->timeout_usec, 0,
usec, 0,
busname_dispatch_timer, n);
if (r < 0)
return r;
@ -372,7 +370,7 @@ static int busname_coldplug(Unit *u) {
if (r < 0)
return r;
r = busname_arm_timer(n);
r = busname_arm_timer(n, usec_add(u->state_change_timestamp.monotonic, n->timeout_usec));
if (r < 0)
return r;
}
@ -397,7 +395,7 @@ static int busname_make_starter(BusName *n, pid_t *_pid) {
pid_t pid;
int r;
r = busname_arm_timer(n);
r = busname_arm_timer(n, usec_add(now(CLOCK_MONOTONIC), n->timeout_usec));
if (r < 0)
goto fail;
@ -475,7 +473,7 @@ static void busname_enter_signal(BusName *n, BusNameState state, BusNameResult f
}
if (r > 0) {
r = busname_arm_timer(n);
r = busname_arm_timer(n, usec_add(now(CLOCK_MONOTONIC), n->timeout_usec));
if (r < 0) {
log_unit_warning_errno(UNIT(n), r, "Failed to arm timer: %m");
goto fail;

View file

@ -49,6 +49,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("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),
SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Service, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST),
@ -124,6 +125,19 @@ static int bus_service_set_transient_property(
unit_write_drop_in_private_format(UNIT(s), mode, name, "Type=%s\n", service_type_to_string(s->type));
}
return 1;
} else if (streq(name, "RuntimeMaxUSec")) {
usec_t u;
r = sd_bus_message_read(message, "t", &u);
if (r < 0)
return r;
if (mode != UNIT_CHECK) {
s->runtime_max_usec = u;
unit_write_drop_in_private_format(UNIT(s), mode, name, "RuntimeMaxSec=" USEC_FMT "us\n", u);
}
return 1;
} else if (STR_IN_SET(name,

View file

@ -932,14 +932,14 @@ int job_start_timer(Job *j) {
j->begin_usec = now(CLOCK_MONOTONIC);
if (j->unit->job_timeout <= 0)
if (j->unit->job_timeout == USEC_INFINITY)
return 0;
r = sd_event_add_time(
j->manager->event,
&j->timer_event_source,
CLOCK_MONOTONIC,
j->begin_usec + j->unit->job_timeout, 0,
usec_add(j->begin_usec, j->unit->job_timeout), 0,
job_dispatch_timer, j);
if (r < 0)
return r;
@ -1117,17 +1117,16 @@ int job_coldplug(Job *j) {
if (j->state == JOB_WAITING)
job_add_to_run_queue(j);
if (j->begin_usec == 0 || j->unit->job_timeout == 0)
if (j->begin_usec == 0 || j->unit->job_timeout == USEC_INFINITY)
return 0;
if (j->timer_event_source)
j->timer_event_source = sd_event_source_unref(j->timer_event_source);
j->timer_event_source = sd_event_source_unref(j->timer_event_source);
r = sd_event_add_time(
j->manager->event,
&j->timer_event_source,
CLOCK_MONOTONIC,
j->begin_usec + j->unit->job_timeout, 0,
usec_add(j->begin_usec, j->unit->job_timeout), 0,
job_dispatch_timer, j);
if (r < 0)
log_debug_errno(r, "Failed to restart timeout for job: %m");

View file

@ -214,6 +214,7 @@ Service.RestartSec, config_parse_sec, 0,
Service.TimeoutSec, config_parse_service_timeout, 0, offsetof(Service, timeout_start_usec)
Service.TimeoutStartSec, config_parse_service_timeout, 0, offsetof(Service, timeout_start_usec)
Service.TimeoutStopSec, config_parse_service_timeout, 0, offsetof(Service, timeout_stop_usec)
Service.RuntimeMaxSec, config_parse_sec, 0, offsetof(Service, runtime_max_usec)
Service.WatchdogSec, config_parse_sec, 0, offsetof(Service, watchdog_usec)
Service.StartLimitInterval, config_parse_sec, 0, offsetof(Service, start_limit.interval)
Service.StartLimitBurst, config_parse_unsigned, 0, offsetof(Service, start_limit.burst)

View file

@ -1743,6 +1743,15 @@ int config_parse_service_timeout(const char *unit,
} else if (streq(lvalue, "TimeoutStartSec"))
s->start_timeout_defined = true;
/* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
* immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
* all other timeouts. */
if (s->timeout_start_usec <= 0)
s->timeout_start_usec = USEC_INFINITY;
if (s->timeout_stop_usec <= 0)
s->timeout_stop_usec = USEC_INFINITY;
return 0;
}

View file

@ -710,8 +710,14 @@ static int parse_config_file(void) {
CONF_PATHS_NULSTR("systemd/system.conf.d") :
CONF_PATHS_NULSTR("systemd/user.conf.d");
config_parse_many(fn, conf_dirs_nulstr, "Manager\0",
config_item_table_lookup, items, false, NULL);
config_parse_many(fn, conf_dirs_nulstr, "Manager\0", config_item_table_lookup, items, false, NULL);
/* Traditionally "0" was used to turn off the default unit timeouts. Fix this up so that we used USEC_INFINITY
* like everywhere else. */
if (arg_default_timeout_start_usec <= 0)
arg_default_timeout_start_usec = USEC_INFINITY;
if (arg_default_timeout_stop_usec <= 0)
arg_default_timeout_stop_usec = USEC_INFINITY;
return 0;
}

View file

@ -152,29 +152,27 @@ static void mount_init(Unit *u) {
u->ignore_on_isolate = true;
}
static int mount_arm_timer(Mount *m) {
static int mount_arm_timer(Mount *m, usec_t usec) {
int r;
assert(m);
if (m->timeout_usec <= 0) {
m->timer_event_source = sd_event_source_unref(m->timer_event_source);
return 0;
}
if (m->timer_event_source) {
r = sd_event_source_set_time(m->timer_event_source, now(CLOCK_MONOTONIC) + m->timeout_usec);
r = sd_event_source_set_time(m->timer_event_source, usec);
if (r < 0)
return r;
return sd_event_source_set_enabled(m->timer_event_source, SD_EVENT_ONESHOT);
}
if (usec == USEC_INFINITY)
return 0;
r = sd_event_add_time(
UNIT(m)->manager->event,
&m->timer_event_source,
CLOCK_MONOTONIC,
now(CLOCK_MONOTONIC) + m->timeout_usec, 0,
usec, 0,
mount_dispatch_timer, m);
if (r < 0)
return r;
@ -653,7 +651,7 @@ static int mount_coldplug(Unit *u) {
if (r < 0)
return r;
r = mount_arm_timer(m);
r = mount_arm_timer(m, usec_add(u->state_change_timestamp.monotonic, m->timeout_usec));
if (r < 0)
return r;
}
@ -725,11 +723,11 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
r = unit_setup_exec_runtime(UNIT(m));
if (r < 0)
goto fail;
return r;
r = mount_arm_timer(m);
r = mount_arm_timer(m, usec_add(now(CLOCK_MONOTONIC), m->timeout_usec));
if (r < 0)
goto fail;
return r;
exec_params.environment = UNIT(m)->manager->environment;
exec_params.confirm_spawn = UNIT(m)->manager->confirm_spawn;
@ -745,21 +743,16 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
m->exec_runtime,
&pid);
if (r < 0)
goto fail;
return r;
r = unit_watch_pid(UNIT(m), pid);
if (r < 0)
/* FIXME: we need to do something here */
goto fail;
return r;
*_pid = pid;
return 0;
fail:
m->timer_event_source = sd_event_source_unref(m->timer_event_source);
return r;
}
static void mount_enter_dead(Mount *m, MountResult f) {
@ -805,7 +798,7 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) {
goto fail;
if (r > 0) {
r = mount_arm_timer(m);
r = mount_arm_timer(m, usec_add(now(CLOCK_MONOTONIC), m->timeout_usec));
if (r < 0)
goto fail;

View file

@ -66,29 +66,27 @@ static void scope_done(Unit *u) {
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
}
static int scope_arm_timer(Scope *s) {
static int scope_arm_timer(Scope *s, usec_t usec) {
int r;
assert(s);
if (s->timeout_stop_usec <= 0) {
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
return 0;
}
if (s->timer_event_source) {
r = sd_event_source_set_time(s->timer_event_source, now(CLOCK_MONOTONIC) + s->timeout_stop_usec);
r = sd_event_source_set_time(s->timer_event_source, usec);
if (r < 0)
return r;
return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT);
}
if (usec == USEC_INFINITY)
return 0;
r = sd_event_add_time(
UNIT(s)->manager->event,
&s->timer_event_source,
CLOCK_MONOTONIC,
now(CLOCK_MONOTONIC) + s->timeout_stop_usec, 0,
usec, 0,
scope_dispatch_timer, s);
if (r < 0)
return r;
@ -190,20 +188,19 @@ static int scope_coldplug(Unit *u) {
assert(s);
assert(s->state == SCOPE_DEAD);
if (s->deserialized_state != s->state) {
if (s->deserialized_state == s->state)
return 0;
if (IN_SET(s->deserialized_state, SCOPE_STOP_SIGKILL, SCOPE_STOP_SIGTERM)) {
r = scope_arm_timer(s);
if (r < 0)
return r;
}
if (!IN_SET(s->deserialized_state, SCOPE_DEAD, SCOPE_FAILED))
unit_watch_all_pids(UNIT(s));
scope_set_state(s, s->deserialized_state);
if (IN_SET(s->deserialized_state, SCOPE_STOP_SIGKILL, SCOPE_STOP_SIGTERM)) {
r = scope_arm_timer(s, usec_add(u->state_change_timestamp.monotonic, s->timeout_stop_usec));
if (r < 0)
return r;
}
if (!IN_SET(s->deserialized_state, SCOPE_DEAD, SCOPE_FAILED))
unit_watch_all_pids(UNIT(s));
scope_set_state(s, s->deserialized_state);
return 0;
}
@ -261,7 +258,7 @@ static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) {
r = 1;
if (r > 0) {
r = scope_arm_timer(s);
r = scope_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_stop_usec));
if (r < 0)
goto fail;

View file

@ -112,6 +112,7 @@ 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->restart_usec = u->manager->default_restart_usec;
s->runtime_max_usec = USEC_INFINITY;
s->type = _SERVICE_TYPE_INVALID;
s->socket_fd = -1;
s->bus_endpoint_fd = -1;
@ -216,7 +217,7 @@ static void service_start_watchdog(Service *s) {
return;
if (s->watchdog_event_source) {
r = sd_event_source_set_time(s->watchdog_event_source, s->watchdog_timestamp.monotonic + s->watchdog_usec);
r = sd_event_source_set_time(s->watchdog_event_source, usec_add(s->watchdog_timestamp.monotonic, s->watchdog_usec));
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to reset watchdog timer: %m");
return;
@ -228,7 +229,7 @@ static void service_start_watchdog(Service *s) {
UNIT(s)->manager->event,
&s->watchdog_event_source,
CLOCK_MONOTONIC,
s->watchdog_timestamp.monotonic + s->watchdog_usec, 0,
usec_add(s->watchdog_timestamp.monotonic, s->watchdog_usec), 0,
service_dispatch_watchdog, s);
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to add watchdog timer: %m");
@ -433,18 +434,21 @@ static int service_arm_timer(Service *s, usec_t usec) {
assert(s);
if (s->timer_event_source) {
r = sd_event_source_set_time(s->timer_event_source, now(CLOCK_MONOTONIC) + usec);
r = sd_event_source_set_time(s->timer_event_source, usec);
if (r < 0)
return r;
return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT);
}
if (usec == USEC_INFINITY)
return 0;
r = sd_event_add_time(
UNIT(s)->manager->event,
&s->timer_event_source,
CLOCK_MONOTONIC,
now(CLOCK_MONOTONIC) + usec, 0,
usec, 0,
service_dispatch_timer, s);
if (r < 0)
return r;
@ -509,6 +513,9 @@ static int service_verify(Service *s) {
if (!s->usb_function_descriptors && s->usb_function_strings)
log_unit_warning(UNIT(s), "Service has USBFunctionStrings= setting, but no USBFunctionDescriptors=. Ignoring.");
if (s->runtime_max_usec != USEC_INFINITY && s->type == SERVICE_ONESHOT)
log_unit_warning(UNIT(s), "MaxRuntimeSec= has no effect in combination with Type=oneshot. Ignoring.");
return 0;
}
@ -624,7 +631,7 @@ static int service_add_extras(Service *s) {
/* Oneshot services have disabled start timeout by default */
if (s->type == SERVICE_ONESHOT && !s->start_timeout_defined)
s->timeout_start_usec = 0;
s->timeout_start_usec = USEC_INFINITY;
service_fix_output(s);
@ -882,6 +889,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_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
@ -954,6 +962,37 @@ static void service_set_state(Service *s, ServiceState state) {
unit_notify(UNIT(s), table[old_state], table[state], s->reload_result == SERVICE_SUCCESS);
}
static usec_t service_coldplug_timeout(Service *s) {
assert(s);
switch (s->deserialized_state) {
case SERVICE_START_PRE:
case SERVICE_START:
case SERVICE_START_POST:
case SERVICE_RELOAD:
return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->timeout_start_usec);
case SERVICE_RUNNING:
return usec_add(UNIT(s)->active_enter_timestamp.monotonic, s->runtime_max_usec);
case SERVICE_STOP:
case SERVICE_STOP_SIGABRT:
case SERVICE_STOP_SIGTERM:
case SERVICE_STOP_SIGKILL:
case SERVICE_STOP_POST:
case SERVICE_FINAL_SIGTERM:
case SERVICE_FINAL_SIGKILL:
return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->timeout_stop_usec);
case SERVICE_AUTO_RESTART:
return usec_add(UNIT(s)->inactive_enter_timestamp.monotonic, s->restart_usec);
default:
return USEC_INFINITY;
}
}
static int service_coldplug(Unit *u) {
Service *s = SERVICE(u);
int r;
@ -964,31 +1003,9 @@ static int service_coldplug(Unit *u) {
if (s->deserialized_state == s->state)
return 0;
if (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_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
usec_t k;
k = IN_SET(s->deserialized_state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD) ? s->timeout_start_usec : s->timeout_stop_usec;
/* For the start/stop timeouts 0 means off */
if (k > 0) {
r = service_arm_timer(s, k);
if (r < 0)
return r;
}
}
if (s->deserialized_state == SERVICE_AUTO_RESTART) {
/* The restart timeouts 0 means immediately */
r = service_arm_timer(s, s->restart_usec);
if (r < 0)
return r;
}
r = service_arm_timer(s, service_coldplug_timeout(s));
if (r < 0)
return r;
if (s->main_pid > 0 &&
pid_is_unwaited(s->main_pid) &&
@ -1175,7 +1192,7 @@ static int service_spawn(
r = unit_setup_exec_runtime(UNIT(s));
if (r < 0)
goto fail;
return r;
if (pass_fds ||
s->exec_context.std_input == EXEC_INPUT_SOCKET ||
@ -1184,55 +1201,42 @@ static int service_spawn(
r = service_collect_fds(s, &fds, &fd_names);
if (r < 0)
goto fail;
return r;
n_fds = r;
}
if (timeout > 0) {
r = service_arm_timer(s, timeout);
if (r < 0)
goto fail;
} else
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), timeout));
if (r < 0)
return r;
r = unit_full_printf_strv(UNIT(s), c->argv, &argv);
if (r < 0)
goto fail;
return r;
our_env = new0(char*, 6);
if (!our_env) {
r = -ENOMEM;
goto fail;
}
if (!our_env)
return -ENOMEM;
if (is_control ? s->notify_access == NOTIFY_ALL : s->notify_access != NOTIFY_NONE)
if (asprintf(our_env + n_env++, "NOTIFY_SOCKET=%s", UNIT(s)->manager->notify_socket) < 0) {
r = -ENOMEM;
goto fail;
}
if (asprintf(our_env + n_env++, "NOTIFY_SOCKET=%s", UNIT(s)->manager->notify_socket) < 0)
return -ENOMEM;
if (s->main_pid > 0)
if (asprintf(our_env + n_env++, "MAINPID="PID_FMT, s->main_pid) < 0) {
r = -ENOMEM;
goto fail;
}
if (asprintf(our_env + n_env++, "MAINPID="PID_FMT, s->main_pid) < 0)
return -ENOMEM;
if (UNIT(s)->manager->running_as != MANAGER_SYSTEM)
if (asprintf(our_env + n_env++, "MANAGERPID="PID_FMT, getpid()) < 0) {
r = -ENOMEM;
goto fail;
}
if (asprintf(our_env + n_env++, "MANAGERPID="PID_FMT, getpid()) < 0)
return -ENOMEM;
if (s->socket_fd >= 0) {
union sockaddr_union sa;
socklen_t salen = sizeof(sa);
r = getpeername(s->socket_fd, &sa.sa, &salen);
if (r < 0) {
r = -errno;
goto fail;
}
if (r < 0)
return -errno;
if (IN_SET(sa.sa.sa_family, AF_INET, AF_INET6)) {
_cleanup_free_ char *addr = NULL;
@ -1241,34 +1245,26 @@ static int service_spawn(
r = sockaddr_pretty(&sa.sa, salen, true, false, &addr);
if (r < 0)
goto fail;
return r;
t = strappend("REMOTE_ADDR=", addr);
if (!t) {
r = -ENOMEM;
goto fail;
}
if (!t)
return -ENOMEM;
our_env[n_env++] = t;
port = sockaddr_port(&sa.sa);
if (port < 0) {
r = port;
goto fail;
}
if (port < 0)
return port;
if (asprintf(&t, "REMOTE_PORT=%u", port) < 0) {
r = -ENOMEM;
goto fail;
}
if (asprintf(&t, "REMOTE_PORT=%u", port) < 0)
return -ENOMEM;
our_env[n_env++] = t;
}
}
final_env = strv_env_merge(2, UNIT(s)->manager->environment, our_env, NULL);
if (!final_env) {
r = -ENOMEM;
goto fail;
}
if (!final_env)
return -ENOMEM;
if (is_control && UNIT(s)->cgroup_path) {
path = strjoina(UNIT(s)->cgroup_path, "/control");
@ -1280,7 +1276,7 @@ static int service_spawn(
r = bus_kernel_create_endpoint(UNIT(s)->manager->running_as == MANAGER_SYSTEM ? "system" : "user",
UNIT(s)->id, &bus_endpoint_path);
if (r < 0)
goto fail;
return r;
/* Pass the fd to the exec_params so that the child process can upload the policy.
* Keep a reference to the fd in the service, so the endpoint is kept alive as long
@ -1314,22 +1310,16 @@ static int service_spawn(
s->exec_runtime,
&pid);
if (r < 0)
goto fail;
return r;
r = unit_watch_pid(UNIT(s), pid);
if (r < 0)
/* FIXME: we need to do something here */
goto fail;
return r;
*_pid = pid;
return 0;
fail:
if (timeout)
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
return r;
}
static int main_pid_good(Service *s) {
@ -1437,7 +1427,7 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
if (allow_restart && service_shall_restart(s)) {
r = service_arm_timer(s, s->restart_usec);
r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->restart_usec));
if (r < 0)
goto fail;
@ -1545,11 +1535,9 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f
goto fail;
if (r > 0) {
if (s->timeout_stop_usec > 0) {
r = service_arm_timer(s, s->timeout_stop_usec);
if (r < 0)
goto fail;
}
r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_stop_usec));
if (r < 0)
goto fail;
service_set_state(s, state);
} else if (IN_SET(state, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM) && s->kill_context.send_sigkill)
@ -1577,8 +1565,7 @@ static void service_enter_stop_by_notify(Service *s) {
unit_watch_all_pids(UNIT(s));
if (s->timeout_stop_usec > 0)
service_arm_timer(s, s->timeout_stop_usec);
service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_stop_usec));
/* The service told us it's stopping, so it's as if we SIGTERM'd it. */
service_set_state(s, SERVICE_STOP_SIGTERM);
@ -1656,8 +1643,10 @@ static void service_enter_running(Service *s, ServiceResult f) {
service_enter_reload_by_notify(s);
else if (s->notify_state == NOTIFY_STOPPING)
service_enter_stop_by_notify(s);
else
else {
service_set_state(s, SERVICE_RUNNING);
service_arm_timer(s, usec_add(UNIT(s)->active_enter_timestamp.monotonic, s->runtime_max_usec));
}
} else if (s->remain_after_exit)
service_set_state(s, SERVICE_EXITED);
@ -1711,6 +1700,7 @@ static void service_kill_control_processes(Service *s) {
static void service_enter_start(Service *s) {
ExecCommand *c;
usec_t timeout;
pid_t pid;
int r;
@ -1742,9 +1732,16 @@ static void service_enter_start(Service *s) {
return;
}
if (IN_SET(s->type, SERVICE_SIMPLE, SERVICE_IDLE))
/* For simple + idle this is the main process. We don't apply any timeout here, but
* service_enter_running() will later apply the .runtime_max_usec timeout. */
timeout = USEC_INFINITY;
else
timeout = s->timeout_start_usec;
r = service_spawn(s,
c,
IN_SET(s->type, SERVICE_FORKING, SERVICE_DBUS, SERVICE_NOTIFY, SERVICE_ONESHOT) ? s->timeout_start_usec : 0,
timeout,
true,
true,
true,
@ -1754,7 +1751,7 @@ static void service_enter_start(Service *s) {
if (r < 0)
goto fail;
if (s->type == SERVICE_SIMPLE || s->type == SERVICE_IDLE) {
if (IN_SET(s->type, SERVICE_SIMPLE, SERVICE_IDLE)) {
/* For simple services we immediately start
* the START_POST binaries. */
@ -1769,9 +1766,7 @@ static void service_enter_start(Service *s) {
s->control_pid = pid;
service_set_state(s, SERVICE_START);
} else if (s->type == SERVICE_ONESHOT ||
s->type == SERVICE_DBUS ||
s->type == SERVICE_NOTIFY) {
} else if (IN_SET(s->type, SERVICE_ONESHOT, SERVICE_DBUS, SERVICE_NOTIFY)) {
/* For oneshot services we wait until the start
* process exited, too, but it is our main process. */
@ -1840,7 +1835,7 @@ static void service_enter_restart(Service *s) {
/* Don't restart things if we are going down anyway */
log_unit_info(UNIT(s), "Stop job pending for unit, delaying automatic restart.");
r = service_arm_timer(s, s->restart_usec);
r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->restart_usec));
if (r < 0)
goto fail;
@ -1870,9 +1865,7 @@ fail:
static void service_enter_reload_by_notify(Service *s) {
assert(s);
if (s->timeout_start_usec > 0)
service_arm_timer(s, s->timeout_start_usec);
service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_start_usec));
service_set_state(s, SERVICE_RELOAD);
}
@ -1913,6 +1906,7 @@ fail:
}
static void service_run_next_control(Service *s) {
usec_t timeout;
int r;
assert(s);
@ -1924,9 +1918,14 @@ static void service_run_next_control(Service *s) {
s->control_command = s->control_command->command_next;
service_unwatch_control_pid(s);
if (IN_SET(s->state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD))
timeout = s->timeout_start_usec;
else
timeout = s->timeout_stop_usec;
r = service_spawn(s,
s->control_command,
IN_SET(s->state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD) ? s->timeout_start_usec : s->timeout_stop_usec,
timeout,
false,
!s->permissions_start_only,
!s->root_directory_start_only,
@ -2882,6 +2881,11 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
service_enter_stop(s, SERVICE_FAILURE_TIMEOUT);
break;
case SERVICE_RUNNING:
log_unit_warning(UNIT(s), "Service reached runtime time limit. Stopping.");
service_enter_stop(s, SERVICE_FAILURE_TIMEOUT);
break;
case SERVICE_RELOAD:
log_unit_warning(UNIT(s), "Reload operation timed out. Stopping.");
service_unwatch_control_pid(s);

View file

@ -118,6 +118,7 @@ struct Service {
usec_t restart_usec;
usec_t timeout_start_usec;
usec_t timeout_stop_usec;
usec_t runtime_max_usec;
dual_timestamp watchdog_timestamp;
usec_t watchdog_usec;

View file

@ -170,29 +170,27 @@ static void socket_done(Unit *u) {
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
}
static int socket_arm_timer(Socket *s) {
static int socket_arm_timer(Socket *s, usec_t usec) {
int r;
assert(s);
if (s->timeout_usec <= 0) {
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
return 0;
}
if (s->timer_event_source) {
r = sd_event_source_set_time(s->timer_event_source, now(CLOCK_MONOTONIC) + s->timeout_usec);
r = sd_event_source_set_time(s->timer_event_source, usec);
if (r < 0)
return r;
return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT);
}
if (usec == USEC_INFINITY)
return 0;
r = sd_event_add_time(
UNIT(s)->manager->event,
&s->timer_event_source,
CLOCK_MONOTONIC,
now(CLOCK_MONOTONIC) + s->timeout_usec, 0,
usec, 0,
socket_dispatch_timer, s);
if (r < 0)
return r;
@ -1494,7 +1492,7 @@ static int socket_coldplug(Unit *u) {
if (r < 0)
return r;
r = socket_arm_timer(s);
r = socket_arm_timer(s, usec_add(u->state_change_timestamp.monotonic, s->timeout_usec));
if (r < 0)
return r;
}
@ -1507,6 +1505,7 @@ static int socket_coldplug(Unit *u) {
SOCKET_STOP_PRE,
SOCKET_STOP_PRE_SIGTERM,
SOCKET_STOP_PRE_SIGKILL)) {
r = socket_open_fds(s);
if (r < 0)
return r;
@ -1548,15 +1547,15 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
r = unit_setup_exec_runtime(UNIT(s));
if (r < 0)
goto fail;
return r;
r = socket_arm_timer(s);
r = socket_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec));
if (r < 0)
goto fail;
return r;
r = unit_full_printf_strv(UNIT(s), c->argv, &argv);
if (r < 0)
goto fail;
return r;
exec_params.argv = argv;
exec_params.environment = UNIT(s)->manager->environment;
@ -1573,26 +1572,22 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
s->exec_runtime,
&pid);
if (r < 0)
goto fail;
return r;
r = unit_watch_pid(UNIT(s), pid);
if (r < 0)
/* FIXME: we need to do something here */
goto fail;
return r;
*_pid = pid;
return 0;
fail:
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
return r;
}
static int socket_chown(Socket *s, pid_t *_pid) {
pid_t pid;
int r;
r = socket_arm_timer(s);
r = socket_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec));
if (r < 0)
goto fail;
@ -1735,7 +1730,7 @@ static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) {
goto fail;
if (r > 0) {
r = socket_arm_timer(s);
r = socket_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec));
if (r < 0)
goto fail;

View file

@ -160,29 +160,27 @@ static void swap_done(Unit *u) {
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
}
static int swap_arm_timer(Swap *s) {
static int swap_arm_timer(Swap *s, usec_t usec) {
int r;
assert(s);
if (s->timeout_usec <= 0) {
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
return 0;
}
if (s->timer_event_source) {
r = sd_event_source_set_time(s->timer_event_source, now(CLOCK_MONOTONIC) + s->timeout_usec);
r = sd_event_source_set_time(s->timer_event_source, usec);
if (r < 0)
return r;
return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT);
}
if (usec == USEC_INFINITY)
return 0;
r = sd_event_add_time(
UNIT(s)->manager->event,
&s->timer_event_source,
CLOCK_MONOTONIC,
now(CLOCK_MONOTONIC) + s->timeout_usec, 0,
usec, 0,
swap_dispatch_timer, s);
if (r < 0)
return r;
@ -552,7 +550,7 @@ static int swap_coldplug(Unit *u) {
if (r < 0)
return r;
r = swap_arm_timer(s);
r = swap_arm_timer(s, usec_add(u->state_change_timestamp.monotonic, s->timeout_usec));
if (r < 0)
return r;
}
@ -633,7 +631,7 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
if (r < 0)
goto fail;
r = swap_arm_timer(s);
r = swap_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec));
if (r < 0)
goto fail;
@ -710,7 +708,7 @@ static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) {
goto fail;
if (r > 0) {
r = swap_arm_timer(s);
r = swap_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec));
if (r < 0)
goto fail;

View file

@ -99,6 +99,7 @@ Unit *unit_new(Manager *m, size_t size) {
u->unit_file_preset = -1;
u->on_failure_job_mode = JOB_REPLACE;
u->cgroup_inotify_wd = -1;
u->job_timeout = USEC_INFINITY;
RATELIMIT_INIT(u->auto_stop_ratelimit, 10 * USEC_PER_SEC, 16);
@ -950,7 +951,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
STRV_FOREACH(j, u->dropin_paths)
fprintf(f, "%s\tDropIn Path: %s\n", prefix, *j);
if (u->job_timeout > 0)
if (u->job_timeout != USEC_INFINITY)
fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout, 0));
if (u->job_timeout_action != FAILURE_ACTION_NONE)

View file

@ -1443,7 +1443,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
eq[0] == '-');
goto finish;
} else if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec")) {
} else if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) {
char *n;
usec_t t;
size_t l;