📄 init.c
字号:
/*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Donn Seeley at Berkeley Software Design, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#ifndef lintstatic char copyright[] ="@(#) Copyright (c) 1991, 1993\n\ The Regents of the University of California. All rights reserved.\n";#endif /* not lint */#ifndef lintstatic char sccsid[] = "@(#)init.c 8.1 (Berkeley) 7/15/93";#endif /* not lint */#include <sys/param.h>#include <sys/sysctl.h>#include <sys/wait.h>#include <db.h>#include <errno.h>#include <fcntl.h>#include <signal.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <syslog.h>#include <time.h>#include <ttyent.h>#include <unistd.h>#ifdef __STDC__#include <stdarg.h>#else#include <varargs.h>#endif#ifdef SECURE#include <pwd.h>#endif#include "pathnames.h"/* * Until the mythical util.h arrives... */extern int login_tty __P((int));extern int logout __P((const char *));extern void logwtmp __P((const char *, const char *, const char *));/* * Sleep times; used to prevent thrashing. */#define GETTY_SPACING 5 /* N secs minimum getty spacing */#define GETTY_SLEEP 30 /* sleep N secs after spacing problem */#define WINDOW_WAIT 3 /* wait N secs after starting window */#define STALL_TIMEOUT 30 /* wait N secs after warning */#define DEATH_WATCH 10 /* wait N secs for procs to die */void handle __P((sig_t, ...));void delset __P((sigset_t *, ...));void stall __P((char *, ...));void warning __P((char *, ...));void emergency __P((char *, ...));void disaster __P((int));void badsys __P((int));/* * We really need a recursive typedef... * The following at least guarantees that the return type of (*state_t)() * is sufficiently wide to hold a function pointer. */typedef long (*state_func_t) __P((void));typedef state_func_t (*state_t) __P((void));state_func_t single_user __P((void));state_func_t runcom __P((void));state_func_t read_ttys __P((void));state_func_t multi_user __P((void));state_func_t clean_ttys __P((void));state_func_t catatonia __P((void));state_func_t death __P((void));enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT;void transition __P((state_t));state_t requested_transition = runcom;void setctty __P((char *));typedef struct init_session { int se_index; /* index of entry in ttys file */ pid_t se_process; /* controlling process */ time_t se_started; /* used to avoid thrashing */ int se_flags; /* status of session */#define SE_SHUTDOWN 0x1 /* session won't be restarted */ char *se_device; /* filename of port */ char *se_getty; /* what to run on that port */ char **se_getty_argv; /* pre-parsed argument array */ char *se_window; /* window system (started only once) */ char **se_window_argv; /* pre-parsed argument array */ struct init_session *se_prev; struct init_session *se_next;} session_t;void free_session __P((session_t *));session_t *new_session __P((session_t *, int, struct ttyent *));session_t *sessions;char **construct_argv __P((char *));void start_window_system __P((session_t *));void collect_child __P((pid_t));pid_t start_getty __P((session_t *));void transition_handler __P((int));void alrm_handler __P((int));void setsecuritylevel __P((int));int getsecuritylevel __P((void));int setupargv __P((session_t *, struct ttyent *));int clang;void clear_session_logs __P((session_t *));int start_session_db __P((void));void add_session __P((session_t *));void del_session __P((session_t *));session_t *find_session __P((pid_t));DB *session_db;/* * The mother of all processes. */intmain(argc, argv) int argc; char **argv;{ int c; struct sigaction sa; sigset_t mask; /* Dispose of random users. */ if (getuid() != 0) { (void)fprintf(stderr, "init: %s\n", strerror(EPERM)); exit (1); } /* System V users like to reexec init. */ if (getpid() != 1) { (void)fprintf(stderr, "init: already running\n"); exit (1); } /* * Note that this does NOT open a file... * Does 'init' deserve its own facility number? */ openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); /* * Create an initial session. */ if (setsid() < 0) warning("initial setsid() failed: %m"); /* * Establish an initial user so that programs running * single user do not freak out and die (like passwd). */ if (setlogin("root") < 0) warning("setlogin() failed: %m"); /* * This code assumes that we always get arguments through flags, * never through bits set in some random machine register. */ while ((c = getopt(argc, argv, "sf")) != -1) switch (c) { case 's': requested_transition = single_user; break; case 'f': runcom_mode = FASTBOOT; break; default: warning("unrecognized flag '-%c'", c); break; } if (optind != argc) warning("ignoring excess arguments"); /* * We catch or block signals rather than ignore them, * so that they get reset on exec. */ handle(badsys, SIGSYS, 0); handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGXCPU, SIGXFSZ, 0); handle(transition_handler, SIGHUP, SIGTERM, SIGTSTP, 0); handle(alrm_handler, SIGALRM, 0); sigfillset(&mask); delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, SIGXCPU, SIGXFSZ, SIGHUP, SIGTERM, SIGTSTP, SIGALRM, 0); sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = SIG_IGN; (void) sigaction(SIGTTIN, &sa, (struct sigaction *)0); (void) sigaction(SIGTTOU, &sa, (struct sigaction *)0); /* * Paranoia. */ close(0); close(1); close(2); /* * Start the state machine. */ transition(requested_transition); /* * Should never reach here. */ return 1;}/* * Associate a function with a signal handler. */void#ifdef __STDC__handle(sig_t handler, ...)#elsehandle(va_alist) va_dcl#endif{ int sig; struct sigaction sa; int mask_everything; va_list ap;#ifndef __STDC__ sig_t handler; va_start(ap); handler = va_arg(ap, sig_t);#else va_start(ap, handler);#endif sa.sa_handler = handler; sigfillset(&mask_everything); while (sig = va_arg(ap, int)) { sa.sa_mask = mask_everything; /* XXX SA_RESTART? */ sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0; sigaction(sig, &sa, (struct sigaction *) 0); } va_end(ap);}/* * Delete a set of signals from a mask. */void#ifdef __STDC__delset(sigset_t *maskp, ...)#elsedelset(va_alist) va_dcl#endif{ int sig; va_list ap;#ifndef __STDC__ sigset_t *maskp; va_start(ap); maskp = va_arg(ap, sigset_t *);#else va_start(ap, maskp);#endif while (sig = va_arg(ap, int)) sigdelset(maskp, sig); va_end(ap);}/* * Log a message and sleep for a while (to give someone an opportunity * to read it and to save log or hardcopy output if the problem is chronic). * NB: should send a message to the session logger to avoid blocking. */void#ifdef __STDC__stall(char *message, ...)#elsestall(va_alist) va_dcl#endif{ va_list ap;#ifndef __STDC__ char *message; va_start(ap); message = va_arg(ap, char *);#else va_start(ap, message);#endif vsyslog(LOG_ALERT, message, ap); va_end(ap); sleep(STALL_TIMEOUT);}/* * Like stall(), but doesn't sleep. * If cpp had variadic macros, the two functions could be #defines for another. * NB: should send a message to the session logger to avoid blocking. */void#ifdef __STDC__warning(char *message, ...)#elsewarning(va_alist) va_dcl#endif{ va_list ap;#ifndef __STDC__ char *message; va_start(ap); message = va_arg(ap, char *);#else va_start(ap, message);#endif vsyslog(LOG_ALERT, message, ap); va_end(ap);}/* * Log an emergency message. * NB: should send a message to the session logger to avoid blocking. */void#ifdef __STDC__emergency(char *message, ...)#elseemergency(va_alist) va_dcl#endif{ va_list ap;#ifndef __STDC__ char *message; va_start(ap); message = va_arg(ap, char *);#else va_start(ap, message);#endif vsyslog(LOG_EMERG, message, ap); va_end(ap);}/* * Catch a SIGSYS signal. * * These may arise if a system does not support sysctl. * We tolerate up to 25 of these, then throw in the towel. */voidbadsys(sig) int sig;{ static int badcount = 0; if (badcount++ < 25) return; disaster(sig);}/* * Catch an unexpected signal. */voiddisaster(sig) int sig;{ emergency("fatal signal: %s", sig < (unsigned) NSIG ? sys_siglist[sig] : "unknown signal"); sleep(STALL_TIMEOUT); _exit(sig); /* reboot */}/* * Get the security level of the kernel. */intgetsecuritylevel(){#ifdef KERN_SECURELVL int name[2], curlevel; size_t len; extern int errno; name[0] = CTL_KERN; name[1] = KERN_SECURELVL; len = sizeof curlevel; if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) { emergency("cannot get kernel security level: %s", strerror(errno)); return (-1); } return (curlevel);#else return (-1);#endif}/* * Set the security level of the kernel. */voidsetsecuritylevel(newlevel) int newlevel;{#ifdef KERN_SECURELVL int name[2], curlevel; extern int errno; curlevel = getsecuritylevel(); if (newlevel == curlevel) return; name[0] = CTL_KERN; name[1] = KERN_SECURELVL; if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) { emergency( "cannot change kernel security level from %d to %d: %s", curlevel, newlevel, strerror(errno)); return; }#ifdef SECURE warning("kernel security level changed from %d to %d", curlevel, newlevel);#endif#endif}/* * Change states in the finite state machine. * The initial state is passed as an argument. */voidtransition(s) state_t s;{ for (;;) s = (state_t) (*s)();}/* * Close out the accounting files for a login session. * NB: should send a message to the session logger to avoid blocking. */voidclear_session_logs(sp) session_t *sp;{ char *line = sp->se_device + sizeof(_PATH_DEV) - 1; if (logout(line)) logwtmp(line, "", "");}/* * Start a session and allocate a controlling terminal. * Only called by children of init after forking. */voidsetctty(name) char *name;{ int fd; (void) revoke(name); sleep (2); /* leave DTR low */ if ((fd = open(name, O_RDWR)) == -1) { stall("can't open %s: %m", name); _exit(1); } if (login_tty(fd) == -1) { stall("can't get %s for controlling terminal: %m", name); _exit(1); }}/* * Bring the system up single user. */state_func_tsingle_user(){ pid_t pid, wpid; int status; sigset_t mask; char *shell = _PATH_BSHELL; char *argv[2];#ifdef SECURE struct ttyent *typ; struct passwd *pp; static const char banner[] = "Enter root password, or ^D to go multi-user\n"; char *clear, *password;#endif /* * 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); bzero(clear, _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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -