]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: rework unit timeout handling, and add new setting RuntimeMaxSec=
authorLennart Poettering <lennart@poettering.net>
Mon, 1 Feb 2016 20:48:10 +0000 (21:48 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 1 Feb 2016 21:18:16 +0000 (22:18 +0100)
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
14 files changed:
src/core/busname.c
src/core/dbus-service.c
src/core/job.c
src/core/load-fragment-gperf.gperf.m4
src/core/load-fragment.c
src/core/main.c
src/core/mount.c
src/core/scope.c
src/core/service.c
src/core/service.h
src/core/socket.c
src/core/swap.c
src/core/unit.c
src/shared/bus-util.c

index a949cd6d3fa84d8ec265f5728e7227a41982a2f6..ed083a84124de330c1df3885f84c6335746cf18a 100644 (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;
index 2529689f5acb59ff50c0cccad846ace48b0190a9..16f50238a1ab7d7e3faad78f1135e6b59a255854 100644 (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,
index d8fdf1b53fecde0c3f53e094f450e335edb3a22d..1dcb872019180941b4dc84991987548368212b96 100644 (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");
index 7211738685eeb364071d52479801068a7743ba8f..2507db1932e6a38f34945f82e92d35fa5ff89884 100644 (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)
index 3b1fbd8b9019343d750a9b7d38ccdd64bbc43fa1..3b37cc4cda39c51fa3fed58ed1475b19d49707ae 100644 (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;
 }
 
index 2e1d07811c41f0c37ac9d24603ffc2d0a7ac0374..84e292afd20a7a90b531921455d34ffb72cda838 100644 (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;
 }
index 2ad4ad4f424e5dd956e11407a727476f461db8eb..7e3a6d578f0fb16e974fe947a796e59f65ce8117 100644 (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;
 
index 1953af1f88a58e1a8e72e4c8c3ab756f68d431e6..7cddee23b8d651db9f5aead06ddbd7fd6a0f8dd1 100644 (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 (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));
+        if (s->deserialized_state == s->state)
+                return 0;
 
-                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;
 
index 355de3e15d68d824eb40b6df24ab9671f7fd590a..edda02b8f3bafffc8e7a4b802c68cb7fe2f6e70f 100644 (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);
index 19efbccfc716eb3c69512cef5ade8fbc616f4e20..24408940d499ec6130c05692fa16078b2c37ee98 100644 (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;
index 2e4173aabc08c16d0fe25b42d0b65a89e1ca3c34..740b748d6577e9f31303eeaf8ef022285a0b3c44 100644 (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;
 
index 5568898bd710eef17ca54e89148324f3b55e98ab..d895e3ced13d4d28d836729fc7c086c4470205da 100644 (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;
 
index 05466a84f9446ebf104217739e74b1d17d13c22f..0c1efc0e166f67ec137f9b02c251ddafde56d22d 100644 (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)
index 9bd7db9aaa9719257ff3cb25723991a80f40bfa5..6df73c560ae757fc28387eb0ebd69b676aab51b2 100644 (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;