1/*-
2 * Copyright (c) 1998 Andrzej Bialecki
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD$
27 */
28
29/*
30 * A primitive version of init(8) with simplistic user interface
31 */
32
33#include <sys/types.h>
34#include <sys/param.h>
35#include <sys/mount.h>
36#include <sys/reboot.h>
37#include <sys/time.h>
38#include <sys/resource.h>
39#include <sys/wait.h>
40#include <ctype.h>
41#include <err.h>
42
43#ifdef USE_HISTORY
44#error "Not yet. But it's quite simple to add - patches are welcome!"
45#endif
46
47#include <errno.h>
48#include <fcntl.h>
49#include <libutil.h>
50#include <paths.h>
51#include <setjmp.h>
52#include <signal.h>
53#include <stdio.h>
54#include <string.h>
55#include <syslog.h>
56#include <unistd.h>
57#include <varargs.h>
58
59#define BUFSIZE 1024
60#define MAX_CONS 12
61
62#define	NONE	0
63#define	SINGLE	1
64#define	MULTI	2
65#define	DEATH	3
66
67#define	FALSE	0
68#define TRUE	1
69
70char cwd[BUFSIZE];
71char vty[]="0123456789abcdef";
72char *progname;
73char *motd=NULL;
74int ncons=MAX_CONS;
75int Reboot=FALSE;
76int transition=MULTI;
77int prevtrans=SINGLE;
78jmp_buf machine;
79
80char *trans[]={ "NONE", "SINGLE", "MULTI", "DEATH" };
81
82extern char **environ;
83
84/* Struct for holding session state */
85struct sess {
86	char tty[16];	/* vty device path */
87	pid_t pid;	/* pid of process running on it */
88	int (*func)(int argc, char **argv);
89			/* internal function to run on it (after forking) */
90} ttys[MAX_CONS];
91
92/* Struct for built-in command */
93struct command {
94	char *cmd;		/* command name */
95	char *descr;		/* command description */
96	char *usage;		/* usage */
97	char *example;		/* example of usage */
98	int (*func)(char *);	/* callback function */
99};
100
101/* Prototypes */
102int cd(char *);
103int pwd(char *);
104int echo(char *);
105int xit(char *);
106int set(char *);
107int unset(char *);
108int env(char *);
109int help(char *);
110int sourcer(char *);
111void do_command(int shell, char *cmdline);
112void transition_handler(int);
113
114/* Table of built-in functions */
115struct command bltins[]={
116	{"cd","Change working directory","cd [dir]","cd /etc",cd},
117	{"pwd","Print current directory","pwd","pwd",pwd},
118	{"exit","Exit from shell()","exit","exit",xit},
119	{"set","Set environment variable","set [VAR=value]","set TERM=xterm",set},
120	{"unset","Unset environment variable","unset VAR","unset EDITOR",unset},
121	{"echo","Echo arguments on stdout","echo arg1 arg2 ...","echo Hello World!",echo},
122	{"env","Print all environment variables","env","env",env},
123	{".","Source-in a file with commands",". filename",". /etc/rc",sourcer},
124	{"?","Print this help :-)","? [command]","? set",help},
125	{NULL,NULL,NULL,NULL,NULL}
126};
127
128/*
129 * Built-in 'cd <path>' handler
130 */
131int
132cd(char *path)
133{
134	if(chdir(path)) return(-1);
135	getcwd(cwd,BUFSIZE);
136	return(0);
137}
138
139/*
140 * Built-in 'pwd' handler
141 */
142int
143pwd(char *dummy)
144{
145
146	if(getcwd(cwd,BUFSIZE)==NULL) return(-1);
147	printf("%s\n",cwd);
148	return(0);
149}
150
151/*
152 * Built-in 'exit' handler
153 */
154int
155xit(char *dummy)
156{
157	_exit(0);
158}
159
160/*
161 * Built-in 'echo' handler
162 */
163int
164echo(char *args)
165{
166	int i=0,j;
167	int len;
168	char c;
169	int s_quote=0,d_quote=0;
170	int sep=0,no_lf=0;
171
172	if(args==NULL) {
173		printf("\n");
174		return;
175	}
176	len=strlen(args);
177	if(len>=2) {
178		if(args[0]=='-' && args[1]=='n') {
179			no_lf++;
180			i=2;
181			while(i<len && (args[i]==' ' || args[i]=='\t')) i++;
182		}
183	}
184	while(i<len) {
185		c=args[i];
186		switch(c) {
187		case ' ':
188		case '\t':
189			if(s_quote||d_quote) {
190				putchar(c);
191			} else if(!sep) {
192				putchar(' ');
193				sep=1;
194			}
195			break;
196		case '\\':
197			i++;
198			c=args[i];
199			switch(c) {
200			case 'n':
201				putchar('\n');
202				break;
203			case 'b':
204				putchar('\b');
205				break;
206			case 't':
207				putchar('\t');
208				break;
209			case 'r':
210				putchar('\r');
211				break;
212			default:
213				putchar(c);
214				break;
215			}
216			break;
217		case '"':
218			if(!d_quote) {
219				d_quote=1;
220				for(j=i+1;j<len;j++) {
221					if(args[j]=='\\') {
222						j++;
223						continue;
224					}
225					if(args[j]=='"') {
226						d_quote=2;
227						break;
228					}
229				}
230				if(d_quote!=2) {
231					printf("\necho(): unmatched \"\n");
232					return;
233				}
234			} else d_quote=0;
235			break;
236		case '\'':
237			if(!s_quote) {
238				s_quote=1;
239				for(j=i+1;j<len;j++) {
240					if(args[j]=='\\') {
241						j++;
242						continue;
243					}
244					if(args[j]=='\'') {
245						s_quote=2;
246						break;
247					}
248				}
249				if(s_quote!=2) {
250					printf("\necho(): unmatched '\n");
251					return;
252				}
253			} else s_quote=0;
254			break;
255		case '`':
256			printf("echo(): backquote not implemented yet!\n");
257			break;
258		default:
259			sep=0;
260			putchar(c);
261			break;
262		}
263		i++;
264	}
265	if(!no_lf) putchar('\n');
266	fflush(stdout);
267}
268
269/*
270 * Built-in 'set VAR=value' handler
271 */
272int
273set(char *var)
274{
275	int res;
276
277	if(var==NULL) return(env(NULL));
278	res=putenv(var);
279	if(res) printf("set: %s\n",strerror(errno));
280	return(res);
281}
282
283/*
284 * Built-in 'env' handler
285 */
286int
287env(char *dummy)
288{
289	char **e;
290
291	e=environ;
292	while(*e!=NULL) {
293		printf("%s\n",*e++);
294	}
295	return(0);
296}
297
298/*
299 * Built-in 'unset VAR' handler
300 */
301int
302unset(char *var)
303{
304	if(var==NULL) {
305		printf("%s: parameter required.\n",progname);
306		return(-1);
307	}
308	return(unsetenv(var));
309}
310
311/*
312 * Built-in '?' handler
313 */
314int
315help(char *cmd)
316{
317	struct command *x;
318	int found=0;
319
320	if(cmd==NULL) {
321		printf("\nBuilt-in commands:\n");
322		printf("-------------------\n");
323		x=bltins;
324		while(x->cmd!=NULL) {
325			printf("%s\t%s\n",x->cmd,x->descr);
326			x++;
327		}
328		printf("\nEnter '? <cmd>' for details.\n\n");
329		return(0);
330	} else {
331		x=bltins;
332		while(x->cmd!=NULL) {
333			if(strcmp(x->cmd,cmd)==0) {
334				found++;
335				break;
336			}
337			x++;
338		}
339		if(found) {
340			printf("\n%s\t%s:\n",x->cmd,x->descr);
341			printf("\tUsage:\n\t\t%s\n",x->usage);
342			printf("\te.g:\n\t\t%s\n\n",x->example);
343			return(0);
344		} else {
345			printf("\n%s: no such command.\n\n",cmd);
346			return(-1);
347		}
348	}
349}
350
351/*
352 * Signal handler for shell()
353 */
354void
355shell_sig(int sig)
356{
357	switch(sig) {
358	case SIGINT:
359	case SIGQUIT:
360	case SIGTERM:
361		/* ignore ? */
362		break;
363	default:
364		break;
365	}
366}
367
368/*
369 * Built-in '.' handler (read-in and execute commands from file)
370 */
371int
372sourcer(char *fname)
373{
374	FILE *fd;
375	char buf[512],*tok,*arg,**av;
376	int ac,len,f,res,i;
377	pid_t pid;
378	char *sep=" \t";
379
380	fd=fopen(fname,"r");
381	if(fd==NULL) {
382		printf("Couldn't open file '%s'\n",fname);
383		return(-1);
384	}
385	while(!feof(fd)) {
386		memset(buf,0,512);
387		if(fgets(buf,512,fd)==NULL) continue;
388		if((*buf=='#') || (*buf=='\n')) continue;
389		len=strlen(buf);
390		buf[len-1]='\0';
391		if(strncmp(buf,"ncons",5)==0) {
392			tok=strtok(buf,sep);
393			tok=strtok(NULL,sep);
394			ncons=atoi(tok);
395			if((ncons<1)||(ncons>MAX_CONS)) {
396				syslog(LOG_EMERG,"%s: bad ncons value; defaulting to %d\n",fname,MAX_CONS);
397				ncons=MAX_CONS;
398			}
399			continue;
400		} else if(strncmp(buf,"motd",4)==0) {
401			tok=strtok(buf,sep);
402			motd=strdup(strtok(NULL,sep));
403			continue;
404		} else {
405			do_command(0,buf);
406		}
407		/* Next command, please. */
408	}
409	fclose(fd);
410	syslog(LOG_EMERG,"Done with %s",fname);
411}
412
413void
414do_command(int shell, char *cmdline)
415{
416	char *tok,*c,*sep=" \t";
417	char **av;
418	struct command *x;
419	int found,len;
420	int ac,i,f,res;
421	int bg=0;
422	pid_t pid;
423
424	len=strlen(cmdline);
425	if(cmdline[len-1]=='&') {
426		bg++;
427		cmdline[len-1]='\0';
428		len--;
429	} else bg=0;
430	tok=strtok(cmdline,sep);
431	x=bltins;
432	found=0;
433	while(x->cmd!=NULL) {
434		if(strcmp(x->cmd,tok)==0) {
435			found++;
436			break;
437		}
438		x++;
439	}
440	if(found) {
441		tok=cmdline+strlen(x->cmd)+1;
442		while(*tok && isblank(*tok) && (tok<(cmdline+len))) tok++;
443		if(*tok==NULL) tok=NULL;
444		x->func(tok);
445		return;
446	}
447	ac=0;
448	av=(char **)calloc(((len+1)/2+1),sizeof(char *));
449	av[ac++]=tok;
450	while((av[ac++]=strtok(NULL,sep))!=NULL)
451		continue;
452	switch((pid=fork())) {
453	case 0:
454		if(shell) {
455			signal(SIGINT,SIG_DFL);
456			signal(SIGQUIT,SIG_DFL);
457			signal(SIGTERM,SIG_DFL);
458			signal(SIGHUP,SIG_DFL);
459		} else {
460			close(0);
461			close(1);
462			close(2);
463			f=open(_PATH_CONSOLE,O_RDWR);
464			dup2(f,0);
465			dup2(f,1);
466			dup2(f,2);
467			if(f>2) close(f);
468		}
469		if(bg) {
470			if(daemon(0,0)) {
471				printf("do_command(%s): failed to run bg: %s\n",
472				av[0],strerror(errno));
473				_exit(100);
474			}
475		}
476		execvp(av[0],av);
477		/* Something went wrong... */
478		printf("do_command(%s): %s\n",av[0],strerror(errno));
479		_exit(100);
480		break;
481	case -1:
482		printf("do_command(): %s\n",strerror(errno));
483		break;
484	default:
485		while(waitpid(pid,&res,0)!=pid) continue;
486		if(WEXITSTATUS(res)) {
487			printf("do_command(%s): exit code=%d\n",
488				av[0],WEXITSTATUS(res));
489		}
490		break;
491	}
492	free(av);
493	return;
494}
495
496/*
497 * This is the user interface. This routine gets executed on each
498 * virtual console serviced by init.
499 *
500 * It works as normal shell does - for each external command it forks
501 * and execs, for each internal command just executes a function.
502 */
503
504int
505shell(int argc, char **argv)
506{
507	char buf[BUFSIZE];
508	char *prompt=" # ";
509	int fd;
510	int res;
511	pid_t mypid;
512
513	if(motd!=NULL) {
514		if((fd=open(motd,O_RDONLY))!=-1) {
515			do {
516				res=read(fd,buf,BUFSIZE);
517				res=write(1,buf,res);
518			} while(res>0);
519			close(fd);
520		}
521	}
522
523	printf("\n\n+=========================================================+\n");
524	printf("| Built-in shell() (enter '?' for short help on commands) |\n");
525	printf("+=========================================================+\n\n");
526	getcwd(cwd,BUFSIZE);
527	mypid=getpid();
528	signal(SIGINT,shell_sig);
529	signal(SIGQUIT,shell_sig);
530	signal(SIGTERM,shell_sig);
531	while(!feof(stdin)) {
532		memset(buf,0,BUFSIZE);
533		printf("(%d)%s%s",mypid,cwd,prompt);
534		fflush(stdout);
535		if(fgets(buf,BUFSIZE-1,stdin)==NULL) continue;
536		buf[strlen(buf)-1]='\0';
537		if(strlen(buf)==0) continue;
538		do_command(1,buf);
539	}
540	return(0);
541}
542
543/*
544 * Stub for executing some external program on a console. This is called
545 * from previously forked copy of our process, so that exec is ok.
546 */
547int
548external_cmd(int argc, char **argv)
549{
550	execvp(argv[0],argv);
551}
552
553/*
554 * Acquire vty and properly attach ourselves to it.
555 * Also, build basic environment for running user interface.
556 */
557
558int
559start_session(int vty, int argc, char **argv)
560{
561	int fd;
562	char *t;
563
564	close(0);
565	close(1);
566	close(2);
567	revoke(ttys[vty].tty);
568	fd=open(ttys[vty].tty,O_RDWR);
569	dup2(fd,0);
570	dup2(fd,1);
571	dup2(fd,2);
572	if(fd>2) close(fd);
573	login_tty(fd);
574	setpgid(0,getpid());
575	putenv("TERM=xterm");
576	putenv("HOME=/");
577	putenv("PATH=/stand:/bin:/usr/bin:/sbin:.");
578	signal(SIGHUP,SIG_DFL);
579	signal(SIGINT,SIG_DFL);
580	signal(SIGQUIT,SIG_DFL);
581	signal(SIGTERM,SIG_DFL);
582	chdir("/");
583	t=(char *)(rindex(ttys[vty].tty,'/')+1);
584	printf("\n\n\nStarting session on %s.\n",t);
585	ttys[vty].func(argc,argv);
586	_exit(0);
587}
588
589/*
590 * Execute system startup script /etc/rc
591 *
592 * (Of course if you don't like it - I don't - you can run anything you
593 * want here. Perhaps it would be useful just to read some config DB and
594 * do these things ourselves, avoiding forking lots of shells and scripts.)
595 */
596
597/* If OINIT_RC is defined, oinit will use it's own configuration file,
598 * /etc/oinit.rc. It's format is described below. Otherwise, it will use
599 * normal /etc/rc interpreted by Bourne shell.
600 */
601#ifndef OINIT_RC
602#ifndef SH_NAME
603#define SH_NAME	"-sh"
604#endif
605#ifndef SH_PATH
606#define SH_PATH	_PATH_BSHELL
607#endif
608#ifndef SH_ARG
609#define SH_ARG	"/etc/rc"
610#endif
611void
612runcom()
613{
614	char *argv[3];
615	pid_t pid;
616	int st;
617	int fd;
618
619	if((pid=fork())==0) {
620		/* child */
621		close(0);
622		close(1);
623		close(2);
624		fd=open(_PATH_CONSOLE,O_RDWR);
625		dup2(fd,0);
626		dup2(fd,1);
627		dup2(fd,2);
628		if(fd>2) close(fd);
629		argv[0]=SH_NAME;
630		argv[1]=SH_ARG;
631		argv[2]=0;
632		execvp(SH_PATH,argv);
633		printf("runcom(): %s\n",strerror(errno));
634		_exit(1);
635	}
636	/* Wait for child to exit */
637	while(pid!=waitpid(pid,(int *)0,0)) continue;
638	return;
639}
640#else
641/* Alternative /etc/rc - default is /etc/oinit.rc. Its format is as follows:
642 * - each empty line or line beginning with a '#' is discarded
643 * - any other line must contain a keyword, or a (nonblocking) command to run.
644 *
645 * Thus far, the following keywords are defined:
646 * ncons <number>	number of virtual consoles to open
647 * motd <pathname>	full path to motd file
648 *
649 * Examples of commands to run:
650 *
651 * ifconfig lo0 inet 127.0.0.1 netmask 255.0.0.0
652 * ifconfig ed0 inet 148.81.168.10 netmask 255.255.255.0
653 * kbdcontrol -l /usr/share/syscons/my_map.kbd
654 */
655void
656runcom(char *fname)
657{
658	int fd;
659
660	close(0);
661	close(1);
662	close(2);
663	fd=open(_PATH_CONSOLE,O_RDWR);
664	dup2(fd,0);
665	dup2(fd,1);
666	dup2(fd,2);
667	if(fd>2) close(fd);
668	sourcer(fname);
669}
670#endif
671
672int
673run_multi()
674{
675	int i,j;
676	pid_t pid;
677	int found;
678
679	/* Run /etc/rc if not in single user */
680#ifndef OINIT_RC
681	if(prevtrans==SINGLE) runcom();
682#else
683	if(prevtrans==SINGLE) runcom(OINIT_RC);
684#endif
685	if(transition!=MULTI) return(-1);
686
687	syslog(LOG_EMERG,"*** Starting multi-user mode ***");
688
689	/* Fork shell interface for each console */
690	for(i=0;i<ncons;i++) {
691		if(ttys[i].pid==0) {
692			switch(pid=fork()) {
693			case 0:
694				start_session(i,0,NULL);
695				break;
696			case -1:
697				printf("%s: %s\n",progname,strerror(errno));
698				break;
699			default:
700				ttys[i].pid=pid;
701				break;
702			}
703		}
704	}
705	/* Initialize any other services we'll use - most probably this will
706	 * be a 'telnet' service (some day...).
707	 */
708	/* */
709
710	/* Emulate multi-user */
711	while(transition==MULTI) {
712		/* XXX Modify this to allow for checking for the input on
713		 * XXX listening sockets, and forking a 'telnet' service.
714		 */
715		/* */
716
717		/* Wait for any process to exit */
718		pid=waitpid(-1,(int *)0,0);
719		if(pid==-1) continue;
720		found=0;
721		j=-1;
722		/* search if it's one of our sessions */
723		for(i=0;i<ncons;i++) {
724			if(ttys[i].pid==pid) {
725				found++;
726				j=i;
727				ttys[j].pid=0;
728				break;
729			}
730		}
731		if(!found) {
732			/* Just collect the process's status */
733			continue;
734		} else {
735			/* restart shell() on a console, if it died */
736			if(transition!=MULTI) return(0);
737			switch(pid=fork()) {
738			case 0:
739				sleep(1);
740				start_session(j,0,NULL);
741				break;
742			case -1:
743				printf("%s: %s\n",progname,strerror(errno));
744				break;
745			default:
746				ttys[j].pid=pid;
747				break;
748			}
749		}
750	}
751}
752
753int clang;
754
755void
756kill_timer(int sig)
757{
758	clang=1;
759}
760
761kill_ttys()
762{
763}
764
765/*
766 * Start a shell on ttyv0 (i.e. the console).
767 */
768
769int
770run_single()
771{
772	int i;
773	pid_t pid,wpid;
774	static int sigs[2]={SIGTERM,SIGKILL};
775
776	syslog(LOG_EMERG,"*** Starting single-user mode ***");
777	/* Kill all existing sessions */
778	syslog(LOG_EMERG,"Killing all existing sessions...");
779	for(i=0;i<MAX_CONS;i++) {
780		kill(ttys[i].pid,SIGHUP);
781		ttys[i].pid=0;
782	}
783	for(i=0;i<2;i++) {
784		if(kill(-1,sigs[i])==-1 && errno==ESRCH) break;
785		clang=0;
786		alarm(10);
787		do {
788			pid=waitpid(-1,(int *)0,WUNTRACED);
789			if(errno==EINTR) continue;
790			else break;
791		} while (clang==0);
792	}
793	if(errno!=ECHILD) {
794		syslog(LOG_EMERG,"Some processes would not die; ps -axl advised");
795	}
796	/* Single-user */
797	switch(pid=fork()) {
798	case 0:
799		start_session(0,0,NULL);
800		break;
801	case -1:
802		printf("%s: %s\n",progname,strerror(errno));
803		printf("The system is seriously hosed. I'm dying...\n");
804		transition=DEATH;
805		return(-1);
806		break;
807	default:
808		do {
809			wpid=waitpid(pid,(int *)0,WUNTRACED);
810		} while(wpid!=pid && transition==SINGLE);
811		if(transition!=DEATH) {
812			prevtrans=transition;
813			transition=MULTI;
814		}
815		break;
816	}
817	return(0);
818}
819
820/*
821 * Transition handler - installed as signal handler.
822 */
823
824void
825transition_handler(int sig)
826{
827
828	switch(sig) {
829	case SIGHUP:
830	case SIGTERM:
831		prevtrans=transition;
832		transition=SINGLE;
833		syslog(LOG_EMERG,"*** Going from %s -> %s\n",trans[prevtrans],trans[transition]);
834		if(prevtrans!=transition) longjmp(machine,sig);
835		break;
836	case SIGINT:
837	case SIGQUIT:
838		prevtrans=transition;
839		transition=DEATH;
840		syslog(LOG_EMERG,"*** Going from %s -> %s\n",trans[prevtrans],trans[transition]);
841		if(prevtrans!=transition) longjmp(machine,sig);
842		break;
843	default:
844		syslog(LOG_EMERG,"pid=%d sig=%s (ignored)\n",getpid(),sys_siglist[sig]);
845		break;
846	}
847}
848
849/*
850 * Change system state appropriately to the signals
851 */
852
853int
854transition_machine()
855{
856	int i;
857
858	while(transition!=DEATH) {
859		switch(transition) {
860		case MULTI:
861			run_multi();
862			break;
863		case SINGLE:
864			run_single();
865			break;
866		}
867	}
868	syslog(LOG_EMERG,"Killing all existing sessions...");
869	/* Kill all sessions */
870	kill(-1,SIGKILL);
871	/* Be nice and wait for them */
872	while(waitpid(-1,(int *)0,WNOHANG|WUNTRACED)>0) continue;
873	unmount("/",0);
874	reboot(RB_AUTOBOOT);
875	/* NOTREACHED */
876}
877
878int
879main(int argc, char **argv)
880{
881	int devfs=0,c,i;
882
883	/* These are copied from real init(8) */
884	if(getuid()!=0)
885		errx(1,"%s",strerror(EPERM));
886	openlog("init",LOG_CONS|LOG_ODELAY,LOG_AUTH);
887	if(setsid()<0)
888		warn("initial setsid() failed");
889	if(setlogin("root")<0)
890		warn("setlogin() failed");
891
892	close(0);
893	close(1);
894	close(2);
895	chdir("/");
896
897	progname=rindex(argv[0],'/');
898	if(progname==NULL) {
899		progname=argv[0];
900	} else progname++;
901
902	transition=MULTI;
903
904	/* We must recognize the same options as real init does */
905	while((c=getopt(argc,argv,"dsf"))!=-1) {
906		switch(c) {
907		case 'd':
908			devfs=1;
909			break;
910		case 's':
911			transition=SINGLE;
912			break;
913		case 'f':
914			break;
915		default:
916			printf("%s: unrecognized flag '-%c'\n",progname,c);
917			break;
918		}
919	}
920	if(devfs)
921		mount("devfs",_PATH_DEV,MNT_NOEXEC|MNT_RDONLY,0);
922
923	/* Fill in the sess structures. */
924	/* XXX Really, should be filled based upon config file. */
925	for(i=0;i<MAX_CONS;i++) {
926		if(i==0) {
927			sprintf(ttys[i].tty,_PATH_CONSOLE);
928		} else {
929			sprintf(ttys[i].tty,"%sv%c",_PATH_TTY,vty[i]);
930		}
931		ttys[i].pid=0;
932		ttys[i].func=shell;
933	}
934
935	getcwd(cwd,BUFSIZE);
936
937	signal(SIGINT,transition_handler);
938	signal(SIGQUIT,transition_handler);
939	signal(SIGTERM,transition_handler);
940	signal(SIGHUP,transition_handler);
941	signal(SIGALRM,kill_timer);
942
943	setjmp(machine);
944	transition_machine(transition);
945	/* NOTREACHED */
946	exit(100);
947}
948