chiark / gitweb /
manager: configurable StartLimit default values
[elogind.git] / src / core / main.c
index 695e232514bca043a5b4072620474fe0b7331159..ede1a121c62a8f38f86bfa0d96c759f11ad43a3a 100644 (file)
@@ -64,7 +64,6 @@
 #endif
 #include "hostname-setup.h"
 #include "machine-id-setup.h"
-#include "locale-setup.h"
 #include "selinux-setup.h"
 #include "ima-setup.h"
 #include "fileio.h"
@@ -88,12 +87,17 @@ static int arg_crash_chvt = -1;
 static bool arg_confirm_spawn = false;
 static bool arg_show_status = true;
 static bool arg_switched_root = false;
-static char **arg_default_controllers = NULL;
 static char ***arg_join_controllers = NULL;
 static ExecOutput arg_default_std_output = EXEC_OUTPUT_JOURNAL;
 static ExecOutput arg_default_std_error = EXEC_OUTPUT_INHERIT;
+static usec_t arg_default_restart_usec = DEFAULT_RESTART_USEC;
+static usec_t arg_default_timeout_start_usec = DEFAULT_TIMEOUT_USEC;
+static usec_t arg_default_timeout_stop_usec = DEFAULT_TIMEOUT_USEC;
+static usec_t arg_default_start_limit_interval = DEFAULT_START_LIMIT_INTERVAL;
+static unsigned arg_default_start_limit_burst = DEFAULT_START_LIMIT_BURST;
 static usec_t arg_runtime_watchdog = 0;
 static usec_t arg_shutdown_watchdog = 10 * USEC_PER_MINUTE;
+static char **arg_default_environment = NULL;
 static struct rlimit *arg_default_rlimit[RLIMIT_NLIMITS] = {};
 static uint64_t arg_capability_bounding_set_drop = 0;
 static nsec_t arg_timer_slack_nsec = (nsec_t) -1;
