[systemd-commits] 6 commits - Makefile.am TODO man/systemd-system.conf.xml man/systemd.service.xml src/core src/resolve src/shared src/test

Lennart Poettering lennart at kemper.freedesktop.org
Fri Aug 22 09:11:19 PDT 2014


 Makefile.am                    |    4 -
 TODO                           |    5 -
 man/systemd-system.conf.xml    |   27 ++++++++
 man/systemd.service.xml        |   54 +++++++++-------
 src/core/cgroup.c              |    4 -
 src/core/failure-action.c      |  132 +++++++++++++++++++++++++++++++++++++++++
 src/core/failure-action.h      |   43 +++++++++++++
 src/core/main.c                |   13 ++++
 src/core/manager.c             |  109 ++++++++++++++++++++++-----------
 src/core/manager.h             |   11 +++
 src/core/service.c             |   77 ++---------------------
 src/core/service.h             |   16 ----
 src/core/shutdown.c            |    3 
 src/core/system.conf           |    3 
 src/resolve/resolved-manager.c |    2 
 src/shared/util.c              |   21 ++++++
 src/shared/util.h              |    2 
 src/test/test-tables.c         |    2 
 18 files changed, 377 insertions(+), 151 deletions(-)

New commits:
commit d74f9e8e8a3dcddb043ef193e4bb14f58efa095f
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Aug 22 18:10:22 2014 +0200

    update TODO

diff --git a/TODO b/TODO
index 3073f3a..0fcd3a0 100644
--- a/TODO
+++ b/TODO
@@ -24,6 +24,9 @@ External:
 
 Features:
 
+* apply start timeout during the "initializing" manager state only,
+  instead of both "initializing" and "starting".
+
 * journald: make use of uid-range.h to managed uid ranges to split
   journals in.
 
@@ -138,8 +141,6 @@ Features:
 * For timer units: add some mechanisms so that timer units that trigger immediately on boot do not have the services
   they run added to the initial transaction and thus confuse Type=idle.
 
