+ /*
+ * If the kernel is in secure mode, downgrade it to insecure mode.
+ */
+ if (getsecuritylevel() > 0)
+ setsecuritylevel(0);
+
+ if ((pid = fork()) == 0) {
+ /*
+ * Start the single user session.
+ */
+ setctty(_PATH_CONSOLE);
+
+#ifdef SECURE
+ /*
+ * Check the root password.
+ * We don't care if the console is 'on' by default;
+ * it's the only tty that can be 'off' and 'secure'.
+ */
+ typ = getttynam("console");
+ pp = getpwnam("root");
+ if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp) {
+ write(2, banner, sizeof banner - 1);
+ for (;;) {
+ clear = getpass("Password:");
+ if (clear == 0 || *clear == '\0')
+ _exit(0);
+ password = crypt(clear, pp->pw_passwd);
+ memset(clear, 0, _PASSWORD_LEN);
+ if (strcmp(password, pp->pw_passwd) == 0)
+ break;
+ warning("single-user login failed\n");
+ }
+ }
+ endttyent();
+ endpwent();
+#endif /* SECURE */
+
+#ifdef DEBUGSHELL
+ {
+ char altshell[128], *cp = altshell;
+ int num;
+
+#define SHREQUEST \
+ "Enter pathname of shell or RETURN for sh: "
+ (void)write(STDERR_FILENO,
+ SHREQUEST, sizeof(SHREQUEST) - 1);
+ while ((num = read(STDIN_FILENO, cp, 1)) != -1 &&
+ num != 0 && *cp != '\n' && cp < &altshell[127])
+ cp++;
+ *cp = '\0';
+ if (altshell[0] != '\0')
+ shell = altshell;
+ }
+#endif /* DEBUGSHELL */
+
+ /*
+ * Unblock signals.
+ * We catch all the interesting ones,
+ * and those are reset to SIG_DFL on exec.
+ */
+ sigemptyset(&mask);
+ sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
+
+ /*
+ * Fire off a shell.
+ * If the default one doesn't work, try the Bourne shell.
+ */
+ argv[0] = "-sh";
+ argv[1] = 0;
+ execv(shell, argv);
+ emergency("can't exec %s for single user: %m", shell);
+ execv(_PATH_BSHELL, argv);
+ emergency("can't exec %s for single user: %m", _PATH_BSHELL);
+ sleep(STALL_TIMEOUT);
+ _exit(1);
+ }
+
+ if (pid == -1) {
+ /*
+ * We are seriously hosed. Do our best.
+ */
+ emergency("can't fork single-user shell, trying again");
+ while (waitpid(-1, (int *) 0, WNOHANG) > 0)
+ continue;
+ return (state_func_t) single_user;
+ }
+
+ requested_transition = 0;
+ do {
+ if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
+ collect_child(wpid);
+ if (wpid == -1) {
+ if (errno == EINTR)
+ continue;
+ warning("wait for single-user shell failed: %m; restarting");
+ return (state_func_t) single_user;
+ }
+ if (wpid == pid && WIFSTOPPED(status)) {
+ warning("init: shell stopped, restarting\n");
+ kill(pid, SIGCONT);
+ wpid = -1;
+ }
+ } while (wpid != pid && !requested_transition);
+
+ if (requested_transition)
+ return (state_func_t) requested_transition;
+
+ if (!WIFEXITED(status)) {
+ if (WTERMSIG(status) == SIGKILL) {
+ /*
+ * reboot(8) killed shell?
+ */
+ warning("single user shell terminated.");
+ sleep(STALL_TIMEOUT);
+ _exit(0);
+ } else {
+ warning("single user shell terminated, restarting");
+ return (state_func_t) single_user;
+ }
+ }
+
+ runcom_mode = FASTBOOT;
+ return (state_func_t) runcom;
+}
+
+/*
+ * Run the system startup script.
+ */
+state_func_t
+runcom()
+{
+ pid_t pid, wpid;
+ int status;
+ char *argv[4];
+ struct sigaction sa;
+
+ if ((pid = fork()) == 0) {
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sa.sa_handler = SIG_IGN;
+ (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0);
+ (void) sigaction(SIGHUP, &sa, (struct sigaction *)0);
+
+ setctty(_PATH_CONSOLE);
+
+ argv[0] = "sh";
+ argv[1] = _PATH_RUNCOM;
+ argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : 0;
+ argv[3] = 0;
+
+ sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0);
+
+ execv(_PATH_BSHELL, argv);
+ stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM);
+ _exit(1); /* force single user mode */
+ }
+
+ if (pid == -1) {
+ emergency("can't fork for %s on %s: %m",
+ _PATH_BSHELL, _PATH_RUNCOM);
+ while (waitpid(-1, (int *) 0, WNOHANG) > 0)
+ continue;
+ sleep(STALL_TIMEOUT);
+ return (state_func_t) single_user;
+ }
+
+ /*
+ * Copied from single_user(). This is a bit paranoid.
+ */
+ do {
+ if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
+ collect_child(wpid);
+ if (wpid == -1) {
+ if (errno == EINTR)
+ continue;
+ warning("wait for %s on %s failed: %m; going to single user mode",
+ _PATH_BSHELL, _PATH_RUNCOM);
+ return (state_func_t) single_user;
+ }
+ if (wpid == pid && WIFSTOPPED(status)) {
+ warning("init: %s on %s stopped, restarting\n",
+ _PATH_BSHELL, _PATH_RUNCOM);
+ kill(pid, SIGCONT);
+ wpid = -1;
+ }
+ } while (wpid != pid);
+
+ if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM &&
+ requested_transition == catatonia) {
+ /* /etc/rc executed /sbin/reboot; wait for the end quietly */
+ sigset_t s;
+
+ sigfillset(&s);
+ for (;;)
+ sigsuspend(&s);