+ if (! (sp = find_session(pid)))
+ return;
+
+ clear_session_logs(sp);
+ del_session(sp);
+ sp->se_process = 0;
+
+ if (sp->se_flags & SE_SHUTDOWN) {
+ if (sprev = sp->se_prev)
+ sprev->se_next = sp->se_next;
+ else
+ sessions = sp->se_next;
+ if (snext = sp->se_next)
+ snext->se_prev = sp->se_prev;
+ free_session(sp);
+ return;
+ }
+
+ if ((pid = start_getty(sp)) == -1) {
+ /* serious trouble */
+ requested_transition = clean_ttys;
+ return;
+ }
+
+ sp->se_process = pid;
+ sp->se_started = time((time_t *) 0);
+ add_session(sp);
+}
+
+/*
+ * Catch a signal and request a state transition.
+ */
+void
+transition_handler(sig)
+ int sig;
+{
+
+ switch (sig) {
+ case SIGHUP:
+ requested_transition = clean_ttys;
+ break;
+ case SIGTERM:
+ requested_transition = death;
+ break;
+ case SIGTSTP:
+ requested_transition = catatonia;
+ break;
+ default:
+ requested_transition = 0;
+ break;
+ }
+}
+
+/*
+ * Take the system multiuser.
+ */
+state_func_t
+multi_user()
+{
+ pid_t pid;
+ register session_t *sp;
+
+ requested_transition = 0;
+
+ /*
+ * If the administrator has not set the security level to -1
+ * to indicate that the kernel should not run multiuser in secure
+ * mode, and the run script has not set a higher level of security
+ * than level 1, then put the kernel into secure mode.
+ */
+ if (getsecuritylevel() == 0)
+ setsecuritylevel(1);
+
+ for (sp = sessions; sp; sp = sp->se_next) {
+ if (sp->se_process)
+ continue;
+ if ((pid = start_getty(sp)) == -1) {
+ /* serious trouble */
+ requested_transition = clean_ttys;
+ break;
+ }
+ sp->se_process = pid;
+ sp->se_started = time((time_t *) 0);
+ add_session(sp);
+ }
+
+ while (!requested_transition)
+ if ((pid = waitpid(-1, (int *) 0, 0)) != -1)
+ collect_child(pid);
+
+ return (state_func_t) requested_transition;
+}
+
+/*
+ * This is an n-squared algorithm. We hope it isn't run often...
+ */
+state_func_t
+clean_ttys()
+{
+ register session_t *sp, *sprev;
+ register struct ttyent *typ;
+ register int session_index = 0;
+ register int devlen;
+
+ if (! sessions)
+ return (state_func_t) multi_user;
+
+ devlen = sizeof(_PATH_DEV) - 1;
+ while (typ = getttyent()) {
+ ++session_index;
+
+ for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next)
+ if (strcmp(typ->ty_name, sp->se_device + devlen) == 0)
+ break;
+
+ if (sp) {
+ if (sp->se_index != session_index) {
+ warning("port %s changed utmp index from %d to %d",
+ sp->se_device, sp->se_index,
+ session_index);
+ sp->se_index = session_index;
+ }
+ if ((typ->ty_status & TTY_ON) == 0 ||
+ typ->ty_getty == 0) {
+ sp->se_flags |= SE_SHUTDOWN;
+ kill(sp->se_process, SIGHUP);
+ continue;
+ }
+ sp->se_flags &= ~SE_SHUTDOWN;
+ if (setupargv(sp, typ) == 0) {
+ warning("can't parse getty for port %s",
+ sp->se_device);
+ sp->se_flags |= SE_SHUTDOWN;
+ kill(sp->se_process, SIGHUP);
+ }
+ continue;
+ }
+
+ new_session(sprev, session_index, typ);
+ }
+
+ endttyent();
+
+ return (state_func_t) multi_user;
+}
+
+/*
+ * Block further logins.
+ */
+state_func_t
+catatonia()
+{
+ register session_t *sp;
+
+ for (sp = sessions; sp; sp = sp->se_next)
+ sp->se_flags |= SE_SHUTDOWN;
+
+ return (state_func_t) multi_user;
+}
+
+/*
+ * Note SIGALRM.
+ */
+void
+alrm_handler(sig)
+ int sig;
+{
+ clang = 1;
+}
+
+/*
+ * Bring the system down to single user.
+ */
+state_func_t
+death()
+{
+ register session_t *sp;
+ register int i;
+ pid_t pid;
+ static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL };
+
+ for (sp = sessions; sp; sp = sp->se_next)
+ sp->se_flags |= SE_SHUTDOWN;
+
+ /* NB: should send a message to the session logger to avoid blocking. */
+ logwtmp("~", "shutdown", "");
+
+ for (i = 0; i < 3; ++i) {
+ if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH)
+ return (state_func_t) single_user;
+
+ clang = 0;
+ alarm(DEATH_WATCH);
+ do
+ if ((pid = waitpid(-1, (int *)0, 0)) != -1)
+ collect_child(pid);
+ while (clang == 0 && errno != ECHILD);
+
+ if (errno == ECHILD)
+ return (state_func_t) single_user;
+ }
+
+ warning("some processes would not die; ps axl advised");
+
+ return (state_func_t) single_user;
+}