-* Add timeout to early-boot, and shut down the system if it is hit. Solves the laptop-in-bag problem and is useful for embedded cases
-
 * Run most system services with cgroupfs read-only and procfs with a more secure mode (doesn't work, since the hidepid= option is per-pid-namespace, not per-mount)
 
 * sd-event: generate a failure of a default event loop is executed out-of-thread

commit d81afec1c9bf4b73e3df8996d65ecae95d19b6db
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Aug 22 18:07:18 2014 +0200

    core: split up "starting" manager state into "initializing" and "starting"
    
    We'll stay in "initializing" until basic.target has reached, at which
    point we will enter "starting".
    
    This is preparation so that we can change the startip timeout to only
    apply to the first phase of startup, not the full procedure.

diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index 9248cb5..6c6e4f5 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -300,7 +300,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const cha
                 char buf[MAX(DECIMAL_STR_MAX(unsigned long), DECIMAL_STR_MAX(usec_t)) + 1];
 
                 sprintf(buf, "%lu\n",
-                        state == MANAGER_STARTING && c->startup_cpu_shares != (unsigned long) -1 ? c->startup_cpu_shares :
+                        IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && c->startup_cpu_shares != (unsigned long) -1 ? c->startup_cpu_shares :
                         c->cpu_shares != (unsigned long) -1 ? c->cpu_shares : 1024);
                 r = cg_set_attribute("cpu", path, "cpu.shares", buf);
                 if (r < 0)
@@ -328,7 +328,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const cha
                 CGroupBlockIODeviceBandwidth *b;
 
                 if (!is_root) {
-                        sprintf(buf, "%lu\n", state == MANAGER_STARTING && c->startup_blockio_weight != (unsigned long) -1 ? c->startup_blockio_weight :
+                        sprintf(buf, "%lu\n", IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && c->startup_blockio_weight != (unsigned long) -1 ? c->startup_blockio_weight :
                                 c->blockio_weight != (unsigned long) -1 ? c->blockio_weight : 1000);
                         r = cg_set_attribute("blkio", path, "blkio.weight", buf);
                         if (r < 0)
diff --git a/src/core/manager.c b/src/core/manager.c
index 7639aee..9abdf47 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -2837,7 +2837,7 @@ static bool manager_get_show_status(Manager *m) {
         if (m->no_console_output)
                 return false;
 
-        if (!IN_SET(manager_state(m), MANAGER_STARTING, MANAGER_STOPPING))
+        if (!IN_SET(manager_state(m), MANAGER_INITIALIZING, MANAGER_STARTING, MANAGER_STOPPING))
                 return false;
 
         if (m->show_status > 0)
@@ -2928,8 +2928,14 @@ ManagerState manager_state(Manager *m) {
         assert(m);
 
         /* Did we ever finish booting? If not then we are still starting up */
-        if (!dual_timestamp_is_set(&m->finish_timestamp))
+        if (!dual_timestamp_is_set(&m->finish_timestamp)) {
+
+                u = manager_get_unit(m, SPECIAL_BASIC_TARGET);
+                if (!u || !UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u)))
+                        return MANAGER_INITIALIZING;
+
                 return MANAGER_STARTING;
+        }
 
         /* Is the special shutdown target queued? If so, we are in shutdown state */
         u = manager_get_unit(m, SPECIAL_SHUTDOWN_TARGET);
@@ -2955,6 +2961,7 @@ ManagerState manager_state(Manager *m) {
 }
 
 static const char *const manager_state_table[_MANAGER_STATE_MAX] = {
+        [MANAGER_INITIALIZING] = "initializing",
         [MANAGER_STARTING] = "starting",
         [MANAGER_RUNNING] = "running",
         [MANAGER_DEGRADED] = "degraded",
diff --git a/src/core/manager.h b/src/core/manager.h
index 7d26c3a..8e3c146 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -38,6 +38,7 @@
 typedef struct Manager Manager;
 
 typedef enum ManagerState {
+        MANAGER_INITIALIZING,
         MANAGER_STARTING,
         MANAGER_RUNNING,
         MANAGER_DEGRADED,

commit f07756bfe25c64119704c93a634162d6c88b5c89
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Aug 22 16:59:46 2014 +0200

    core: introduce "poweroff" as new failure action types
    
    Also, change the default action on a system start-up timeout to powering off.

diff --git a/man/systemd-system.conf.xml b/man/systemd-system.conf.xml
index 4869002..1fad1db 100644
--- a/man/systemd-system.conf.xml
+++ b/man/systemd-system.conf.xml
@@ -298,7 +298,7 @@
                                 setting, see
                                 <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
                                 for details. Defaults to
-                                <option>reboot-force</option>. <varname>StartTimeoutRebootArgument=</varname>
+                                <option>poweroff-force</option>. <varname>StartTimeoutRebootArgument=</varname>
                                 configures an optional reboot string
                                 to pass to the
                                 <citerefentry><refentrytitle>reboot</refentrytitle><manvolnum>2</manvolnum></citerefentry>
diff --git a/man/systemd.service.xml b/man/systemd.service.xml
index 20d2a0d..8b17f85 100644
--- a/man/systemd.service.xml
+++ b/man/systemd.service.xml
@@ -1131,26 +1131,35 @@ ExecStart=/bin/echo $ONE $TWO ${TWO}</programlisting>
                                 hit. Takes one of
                                 <option>none</option>,
                                 <option>reboot</option>,
-                                <option>reboot-force</option>, or
-                                <option>reboot-immediate</option>. If
-                                <option>none</option> is set,
-                                hitting the rate limit will trigger no
-                                action besides that the start will not
-                                be permitted. <option>reboot</option>
+                                <option>reboot-force</option>,
+                                <option>reboot-immediate</option>,
+                                <option>poweroff</option>,
+                                <option>poweroff-force</option> or
+                                <option>poweroff-immediate</option>. If
+                                <option>none</option> is set, hitting
+                                the rate limit will trigger no action
+                                besides that the start will not be
+                                permitted. <option>reboot</option>
                                 causes a reboot following the normal
                                 shutdown procedure (i.e. equivalent to
                                 <command>systemctl reboot</command>).
-                                <option>reboot-force</option> causes
-                                a forced reboot which will terminate
-                                all processes forcibly but should
-                                cause no dirty file systems on reboot
+                                <option>reboot-force</option> causes a
+                                forced reboot which will terminate all
+                                processes forcibly but should cause no
+                                dirty file systems on reboot
                                 (i.e. equivalent to <command>systemctl
                                 reboot -f</command>) and
                                 <option>reboot-immediate</option>
                                 causes immediate execution of the
                                 <citerefentry><refentrytitle>reboot</refentrytitle><manvolnum>2</manvolnum></citerefentry>
                                 system call, which might result in
-                                data loss. Defaults to
+                                data loss. Similar,
+                                <option>poweroff</option>,
+                                <option>poweroff-force</option>,
+                                <option>poweroff-immediate</option>
+                                have the effect of powering down the
+                                system with similar
+                                semantics. Defaults to
                                 <option>none</option>.</para></listitem>
                         </varlistentry>
 
diff --git a/src/core/failure-action.c b/src/core/failure-action.c
index ca807b6..9417474 100644
--- a/src/core/failure-action.c
+++ b/src/core/failure-action.c
@@ -40,10 +40,19 @@ int failure_action(
         assert(action >= 0);
         assert(action < _FAILURE_ACTION_MAX);
 
-        switch (action) {
+        if (action == FAILURE_ACTION_NONE)
+                return -ECANCELED;
 
-        case FAILURE_ACTION_NONE:
-                break;
+        if (m->running_as == SYSTEMD_USER) {
+                /* Downgrade all options to simply exiting if we run
+                 * in user mode */
+
+                log_warning("Exiting as result of failure.");
+                m->exit_code = MANAGER_EXIT;
+                return -ECANCELED;
+        }
+
+        switch (action) {
 
         case FAILURE_ACTION_REBOOT: {
                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -78,6 +87,32 @@ int failure_action(
                 reboot(RB_AUTOBOOT);
                 break;
 
+        case FAILURE_ACTION_POWEROFF: {
+                _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+
+                log_warning("Powering off as result of failure.");
+
+                r = manager_add_job_by_name(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE, true, &error, NULL);
+                if (r < 0)
+                        log_error("Failed to poweroff: %s.", bus_error_message(&error, r));
+
+                break;
+        }
+
+        case FAILURE_ACTION_POWEROFF_FORCE:
+                log_warning("Forcibly powering off as result of failure.");
+                m->exit_code = MANAGER_POWEROFF;
+                break;
+
+        case FAILURE_ACTION_POWEROFF_IMMEDIATE:
+                log_warning("Powering off immediately as result of failure.");
+
+                sync();
+
+                log_info("Powering off.");
+                reboot(RB_POWER_OFF);
+                break;
+
         default:
                 assert_not_reached("Unknown failure action");
         }
@@ -89,6 +124,9 @@ static const char* const failure_action_table[_FAILURE_ACTION_MAX] = {
         [FAILURE_ACTION_NONE] = "none",
         [FAILURE_ACTION_REBOOT] = "reboot",
         [FAILURE_ACTION_REBOOT_FORCE] = "reboot-force",
-        [FAILURE_ACTION_REBOOT_IMMEDIATE] = "reboot-immediate"
+        [FAILURE_ACTION_REBOOT_IMMEDIATE] = "reboot-immediate",
+        [FAILURE_ACTION_POWEROFF] = "poweroff",
+        [FAILURE_ACTION_POWEROFF_FORCE] = "poweroff-force",
+        [FAILURE_ACTION_POWEROFF_IMMEDIATE] = "poweroff-immediate"
 };
 DEFINE_STRING_TABLE_LOOKUP(failure_action, FailureAction);
diff --git a/src/core/failure-action.h b/src/core/failure-action.h
index 5353192..1af4dd9 100644
--- a/src/core/failure-action.h
+++ b/src/core/failure-action.h
@@ -27,6 +27,9 @@ typedef enum FailureAction {
         FAILURE_ACTION_REBOOT,
         FAILURE_ACTION_REBOOT_FORCE,
         FAILURE_ACTION_REBOOT_IMMEDIATE,
+        FAILURE_ACTION_POWEROFF,
+        FAILURE_ACTION_POWEROFF_FORCE,
+        FAILURE_ACTION_POWEROFF_IMMEDIATE,
         _FAILURE_ACTION_MAX,
         _FAILURE_ACTION_INVALID = -1
 } FailureAction;
diff --git a/src/core/main.c b/src/core/main.c
index ed69016..bd148b1 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -117,7 +117,7 @@ static bool arg_default_cpu_accounting = false;
 static bool arg_default_blockio_accounting = false;
 static bool arg_default_memory_accounting = false;
 static usec_t arg_start_timeout_usec = DEFAULT_MANAGER_START_TIMEOUT_USEC;
-static FailureAction arg_start_timeout_action = FAILURE_ACTION_REBOOT_FORCE;
+static FailureAction arg_start_timeout_action = FAILURE_ACTION_POWEROFF_FORCE;
 static char *arg_start_timeout_reboot_arg = NULL;
 
 static void nop_handler(int sig) {}
diff --git a/src/core/manager.c b/src/core/manager.c
index 7508fef..7639aee 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -436,7 +436,7 @@ int manager_new(SystemdRunningAs running_as, bool test_run, Manager **_m) {
         m->exit_code = _MANAGER_EXIT_CODE_INVALID;
         m->default_timer_accuracy_usec = USEC_PER_MINUTE;
         m->start_timeout_usec = DEFAULT_MANAGER_START_TIMEOUT_USEC;
-        m->start_timeout_action = FAILURE_ACTION_REBOOT_FORCE;
+        m->start_timeout_action = FAILURE_ACTION_POWEROFF_FORCE;
 
         m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1;
 
diff --git a/src/core/shutdown.c b/src/core/shutdown.c
index 1abc140..0e2ea57 100644
--- a/src/core/shutdown.c
+++ b/src/core/shutdown.c
@@ -435,8 +435,7 @@ int main(int argc, char *argv[]) {
 
                         if (read_one_line_file(REBOOT_PARAM_FILE, &param) >= 0) {
                                 log_info("Rebooting with argument '%s'.", param);
-                                syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
-                                        LINUX_REBOOT_CMD_RESTART2, param);
+                                syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param);
                         }
                 }
 
diff --git a/src/core/system.conf b/src/core/system.conf
index 45448de..5a723bb 100644
--- a/src/core/system.conf
+++ b/src/core/system.conf
@@ -24,7 +24,7 @@
 #SystemCallArchitectures=
 #TimerSlackNSec=
 #StartTimeoutSec=15min
-#StartTimeoutAction=reboot-force
+#StartTimeoutAction=poweroff-force
 #StartTimeoutRebootArgument=
 #DefaultTimerAccuracySec=1min
 #DefaultStandardOutput=journal

commit c4147df156835513c43260a14fc9f7af177f737f
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Aug 22 16:58:25 2014 +0200

    resolved: fix typo in log message

diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index 659b1da..f979897 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -449,7 +449,7 @@ static int manager_llmnr_start(Manager *m) {
         return 0;
 
 eaddrinuse:
-        log_warning("There appears to be another LLMNR respondering running. Turning off LLMNR support.");
+        log_warning("There appears to be another LLMNR responder running. Turning off LLMNR support.");
         m->llmnr_support = SUPPORT_NO;
         manager_llmnr_stop(m);
 

commit e12919e8be5c80efe09a57f642bbd2411b313ced
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Aug 22 16:41:00 2014 +0200

    core: print 'startup finished' messages even if we log to console

diff --git a/src/core/manager.c b/src/core/manager.c
index 1bb0c90..7508fef 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -2539,44 +2539,41 @@ void manager_check_finished(Manager *m) {
                         kernel_usec = m->initrd_timestamp.monotonic - m->kernel_timestamp.monotonic;
                         initrd_usec = m->userspace_timestamp.monotonic - m->initrd_timestamp.monotonic;
 
-                        if (!log_on_console())
-                                log_struct(LOG_INFO,
-                                           MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED),
-                                           "KERNEL_USEC="USEC_FMT, kernel_usec,
-                                           "INITRD_USEC="USEC_FMT, initrd_usec,
-                                           "USERSPACE_USEC="USEC_FMT, userspace_usec,
-                                           "MESSAGE=Startup finished in %s (kernel) + %s (initrd) + %s (userspace) = %s.",
-                                           format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC),
-                                           format_timespan(initrd, sizeof(initrd), initrd_usec, USEC_PER_MSEC),
-                                           format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC),
-                                           format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC),
-                                           NULL);
+                        log_struct(LOG_INFO,
+                                   MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED),
+                                   "KERNEL_USEC="USEC_FMT, kernel_usec,
+                                   "INITRD_USEC="USEC_FMT, initrd_usec,
+                                   "USERSPACE_USEC="USEC_FMT, userspace_usec,
+                                   "MESSAGE=Startup finished in %s (kernel) + %s (initrd) + %s (userspace) = %s.",
+                                   format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC),
+                                   format_timespan(initrd, sizeof(initrd), initrd_usec, USEC_PER_MSEC),
+                                   format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC),
+                                   format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC),
+                                   NULL);
                 } else {
                         kernel_usec = m->userspace_timestamp.monotonic - m->kernel_timestamp.monotonic;
                         initrd_usec = 0;
 
-                        if (!log_on_console())
-                                log_struct(LOG_INFO,
-                                           MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED),
-                                           "KERNEL_USEC="USEC_FMT, kernel_usec,
-                                           "USERSPACE_USEC="USEC_FMT, userspace_usec,
-                                           "MESSAGE=Startup finished in %s (kernel) + %s (userspace) = %s.",
-                                           format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC),
-                                           format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC),
-                                           format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC),
-                                           NULL);
-                }
-        } else {
-                firmware_usec = loader_usec = initrd_usec = kernel_usec = 0;
-                total_usec = userspace_usec = m->finish_timestamp.monotonic - m->userspace_timestamp.monotonic;
-
-                if (!log_on_console())
                         log_struct(LOG_INFO,
                                    MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED),
+                                   "KERNEL_USEC="USEC_FMT, kernel_usec,
                                    "USERSPACE_USEC="USEC_FMT, userspace_usec,
-                                   "MESSAGE=Startup finished in %s.",
+                                   "MESSAGE=Startup finished in %s (kernel) + %s (userspace) = %s.",
+                                   format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC),
+                                   format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC),
                                    format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC),
                                    NULL);
