char *shell = _PATH_BSHELL;
char shell[MAXPATHLEN];		 char name[MAXPATHLEN];
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;#ifndef LETS_GET_SMALL	/* 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);#endif /* LETS_GET_SMALL */	/*	 * 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");#ifndef LETS_GET_SMALL	/*	 * 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");#else /* LETS_GET_SMALL */	requested_transition = single_user;#endif /* LETS_GET_SMALL */	/*	 * 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", strsignal(sig));	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);				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;		setenv("PATH", _PATH_STDPATH, 1);		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;#ifndef LETS_GET_SMALL	return (state_func_t) runcom;#else /* LETS_GET_SMALL */	return (state_func_t) single_user;#endif /* LETS_GET_SMALL */}#ifndef LETS_GET_SMALL/* * 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;	memcpy(&ret, data.data, 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;}