@@ -105,7 +109,10 @@ static void nop_handler(int sig) {
 
 _noreturn_ static void crash(int sig) {
 
-        if (!arg_dump_core)
+        if (getpid() != 1)
+                /* Pass this on immediately, if this is not PID 1 */
+                raise(sig);
+        else if (!arg_dump_core)
                 log_error("Caught <%s>, not dumping core.", signal_to_string(sig));
         else {
                 struct sigaction sa = {
@@ -115,7 +122,7 @@ _noreturn_ static void crash(int sig) {
                 pid_t pid;
 
                 /* We want to wait for the core process, hence let's enable SIGCHLD */
-                assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
+                sigaction(SIGCHLD, &sa, NULL);
 
                 pid = fork();
                 if (pid < 0)
@@ -127,7 +134,7 @@ _noreturn_ static void crash(int sig) {
                         /* Enable default signal handler for core dump */
                         zero(sa);
                         sa.sa_handler = SIG_DFL;
-                        assert_se(sigaction(sig, &sa, NULL) == 0);
+                        sigaction(sig, &sa, NULL);
 
                         /* Don't limit the core dump size */
                         rl.rlim_cur = RLIM_INFINITY;
@@ -135,7 +142,7 @@ _noreturn_ static void crash(int sig) {
                         setrlimit(RLIMIT_CORE, &rl);
 
                         /* Just to be sure... */
-                        assert_se(chdir("/") == 0);
+                        chdir("/");
 
                         /* Raise the signal again */
                         raise(sig);
@@ -346,32 +353,21 @@ static int parse_proc_cmdline_word(const char *word) {
                         arg_default_std_error = r;
         } else if (startswith(word, "systemd.setenv=")) {
                 _cleanup_free_ char *cenv = NULL;
-                char *eq;
-                int r;
 
                 cenv = strdup(word + 15);
                 if (!cenv)
                         return -ENOMEM;
 
-                eq = strchr(cenv, '=');
-                if (!eq) {
-                        if (!env_name_is_valid(cenv))
-                                log_warning("Environment variable name '%s' is not valid. Ignoring.", cenv);
-                        else  {
-                                r = unsetenv(cenv);
-                                if (r < 0)
-                                        log_warning("Unsetting environment variable '%s' failed, ignoring: %m", cenv);
-                        }
-                } else {
-                        if (!env_assignment_is_valid(cenv))
-                                log_warning("Environment variable assignment '%s' is not valid. Ignoring.", cenv);
-                        else {
-                                *eq = 0;
-                                r = setenv(cenv, eq + 1, 1);
-                                if (r < 0)
-                                        log_warning("Setting environment variable '%s=%s' failed, ignoring: %m", cenv, eq + 1);
-                        }
-                }
+                if (env_assignment_is_valid(cenv)) {
+                        char **env;
+
+                        env = strv_env_set(arg_default_environment, cenv);
+                        if (env)
+                                arg_default_environment = env;
+                        else
+                                log_warning("Setting environment variable '%s' failed, ignoring: %m", cenv);
+                } else
+                        log_warning("Environment variable name '%s' is not valid. Ignoring.", cenv);
 
         } else if (startswith(word, "systemd.") ||
                    (in_initrd() && startswith(word, "rd.systemd."))) {
@@ -410,7 +406,14 @@ static int parse_proc_cmdline_word(const char *word) {
 
         } else if (streq(word, "quiet"))
                 arg_show_status = false;
-        else if (!in_initrd()) {
+        else if (streq(word, "debug")) {
+                /* Log to kmsg, the journal socket will fill up before the
+                 * journal is started and tools running during that time
+                 * will block with every log message for for 60 seconds,
+                 * before they give up. */
+                log_set_max_level(LOG_DEBUG);
+                log_set_target(LOG_TARGET_KMSG);
+        } else if (!in_initrd()) {
                 unsigned i;
 
                 /* SysV compatibility */
@@ -636,14 +639,19 @@ static int parse_config_file(void) {
                 { "Manager", "ShowStatus",            config_parse_bool,         0, &arg_show_status         },
                 { "Manager", "CrashChVT",             config_parse_int,          0, &arg_crash_chvt          },
                 { "Manager", "CPUAffinity",           config_parse_cpu_affinity2, 0, NULL                    },
-                { "Manager", "DefaultControllers",    config_parse_strv,         0, &arg_default_controllers },
                 { "Manager", "DefaultStandardOutput", config_parse_output,       0, &arg_default_std_output  },
                 { "Manager", "DefaultStandardError",  config_parse_output,       0, &arg_default_std_error   },
+                { "Manager", "DefaultTimeoutStartSec", config_parse_sec,         0, &arg_default_timeout_start_usec },
+                { "Manager", "DefaultTimeoutStopSec", config_parse_sec,          0, &arg_default_timeout_stop_usec  },
+                { "Manager", "DefaultRestartSec",     config_parse_sec,          0, &arg_default_restart_usec  },
+                { "Manager", "DefaultStartLimitInterval", config_parse_sec,      0, &arg_default_start_limit_interval },
+                { "Manager", "DefaultStartLimitBurst", config_parse_unsigned,    0, &arg_default_start_limit_burst },
                 { "Manager", "JoinControllers",       config_parse_join_controllers, 0, &arg_join_controllers },
                 { "Manager", "RuntimeWatchdogSec",    config_parse_sec,          0, &arg_runtime_watchdog    },
                 { "Manager", "ShutdownWatchdogSec",   config_parse_sec,          0, &arg_shutdown_watchdog   },
                 { "Manager", "CapabilityBoundingSet", config_parse_bounding_set, 0, &arg_capability_bounding_set_drop },
                 { "Manager", "TimerSlackNSec",        config_parse_nsec,         0, &arg_timer_slack_nsec    },
+                { "Manager", "DefaultEnvironment",    config_parse_environ,      0, &arg_default_environment },
                 { "Manager", "DefaultLimitCPU",       config_parse_limit,        0, &arg_default_rlimit[RLIMIT_CPU]},
                 { "Manager", "DefaultLimitFSIZE",     config_parse_limit,        0, &arg_default_rlimit[RLIMIT_FSIZE]},
                 { "Manager", "DefaultLimitDATA",      config_parse_limit,        0, &arg_default_rlimit[RLIMIT_DATA]},
@@ -667,7 +675,7 @@ static int parse_config_file(void) {
         const char *fn;
         int r;
 
-        fn = arg_running_as == SYSTEMD_SYSTEM ? SYSTEM_CONFIG_FILE : USER_CONFIG_FILE;
+        fn = arg_running_as == SYSTEMD_SYSTEM ? PKGSYSCONFDIR "/system.conf" : PKGSYSCONFDIR "/user.conf";
         f = fopen(fn, "re");
         if (!f) {
                 if (errno == ENOENT)
@@ -687,19 +695,14 @@ static int parse_config_file(void) {
 static int parse_proc_cmdline(void) {
         _cleanup_free_ char *line = NULL;
         char *w, *state;
-        int r;
         size_t l;
+        int r;
 
-        /* Don't read /proc/cmdline if we are in a container, since
-         * that is only relevant for the host system */
-        if (detect_container(NULL) > 0)
-                return 0;
-
-        r = read_one_line_file("/proc/cmdline", &line);
-        if (r < 0) {
+        r = proc_cmdline(&line);
+        if (r < 0)
                 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
+        if (r <= 0)
                 return 0;
-        }
 
         FOREACH_WORD_QUOTED(w, l, line, state) {
                 _cleanup_free_ char *word;
@@ -737,7 +740,6 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_SHOW_STATUS,
                 ARG_DESERIALIZE,
                 ARG_SWITCHED_ROOT,
-                ARG_INTROSPECT,
                 ARG_DEFAULT_STD_OUTPUT,
                 ARG_DEFAULT_STD_ERROR
         };
@@ -760,7 +762,6 @@ static int parse_argv(int argc, char *argv[]) {
                 { "show-status",              optional_argument, NULL, ARG_SHOW_STATUS              },
                 { "deserialize",              required_argument, NULL, ARG_DESERIALIZE              },
                 { "switched-root",            no_argument,       NULL, ARG_SWITCHED_ROOT            },
-                { "introspect",               optional_argument, NULL, ARG_INTROSPECT               },
                 { "default-standard-output",  required_argument, NULL, ARG_DEFAULT_STD_OUTPUT,      },
                 { "default-standard-error",   required_argument, NULL, ARG_DEFAULT_STD_ERROR,       },
                 { NULL,                       0,                 NULL, 0                            }
@@ -932,27 +933,6 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_switched_root = true;
                         break;
 
-                case ARG_INTROSPECT: {
-                        const char * const * i = NULL;
-
-                        for (i = bus_interface_table; *i; i += 2)
-                                if (!optarg || streq(i[0], optarg)) {
-                                        fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
-                                              "<node>\n", stdout);
-                                        fputs(i[1], stdout);
-                                        fputs("</node>\n", stdout);
-
-                                        if (optarg)
-                                                break;
-                                }
-
-                        if (!i[0] && optarg)
-                                log_error("Unknown interface %s.", optarg);
-
-                        arg_action = ACTION_DONE;
-                        break;
-                }
-
                 case 'h':
                         arg_action = ACTION_HELP;
                         break;
@@ -998,11 +978,13 @@ static int parse_argv(int argc, char *argv[]) {
                  * relevant for the container, hence we rely on argv[]
                  * instead. */
 
-                for (a = argv; a < argv + argc; a++)
-                        if ((r = parse_proc_cmdline_word(*a)) < 0) {
+                for (a = argv; a < argv + argc; a++) {
+                        r = parse_proc_cmdline_word(*a);
+                        if (r < 0) {
                                 log_error("Failed on cmdline argument %s: %s", *a, strerror(-r));
                                 return r;
                         }
+                }
         }
 
         return 0;
@@ -1015,7 +997,6 @@ static int help(void) {
                "  -h --help                      Show this help\n"
                "     --test                      Determine startup sequence, dump it and exit\n"
                "     --dump-configuration-items  Dump understood unit configuration items\n"
-               "     --introspect[=INTERFACE]    Extract D-Bus interface data\n"
                "     --unit=UNIT                 Set default unit\n"
                "     --system                    Run a system instance, even if PID != 1\n"
                "     --user                      Run a user instance\n"
@@ -1050,15 +1031,16 @@ static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool switching
         assert(_f);
         assert(_fds);
 
-        /* Make sure nothing is really destructed when we shut down */
-        m->n_reloading ++;
-
         r = manager_open_serialization(m, &f);
         if (r < 0) {
                 log_error("Failed to create serialization file: %s", strerror(-r));
                 goto fail;
         }
 
+        /* Make sure nothing is really destructed when we shut down */
+        m->n_reloading ++;
+        bus_broadcast_reloading(m, true);
+
         fds = fdset_new();
         if (!fds) {
                 r = -ENOMEM;
@@ -1139,25 +1121,6 @@ static int bump_rlimit_nofile(struct rlimit *saved_rlimit) {
         return 0;
 }
 
-static struct dual_timestamp* parse_initrd_timestamp(struct dual_timestamp *t) {
-        const char *e;
-        unsigned long long a, b;
-
-        assert(t);
-
-        e = getenv("RD_TIMESTAMP");
-        if (!e)
-                return NULL;
-
-        if (sscanf(e, "%llu %llu", &a, &b) != 2)
-                return NULL;
-
-        t->realtime = (usec_t) a;
-        t->monotonic = (usec_t) b;
-
-        return t;
-}
-
 static void test_mtab(void) {
         char *p;
 
@@ -1236,6 +1199,8 @@ int main(int argc, char *argv[]) {
         bool reexecute = false;
         const char *shutdown_verb = NULL;
         dual_timestamp initrd_timestamp = { 0ULL, 0ULL };
+        dual_timestamp userspace_timestamp = { 0ULL, 0ULL };
+        dual_timestamp kernel_timestamp = { 0ULL, 0ULL };
         static char systemd[] = "systemd";
         bool skip_setup = false;
         int j;
@@ -1257,6 +1222,9 @@ int main(int argc, char *argv[]) {
         }
 #endif
 
+        dual_timestamp_from_monotonic(&kernel_timestamp, 0);
+        dual_timestamp_get(&userspace_timestamp);
+
         /* Determine if this is a reexecution or normal bootup. We do
          * the full command line parsing much later, so let's just
          * have a quick peek here. */
@@ -1280,6 +1248,10 @@ int main(int argc, char *argv[]) {
 
         log_show_color(isatty(STDERR_FILENO) > 0);
 
+        /* Disable the umask logic */
+        if (getpid() == 1)
+                umask(0);
+
         if (getpid() == 1 && detect_container(NULL) <= 0) {
 
                 /* Running outside of a container as PID 1 */
@@ -1288,18 +1260,8 @@ int main(int argc, char *argv[]) {
                 log_set_target(LOG_TARGET_KMSG);
                 log_open();
 
-                if (in_initrd()) {
-                        char *rd_timestamp = NULL;
-
-                        dual_timestamp_get(&initrd_timestamp);
-                        asprintf(&rd_timestamp, "%llu %llu",
-                                 (unsigned long long) initrd_timestamp.realtime,
-                                 (unsigned long long) initrd_timestamp.monotonic);
-                        if (rd_timestamp) {
-                                setenv("RD_TIMESTAMP", rd_timestamp, 1);
-                                free(rd_timestamp);
-                        }
-                }
+                if (in_initrd())
+                        initrd_timestamp = userspace_timestamp;
 
                 if (!skip_setup) {
                         mount_setup_early();
@@ -1335,10 +1297,10 @@ int main(int argc, char *argv[]) {
                                  */
                                 hwclock_reset_timezone();
 
-                                /* Tell the kernel our time zone */
+                                /* Tell the kernel our timezone */
                                 r = hwclock_set_timezone(NULL);
                                 if (r < 0)
-                                        log_error("Failed to set the kernel's time zone, ignoring: %s", strerror(-r));
+                                        log_error("Failed to set the kernel's timezone, ignoring: %s", strerror(-r));
                         }
                 }
 
@@ -1351,7 +1313,6 @@ int main(int argc, char *argv[]) {
                 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
 
         } else if (getpid() == 1) {
-
                 /* Running inside a container, as PID 1 */
                 arg_running_as = SYSTEMD_SYSTEM;
                 log_set_target(LOG_TARGET_CONSOLE);
@@ -1360,12 +1321,21 @@ int main(int argc, char *argv[]) {
                 /* For the later on, see above... */
                 log_set_target(LOG_TARGET_JOURNAL);
 
-        } else {
+                /* clear the kernel timestamp,
+                 * because we are in a container */
+                kernel_timestamp.monotonic = 0ULL;
+                kernel_timestamp.realtime = 0ULL;
 
+        } else {
                 /* Running as user instance */
                 arg_running_as = SYSTEMD_USER;
                 log_set_target(LOG_TARGET_AUTO);
                 log_open();
+
+                /* clear the kernel timestamp,
+                 * because we are not PID 1 */
+                kernel_timestamp.monotonic = 0ULL;
+                kernel_timestamp.realtime = 0ULL;
         }
 
         /* Initialize default unit */
@@ -1390,7 +1360,6 @@ int main(int argc, char *argv[]) {
         /* Reset all signal handlers. */
         assert_se(reset_all_signal_handlers() == 0);
 
-        /* If we are init, we can block sigkill. Yay. */
         ignore_signals(SIGNALS_IGNORE, -1);
 
         if (parse_config_file() < 0)
@@ -1440,6 +1409,12 @@ int main(int argc, char *argv[]) {
                 goto finish;
         }
 
+        if (arg_running_as == SYSTEMD_USER &&
+            !getenv("XDG_RUNTIME_DIR")) {
+                log_error("Trying to run as user instance, but $XDG_RUNTIME_DIR is not set.");
+                goto finish;
+        }
+
         assert_se(arg_action == ACTION_RUN || arg_action == ACTION_TEST);
 
         /* Close logging fds, in order not to confuse fdset below */
@@ -1456,59 +1431,12 @@ int main(int argc, char *argv[]) {
         if (serialization)
                 assert_se(fdset_remove(fds, fileno(serialization)) >= 0);
 
-        /* Set up PATH unless it is already set */
-        setenv("PATH",
-#ifdef HAVE_SPLIT_USR
-               "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
-#else
-               "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin",
-#endif
-               arg_running_as == SYSTEMD_SYSTEM);
-
-        if (arg_running_as == SYSTEMD_SYSTEM) {
-                /* Parse the data passed to us. We leave this
-                 * variables set, but the manager later on will not
-                 * pass them on to our children. */
-                if (!in_initrd())
-                        parse_initrd_timestamp(&initrd_timestamp);
-
-                /* Unset some environment variables passed in from the
-                 * kernel that don't really make sense for us. */
-                unsetenv("HOME");
-                unsetenv("TERM");
-
-                /* When we are invoked by a shell, these might be set,
-                 * but make little sense to pass on */
-                unsetenv("PWD");
-                unsetenv("SHLVL");
-                unsetenv("_");
-
-                /* When we are invoked by a chroot-like tool such as
-                 * nspawn, these might be set, but make little sense
-                 * to pass on */
-                unsetenv("USER");
-                unsetenv("LOGNAME");
-
-                /* We suppress the socket activation env vars, as
-                 * we'll try to match *any* open fd to units if
-                 * possible. */
-                unsetenv("LISTEN_FDS");
-                unsetenv("LISTEN_PID");
-
-                /* All other variables are left as is, so that clients
-                 * can still read them via /proc/1/environ */
-        }
-
-        /* Move out of the way, so that we won't block unmounts */
-        assert_se(chdir("/")  == 0);
-
-        if (arg_running_as == SYSTEMD_SYSTEM) {
+        if (arg_running_as == SYSTEMD_SYSTEM)
                 /* Become a session leader if we aren't one yet. */
                 setsid();
 
-                /* Disable the umask logic */
-                umask(0);
-        }
+        /* Move out of the way, so that we won't block unmounts */
+        assert_se(chdir("/")  == 0);
 
         /* Make sure D-Bus doesn't fiddle with the SIGPIPE handlers */
         dbus_connection_set_change_sigpipe(FALSE);
@@ -1547,8 +1475,6 @@ int main(int argc, char *argv[]) {
                 log_debug(PACKAGE_STRING " running in user mode. (" SYSTEMD_FEATURES ")");
 
         if (arg_running_as == SYSTEMD_SYSTEM && !skip_setup) {
-                locale_setup();
-
                 if (arg_show_status || plymouth_running())
                         status_welcome();
 
@@ -1572,14 +1498,14 @@ int main(int argc, char *argv[]) {
                         log_error("Failed to adjust timer slack: %m");
 
         if (arg_capability_bounding_set_drop) {
-                r = capability_bounding_set_drop(arg_capability_bounding_set_drop, true);
+                r = capability_bounding_set_drop_usermode(arg_capability_bounding_set_drop);
                 if (r < 0) {
-                        log_error("Failed to drop capability bounding set: %s", strerror(-r));
+                        log_error("Failed to drop capability bounding set of usermode helpers: %s", strerror(-r));
                         goto finish;
                 }
-                r = capability_bounding_set_drop_usermode(arg_capability_bounding_set_drop);
+                r = capability_bounding_set_drop(arg_capability_bounding_set_drop, true);
                 if (r < 0) {
-                        log_error("Failed to drop capability bounding set of usermode helpers: %s", strerror(-r));
+                        log_error("Failed to drop capability bounding set: %s", strerror(-r));
                         goto finish;
                 }
         }
@@ -1596,7 +1522,7 @@ int main(int argc, char *argv[]) {
         if (arg_running_as == SYSTEMD_SYSTEM)
                 bump_rlimit_nofile(&saved_rlimit_nofile);
 
-        r = manager_new(arg_running_as, &m);
+        r = manager_new(arg_running_as, !!serialization, &m);
         if (r < 0) {
                 log_error("Failed to allocate manager object: %s", strerror(-r));
                 goto finish;
@@ -1605,16 +1531,21 @@ int main(int argc, char *argv[]) {
         m->confirm_spawn = arg_confirm_spawn;
         m->default_std_output = arg_default_std_output;
         m->default_std_error = arg_default_std_error;
+        m->default_restart_usec = arg_default_restart_usec;
+        m->default_timeout_start_usec = arg_default_timeout_start_usec;
+        m->default_timeout_stop_usec = arg_default_timeout_stop_usec;
+        m->default_start_limit_interval = arg_default_start_limit_interval;
+        m->default_start_limit_burst = arg_default_start_limit_burst;
         m->runtime_watchdog = arg_runtime_watchdog;
         m->shutdown_watchdog = arg_shutdown_watchdog;
+        m->userspace_timestamp = userspace_timestamp;
+        m->kernel_timestamp = kernel_timestamp;
+        m->initrd_timestamp = initrd_timestamp;
 
         manager_set_default_rlimits(m, arg_default_rlimit);
 
-        if (dual_timestamp_is_set(&initrd_timestamp))
-                m->initrd_timestamp = initrd_timestamp;
-
-        if (arg_default_controllers)
-                manager_set_default_controllers(m, arg_default_controllers);
+        if (arg_default_environment)
+                manager_environment_add(m, arg_default_environment);
 
         manager_set_show_status(m, arg_show_status);
 
@@ -1630,6 +1561,7 @@ 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;
 
         if (serialization) {
                 fclose(serialization);
@@ -1649,7 +1581,7 @@ int main(int argc, char *argv[]) {
                 if (r < 0) {
                         log_error("Failed to load default target: %s", bus_error(&error, r));
                         dbus_error_free(&error);
-                } else if (target->load_state == UNIT_ERROR)
+                } else if (target->load_state == UNIT_ERROR || target->load_state == UNIT_NOT_FOUND)
                         log_error("Failed to load default target: %s", strerror(-target->load_error));
                 else if (target->load_state == UNIT_MASKED)
                         log_error("Default target masked.");
@@ -1662,7 +1594,7 @@ int main(int argc, char *argv[]) {
                                 log_error("Failed to load rescue target: %s", bus_error(&error, r));
                                 dbus_error_free(&error);
                                 goto finish;
-                        } else if (target->load_state == UNIT_ERROR) {
+                        } else if (target->load_state == UNIT_ERROR || target->load_state == UNIT_NOT_FOUND) {
                                 log_error("Failed to load rescue target: %s", strerror(-target->load_error));
                                 goto finish;
                         } else if (target->load_state == UNIT_MASKED) {
@@ -1785,7 +1717,6 @@ finish:
                 free(arg_default_rlimit[j]);
 
         free(arg_default_unit);
-        strv_free(arg_default_controllers);
         free_join_controllers();
 
         dbus_shutdown();
@@ -1845,6 +1776,10 @@ finish:
                         args[i++] = sfd;
                         args[i++] = NULL;
 
+                        /* do not pass along the environment we inherit from the kernel or initrd */
+                        if (switch_root_dir)
+                                clearenv();
+
                         assert(i <= args_size);
                         execv(args[0], (char* const*) args);
                 }
@@ -1926,6 +1861,12 @@ finish:
                         watchdog_close(true);
                 }
 
+                /* Avoid the creation of new processes forked by the
+                 * kernel; at this point, we will not listen to the
+                 * signals anyway */
+                if (detect_container(NULL) <= 0)
+                        cg_uninstall_release_agent(SYSTEMD_CGROUP_CONTROLLER);
+
                 execve(SYSTEMD_SHUTDOWN_BINARY_PATH, (char **) command_line, env_block);
                 free(env_block);
                 log_error("Failed to execute shutdown binary, freezing: %m");