+static bool dump_core = true;
+static bool crash_shell = false;
+static int crash_chvt = -1;
+static bool confirm_spawn = false;
+static FILE* serialization = NULL;
+
+_noreturn static void freeze(void) {
+ for (;;)
+ pause();
+}
+
+static void nop_handler(int sig) {
+}
+
+_noreturn static void crash(int sig) {
+
+ if (!dump_core)
+ log_error("Caught <%s>, not dumping core.", strsignal(sig));
+ else {
+ struct sigaction sa;
+ pid_t pid;
+
+ /* We want to wait for the core process, hence let's enable SIGCHLD */
+ zero(sa);
+ sa.sa_handler = nop_handler;
+ sa.sa_flags = SA_NOCLDSTOP|SA_RESTART;
+ assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
+
+ if ((pid = fork()) < 0)
+ log_error("Caught <%s>, cannot fork for core dump: %s", strsignal(sig), strerror(errno));
+
+ else if (pid == 0) {
+ struct rlimit rl;
+
+ /* Enable default signal handler for core dump */
+ zero(sa);
+ sa.sa_handler = SIG_DFL;
+ assert_se(sigaction(sig, &sa, NULL) == 0);
+
+ /* Don't limit the core dump size */
+ zero(rl);
+ rl.rlim_cur = RLIM_INFINITY;
+ rl.rlim_max = RLIM_INFINITY;
+ setrlimit(RLIMIT_CORE, &rl);
+
+ /* Just to be sure... */
+ assert_se(chdir("/") == 0);
+
+ /* Raise the signal again */
+ raise(sig);
+
+ assert_not_reached("We shouldn't be here...");
+ _exit(1);
+
+ } else {
+ int status, r;
+
+ /* Order things nicely. */
+ if ((r = waitpid(pid, &status, 0)) < 0)
+ log_error("Caught <%s>, waitpid() failed: %s", strsignal(sig), strerror(errno));
+ else if (!WCOREDUMP(status))
+ log_error("Caught <%s>, core dump failed.", strsignal(sig));
+ else
+ log_error("Caught <%s>, dumped core as pid %llu.", strsignal(sig), (unsigned long long) pid);
+ }
+ }
+
+ if (crash_chvt)
+ chvt(crash_chvt);
+
+ if (crash_shell) {
+ struct sigaction sa;
+ pid_t pid;
+
+ log_info("Executing crash shell in 10s...");
+ sleep(10);
+
+ /* Let the kernel reap children for us */
+ zero(sa);
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART;
+ assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
+
+ if ((pid = fork()) < 0)
+ log_error("Failed to fork off crash shell: %s", strerror(errno));
+ else if (pid == 0) {
+ int fd, r;
+
+ if ((fd = acquire_terminal("/dev/console", false, true)) < 0) {
+ log_error("Failed to acquire terminal: %s", strerror(-fd));
+ _exit(1);
+ }
+
+ if ((r = make_stdio(fd)) < 0) {
+ log_error("Failed to duplicate terminal fd: %s", strerror(-r));
+ _exit(1);
+ }
+
+ execl("/bin/sh", "/bin/sh", NULL);
+
+ log_error("execl() failed: %s", strerror(errno));
+ _exit(1);
+ }
+
+ log_info("Successfully spawned crash shall as pid %llu.", (unsigned long long) pid);
+ }
+
+ log_info("Freezing execution.");
+ freeze();
+}
+
+static void install_crash_handler(void) {
+ struct sigaction sa;
+
+ zero(sa);
+
+ sa.sa_handler = crash;
+ sa.sa_flags = SA_NODEFER;
+
+ assert_se(sigaction(SIGSEGV, &sa, NULL) == 0);
+ assert_se(sigaction(SIGILL, &sa, NULL) == 0);
+ assert_se(sigaction(SIGFPE, &sa, NULL) == 0);
+ assert_se(sigaction(SIGBUS, &sa, NULL) == 0);
+ assert_se(sigaction(SIGQUIT, &sa, NULL) == 0);
+ assert_se(sigaction(SIGABRT, &sa, NULL) == 0);
+}
+
+static int make_null_stdio(void) {
+ int null_fd, r;
+
+ if ((null_fd = open("/dev/null", O_RDWR)) < 0) {
+ log_error("Failed to open /dev/null: %m");
+ return -errno;
+ }
+
+ if ((r = make_stdio(null_fd)) < 0)
+ log_warning("Failed to dup2() device: %s", strerror(-r));
+
+ return r;
+}
+
+static int console_setup(bool do_reset) {
+ int tty_fd, r;
+
+ /* If we are init, we connect stdin/stdout/stderr to /dev/null
+ * and make sure we don't have a controlling tty. */
+
+ release_terminal();
+
+ if (!do_reset)
+ return 0;
+
+ if ((tty_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0) {
+ log_error("Failed to open /dev/console: %s", strerror(-tty_fd));
+ return -tty_fd;
+ }
+
+ if ((r = reset_terminal(tty_fd)) < 0)
+ log_error("Failed to reset /dev/console: %s", strerror(-r));
+
+ close_nointr_nofail(tty_fd);
+ return r;
+}
+