📄 init.c
字号:
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_truncom(){ 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); } if (!WIFEXITED(status)) { warning("%s on %s terminated abnormally, going to single user mode", _PATH_BSHELL, _PATH_RUNCOM); return (state_func_t) single_user; } if (WEXITSTATUS(status)) return (state_func_t) single_user; runcom_mode = AUTOBOOT; /* the default */ /* NB: should send a message to the session logger to avoid blocking. */ logwtmp("~", "reboot", ""); return (state_func_t) read_ttys;}/* * Open the session database. * * NB: We could pass in the size here; is it necessary? */intstart_session_db(){ if (session_db && (*session_db->close)(session_db)) emergency("session database close: %s", strerror(errno)); if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) { emergency("session database open: %s", strerror(errno)); return (1); } return (0); }/* * Add a new login session. */voidadd_session(sp) session_t *sp;{ DBT key; DBT data; key.data = &sp->se_process; key.size = sizeof sp->se_process; data.data = &sp; data.size = sizeof sp; if ((*session_db->put)(session_db, &key, &data, 0)) emergency("insert %d: %s", sp->se_process, strerror(errno));}/* * Delete an old login session. */voiddel_session(sp) session_t *sp;{ DBT key; key.data = &sp->se_process; key.size = sizeof sp->se_process; if ((*session_db->del)(session_db, &key, 0)) emergency("delete %d: %s", sp->se_process, strerror(errno));}/* * Look up a login session by pid. */session_t *#ifdef __STDC__find_session(pid_t pid)#elsefind_session(pid) pid_t pid;#endif{ DBT key; DBT data; session_t *ret; key.data = &pid; key.size = sizeof pid; if ((*session_db->get)(session_db, &key, &data, 0) != 0) return 0; bcopy(data.data, (char *)&ret, sizeof(ret)); return ret;}/* * Construct an argument vector from a command line. */char **construct_argv(command) char *command;{ register int argc = 0; register char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1) * sizeof (char *)); static const char separators[] = " \t"; if ((argv[argc++] = strtok(command, separators)) == 0) return 0; while (argv[argc++] = strtok((char *) 0, separators)) continue; return argv;}/* * Deallocate a session descriptor. */voidfree_session(sp) register session_t *sp;{ free(sp->se_device); if (sp->se_getty) { free(sp->se_getty); free(sp->se_getty_argv); } if (sp->se_window) { free(sp->se_window); free(sp->se_window_argv); } free(sp);}/* * Allocate a new session descriptor. */session_t *new_session(sprev, session_index, typ) session_t *sprev; int session_index; register struct ttyent *typ;{ register session_t *sp; if ((typ->ty_status & TTY_ON) == 0 || typ->ty_name == 0 || typ->ty_getty == 0) return 0; sp = (session_t *) malloc(sizeof (session_t)); bzero(sp, sizeof *sp); sp->se_index = session_index; sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name)); (void) sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name); if (setupargv(sp, typ) == 0) { free_session(sp); return (0); } sp->se_next = 0; if (sprev == 0) { sessions = sp; sp->se_prev = 0; } else { sprev->se_next = sp; sp->se_prev = sprev; } return sp;}/* * Calculate getty and if useful window argv vectors. */intsetupargv(sp, typ) session_t *sp; struct ttyent *typ;{ if (sp->se_getty) { free(sp->se_getty); free(sp->se_getty_argv); } sp->se_getty = malloc(strlen(typ->ty_getty) + strlen(typ->ty_name) + 2); (void) sprintf(sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name); sp->se_getty_argv = construct_argv(sp->se_getty); if (sp->se_getty_argv == 0) { warning("can't parse getty for port %s", sp->se_device); free(sp->se_getty); sp->se_getty = 0; return (0); } if (typ->ty_window) { if (sp->se_window) free(sp->se_window); sp->se_window = strdup(typ->ty_window); sp->se_window_argv = construct_argv(sp->se_window); if (sp->se_window_argv == 0) { warning("can't parse window for port %s", sp->se_device); free(sp->se_window); sp->se_window = 0; return (0); } } return (1);}/* * Walk the list of ttys and create sessions for each active line. */state_func_tread_ttys(){ int session_index = 0; register session_t *sp, *snext; register struct ttyent *typ; /* * Destroy any previous session state. * There shouldn't be any, but just in case... */ for (sp = sessions; sp; sp = snext) { if (sp->se_process) clear_session_logs(sp); snext = sp->se_next; free_session(sp); } sessions = 0; if (start_session_db()) return (state_func_t) single_user; /* * Allocate a session entry for each active port. * Note that sp starts at 0. */ while (typ = getttyent()) if (snext = new_session(sp, ++session_index, typ)) sp = snext; endttyent(); return (state_func_t) multi_user;}/* * Start a window system running. */voidstart_window_system(sp) session_t *sp;{ pid_t pid; sigset_t mask; if ((pid = fork()) == -1) { emergency("can't fork for window system on port %s: %m", sp->se_device); /* hope that getty fails and we can try again */ return; } if (pid) return; sigemptyset(&mask); sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); if (setsid() < 0) emergency("setsid failed (window) %m"); execv(sp->se_window_argv[0], sp->se_window_argv); stall("can't exec window system '%s' for port %s: %m", sp->se_window_argv[0], sp->se_device); _exit(1);}/* * Start a login session running. */pid_tstart_getty(sp) session_t *sp;{ pid_t pid; sigset_t mask; time_t current_time = time((time_t *) 0); /* * fork(), not vfork() -- we can't afford to block. */ if ((pid = fork()) == -1) { emergency("can't fork for getty on port %s: %m", sp->se_device); return -1; } if (pid) return pid; if (current_time > sp->se_started && current_time - sp->se_started < GETTY_SPACING) { warning("getty repeating too quickly on port %s, sleeping", sp->se_device); sleep((unsigned) GETTY_SLEEP); } if (sp->se_window) { start_window_system(sp); sleep(WINDOW_WAIT); } sigemptyset(&mask); sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); execv(sp->se_getty_argv[0], sp->se_getty_argv); stall("can't exec getty '%s' for port %s: %m", sp->se_getty_argv[0], sp->se_device); _exit(1);}/* * Collect exit status for a child. * If an exiting login, start a new login running. */void#ifdef __STDC__collect_child(pid_t pid)#elsecollect_child(pid) pid_t pid;#endif{ register session_t *sp, *sprev, *snext; if (! sessions) return; 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. */voidtransition_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_tmulti_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_tclean_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_tcatatonia(){ 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. */voidalrm_handler(sig) int sig;{ clang = 1;}/* * Bring the system down to single user. */state_func_tdeath(){ 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;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -