]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: rework crash handling
authorLennart Poettering <lennart@poettering.net>
Wed, 23 Sep 2015 21:13:06 +0000 (23:13 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 29 Sep 2015 19:55:51 +0000 (21:55 +0200)
This introduces a new systemd.crash_reboot=1 kernel command line option
that triggers a reboot after crashing.

This also cleans up crash VT handling. Specifically, it cleans up the
configuration setting, to be between 1..63 or a boolean. This is to
replace the previous logic where "-1" meant disabled. We continue to
accept that setting, but only document the boolean syntax instead.

This also brings the documentation of the default settings in sync with
what actually happens.

The CrashChVT= configuration file setting is renamed to CrashChangeVT=,
following our usual logic of not abbreviating unnecessarily. The old
setting stays support for compat reasons.

Fixes #1300

man/kernel-command-line.xml
man/systemd-system.conf.xml
man/systemd.xml
src/basic/terminal-util.c
src/core/main.c
src/core/shutdown.c
src/core/system.conf

index eb7372702750de2a42a295d02a1d9db2b6722c16..2f81746b5eb30598bf7972cfb618e79557eec6cb 100644 (file)
@@ -79,8 +79,9 @@
         <term><varname>systemd.unit=</varname></term>
         <term><varname>rd.systemd.unit=</varname></term>
         <term><varname>systemd.dump_core=</varname></term>
-        <term><varname>systemd.crash_shell=</varname></term>
         <term><varname>systemd.crash_chvt=</varname></term>
+        <term><varname>systemd.crash_shell=</varname></term>
+        <term><varname>systemd.crash_reboot=</varname></term>
         <term><varname>systemd.confirm_spawn=</varname></term>
         <term><varname>systemd.show_status=</varname></term>
         <term><varname>systemd.log_target=</varname></term>
index a4ba0959eaddc265dddc2d371dc2be60204df1eb..56db9ff17eb1bd9be573264802f976bf9553746a 100644 (file)
         <term><varname>LogColor=</varname></term>
         <term><varname>LogLocation=</varname></term>
         <term><varname>DumpCore=yes</varname></term>
+        <term><varname>CrashChangeVT=no</varname></term>
         <term><varname>CrashShell=no</varname></term>
+        <term><varname>CrashReboot=no</varname></term>
         <term><varname>ShowStatus=yes</varname></term>
-        <term><varname>CrashChVT=1</varname></term>
         <term><varname>DefaultStandardOutput=journal</varname></term>
         <term><varname>DefaultStandardError=inherit</varname></term>
 
index 479f55de76263cbaf132cf0bbf3d22a2867524c0..9e927c32044ba8dde8aad17f35e7fd0d58db0790 100644 (file)
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*-nxml-*-->
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
 <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
 
       <varlistentry>
         <term><option>--dump-core</option></term>
 
-        <listitem><para>Dump core on crash. This switch has no effect
-        when run as user instance.</para></listitem>
+        <listitem><para>Enable core dumping on crash. This switch has
+        no effect when running as user instance. This setting may also
+        be enabled during boot on the kernel command line via the
+        <varname>systemd.dump_core=</varname> option, see
+        below.</para></listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><option>--crash-vt=</option><replaceable>VT</replaceable></term>
+
+        <listitem><para>Switch to a specific virtual console (VT) on
+        crash. Takes a positive integer in the range 1..63, or a
+        boolean argument. If an integer is passed, selects which VT to
+        switch to. If <constant>yes</constant>, the VT kernel messages
+        are written to is selected. If <constant>no</constant>, no VT
+        switch is attempted. This switch has no effect when running as
+        user instance. This setting may also be enabled during boot,
+        on the kernel command line via the
+        <varname>systemd.crash_vt=</varname> option, see
+        below.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--crash-shell</option></term>
 
-        <listitem><para>Run shell on
-        crash. This switch has no effect when
-        run as user
-        instance.</para></listitem>
+        <listitem><para>Run a shell on crash. This switch has no
+        effect when running as user instance. This setting may also be
+        enabled during boot, on the kernel command line via the
+        <varname>systemd.crash_shell=</varname> option, see
+        below.</para></listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><option>--crash-reboot</option></term>
+
+        <listitem><para>Automatically reboot the system on crash. This
+        switch has no effect when running as user instance. This
+        setting may also be enabled during boot, on the kernel command
+        line via the <varname>systemd.crash_reboot=</varname> option,
+        see below.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--confirm-spawn</option></term>
 
         <term><varname>systemd.dump_core=</varname></term>
 
         <listitem><para>Takes a boolean argument. If
-        <option>true</option>, systemd dumps core when it crashes.
-        Otherwise, no core dump is created. Defaults to
-        <option>true</option>.</para></listitem>
+        <option>yes</option>, the systemd manager (PID 1) dumps core
+        when it crashes.  Otherwise, no core dump is created. Defaults
+        to <option>yes</option>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>systemd.crash_chvt=</varname></term>
+
+        <listitem><para>Takes a positive integer, or a boolean
+        argument. If a positive integer (in the range 1..63) is
+        specified the system manager (PID 1) will activate the specified
+        virtual terminal (VT) when it crashes. Defaults to
+        <constant>no</constant>, meaning that no such switch is
+        attempted. If set to <constant>yes</constant> the VT the
+        kernel messages are written to is selected.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>systemd.crash_shell=</varname></term>
 
         <listitem><para>Takes a boolean argument. If
-        <option>true</option>, systemd spawns a shell when it crashes.
-        Otherwise, no shell is spawned. Defaults to
-        <option>false</option>, for security reasons, as the shell is
-        not protected by any password
+        <option>yes</option>, the system manager (PID 1) spawns a
+        shell when it crashes, after a 10s delay. Otherwise, no shell
+        is spawned. Defaults to <option>no</option>, for security
+        reasons, as the shell is not protected by password
         authentication.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>systemd.crash_chvt=</varname></term>
+        <term><varname>systemd.crash_reboot=</varname></term>
 
-        <listitem><para>Takes an integer argument. If positive systemd
-        activates the specified virtual terminal when it crashes.
-        Defaults to <constant>-1</constant>.</para></listitem>
+        <listitem><para>Takes a boolean argument. If
+        <option>yes</option>, the system manager (PID 1) will reboot
+        the machine automatically when it crashes, after a 10s delay.
+        Otherwise, the system will hang indefinitely. Defaults to
+        <option>no</option>, in order to avoid a reboot loop. If
+        combined with <varname>systemd.crash_shell=</varname>, it is
+        first attempted to invoke a shell, and if this is not
+        successful the system is rebooted.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>systemd.confirm_spawn=</varname></term>
 
         <listitem><para>Takes a boolean argument. If
-        <option>true</option>, asks for confirmation when spawning
-        processes. Defaults to
-        <option>false</option>.</para></listitem>
+        <option>yes</option>, the system manager (PID 1) asks for
+        confirmation when spawning processes. Defaults to
+        <option>no</option>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>systemd.show_status=</varname></term>
 
         <listitem><para>Takes a boolean argument or the constant
-        <constant>auto</constant>. If <option>true</option>, shows
-        terse service status updates on the console during bootup.
-        <constant>auto</constant> behaves like <option>false</option>
-        until a service fails or there is a significant delay in boot.
-        Defaults to <option>true</option>, unless
-        <option>quiet</option> is passed as kernel command line option
-        in which case it defaults to
+        <constant>auto</constant>. If <option>yes</option>, the
+        systemd manager (PID 1) shows terse service status updates on
+        the console during bootup.  <constant>auto</constant> behaves
+        like <option>false</option> until a service fails or there is
+        a significant delay in boot.  Defaults to
+        <option>yes</option>, unless <option>quiet</option> is passed
+        as kernel command line option in which case it defaults to
         <constant>auto</constant>.</para></listitem>
       </varlistentry>
 
index dd3d525854a2ec72e714354c0df0f1daab907339..287e0dfa1351db61b70a010b6387c8ba5fd72764 100644 (file)
@@ -48,7 +48,7 @@ int chvt(int vt) {
         if (fd < 0)
                 return -errno;
 
-        if (vt < 0) {
+        if (vt <= 0) {
                 int tiocl[2] = {
                         TIOCL_GETKMSGREDIRECT,
                         0
index 9634feb8a648f735a2b98e74ce8e6c762346263a..bac20432810aabd003967dd8b4f1dea7efd69ae1 100644 (file)
@@ -27,6 +27,7 @@
 #include <string.h>
 #include <sys/mount.h>
 #include <sys/prctl.h>
+#include <sys/reboot.h>
 #include <sys/stat.h>
 #include <unistd.h>
 #ifdef HAVE_SECCOMP
@@ -87,8 +88,9 @@ static enum {
 static char *arg_default_unit = NULL;
 static ManagerRunningAs arg_running_as = _MANAGER_RUNNING_AS_INVALID;
 static bool arg_dump_core = true;
-static bool arg_crash_shell = false;
 static int arg_crash_chvt = -1;
+static bool arg_crash_shell = false;
+static bool arg_crash_reboot = false;
 static bool arg_confirm_spawn = false;
 static ShowStatus arg_show_status = _SHOW_STATUS_UNSET;
 static bool arg_switched_root = false;
@@ -123,6 +125,21 @@ static void pager_open_if_enabled(void) {
         pager_open(false);
 }
 
+noreturn static void freeze_or_reboot(void) {
+
+        if (arg_crash_reboot) {
+                log_notice("Rebooting in 10s...");
+                (void) sleep(10);
+
+                log_notice("Rebooting now...");
+                (void) reboot(RB_AUTOBOOT);
+                log_emergency_errno(errno, "Failed to reboot: %m");
+        }
+
+        log_emergency("Freezing execution.");
+        freeze();
+}
+
 noreturn static void crash(int sig) {
 
         if (getpid() != 1)
@@ -188,7 +205,7 @@ noreturn static void crash(int sig) {
                 }
         }
 
-        if (arg_crash_chvt)
+        if (arg_crash_chvt >= 0)
                 (void) chvt(arg_crash_chvt);
 
         if (arg_crash_shell) {
@@ -198,7 +215,7 @@ noreturn static void crash(int sig) {
                 };
                 pid_t pid;
 
-                log_info("Executing crash shell in 10s...");
+                log_notice("Executing crash shell in 10s...");
                 (void) sleep(10);
 
                 /* Let the kernel reap children for us */
@@ -208,17 +225,20 @@ noreturn static void crash(int sig) {
                 if (pid < 0)
                         log_emergency_errno(errno, "Failed to fork off crash shell: %m");
                 else if (pid == 0) {
+                        (void) setsid();
                         (void) make_console_stdio();
                         (void) execle("/bin/sh", "/bin/sh", NULL, environ);
 
                         log_emergency_errno(errno, "execle() failed: %m");
+                        freeze_or_reboot();
                         _exit(EXIT_FAILURE);
-                } else
-                        log_info("Successfully spawned crash shell as PID "PID_FMT".", pid);
+                } else {
+                        log_info("Spawned crash shell as PID "PID_FMT".", pid);
+                        freeze();
+                }
         }
 
-        log_emergency("Freezing execution.");
-        freeze();
+        freeze_or_reboot();
 }
 
 static void install_crash_handler(void) {
@@ -252,6 +272,24 @@ static int console_setup(void) {
         return 0;
 }
 
+static int parse_crash_chvt(const char *value) {
+        int b;
+
+        if (safe_atoi(value, &arg_crash_chvt) >= 0)
+                return 0;
+
+        b = parse_boolean(value);
+        if (b < 0)
+                return b;
+
+        if (b > 0)
+                arg_crash_chvt = 0; /* switch to where kmsg goes */
+        else
+                arg_crash_chvt = -1; /* turn off switching */
+
+        return 0;
+}
+
 static int parse_proc_cmdline_item(const char *key, const char *value) {
 
         static const char * const rlmap[] = {
@@ -290,6 +328,11 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
                 else
                         arg_dump_core = r;
 
+        } else if (streq(key, "systemd.crash_chvt") && value) {
+
+                if (parse_crash_chvt(value) < 0)
+                        log_warning("Failed to parse crash chvt switch %s. Ignoring.", value);
+
         } else if (streq(key, "systemd.crash_shell") && value) {
 
                 r = parse_boolean(value);
@@ -298,12 +341,13 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
                 else
                         arg_crash_shell = r;
 
-        } else if (streq(key, "systemd.crash_chvt") && value) {
+        } else if (streq(key, "systemd.crash_reboot") && value) {
 
-                if (safe_atoi(value, &r) < 0)
-                        log_warning("Failed to parse crash chvt switch %s. Ignoring.", value);
+                r = parse_boolean(value);
+                if (r < 0)
+                        log_warning("Failed to parse crash reboot switch %s. Ignoring.", value);
                 else
-                        arg_crash_chvt = r;
+                        arg_crash_reboot = r;
 
         } else if (streq(key, "systemd.confirm_spawn") && value) {
 
@@ -461,6 +505,34 @@ static int config_parse_show_status(
         return 0;
 }
 
+static int config_parse_crash_chvt(
+                const char* unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = parse_crash_chvt(rvalue);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CrashChangeVT= setting, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        return 0;
+}
+
 static int config_parse_join_controllers(const char *unit,
                                          const char *filename,
                                          unsigned line,
@@ -571,9 +643,11 @@ static int parse_config_file(void) {
                 { "Manager", "LogColor",                  config_parse_color,            0, NULL                                   },
                 { "Manager", "LogLocation",               config_parse_location,         0, NULL                                   },
                 { "Manager", "DumpCore",                  config_parse_bool,             0, &arg_dump_core                         },
+                { "Manager", "CrashChVT", /* legacy */    config_parse_crash_chvt,       0, NULL                                   },
+                { "Manager", "CrashChangeVT",             config_parse_crash_chvt,       0, NULL                                   },
                 { "Manager", "CrashShell",                config_parse_bool,             0, &arg_crash_shell                       },
+                { "Manager", "CrashReboot",               config_parse_bool,             0, &arg_crash_reboot                      },
                 { "Manager", "ShowStatus",                config_parse_show_status,      0, &arg_show_status                       },
-                { "Manager", "CrashChVT",                 config_parse_int,              0, &arg_crash_chvt                        },
                 { "Manager", "CPUAffinity",               config_parse_cpu_affinity2,    0, NULL                                   },
                 { "Manager", "JoinControllers",           config_parse_join_controllers, 0, &arg_join_controllers                  },
                 { "Manager", "RuntimeWatchdogSec",        config_parse_sec,              0, &arg_runtime_watchdog                  },
@@ -661,7 +735,9 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_VERSION,
                 ARG_DUMP_CONFIGURATION_ITEMS,
                 ARG_DUMP_CORE,
+                ARG_CRASH_CHVT,
                 ARG_CRASH_SHELL,
+                ARG_CRASH_REBOOT,
                 ARG_CONFIRM_SPAWN,
                 ARG_SHOW_STATUS,
                 ARG_DESERIALIZE,
@@ -684,7 +760,9 @@ static int parse_argv(int argc, char *argv[]) {
                 { "version",                  no_argument,       NULL, ARG_VERSION                  },
                 { "dump-configuration-items", no_argument,       NULL, ARG_DUMP_CONFIGURATION_ITEMS },
                 { "dump-core",                optional_argument, NULL, ARG_DUMP_CORE                },
+                { "crash-chvt",               required_argument, NULL, ARG_CRASH_CHVT               },
                 { "crash-shell",              optional_argument, NULL, ARG_CRASH_SHELL              },
+                { "crash-reboot",             optional_argument, NULL, ARG_CRASH_REBOOT             },
                 { "confirm-spawn",            optional_argument, NULL, ARG_CONFIRM_SPAWN            },
                 { "show-status",              optional_argument, NULL, ARG_SHOW_STATUS              },
                 { "deserialize",              required_argument, NULL, ARG_DESERIALIZE              },
@@ -802,21 +880,42 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_DUMP_CORE:
-                        r = optarg ? parse_boolean(optarg) : 1;
-                        if (r < 0) {
-                                log_error("Failed to parse dump core boolean %s.", optarg);
-                                return r;
+                        if (!optarg)
+                                arg_dump_core = true;
+                        else {
+                                r = parse_boolean(optarg);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to parse dump core boolean: %s", optarg);
+                                arg_dump_core = r;
                         }
-                        arg_dump_core = r;
+                        break;
+
+                case ARG_CRASH_CHVT:
+                        r = parse_crash_chvt(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse crash virtual terminal index: %s", optarg);
                         break;
 
                 case ARG_CRASH_SHELL:
-                        r = optarg ? parse_boolean(optarg) : 1;
-                        if (r < 0) {
-                                log_error("Failed to parse crash shell boolean %s.", optarg);
-                                return r;
+                        if (!optarg)
+                                arg_crash_shell = true;
+                        else {
+                                r = parse_boolean(optarg);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to parse crash shell boolean: %s", optarg);
+                                arg_crash_shell = r;
+                        }
+                        break;
+
+                case ARG_CRASH_REBOOT:
+                        if (!optarg)
+                                arg_crash_reboot = true;
+                        else {
+                                r = parse_boolean(optarg);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to parse crash shell boolean: %s", optarg);
+                                arg_crash_reboot = r;
                         }
-                        arg_crash_shell = r;
                         break;
 
                 case ARG_CONFIRM_SPAWN:
@@ -846,17 +945,16 @@ static int parse_argv(int argc, char *argv[]) {
                         r = safe_atoi(optarg, &fd);
                         if (r < 0 || fd < 0) {
                                 log_error("Failed to parse deserialize option %s.", optarg);
-                                return r < 0 ? r : -EINVAL;
+                                return -EINVAL;
                         }
 
-                        fd_cloexec(fd, true);
+                        (void) fd_cloexec(fd, true);
 
                         f = fdopen(fd, "r");
                         if (!f)
                                 return log_error_errno(errno, "Failed to open serialization fd: %m");
 
                         safe_fclose(arg_serialization);
-
                         arg_serialization = f;
 
                         break;
@@ -916,14 +1014,16 @@ static int help(void) {
                "     --unit=UNIT                 Set default unit\n"
                "     --system                    Run a system instance, even if PID != 1\n"
                "     --user                      Run a user instance\n"
-               "     --dump-core[=0|1]           Dump core on crash\n"
-               "     --crash-shell[=0|1]         Run shell on crash\n"
-               "     --confirm-spawn[=0|1]       Ask for confirmation when spawning processes\n"
-               "     --show-status[=0|1]         Show status updates on the console during bootup\n"
+               "     --dump-core[=BOOL]          Dump core on crash\n"
+               "     --crash-vt=NR               Change to specified VT on crash\n"
+               "     --crash-reboot[=BOOL]       Reboot on crash\n"
+               "     --crash-shell[=BOOL]        Run shell on crash\n"
+               "     --confirm-spawn[=BOOL]      Ask for confirmation when spawning processes\n"
+               "     --show-status[=BOOL]        Show status updates on the console during bootup\n"
                "     --log-target=TARGET         Set log target (console, journal, kmsg, journal-or-kmsg, null)\n"
                "     --log-level=LEVEL           Set log level (debug, info, notice, warning, err, crit, alert, emerg)\n"
-               "     --log-color[=0|1]           Highlight important log messages\n"
-               "     --log-location[=0|1]        Include code location in log messages\n"
+               "     --log-color[=BOOL]          Highlight important log messages\n"
+               "     --log-location[=BOOL]       Include code location in log messages\n"
                "     --default-standard-output=  Set default standard output for services\n"
                "     --default-standard-error=   Set default standard error output for services\n",
                program_invocation_short_name);
@@ -1032,7 +1132,7 @@ static void test_mtab(void) {
         log_error("/etc/mtab is not a symlink or not pointing to /proc/self/mounts. "
                   "This is not supported anymore. "
                   "Please make sure to replace this file by a symlink to avoid incorrect or misleading mount(8) output.");
-        freeze();
+        freeze_or_reboot();
 }
 
 static void test_usr(void) {
@@ -1964,7 +2064,7 @@ finish:
                         manager_status_printf(NULL, STATUS_TYPE_EMERGENCY,
                                               ANSI_HIGHLIGHT_RED "!!!!!!" ANSI_NORMAL,
                                               "%s, freezing.", error_message);
-                freeze();
+                freeze_or_reboot();
         }
 
         return retval;
index 5296efce1d68471a1e8394f5c878d53003d3012d..27c581d9c10ca2ef57b51c4d55b80860a2cdc4ef 100644 (file)
@@ -430,6 +430,5 @@ int main(int argc, char *argv[]) {
 
   error:
         log_emergency_errno(r, "Critical error while doing system shutdown: %m");
-
         freeze();
 }
index 231609033bf6d24bbf9e68b45d5f43f6b2c4a2c1..c30c595413069a161ea433a8fadbddf7a7621002 100644 (file)
 #LogColor=yes
 #LogLocation=no
 #DumpCore=yes
-#CrashShell=no
 #ShowStatus=yes
-#CrashChVT=1
+#CrashChangeVT=no
+#CrashShell=no
+#CrashReboot=no
 #CPUAffinity=1 2
 #JoinControllers=cpu,cpuacct net_cls,net_prio
 #RuntimeWatchdogSec=0