+                }
+        } else {
+                firmware_usec = loader_usec = initrd_usec = kernel_usec = 0;
+                total_usec = userspace_usec = m->finish_timestamp.monotonic - m->userspace_timestamp.monotonic;
+
+                log_struct(LOG_INFO,
+                           MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED),
+                           "USERSPACE_USEC="USEC_FMT, userspace_usec,
+                           "MESSAGE=Startup finished in %s.",
+                           format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC),
+                           NULL);
         }
 
         SET_FOREACH(u, m->startup_units, i)

commit 2928b0a863091f8f291fddb168988711afd389ef
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Aug 22 16:36:38 2014 +0200

    core: add support for a configurable system-wide start-up timeout
    
    When this system-wide start-up timeout is hit we execute one of the
    failure actions already implemented for services that fail.
    
    This should not only be useful on embedded devices, but also on laptops
    which have the power-button reachable when the lid is closed. This
    devices, when in a backpack might get powered on by accident due to the
    easily reachable power button. We want to make sure that the system
    turns itself off if it starts up due this after a while.
    
    When the system manages to fully start-up logind will suspend the
    machine by default if the lid is closed. However, in some cases we don't
    even get as far as logind, and the boot hangs much earlier, for example
    because we ask for a LUKS password that nobody ever enters.
    
    Yeah, this is a real-life problem on my Yoga 13, which has one of those
    easily accessible power buttons, even if the device is closed.

