]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/core/main.c
util-lib: move a number of fs operations into fs-util.[ch]
[thirdparty/systemd.git] / src / core / main.c
index be7d41907a846c482967c24ea4f837ce58fc0a76..a931b10b9fad661c86eadddb347da8da4b0a9eaa 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <stdio.h>
 #include <errno.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/stat.h>
+#include <fcntl.h>
 #include <getopt.h>
 #include <signal.h>
-#include <fcntl.h>
-#include <sys/prctl.h>
+#include <stdio.h>
+#include <string.h>
 #include <sys/mount.h>
-
-#ifdef HAVE_VALGRIND_VALGRIND_H
-#include <valgrind/valgrind.h>
-#endif
+#include <sys/prctl.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <unistd.h>
 #ifdef HAVE_SECCOMP
 #include <seccomp.h>
 #endif
+#ifdef HAVE_VALGRIND_VALGRIND_H
+#include <valgrind/valgrind.h>
+#endif
 
 #include "sd-daemon.h"
 #include "sd-bus.h"
-#include "log.h"
-#include "fdset.h"
-#include "special.h"
-#include "conf-parser.h"
-#include "missing.h"
-#include "pager.h"
-#include "build.h"
-#include "strv.h"
-#include "def.h"
-#include "virt.h"
+
 #include "architecture.h"
-#include "watchdog.h"
-#include "switch-root.h"
+#include "build.h"
+#include "bus-error.h"
+#include "bus-util.h"
 #include "capability.h"
-#include "killall.h"
-#include "env-util.h"
 #include "clock-util.h"
+#include "conf-parser.h"
+#include "cpu-set-util.h"
+#include "dbus-manager.h"
+#include "def.h"
+#include "env-util.h"
+#include "fd-util.h"
+#include "fdset.h"
 #include "fileio.h"
-#include "bus-error.h"
-#include "bus-util.h"
-#include "selinux-util.h"
 #include "formats-util.h"
-#include "process-util.h"
-#include "terminal-util.h"
-#include "signal-util.h"
-#include "manager.h"
-#include "dbus-manager.h"
+#include "fs-util.h"
+#include "hostname-setup.h"
+#include "ima-setup.h"
+#include "killall.h"
+#include "kmod-setup.h"
 #include "load-fragment.h"
-
-#include "mount-setup.h"
+#include "log.h"
 #include "loopback-setup.h"
-#include "hostname-setup.h"
 #include "machine-id-setup.h"
+#include "manager.h"
+#include "missing.h"
+#include "mount-setup.h"
+#include "pager.h"
+#include "parse-util.h"
+#include "process-util.h"
+#include "rlimit-util.h"
 #include "selinux-setup.h"
-#include "ima-setup.h"
+#include "selinux-util.h"
+#include "signal-util.h"
 #include "smack-setup.h"