diff --git a/Makefile.am b/Makefile.am
index 4028112..cbf98bd 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1112,7 +1112,9 @@ libsystemd_core_la_SOURCES = \
 	src/core/audit-fd.c \
 	src/core/audit-fd.h \
 	src/core/show-status.c \
-	src/core/show-status.h
+	src/core/show-status.h \
+	src/core/failure-action.c \
+	src/core/failure-action.h
 
 if HAVE_KMOD
 libsystemd_core_la_SOURCES += \
diff --git a/man/systemd-system.conf.xml b/man/systemd-system.conf.xml
index 6105c51..4869002 100644
--- a/man/systemd-system.conf.xml
+++ b/man/systemd-system.conf.xml
@@ -254,7 +254,6 @@
                                 signal.</para></listitem>
                         </varlistentry>
 
-
                         <varlistentry>
                                 <term><varname>TimerSlackNSec=</varname></term>
 
@@ -281,6 +280,32 @@
                         </varlistentry>
 
                         <varlistentry>
+                                <term><varname>StartTimeoutSec=</varname></term>
+                                <term><varname>StartTimeoutAction=</varname></term>
+                                <term><varname>StartTimeoutRebootArgument=</varname></term>
+
+                                <listitem><para>Configures an over-all
+                                system start-up timeout and controls
+                                what to do when the timeout is
+                                reached. <varname>StartTimeoutSec=</varname>
+                                specifies the timeout, and defaults to
+                                <literal>15min</literal>. <varname>StartTimeoutAction=</varname>
+                                configures the action to take when the
+                                system did not finish boot-up within
+                                the specified time. It takes the same
+                                values as the per-service
+                                <varname>StartLimitAction=</varname>
+                                setting, see
+                                <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+                                for details. Defaults to
+                                <option>reboot-force</option>. <varname>StartTimeoutRebootArgument=</varname>
+                                configures an optional reboot string
+                                to pass to the
+                                <citerefentry><refentrytitle>reboot</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+                                system call.</para></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
                                 <term><varname>DefaultTimerAccuracySec=</varname></term>
 
                                 <listitem><para>Sets the default