-#include "kmod-setup.h"
+#include "special.h"
+#include "strv.h"
+#include "switch-root.h"
+#include "terminal-util.h"
+#include "user-util.h"
+#include "virt.h"
+#include "watchdog.h"
 
 static enum {
         ACTION_RUN,
@@ -88,8 +94,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;
@@ -114,8 +121,7 @@ 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 void nop_handler(int sig) {}
+static bool arg_default_tasks_accounting = false;
 
 static void pager_open_if_enabled(void) {
 
@@ -125,49 +131,66 @@ 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) {
+        struct sigaction sa;
+        pid_t pid;
 
         if (getpid() != 1)
                 /* Pass this on immediately, if this is not PID 1 */
-                raise(sig);
+                (void) raise(sig);
         else if (!arg_dump_core)
                 log_emergency("Caught <%s>, not dumping core.", signal_to_string(sig));
         else {
-                struct sigaction sa = {
-                        .sa_handler = nop_handler,
+                sa = (struct sigaction) {
+                        .sa_handler = nop_signal_handler,
                         .sa_flags = SA_NOCLDSTOP|SA_RESTART,
                 };
-                pid_t pid;
 
                 /* We want to wait for the core process, hence let's enable SIGCHLD */
-                sigaction(SIGCHLD, &sa, NULL);
+                (void) sigaction(SIGCHLD, &sa, NULL);
 
                 pid = raw_clone(SIGCHLD, NULL);
                 if (pid < 0)
                         log_emergency_errno(errno, "Caught <%s>, cannot fork for core dump: %m", signal_to_string(sig));
-
                 else if (pid == 0) {
-                        struct rlimit rl = {};
+                        struct rlimit rl = {
+                                .rlim_cur = RLIM_INFINITY,
+                                .rlim_max = RLIM_INFINITY,
+                        };
 
                         /* Enable default signal handler for core dump */
-                        zero(sa);
-                        sa.sa_handler = SIG_DFL;
-                        sigaction(sig, &sa, NULL);
+                        sa = (struct sigaction) {
+                                .sa_handler = SIG_DFL,
+                        };
+                        (void) sigaction(sig, &sa, NULL);
 
                         /* Don't limit the core dump size */
-                        rl.rlim_cur = RLIM_INFINITY;
-                        rl.rlim_max = RLIM_INFINITY;
-                        setrlimit(RLIMIT_CORE, &rl);
+                        (void) setrlimit(RLIMIT_CORE, &rl);
 
                         /* Just to be sure... */
                         (void) chdir("/");
 
                         /* Raise the signal again */
                         pid = raw_getpid();
-                        kill(pid, sig); /* raise() would kill the parent */
+                        (void) kill(pid, sig); /* raise() would kill the parent */
 
                         assert_not_reached("We shouldn't be here...");
-                        _exit(1);
+                        _exit(EXIT_FAILURE);
                 } else {
                         siginfo_t status;
                         int r;
@@ -189,37 +212,38 @@ noreturn static void crash(int sig) {
                 }
         }
 
-        if (arg_crash_chvt)
-                chvt(arg_crash_chvt);
+        if (arg_crash_chvt >= 0)
+                (void) chvt(arg_crash_chvt);
 
-        if (arg_crash_shell) {
-                struct sigaction sa = {
-                        .sa_handler = SIG_IGN,
-                        .sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART,
-                };
-                pid_t pid;
+        sa = (struct sigaction) {
+                .sa_handler = SIG_IGN,
+                .sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART,
+        };
 
-                log_info("Executing crash shell in 10s...");
-                sleep(10);
+        /* Let the kernel reap children for us */
+        (void) sigaction(SIGCHLD, &sa, NULL);
 
-                /* Let the kernel reap children for us */
-                assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
+        if (arg_crash_shell) {
+                log_notice("Executing crash shell in 10s...");
+                (void) sleep(10);
 
                 pid = raw_clone(SIGCHLD, NULL);
                 if (pid < 0)
                         log_emergency_errno(errno, "Failed to fork off crash shell: %m");
                 else if (pid == 0) {
-                        make_console_stdio();
-                        execle("/bin/sh", "/bin/sh", NULL, environ);
+                        (void) setsid();
+                        (void) make_console_stdio();
+                        (void) execle("/bin/sh", "/bin/sh", NULL, environ);
 
                         log_emergency_errno(errno, "execle() failed: %m");
-                        _exit(1);
-                } else
-                        log_info("Successfully spawned crash shell as PID "PID_FMT".", pid);
+                        _exit(EXIT_FAILURE);
+                } else {
+                        log_info("Spawned crash shell as PID "PID_FMT".", pid);
+                        (void) wait_for_terminate(pid, NULL);
+                }
         }
 
-        log_emergency("Freezing execution.");
-        freeze();
+        freeze_or_reboot();
 }
 
 static void install_crash_handler(void) {
@@ -253,17 +277,20 @@ static int console_setup(void) {
         return 0;
 }
 
-static int set_default_unit(const char *u) {
-        char *c;
+static int parse_crash_chvt(const char *value) {
+        int b;
 
-        assert(u);
+        if (safe_atoi(value, &arg_crash_chvt) >= 0)
+                return 0;
 
-        c = strdup(u);
-        if (!c)
-                return -ENOMEM;
+        b = parse_boolean(value);
+        if (b < 0)
+                return b;
 
-        free(arg_default_unit);
-        arg_default_unit = c;
+        if (b > 0)
+                arg_crash_chvt = 0; /* switch to where kmsg goes */
+        else
+                arg_crash_chvt = -1; /* turn off switching */
 
         return 0;
 }
@@ -291,12 +318,12 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
         if (streq(key, "systemd.unit") && value) {
 
                 if (!in_initrd())
-                        return set_default_unit(value);
+                        return free_and_strdup(&arg_default_unit, value);
 
         } else if (streq(key, "rd.systemd.unit") && value) {
 
                 if (in_initrd())
-                        return set_default_unit(value);
+                        return free_and_strdup(&arg_default_unit, value);
 
         } else if (streq(key, "systemd.dump_core") && value) {
 
@@ -306,6 +333,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);
@@ -314,12 +346,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) {
 
@@ -383,7 +416,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
                 /* SysV compatibility */
                 for (i = 0; i < ELEMENTSOF(rlmap); i += 2)
                         if (streq(key, rlmap[i]))
-                                return set_default_unit(rlmap[i+1]);
+                                return free_and_strdup(&arg_default_unit, rlmap[i+1]);
         }
 
         return 0;
@@ -409,9 +442,9 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
                                                                       \
                 r = func(rvalue);                                     \
                 if (r < 0)                                            \
-                        log_syntax(unit, LOG_ERR, filename, line, -r, \
-                                   "Invalid " descr "'%s': %s",       \
-                                   rvalue, strerror(-r));             \
+                        log_syntax(unit, LOG_ERR, filename, line, r,  \
+                                   "Invalid " descr "'%s': %m",       \
+                                   rvalue);                           \
                                                                       \
                 return 0;                                             \
         }
@@ -433,48 +466,15 @@ static int config_parse_cpu_affinity2(
                 void *data,
                 void *userdata) {
 
-        const char *whole_rvalue = rvalue;
         _cleanup_cpu_free_ cpu_set_t *c = NULL;
-        unsigned ncpus = 0;
+        int ncpus;
 
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
+        ncpus = parse_cpu_set_and_warn(rvalue, &c, unit, filename, line, lvalue);
+        if (ncpus < 0)
+                return ncpus;
 
-        for (;;) {
-                _cleanup_free_ char *word = NULL;
-                unsigned cpu;
-                int r;
-
-                r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES);
-                if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue);
-                        return r;
-                }
-                if (r == 0)
-                        break;
-
-                r = safe_atou(word, &cpu);
-
-                if (!c)
-                        if (!(c = cpu_set_malloc(&ncpus)))
-                                return log_oom();
-
-                if (r < 0 || cpu >= ncpus) {
-                        log_syntax(unit, LOG_ERR, filename, line, -r,
-                                   "Failed to parse CPU affinity '%s'", rvalue);
-                        return -EBADMSG;
-                }
-
-                CPU_SET_S(cpu, CPU_ALLOC_SIZE(ncpus), c);
-        }
-        if (!isempty(rvalue))
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Trailing garbage, ignoring.");
-
-        if (c)
-                if (sched_setaffinity(0, CPU_ALLOC_SIZE(ncpus), c) < 0)
-                        log_warning("Failed to set CPU affinity: %m");
+        if (sched_setaffinity(0, CPU_ALLOC_SIZE(ncpus), c) < 0)
+                log_warning("Failed to set CPU affinity: %m");
 
         return 0;
 }