diff --git a/man/systemd.service.xml b/man/systemd.service.xml
index e584a1f..20d2a0d 100644
--- a/man/systemd.service.xml
+++ b/man/systemd.service.xml
@@ -1155,29 +1155,30 @@ ExecStart=/bin/echo $ONE $TWO ${TWO}</programlisting>
                         </varlistentry>
 
                         <varlistentry>
+                                <term><varname>FailureAction=</varname></term>
+                                <listitem><para>Configure the action
+                                to take when the service enters a failed
+                                state. Takes the same values as
+                                <varname>StartLimitAction=</varname>
+                                and executes the same actions.
+                                Defaults to <option>none</option>.
+                                </para></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
                                 <term><varname>RebootArgument=</varname></term>
                                 <listitem><para>Configure the optional
                                 argument for the
                                 <citerefentry><refentrytitle>reboot</refentrytitle><manvolnum>2</manvolnum></citerefentry>
                                 system call if
                                 <varname>StartLimitAction=</varname>
+                                or <varname>FailureAction=</varname>
                                 is a reboot action. This works just
                                 like the optional argument to
                                 <command>systemctl reboot</command>
                                 command.</para></listitem>
                         </varlistentry>
 
-                        <varlistentry>
-                                <term><varname>FailureAction=</varname></term>
-                                <listitem><para>Configure the action
-                                to take when the service enters a failed
-                                state. Takes the same values as
-                                <varname>StartLimitAction=</varname>
-                                and executes the same actions.
-                                Defaults to <option>none</option>.
-                                </para></listitem>
-                        </varlistentry>
-
                 </variablelist>
 
                 <para>Check
diff --git a/src/core/failure-action.c b/src/core/failure-action.c
new file mode 100644
index 0000000..ca807b6
--- /dev/null
+++ b/src/core/failure-action.c
@@ -0,0 +1,94 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Lennart Poettering
+  Copyright 2012 Michael Olbrich
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/reboot.h>
+#include <linux/reboot.h>
+#include <sys/syscall.h>
+
+#include "bus-util.h"
+#include "bus-error.h"
+#include "special.h"
+#include "failure-action.h"
+
+int failure_action(
+                Manager *m,
+                FailureAction action,
+                const char *reboot_arg) {
+
+        int r;
+
+        assert(m);
+        assert(action >= 0);
+        assert(action < _FAILURE_ACTION_MAX);
+
+        switch (action) {
+
+        case FAILURE_ACTION_NONE:
+                break;
+
+        case FAILURE_ACTION_REBOOT: {
+                _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+
+                log_warning("Rebooting as result of failure.");
+
+                update_reboot_param_file(reboot_arg);
+                r = manager_add_job_by_name(m, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE, true, &error, NULL);
+                if (r < 0)
+                        log_error("Failed to reboot: %s.", bus_error_message(&error, r));
+
+                break;
+        }
+
+        case FAILURE_ACTION_REBOOT_FORCE:
+                log_warning("Forcibly rebooting as result of failure.");
+                update_reboot_param_file(reboot_arg);
+                m->exit_code = MANAGER_REBOOT;
+                break;
+
+        case FAILURE_ACTION_REBOOT_IMMEDIATE:
+                log_warning("Rebooting immediately as result of failure.");
+
+                sync();
+
+                if (reboot_arg) {
+                        log_info("Rebooting with argument '%s'.", reboot_arg);
+                        syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, reboot_arg);
+                }
+
+                log_info("Rebooting.");
+                reboot(RB_AUTOBOOT);
+                break;
+
+        default:
+                assert_not_reached("Unknown failure action");
+        }
+
+        return -ECANCELED;
+}
+
+static const char* const failure_action_table[_FAILURE_ACTION_MAX] = {
+        [FAILURE_ACTION_NONE] = "none",
+        [FAILURE_ACTION_REBOOT] = "reboot",
+        [FAILURE_ACTION_REBOOT_FORCE] = "reboot-force",
+        [FAILURE_ACTION_REBOOT_IMMEDIATE] = "reboot-immediate"
+};
+DEFINE_STRING_TABLE_LOOKUP(failure_action, FailureAction);
diff --git a/src/core/failure-action.h b/src/core/failure-action.h
new file mode 100644
index 0000000..5353192
--- /dev/null
+++ b/src/core/failure-action.h
@@ -0,0 +1,40 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Lennart Poettering
+  Copyright 2012 Michael Olbrich
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef enum FailureAction {
+        FAILURE_ACTION_NONE,
+        FAILURE_ACTION_REBOOT,
+        FAILURE_ACTION_REBOOT_FORCE,
+        FAILURE_ACTION_REBOOT_IMMEDIATE,
+        _FAILURE_ACTION_MAX,
+        _FAILURE_ACTION_INVALID = -1
+} FailureAction;
+
+#include "macro.h"
+#include "manager.h"
+
+int failure_action(Manager *m, FailureAction action, const char *reboot_arg);
+
+const char* failure_action_to_string(FailureAction i) _const_;
+FailureAction failure_action_from_string(const char *s) _pure_;
diff --git a/src/core/main.c b/src/core/main.c
index 792b316..ed69016 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -116,6 +116,9 @@ static FILE* arg_serialization = NULL;
 static bool arg_default_cpu_accounting = false;
 static bool arg_default_blockio_accounting = false;
 static bool arg_default_memory_accounting = false;
+static usec_t arg_start_timeout_usec = DEFAULT_MANAGER_START_TIMEOUT_USEC;
+static FailureAction arg_start_timeout_action = FAILURE_ACTION_REBOOT_FORCE;
+static char *arg_start_timeout_reboot_arg = NULL;
 
 static void nop_handler(int sig) {}
 
@@ -669,6 +672,9 @@ static int parse_config_file(void) {
                 { "Manager", "DefaultCPUAccounting",      config_parse_bool,             0, &arg_default_cpu_accounting            },
                 { "Manager", "DefaultBlockIOAccounting",  config_parse_bool,             0, &arg_default_blockio_accounting        },
                 { "Manager", "DefaultMemoryAccounting",   config_parse_bool,             0, &arg_default_memory_accounting         },
+                { "Manager", "StartTimeoutSec",           config_parse_sec,              0, &arg_start_timeout_usec                },
+                { "Manager", "StartTimeoutAction",        config_parse_failure_action,   0, &arg_start_timeout_action              },
+                { "Manager", "StartTimeoutRebootArgument",config_parse_string,           0, &arg_start_timeout_reboot_arg          },
                 {}
         };
 
@@ -1628,6 +1634,10 @@ int main(int argc, char *argv[]) {
         m->default_memory_accounting = arg_default_memory_accounting;
         m->runtime_watchdog = arg_runtime_watchdog;
         m->shutdown_watchdog = arg_shutdown_watchdog;
+        m->start_timeout_usec = arg_start_timeout_usec;
+        m->start_timeout_action = arg_start_timeout_action;
+        free_and_strdup(&m->start_timeout_reboot_arg, arg_start_timeout_reboot_arg);
+
         m->userspace_timestamp = userspace_timestamp;
         m->kernel_timestamp = kernel_timestamp;
         m->initrd_timestamp = initrd_timestamp;
@@ -1816,6 +1826,9 @@ finish:
         set_free(arg_syscall_archs);
         arg_syscall_archs = NULL;
 
+        free(arg_start_timeout_reboot_arg);
+        arg_start_timeout_reboot_arg = NULL;
+
         label_finish();
 
         if (reexecute) {
diff --git a/src/core/manager.c b/src/core/manager.c
index 7401817..1bb0c90 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -435,6 +435,8 @@ int manager_new(SystemdRunningAs running_as, bool test_run, Manager **_m) {
         m->running_as = running_as;
         m->exit_code = _MANAGER_EXIT_CODE_INVALID;
         m->default_timer_accuracy_usec = USEC_PER_MINUTE;
+        m->start_timeout_usec = DEFAULT_MANAGER_START_TIMEOUT_USEC;
+        m->start_timeout_action = FAILURE_ACTION_REBOOT_FORCE;
 
         m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1;
 
@@ -823,6 +825,9 @@ void manager_free(Manager *m) {
 
         manager_close_idle_pipe(m);
 
+        sd_event_source_unref(m->start_timeout_event_source);
+        free(m->start_timeout_reboot_arg);
+
         udev_unref(m->udev);
         sd_event_unref(m->event);
 
@@ -970,6 +975,20 @@ static int manager_distribute_fds(Manager *m, FDSet *fds) {
         return 0;
 }
 
+static int on_start_timeout(sd_event_source *s, usec_t usec, void *userdata) {
+        Manager *m = userdata;
+
+        assert(s);
+        assert(m);
+
+        m->start_timeout_event_source = sd_event_source_unref(m->start_timeout_event_source);
+
+        log_error("Startup timed out.");
+
+        failure_action(m, m->start_timeout_action, m->start_timeout_reboot_arg);
+        return 0;
+}
+
 int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
         int r, q;
 
@@ -1042,6 +1061,22 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
                 m->send_reloading_done = true;
         }
 
+        /* Possibly set up a start timeout */
+        if (!dual_timestamp_is_set(&m->finish_timestamp)) {
+                m->start_timeout_event_source = sd_event_source_unref(m->start_timeout_event_source);
+
+                if (m->start_timeout_usec) {
+                        r = sd_event_add_time(
+                                        m->event,
+                                        &m->start_timeout_event_source,
+                                        CLOCK_MONOTONIC,
+                                        now(CLOCK_MONOTONIC) + m->start_timeout_usec, 0,
+                                        on_start_timeout, m);
+                        if (r < 0)
+                                log_error("Failed to add start timeout event: %s", strerror(-r));
+                }
+        }
+
         return r;
 }
 
@@ -2462,10 +2497,8 @@ void manager_check_finished(Manager *m) {
 
         if (hashmap_size(m->jobs) > 0) {
 
-                if (m->jobs_in_progress_event_source) {
-                        sd_event_source_set_time(m->jobs_in_progress_event_source,
-                                                 now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC);
-                }
+                if (m->jobs_in_progress_event_source)
+                        sd_event_source_set_time(m->jobs_in_progress_event_source, now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC);
 
                 return;
         }
@@ -2487,6 +2520,8 @@ void manager_check_finished(Manager *m) {
 
         dual_timestamp_get(&m->finish_timestamp);
 
+        m->start_timeout_event_source = sd_event_source_unref(m->start_timeout_event_source);
+
         if (m->running_as == SYSTEMD_SYSTEM && detect_container(NULL) <= 0) {
 
                 /* Note that m->kernel_usec.monotonic is always at 0,
diff --git a/src/core/manager.h b/src/core/manager.h
index 7cb76f7..7d26c3a 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -33,6 +33,8 @@
 /* Enforce upper limit how many names we allow */
 #define MANAGER_MAX_NAMES 131072 /* 128K */
 
+#define DEFAULT_MANAGER_START_TIMEOUT_USEC (15*USEC_PER_MINUTE)
+
 typedef struct Manager Manager;
 
 typedef enum ManagerState {
@@ -69,6 +71,7 @@ typedef enum ManagerExitCode {
 #include "unit-name.h"
 #include "exit-status.h"
 #include "show-status.h"
+#include "failure-action.h"
 
 struct Manager {
         /* Note that the set of units we know of is allowed to be
@@ -152,6 +155,7 @@ struct Manager {
         dual_timestamp initrd_timestamp;
         dual_timestamp userspace_timestamp;
         dual_timestamp finish_timestamp;
+
         dual_timestamp security_start_timestamp;
         dual_timestamp security_finish_timestamp;
         dual_timestamp generators_start_timestamp;
@@ -279,6 +283,12 @@ struct Manager {
 
         /* Used for processing polkit authorization responses */
         Hashmap *polkit_registry;
+
+        /* System wide startup timeouts */
+        usec_t start_timeout_usec;
+        sd_event_source *start_timeout_event_source;
+        FailureAction start_timeout_action;
+        char *start_timeout_reboot_arg;
 };
 
 int manager_new(SystemdRunningAs running_as, bool test_run, Manager **m);
diff --git a/src/core/service.c b/src/core/service.c
index 1b864c4..223e4b3 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -23,9 +23,6 @@
 #include <signal.h>
 #include <dirent.h>
 #include <unistd.h>
-#include <sys/reboot.h>
-#include <linux/reboot.h>
-#include <sys/syscall.h>
 
 #include "async.h"
 #include "manager.h"
@@ -1052,8 +1049,6 @@ static int cgroup_good(Service *s) {
         return !r;
 }
 
-static int service_execute_action(Service *s, FailureAction action, const char *reason, bool log_action_none);
-
 static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) {
         int r;
         assert(s);
@@ -1063,8 +1058,10 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
 
         service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD);
 
-        if (s->result != SERVICE_SUCCESS)
-                service_execute_action(s, s->failure_action, "failed", false);
+        if (s->result != SERVICE_SUCCESS) {
+                log_warning_unit(UNIT(s)->id, "%s failed.", UNIT(s)->id);
+                failure_action(UNIT(s)->manager, s->failure_action, s->reboot_arg);
+        }
 
         if (allow_restart &&
             !s->forbid_restart &&
@@ -1601,67 +1598,15 @@ fail:
         service_enter_stop(s, SERVICE_FAILURE_RESOURCES);
 }
 
-static int service_execute_action(Service *s, FailureAction action, const char *reason, bool log_action_none) {
-        assert(s);
-
-        if (action == SERVICE_FAILURE_ACTION_REBOOT ||
-            action == SERVICE_FAILURE_ACTION_REBOOT_FORCE)
-                update_reboot_param_file(s->reboot_arg);
-
-        switch (action) {
-
-        case SERVICE_FAILURE_ACTION_NONE:
-                if (log_action_none)
-                        log_warning_unit(UNIT(s)->id, "%s %s, refusing to start.", UNIT(s)->id, reason);
-                break;
-
-        case SERVICE_FAILURE_ACTION_REBOOT: {
-                _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
-                int r;
-
-                log_warning_unit(UNIT(s)->id, "%s %s, rebooting.", UNIT(s)->id, reason);
-
-                r = manager_add_job_by_name(UNIT(s)->manager, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE, true, &error, NULL);
-                if (r < 0)
-                        log_error_unit(UNIT(s)->id, "Failed to reboot: %s.", bus_error_message(&error, r));
-
-                break;
-        }
-
-        case SERVICE_FAILURE_ACTION_REBOOT_FORCE:
-                log_warning_unit(UNIT(s)->id, "%s %s, forcibly rebooting.", UNIT(s)->id, reason);
-                UNIT(s)->manager->exit_code = MANAGER_REBOOT;
-                break;
-
-        case SERVICE_FAILURE_ACTION_REBOOT_IMMEDIATE:
-                log_warning_unit(UNIT(s)->id, "%s %s, rebooting immediately.", UNIT(s)->id, reason);
-
-                sync();
-
-                if (s->reboot_arg) {
-                        log_info("Rebooting with argument '%s'.", s->reboot_arg);
-                        syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, s->reboot_arg);
-                }
-
-                log_info("Rebooting.");
-                reboot(RB_AUTOBOOT);
-                break;
-
-        default:
-                log_error_unit(UNIT(s)->id, "failure action=%i", action);
-                assert_not_reached("Unknown FailureAction.");
-        }
-
-        return -ECANCELED;
-}
-
 static int service_start_limit_test(Service *s) {
         assert(s);
 
         if (ratelimit_test(&s->start_limit))
                 return 0;
 
-        return service_execute_action(s, s->start_limit_action, "start request repeated too quickly", true);
+        log_warning_unit(UNIT(s)->id, "start request repeated too quickly for %s", UNIT(s)->id);
+
+        return failure_action(UNIT(s)->manager, s->start_limit_action, s->reboot_arg);
 }
 
 static int service_start(Unit *u) {
@@ -2908,14 +2853,6 @@ static const char* const service_result_table[_SERVICE_RESULT_MAX] = {
 
 DEFINE_STRING_TABLE_LOOKUP(service_result, ServiceResult);
 
-static const char* const failure_action_table[_SERVICE_FAILURE_ACTION_MAX] = {
-        [SERVICE_FAILURE_ACTION_NONE] = "none",
-        [SERVICE_FAILURE_ACTION_REBOOT] = "reboot",
-        [SERVICE_FAILURE_ACTION_REBOOT_FORCE] = "reboot-force",
-        [SERVICE_FAILURE_ACTION_REBOOT_IMMEDIATE] = "reboot-immediate"
-};
-DEFINE_STRING_TABLE_LOOKUP(failure_action, FailureAction);
-
 const UnitVTable service_vtable = {
         .object_size = sizeof(Service),
         .exec_context_offset = offsetof(Service, exec_context),
diff --git a/src/core/service.h b/src/core/service.h
index 0227321..5bcfd14 100644
--- a/src/core/service.h
+++ b/src/core/service.h
@@ -28,6 +28,7 @@ typedef struct Service Service;
 #include "ratelimit.h"
 #include "kill.h"
 #include "exit-status.h"
+#include "failure-action.h"
 
 typedef enum ServiceState {
         SERVICE_DEAD,
@@ -113,15 +114,6 @@ typedef enum ServiceResult {
         _SERVICE_RESULT_INVALID = -1
 } ServiceResult;
 
-typedef enum FailureAction {
-        SERVICE_FAILURE_ACTION_NONE,
-        SERVICE_FAILURE_ACTION_REBOOT,
-        SERVICE_FAILURE_ACTION_REBOOT_FORCE,
-        SERVICE_FAILURE_ACTION_REBOOT_IMMEDIATE,
-        _SERVICE_FAILURE_ACTION_MAX,
-        _SERVICE_FAILURE_ACTION_INVALID = -1
-} FailureAction;
-
 struct Service {
         Unit meta;
 
@@ -193,10 +185,9 @@ struct Service {
         char *status_text;
         int status_errno;
 
-        FailureAction failure_action;
-
         RateLimit start_limit;
         FailureAction start_limit_action;
+        FailureAction failure_action;
         char *reboot_arg;
 
         UnitRef accept_socket;
@@ -234,6 +225,3 @@ NotifyState notify_state_from_string(const char *s) _pure_;
 
 const char* service_result_to_string(ServiceResult i) _const_;
 ServiceResult service_result_from_string(const char *s) _pure_;
-
-const char* failure_action_to_string(FailureAction i) _const_;
-FailureAction failure_action_from_string(const char *s) _pure_;
diff --git a/src/core/system.conf b/src/core/system.conf
index 65a35a0..45448de 100644
--- a/src/core/system.conf
+++ b/src/core/system.conf
@@ -23,6 +23,9 @@
 #CapabilityBoundingSet=
 #SystemCallArchitectures=
 #TimerSlackNSec=
+#StartTimeoutSec=15min
+#StartTimeoutAction=reboot-force
+#StartTimeoutRebootArgument=
 #DefaultTimerAccuracySec=1min
 #DefaultStandardOutput=journal
 #DefaultStandardError=inherit
diff --git a/src/shared/util.c b/src/shared/util.c
index a54e879..fc6f668 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -7137,3 +7137,24 @@ int unquote_many_words(const char **p, ...) {
 
         return c;
 }
+
+int free_and_strdup(char **p, const char *s) {
+        char *t;
+
+        assert(p);
+
+        /* Replaces a string pointer with an strdup()ed new string,
+         * possibly freeing the old one. */
+
+        if (s) {
+                t = strdup(s);
+                if (!t)
+                        return -ENOMEM;
+        } else
+                t = NULL;
+
+        free(*p);
+        *p = t;
+
+        return 0;
+}
diff --git a/src/shared/util.h b/src/shared/util.h
index 8cd47b8..cd947db 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -978,3 +978,5 @@ int is_symlink(const char *path);
 
 int unquote_first_word(const char **p, char **ret);
 int unquote_many_words(const char **p, ...) _sentinel_;
+
+int free_and_strdup(char **p, const char *s);
diff --git a/src/test/test-tables.c b/src/test/test-tables.c
index 88e7d10..58fe443 100644
--- a/src/test/test-tables.c
+++ b/src/test/test-tables.c
@@ -63,7 +63,7 @@ int main(int argc, char **argv) {
         test_table(device_state, DEVICE_STATE);
         test_table(exec_input, EXEC_INPUT);
         test_table(exec_output, EXEC_OUTPUT);
-        test_table(failure_action, SERVICE_FAILURE_ACTION);
+        test_table(failure_action, FAILURE_ACTION);
         test_table(job_mode, JOB_MODE);
         test_table(job_result, JOB_RESULT);
         test_table(job_state, JOB_STATE);



More information about the systemd-commits mailing list