@@ -501,29 +501,38 @@ static int config_parse_show_status(
 
         k = parse_show_status(rvalue, b);
         if (k < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, -k,
-                           "Failed to parse show status setting, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse show status setting, ignoring: %s", rvalue);
                 return 0;
         }
 
         return 0;
 }
 
-static void strv_free_free(char ***l) {
-        char ***i;
+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) {
 
-        if (!l)
-                return;
+        int r;
 
-        for (i = l; *i; i++)
-                strv_free(*i);
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
 
-        free(l);
-}
+        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;
+        }
 
-static void free_join_controllers(void) {
-        strv_free_free(arg_join_controllers);
-        arg_join_controllers = NULL;
+        return 0;
 }
 
 static int config_parse_join_controllers(const char *unit,
@@ -544,7 +553,7 @@ static int config_parse_join_controllers(const char *unit,
         assert(lvalue);
         assert(rvalue);
 
-        free_join_controllers();
+        arg_join_controllers = strv_free_free(arg_join_controllers);
 
         for (;;) {
                 _cleanup_free_ char *word = NULL;
@@ -561,7 +570,7 @@ static int config_parse_join_controllers(const char *unit,
 
                 l = strv_split(word, ",");
                 if (!l)
-                        log_oom();
+                        return log_oom();
                 strv_uniq(l);
 
                 if (strv_length(l) <= 1) {
@@ -595,7 +604,7 @@ static int config_parse_join_controllers(const char *unit,
                         for (a = arg_join_controllers; *a; a++) {
 
                                 if (strv_overlap(*a, l)) {
-                                        if (strv_extend_strv(&l, *a) < 0) {
+                                        if (strv_extend_strv(&l, *a, false) < 0) {
                                                 strv_free(l);
                                                 strv_free_free(t);
                                                 return log_oom();
@@ -622,8 +631,7 @@ static int config_parse_join_controllers(const char *unit,
                 }
         }
         if (!isempty(rvalue))
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Trailing garbage, ignoring.");
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
 
         return 0;
 }
@@ -636,9 +644,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                  },
@@ -676,6 +686,7 @@ 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", "DefaultTasksAccounting",    config_parse_bool,             0, &arg_default_tasks_accounting          },
                 {}
         };
 
@@ -704,6 +715,7 @@ static void manager_set_defaults(Manager *m) {
         m->default_cpu_accounting = arg_default_cpu_accounting;
         m->default_blockio_accounting = arg_default_blockio_accounting;
         m->default_memory_accounting = arg_default_memory_accounting;
+        m->default_tasks_accounting = arg_default_tasks_accounting;
 
         manager_set_default_rlimits(m, arg_default_rlimit);
         manager_environment_add(m, NULL, arg_default_environment);
@@ -724,7 +736,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,
@@ -747,7 +761,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              },
@@ -832,7 +848,7 @@ static int parse_argv(int argc, char *argv[]) {
 
                 case ARG_UNIT:
 
-                        r = set_default_unit(optarg);
+                        r = free_and_strdup(&arg_default_unit, optarg);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to set default unit %s: %m", optarg);
 
@@ -865,21 +881,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:
@@ -909,18 +946,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");
 
-                        if (arg_serialization)
-                                fclose(arg_serialization);
-
+                        safe_fclose(arg_serialization);
                         arg_serialization = f;
 
                         break;
@@ -980,14 +1015,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);
@@ -995,16 +1032,9 @@ static int help(void) {
         return 0;
 }
 
-static int version(void) {
-        puts(PACKAGE_STRING);
-        puts(SYSTEMD_FEATURES);
-
-        return 0;
-}
-
 static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool switching_root) {
-        FILE *f = NULL;
-        FDSet *fds = NULL;
+        _cleanup_fdset_free_ FDSet *fds = NULL;
+        _cleanup_fclose_ FILE *f = NULL;
         int r;
 
         assert(m);
@@ -1012,57 +1042,39 @@ static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool switching
         assert(_fds);
 
         r = manager_open_serialization(m, &f);
-        if (r < 0) {
-                log_error_errno(r, "Failed to create serialization file: %m");
-                goto fail;
-        }
+        if (r < 0)
+                return log_error_errno(r, "Failed to create serialization file: %m");
 
         /* Make sure nothing is really destructed when we shut down */
         m->n_reloading ++;
         bus_manager_send_reloading(m, true);
 
         fds = fdset_new();
-        if (!fds) {
-                r = -ENOMEM;
-                log_error_errno(r, "Failed to allocate fd set: %m");
-                goto fail;
-        }
+        if (!fds)
+                return log_oom();
 
         r = manager_serialize(m, f, fds, switching_root);
-        if (r < 0) {
-                log_error_errno(r, "Failed to serialize state: %m");
-                goto fail;
-        }
+        if (r < 0)
+                return log_error_errno(r, "Failed to serialize state: %m");
 
-        if (fseeko(f, 0, SEEK_SET) < 0) {
-                log_error_errno(errno, "Failed to rewind serialization fd: %m");
-                goto fail;
-        }
+        if (fseeko(f, 0, SEEK_SET) == (off_t) -1)
+                return log_error_errno(errno, "Failed to rewind serialization fd: %m");
 
         r = fd_cloexec(fileno(f), false);
-        if (r < 0) {
-                log_error_errno(r, "Failed to disable O_CLOEXEC for serialization: %m");
-                goto fail;
-        }
+        if (r < 0)
+                return log_error_errno(r, "Failed to disable O_CLOEXEC for serialization: %m");
 
         r = fdset_cloexec(fds, false);
-        if (r < 0) {
-                log_error_errno(r, "Failed to disable O_CLOEXEC for serialization fds: %m");
-                goto fail;
-        }
+        if (r < 0)
+                return log_error_errno(r, "Failed to disable O_CLOEXEC for serialization fds: %m");
 
         *_f = f;
         *_fds = fds;
 
-        return 0;
-
-fail:
-        fdset_free(fds);
-
-        if (f)
-                fclose(f);
+        f = NULL;
+        fds = NULL;
 
-        return r;
+        return 0;
 }
 
 static int bump_rlimit_nofile(struct rlimit *saved_rlimit) {
@@ -1120,8 +1132,8 @@ 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();
+                  "Please replace /etc/mtab with a symlink to /proc/self/mounts.");
+        freeze_or_reboot();
 }
 
 static void test_usr(void) {
@@ -1147,15 +1159,19 @@ static int initialize_join_controllers(void) {
                 return -ENOMEM;
 
         arg_join_controllers[0] = strv_new("cpu", "cpuacct", NULL);
-        arg_join_controllers[1] = strv_new("net_cls", "net_prio", NULL);
-        arg_join_controllers[2] = NULL;
+        if (!arg_join_controllers[0])
+                goto oom;
 
-        if (!arg_join_controllers[0] || !arg_join_controllers[1]) {
-                free_join_controllers();
-                return -ENOMEM;
-        }
+        arg_join_controllers[1] = strv_new("net_cls", "net_prio", NULL);
+        if (!arg_join_controllers[1])
+                goto oom;
 
+        arg_join_controllers[2] = NULL;
         return 0;
+
+oom:
+        arg_join_controllers = strv_free_free(arg_join_controllers);
+        return -ENOMEM;
 }
 
 static int enforce_syscall_archs(Set *archs) {
@@ -1399,7 +1415,7 @@ int main(int argc, char *argv[]) {
         }
 
         /* Initialize default unit */
-        r = set_default_unit(SPECIAL_DEFAULT_TARGET);
+        r = free_and_strdup(&arg_default_unit, SPECIAL_DEFAULT_TARGET);
         if (r < 0) {
                 log_emergency_errno(r, "Failed to set default unit %s: %m", SPECIAL_DEFAULT_TARGET);
                 error_message = "Failed to set default unit";
@@ -1676,13 +1692,9 @@ int main(int argc, char *argv[]) {
 
         /* This will close all file descriptors that were opened, but
          * not claimed by any unit. */
-        fdset_free(fds);
-        fds = NULL;
+        fds = fdset_free(fds);
 
-        if (arg_serialization) {
-                fclose(arg_serialization);
-                arg_serialization = NULL;
-        }
+        arg_serialization = safe_fclose(arg_serialization);
 
         if (queue_default_job) {
                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -1766,11 +1778,6 @@ int main(int argc, char *argv[]) {
 
                 switch (m->exit_code) {
 
-                case MANAGER_EXIT:
-                        retval = EXIT_SUCCESS;
-                        log_debug("Exit.");
-                        goto finish;
-
                 case MANAGER_RELOAD:
                         log_info("Reloading.");
 
@@ -1812,11 +1819,21 @@ int main(int argc, char *argv[]) {
                         log_notice("Switching root.");
                         goto finish;
 
+                case MANAGER_EXIT:
+                        retval = m->return_value;
+
+                        if (m->running_as == MANAGER_USER) {
+                                log_debug("Exit.");
+                                goto finish;
+                        }
+
+                        /* fallthrough */
                 case MANAGER_REBOOT:
                 case MANAGER_POWEROFF:
                 case MANAGER_HALT:
                 case MANAGER_KEXEC: {
                         static const char * const table[_MANAGER_EXIT_CODE_MAX] = {
+                                [MANAGER_EXIT] = "exit",
                                 [MANAGER_REBOOT] = "reboot",
                                 [MANAGER_POWEROFF] = "poweroff",
                                 [MANAGER_HALT] = "halt",
@@ -1840,20 +1857,16 @@ finish:
 
         if (m)
                 arg_shutdown_watchdog = m->shutdown_watchdog;
+
         m = manager_free(m);
 
-        for (j = 0; j < ELEMENTSOF(arg_default_rlimit); j++) {
+        for (j = 0; j < ELEMENTSOF(arg_default_rlimit); j++)
                 arg_default_rlimit[j] = mfree(arg_default_rlimit[j]);
-        }
 
         arg_default_unit = mfree(arg_default_unit);
-
-        free_join_controllers();
-
+        arg_join_controllers = strv_free_free(arg_join_controllers);
         arg_default_environment = strv_free(arg_default_environment);
-
-        set_free(arg_syscall_archs);
-        arg_syscall_archs = NULL;
+        arg_syscall_archs = set_free(arg_syscall_archs);
 
         mac_selinux_finish();
 
@@ -1870,7 +1883,7 @@ finish:
                  * that the new systemd can pass the kernel default to
                  * its child processes */
                 if (saved_rlimit_nofile.rlim_cur > 0)
-                        setrlimit(RLIMIT_NOFILE, &saved_rlimit_nofile);
+                        (void) setrlimit(RLIMIT_NOFILE, &saved_rlimit_nofile);
 
                 if (switch_root_dir) {
                         /* Kill all remaining processes from the
@@ -1912,10 +1925,10 @@ finish:
 
                         /* do not pass along the environment we inherit from the kernel or initrd */
                         if (switch_root_dir)
-                                clearenv();
+                                (void) clearenv();
 
                         assert(i <= args_size);
-                        execv(args[0], (char* const*) args);
+                        (void) execv(args[0], (char* const*) args);
                 }
 
                 /* Try the fallback, if there is any, without any
@@ -1924,18 +1937,11 @@ finish:
                  * getopt() in argv[], and some cleanups in envp[],
                  * but let's hope that doesn't matter.) */
 
-                if (arg_serialization) {
-                        fclose(arg_serialization);
-                        arg_serialization = NULL;
-                }
-
-                if (fds) {
-                        fdset_free(fds);
-                        fds = NULL;
-                }
+                arg_serialization = safe_fclose(arg_serialization);
+                fds = fdset_free(fds);
 
                 /* Reopen the console */
-                make_console_stdio();
+                (void) make_console_stdio();
 
                 for (j = 1, i = 1; j < (unsigned) argc; j++)
                         args[i++] = argv[j];
@@ -1949,33 +1955,26 @@ finish:
 
                 if (switch_root_init) {
                         args[0] = switch_root_init;
-                        execv(args[0], (char* const*) args);
+                        (void) execv(args[0], (char* const*) args);
                         log_warning_errno(errno, "Failed to execute configured init, trying fallback: %m");
                 }
 
                 args[0] = "/sbin/init";
-                execv(args[0], (char* const*) args);
+                (void) execv(args[0], (char* const*) args);
 
                 if (errno == ENOENT) {
                         log_warning("No /sbin/init, trying fallback");
 
                         args[0] = "/bin/sh";
                         args[1] = NULL;
-                        execv(args[0], (char* const*) args);
+                        (void) execv(args[0], (char* const*) args);
                         log_error_errno(errno, "Failed to execute /bin/sh, giving up: %m");
                 } else
                         log_warning_errno(errno, "Failed to execute /sbin/init, giving up: %m");
         }
 
-        if (arg_serialization) {
-                fclose(arg_serialization);
-                arg_serialization = NULL;
-        }
-
-        if (fds) {
-                fdset_free(fds);
-                fds = NULL;
-        }
+        arg_serialization = safe_fclose(arg_serialization);
+        fds = fdset_free(fds);
 
 #ifdef HAVE_VALGRIND_VALGRIND_H
         /* If we are PID 1 and running under valgrind, then let's exit
@@ -1988,7 +1987,8 @@ finish:
 
         if (shutdown_verb) {
                 char log_level[DECIMAL_STR_MAX(int) + 1];
-                const char* command_line[9] = {
+                char exit_code[DECIMAL_STR_MAX(uint8_t) + 1];
+                const char* command_line[11] = {
                         SYSTEMD_SHUTDOWN_BINARY_PATH,
                         shutdown_verb,
                         "--log-level", log_level,
@@ -2003,6 +2003,7 @@ finish:
                 xsprintf(log_level, "%d", log_get_max_level());
 
                 switch (log_get_target()) {
+
                 case LOG_TARGET_KMSG:
                 case LOG_TARGET_JOURNAL_OR_KMSG:
                 case LOG_TARGET_SYSLOG_OR_KMSG:
@@ -2025,6 +2026,12 @@ finish:
                 if (log_get_show_location())
                         command_line[pos++] = "--log-location";
 
+                if (streq(shutdown_verb, "exit")) {
+                        command_line[pos++] = "--exit-code";
+                        command_line[pos++] = exit_code;
+                        xsprintf(exit_code, "%d", retval);
+                }
+
                 assert(pos < ELEMENTSOF(command_line));
 
                 if (arm_reboot_watchdog && arg_shutdown_watchdog > 0) {
@@ -2038,7 +2045,7 @@ finish:
 
                         /* Tell the binary how often to ping, ignore failure */
                         if (asprintf(&e, "WATCHDOG_USEC="USEC_FMT, arg_shutdown_watchdog) > 0)
-                                strv_push(&env_block, e);
+                                (void) strv_push(&env_block, e);
                 } else
                         watchdog_close(true);
 
@@ -2056,9 +2063,9 @@ finish:
         if (getpid() == 1) {
                 if (error_message)
                         manager_status_printf(NULL, STATUS_TYPE_EMERGENCY,
-                                              ANSI_HIGHLIGHT_RED_ON "!!!!!!" ANSI_HIGHLIGHT_OFF,
+                                              ANSI_HIGHLIGHT_RED "!!!!!!" ANSI_NORMAL,
                                               "%s, freezing.", error_message);
-                freeze();
+                freeze_or_reboot();
         }
 
         return retval;