138589Sabial/*-
238589Sabial * Copyright (c) 1998 Andrzej Bialecki
338589Sabial * All rights reserved.
438589Sabial *
538589Sabial * Redistribution and use in source and binary forms, with or without
638589Sabial * modification, are permitted provided that the following conditions
738589Sabial * are met:
838589Sabial * 1. Redistributions of source code must retain the above copyright
938589Sabial *    notice, this list of conditions and the following disclaimer.
1038589Sabial * 2. Redistributions in binary form must reproduce the above copyright
1138589Sabial *    notice, this list of conditions and the following disclaimer in the
1238589Sabial *    documentation and/or other materials provided with the distribution.
1338589Sabial *
1438589Sabial * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1538589Sabial * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1638589Sabial * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1738589Sabial * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1838589Sabial * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1938589Sabial * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2038589Sabial * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2138589Sabial * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2238589Sabial * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2338589Sabial * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2438589Sabial * SUCH DAMAGE.
2538589Sabial *
2650479Speter * $FreeBSD$
2738589Sabial */
2838589Sabial
2938589Sabial/*
3038589Sabial * A primitive version of init(8) with simplistic user interface
3138589Sabial */
3238589Sabial
3338589Sabial#include <sys/types.h>
3438589Sabial#include <sys/param.h>
3538589Sabial#include <sys/mount.h>
3638589Sabial#include <sys/reboot.h>
3738589Sabial#include <sys/time.h>
3838589Sabial#include <sys/resource.h>
3938589Sabial#include <sys/wait.h>
4038589Sabial#include <ctype.h>
4138589Sabial#include <err.h>
4238589Sabial
4338589Sabial#ifdef USE_HISTORY
4438589Sabial#error "Not yet. But it's quite simple to add - patches are welcome!"
4538589Sabial#endif
4638589Sabial
4738589Sabial#include <errno.h>
4838589Sabial#include <fcntl.h>
4938589Sabial#include <libutil.h>
5038589Sabial#include <paths.h>
5138589Sabial#include <setjmp.h>
5238589Sabial#include <signal.h>
5338589Sabial#include <stdio.h>
5438589Sabial#include <string.h>
5538589Sabial#include <syslog.h>
5638589Sabial#include <unistd.h>
5738589Sabial#include <varargs.h>
5838589Sabial
5938589Sabial#define BUFSIZE 1024
6038589Sabial#define MAX_CONS 12
6138589Sabial
6238589Sabial#define	NONE	0
6338589Sabial#define	SINGLE	1
6438589Sabial#define	MULTI	2
6538589Sabial#define	DEATH	3
6638589Sabial
6738589Sabial#define	FALSE	0
6838589Sabial#define TRUE	1
6938589Sabial
7038589Sabialchar cwd[BUFSIZE];
7138589Sabialchar vty[]="0123456789abcdef";
7238589Sabialchar *progname;
7338589Sabialchar *motd=NULL;
7438589Sabialint ncons=MAX_CONS;
7538589Sabialint Reboot=FALSE;
7638589Sabialint transition=MULTI;
7738589Sabialint prevtrans=SINGLE;
7838589Sabialjmp_buf machine;
7938589Sabial
8038589Sabialchar *trans[]={ "NONE", "SINGLE", "MULTI", "DEATH" };
8138589Sabial
8238589Sabialextern char **environ;
8338589Sabial
8438589Sabial/* Struct for holding session state */
8538589Sabialstruct sess {
8641191Sabial	char tty[16];	/* vty device path */
8738589Sabial	pid_t pid;	/* pid of process running on it */
8838589Sabial	int (*func)(int argc, char **argv);
8938589Sabial			/* internal function to run on it (after forking) */
9038589Sabial} ttys[MAX_CONS];
9138589Sabial
9238589Sabial/* Struct for built-in command */
9338589Sabialstruct command {
9438589Sabial	char *cmd;		/* command name */
9538589Sabial	char *descr;		/* command description */
9638589Sabial	char *usage;		/* usage */
9738589Sabial	char *example;		/* example of usage */
9838589Sabial	int (*func)(char *);	/* callback function */
9938589Sabial};
10038589Sabial
10138589Sabial/* Prototypes */
102104744Salfredint cd(char *);
103104744Salfredint pwd(char *);
104104744Salfredint echo(char *);
105104744Salfredint xit(char *);
106104744Salfredint set(char *);
107104744Salfredint unset(char *);
108104744Salfredint env(char *);
109104744Salfredint help(char *);
110104744Salfredint sourcer(char *);
111104744Salfredvoid do_command(int shell, char *cmdline);
112104744Salfredvoid transition_handler(int);
11338589Sabial
11438589Sabial/* Table of built-in functions */
11538589Sabialstruct command bltins[]={
11638589Sabial	{"cd","Change working directory","cd [dir]","cd /etc",cd},
11738589Sabial	{"pwd","Print current directory","pwd","pwd",pwd},
11838589Sabial	{"exit","Exit from shell()","exit","exit",xit},
119199251Sed	{"set","Set environment variable","set [VAR=value]","set TERM=xterm",set},
12038589Sabial	{"unset","Unset environment variable","unset VAR","unset EDITOR",unset},
12138589Sabial	{"echo","Echo arguments on stdout","echo arg1 arg2 ...","echo Hello World!",echo},
12238589Sabial	{"env","Print all environment variables","env","env",env},
12338589Sabial	{".","Source-in a file with commands",". filename",". /etc/rc",sourcer},
12438589Sabial	{"?","Print this help :-)","? [command]","? set",help},
12538589Sabial	{NULL,NULL,NULL,NULL,NULL}
12638589Sabial};
12738589Sabial
12838589Sabial/*
12938589Sabial * Built-in 'cd <path>' handler
13038589Sabial */
13138589Sabialint
13238589Sabialcd(char *path)
13338589Sabial{
13438589Sabial	if(chdir(path)) return(-1);
13538589Sabial	getcwd(cwd,BUFSIZE);
13638589Sabial	return(0);
13738589Sabial}
13838589Sabial
13938589Sabial/*
14038589Sabial * Built-in 'pwd' handler
14138589Sabial */
14238589Sabialint
14338589Sabialpwd(char *dummy)
14438589Sabial{
14538589Sabial
14638589Sabial	if(getcwd(cwd,BUFSIZE)==NULL) return(-1);
14738589Sabial	printf("%s\n",cwd);
14838589Sabial	return(0);
14938589Sabial}
15038589Sabial
15138589Sabial/*
15238589Sabial * Built-in 'exit' handler
15338589Sabial */
15438589Sabialint
15538589Sabialxit(char *dummy)
15638589Sabial{
15738589Sabial	_exit(0);
15838589Sabial}
15938589Sabial
16038589Sabial/*
16138589Sabial * Built-in 'echo' handler
16238589Sabial */
16338589Sabialint
16438589Sabialecho(char *args)
16538589Sabial{
16638589Sabial	int i=0,j;
16738589Sabial	int len;
16838589Sabial	char c;
16938589Sabial	int s_quote=0,d_quote=0;
17038589Sabial	int sep=0,no_lf=0;
17138589Sabial
17238589Sabial	if(args==NULL) {
17338589Sabial		printf("\n");
17438589Sabial		return;
17538589Sabial	}
17638589Sabial	len=strlen(args);
17738589Sabial	if(len>=2) {
17838589Sabial		if(args[0]=='-' && args[1]=='n') {
17938589Sabial			no_lf++;
18038589Sabial			i=2;
18138589Sabial			while(i<len && (args[i]==' ' || args[i]=='\t')) i++;
18238589Sabial		}
18338589Sabial	}
18438589Sabial	while(i<len) {
18538589Sabial		c=args[i];
18638589Sabial		switch(c) {
18738589Sabial		case ' ':
18838589Sabial		case '\t':
18938589Sabial			if(s_quote||d_quote) {
19038589Sabial				putchar(c);
19138589Sabial			} else if(!sep) {
19238589Sabial				putchar(' ');
19338589Sabial				sep=1;
19438589Sabial			}
19538589Sabial			break;
19638589Sabial		case '\\':
19738589Sabial			i++;
19838589Sabial			c=args[i];
19938589Sabial			switch(c) {
20038589Sabial			case 'n':
20138589Sabial				putchar('\n');
20238589Sabial				break;
20338589Sabial			case 'b':
20438589Sabial				putchar('\b');
20538589Sabial				break;
20638589Sabial			case 't':
20738589Sabial				putchar('\t');
20838589Sabial				break;
20938589Sabial			case 'r':
21038589Sabial				putchar('\r');
21138589Sabial				break;
21238589Sabial			default:
21338589Sabial				putchar(c);
21438589Sabial				break;
21538589Sabial			}
21638589Sabial			break;
21738589Sabial		case '"':
21838589Sabial			if(!d_quote) {
21938589Sabial				d_quote=1;
22038589Sabial				for(j=i+1;j<len;j++) {
22138589Sabial					if(args[j]=='\\') {
22238589Sabial						j++;
22338589Sabial						continue;
22438589Sabial					}
22538589Sabial					if(args[j]=='"') {
22638589Sabial						d_quote=2;
22738589Sabial						break;
22838589Sabial					}
22938589Sabial				}
23038589Sabial				if(d_quote!=2) {
23138589Sabial					printf("\necho(): unmatched \"\n");
23238589Sabial					return;
23338589Sabial				}
23438589Sabial			} else d_quote=0;
23538589Sabial			break;
23638589Sabial		case '\'':
23738589Sabial			if(!s_quote) {
23838589Sabial				s_quote=1;
23938589Sabial				for(j=i+1;j<len;j++) {
24038589Sabial					if(args[j]=='\\') {
24138589Sabial						j++;
24238589Sabial						continue;
24338589Sabial					}
24438589Sabial					if(args[j]=='\'') {
24538589Sabial						s_quote=2;
24638589Sabial						break;
24738589Sabial					}
24838589Sabial				}
24938589Sabial				if(s_quote!=2) {
25038589Sabial					printf("\necho(): unmatched '\n");
25138589Sabial					return;
25238589Sabial				}
25338589Sabial			} else s_quote=0;
25438589Sabial			break;
25538589Sabial		case '`':
25638589Sabial			printf("echo(): backquote not implemented yet!\n");
25738589Sabial			break;
25838589Sabial		default:
25938589Sabial			sep=0;
26038589Sabial			putchar(c);
26138589Sabial			break;
26238589Sabial		}
26338589Sabial		i++;
26438589Sabial	}
26538589Sabial	if(!no_lf) putchar('\n');
26638589Sabial	fflush(stdout);
26738589Sabial}
26838589Sabial
26938589Sabial/*
27038589Sabial * Built-in 'set VAR=value' handler
27138589Sabial */
27238589Sabialint
27338589Sabialset(char *var)
27438589Sabial{
27538589Sabial	int res;
27638589Sabial
27738589Sabial	if(var==NULL) return(env(NULL));
27838589Sabial	res=putenv(var);
27938589Sabial	if(res) printf("set: %s\n",strerror(errno));
28038589Sabial	return(res);
28138589Sabial}
28238589Sabial
28338589Sabial/*
28438589Sabial * Built-in 'env' handler
28538589Sabial */
28638589Sabialint
28738589Sabialenv(char *dummy)
28838589Sabial{
28938589Sabial	char **e;
29038589Sabial
29138589Sabial	e=environ;
29238589Sabial	while(*e!=NULL) {
29338589Sabial		printf("%s\n",*e++);
29438589Sabial	}
29538589Sabial	return(0);
29638589Sabial}
29738589Sabial
29838589Sabial/*
29938589Sabial * Built-in 'unset VAR' handler
30038589Sabial */
30138589Sabialint
30238589Sabialunset(char *var)
30338589Sabial{
30438589Sabial	if(var==NULL) {
30538589Sabial		printf("%s: parameter required.\n",progname);
30638589Sabial		return(-1);
30738589Sabial	}
30838589Sabial	return(unsetenv(var));
30938589Sabial}
31038589Sabial
31138589Sabial/*
31238589Sabial * Built-in '?' handler
31338589Sabial */
31438589Sabialint
31538589Sabialhelp(char *cmd)
31638589Sabial{
31738589Sabial	struct command *x;
31838589Sabial	int found=0;
31938589Sabial
32038589Sabial	if(cmd==NULL) {
32138589Sabial		printf("\nBuilt-in commands:\n");
32238589Sabial		printf("-------------------\n");
32338589Sabial		x=bltins;
32438589Sabial		while(x->cmd!=NULL) {
32538589Sabial			printf("%s\t%s\n",x->cmd,x->descr);
32638589Sabial			x++;
32738589Sabial		}
32838589Sabial		printf("\nEnter '? <cmd>' for details.\n\n");
32938589Sabial		return(0);
33038589Sabial	} else {
33138589Sabial		x=bltins;
33238589Sabial		while(x->cmd!=NULL) {
33338589Sabial			if(strcmp(x->cmd,cmd)==0) {
33438589Sabial				found++;
33538589Sabial				break;
33638589Sabial			}
33738589Sabial			x++;
33838589Sabial		}
33938589Sabial		if(found) {
34038589Sabial			printf("\n%s\t%s:\n",x->cmd,x->descr);
34138589Sabial			printf("\tUsage:\n\t\t%s\n",x->usage);
34238589Sabial			printf("\te.g:\n\t\t%s\n\n",x->example);
34338589Sabial			return(0);
34438589Sabial		} else {
34538589Sabial			printf("\n%s: no such command.\n\n",cmd);
34638589Sabial			return(-1);
34738589Sabial		}
34838589Sabial	}
34938589Sabial}
35038589Sabial
35138589Sabial/*
35238589Sabial * Signal handler for shell()
35338589Sabial */
35438589Sabialvoid
35538589Sabialshell_sig(int sig)
35638589Sabial{
35738589Sabial	switch(sig) {
35838589Sabial	case SIGINT:
35938589Sabial	case SIGQUIT:
36038589Sabial	case SIGTERM:
36138589Sabial		/* ignore ? */
36238589Sabial		break;
36338589Sabial	default:
36438589Sabial		break;
36538589Sabial	}
36638589Sabial}
36738589Sabial
36838589Sabial/*
36938589Sabial * Built-in '.' handler (read-in and execute commands from file)
37038589Sabial */
37138589Sabialint
37238589Sabialsourcer(char *fname)
37338589Sabial{
37438589Sabial	FILE *fd;
37538589Sabial	char buf[512],*tok,*arg,**av;
37638589Sabial	int ac,len,f,res,i;
37738589Sabial	pid_t pid;
37838589Sabial	char *sep=" \t";
37938589Sabial
38038589Sabial	fd=fopen(fname,"r");
38138589Sabial	if(fd==NULL) {
38238589Sabial		printf("Couldn't open file '%s'\n",fname);
38338589Sabial		return(-1);
38438589Sabial	}
38538589Sabial	while(!feof(fd)) {
38638589Sabial		memset(buf,0,512);
38738589Sabial		if(fgets(buf,512,fd)==NULL) continue;
38838589Sabial		if((*buf=='#') || (*buf=='\n')) continue;
38938589Sabial		len=strlen(buf);
39038589Sabial		buf[len-1]='\0';
39138589Sabial		if(strncmp(buf,"ncons",5)==0) {
39238589Sabial			tok=strtok(buf,sep);
39338589Sabial			tok=strtok(NULL,sep);
39438589Sabial			ncons=atoi(tok);
39538589Sabial			if((ncons<1)||(ncons>MAX_CONS)) {
39638589Sabial				syslog(LOG_EMERG,"%s: bad ncons value; defaulting to %d\n",fname,MAX_CONS);
39738589Sabial				ncons=MAX_CONS;
39838589Sabial			}
39938589Sabial			continue;
40038589Sabial		} else if(strncmp(buf,"motd",4)==0) {
40138589Sabial			tok=strtok(buf,sep);
40238589Sabial			motd=strdup(strtok(NULL,sep));
40338589Sabial			continue;
40438589Sabial		} else {
40538589Sabial			do_command(0,buf);
40638589Sabial		}
40738589Sabial		/* Next command, please. */
40838589Sabial	}
40938589Sabial	fclose(fd);
41038589Sabial	syslog(LOG_EMERG,"Done with %s",fname);
41138589Sabial}
41238589Sabial
41338589Sabialvoid
41438589Sabialdo_command(int shell, char *cmdline)
41538589Sabial{
41638589Sabial	char *tok,*c,*sep=" \t";
41738589Sabial	char **av;
41838589Sabial	struct command *x;
41938589Sabial	int found,len;
42038589Sabial	int ac,i,f,res;
42138589Sabial	int bg=0;
42238589Sabial	pid_t pid;
42338589Sabial
42438589Sabial	len=strlen(cmdline);
42538589Sabial	if(cmdline[len-1]=='&') {
42638589Sabial		bg++;
42738589Sabial		cmdline[len-1]='\0';
42838589Sabial		len--;
42938589Sabial	} else bg=0;
43038589Sabial	tok=strtok(cmdline,sep);
43138589Sabial	x=bltins;
43238589Sabial	found=0;
43338589Sabial	while(x->cmd!=NULL) {
43438589Sabial		if(strcmp(x->cmd,tok)==0) {
43538589Sabial			found++;
43638589Sabial			break;
43738589Sabial		}
43838589Sabial		x++;
43938589Sabial	}
44038589Sabial	if(found) {
44138589Sabial		tok=cmdline+strlen(x->cmd)+1;
44238589Sabial		while(*tok && isblank(*tok) && (tok<(cmdline+len))) tok++;
44338589Sabial		if(*tok==NULL) tok=NULL;
44438589Sabial		x->func(tok);
44538589Sabial		return;
44638589Sabial	}
44738589Sabial	ac=0;
44838589Sabial	av=(char **)calloc(((len+1)/2+1),sizeof(char *));
44938589Sabial	av[ac++]=tok;
45038589Sabial	while((av[ac++]=strtok(NULL,sep))!=NULL)
45138589Sabial		continue;
45238589Sabial	switch((pid=fork())) {
45338589Sabial	case 0:
45438589Sabial		if(shell) {
45538589Sabial			signal(SIGINT,SIG_DFL);
45638589Sabial			signal(SIGQUIT,SIG_DFL);
45738589Sabial			signal(SIGTERM,SIG_DFL);
45838589Sabial			signal(SIGHUP,SIG_DFL);
45938589Sabial		} else {
46038589Sabial			close(0);
46138589Sabial			close(1);
46238589Sabial			close(2);
46338589Sabial			f=open(_PATH_CONSOLE,O_RDWR);
46438589Sabial			dup2(f,0);
46538589Sabial			dup2(f,1);
46638589Sabial			dup2(f,2);
46738589Sabial			if(f>2) close(f);
46838589Sabial		}
46938589Sabial		if(bg) {
47038589Sabial			if(daemon(0,0)) {
47138589Sabial				printf("do_command(%s): failed to run bg: %s\n",
47238589Sabial				av[0],strerror(errno));
47338589Sabial				_exit(100);
47438589Sabial			}
47538589Sabial		}
47638589Sabial		execvp(av[0],av);
47738589Sabial		/* Something went wrong... */
47838589Sabial		printf("do_command(%s): %s\n",av[0],strerror(errno));
47938589Sabial		_exit(100);
48038589Sabial		break;
48138589Sabial	case -1:
48238589Sabial		printf("do_command(): %s\n",strerror(errno));
48338589Sabial		break;
48438589Sabial	default:
48538589Sabial		while(waitpid(pid,&res,0)!=pid) continue;
48638589Sabial		if(WEXITSTATUS(res)) {
48740412Sabial			printf("do_command(%s): exit code=%d\n",
48838589Sabial				av[0],WEXITSTATUS(res));
48938589Sabial		}
49038589Sabial		break;
49138589Sabial	}
49238589Sabial	free(av);
49338589Sabial	return;
49438589Sabial}
49538589Sabial
49638589Sabial/*
49738589Sabial * This is the user interface. This routine gets executed on each
49838589Sabial * virtual console serviced by init.
49938589Sabial *
50038589Sabial * It works as normal shell does - for each external command it forks
50138589Sabial * and execs, for each internal command just executes a function.
50238589Sabial */
50338589Sabial
50438589Sabialint
50538589Sabialshell(int argc, char **argv)
50638589Sabial{
50738589Sabial	char buf[BUFSIZE];
50838589Sabial	char *prompt=" # ";
50938589Sabial	int fd;
51038589Sabial	int res;
51138589Sabial	pid_t mypid;
51238589Sabial
51338589Sabial	if(motd!=NULL) {
51438589Sabial		if((fd=open(motd,O_RDONLY))!=-1) {
51538589Sabial			do {
51638589Sabial				res=read(fd,buf,BUFSIZE);
51738589Sabial				res=write(1,buf,res);
51838589Sabial			} while(res>0);
51938589Sabial			close(fd);
52038589Sabial		}
52138589Sabial	}
52238589Sabial
52338589Sabial	printf("\n\n+=========================================================+\n");
52438589Sabial	printf("| Built-in shell() (enter '?' for short help on commands) |\n");
52538589Sabial	printf("+=========================================================+\n\n");
52638589Sabial	getcwd(cwd,BUFSIZE);
52738589Sabial	mypid=getpid();
52838589Sabial	signal(SIGINT,shell_sig);
52938589Sabial	signal(SIGQUIT,shell_sig);
53038589Sabial	signal(SIGTERM,shell_sig);
53138589Sabial	while(!feof(stdin)) {
53238589Sabial		memset(buf,0,BUFSIZE);
53338589Sabial		printf("(%d)%s%s",mypid,cwd,prompt);
53438589Sabial		fflush(stdout);
53538589Sabial		if(fgets(buf,BUFSIZE-1,stdin)==NULL) continue;
53638589Sabial		buf[strlen(buf)-1]='\0';
53738589Sabial		if(strlen(buf)==0) continue;
53838589Sabial		do_command(1,buf);
53938589Sabial	}
54038589Sabial	return(0);
54138589Sabial}
54238589Sabial
54338589Sabial/*
54438589Sabial * Stub for executing some external program on a console. This is called
54538589Sabial * from previously forked copy of our process, so that exec is ok.
54638589Sabial */
54738589Sabialint
54838589Sabialexternal_cmd(int argc, char **argv)
54938589Sabial{
55038589Sabial	execvp(argv[0],argv);
55138589Sabial}
55238589Sabial
55338589Sabial/*
55438589Sabial * Acquire vty and properly attach ourselves to it.
55538589Sabial * Also, build basic environment for running user interface.
55638589Sabial */
55738589Sabial
55838589Sabialint
55938589Sabialstart_session(int vty, int argc, char **argv)
56038589Sabial{
56138589Sabial	int fd;
56238589Sabial	char *t;
56338589Sabial
56438589Sabial	close(0);
56538589Sabial	close(1);
56638589Sabial	close(2);
56738589Sabial	revoke(ttys[vty].tty);
56838589Sabial	fd=open(ttys[vty].tty,O_RDWR);
56938589Sabial	dup2(fd,0);
57038589Sabial	dup2(fd,1);
57138589Sabial	dup2(fd,2);
57238589Sabial	if(fd>2) close(fd);
57338589Sabial	login_tty(fd);
57438589Sabial	setpgid(0,getpid());
575199251Sed	putenv("TERM=xterm");
57638589Sabial	putenv("HOME=/");
57738589Sabial	putenv("PATH=/stand:/bin:/usr/bin:/sbin:.");
57838589Sabial	signal(SIGHUP,SIG_DFL);
57938589Sabial	signal(SIGINT,SIG_DFL);
58038589Sabial	signal(SIGQUIT,SIG_DFL);
58138589Sabial	signal(SIGTERM,SIG_DFL);
58238589Sabial	chdir("/");
58338589Sabial	t=(char *)(rindex(ttys[vty].tty,'/')+1);
58438589Sabial	printf("\n\n\nStarting session on %s.\n",t);
58538589Sabial	ttys[vty].func(argc,argv);
58638589Sabial	_exit(0);
58738589Sabial}
58838589Sabial
58938589Sabial/*
59038589Sabial * Execute system startup script /etc/rc
59138589Sabial *
59238589Sabial * (Of course if you don't like it - I don't - you can run anything you
59338589Sabial * want here. Perhaps it would be useful just to read some config DB and
59438589Sabial * do these things ourselves, avoiding forking lots of shells and scripts.)
59538589Sabial */
59638589Sabial
59738589Sabial/* If OINIT_RC is defined, oinit will use it's own configuration file,
59838589Sabial * /etc/oinit.rc. It's format is described below. Otherwise, it will use
59938589Sabial * normal /etc/rc interpreted by Bourne shell.
60038589Sabial */
60138589Sabial#ifndef OINIT_RC
60271295Salex#ifndef SH_NAME
60371295Salex#define SH_NAME	"-sh"
60471295Salex#endif
60571295Salex#ifndef SH_PATH
60671295Salex#define SH_PATH	_PATH_BSHELL
60771295Salex#endif
60871295Salex#ifndef SH_ARG
60971295Salex#define SH_ARG	"/etc/rc"
61071295Salex#endif
61138589Sabialvoid
61238589Sabialruncom()
61338589Sabial{
61438589Sabial	char *argv[3];
61538589Sabial	pid_t pid;
61638589Sabial	int st;
61738589Sabial	int fd;
61838589Sabial
61938589Sabial	if((pid=fork())==0) {
62038589Sabial		/* child */
62138589Sabial		close(0);
62238589Sabial		close(1);
62338589Sabial		close(2);
62438589Sabial		fd=open(_PATH_CONSOLE,O_RDWR);
62538589Sabial		dup2(fd,0);
62638589Sabial		dup2(fd,1);
62738589Sabial		dup2(fd,2);
62838589Sabial		if(fd>2) close(fd);
62971295Salex		argv[0]=SH_NAME;
63071295Salex		argv[1]=SH_ARG;
63138589Sabial		argv[2]=0;
63271295Salex		execvp(SH_PATH,argv);
63338589Sabial		printf("runcom(): %s\n",strerror(errno));
63438589Sabial		_exit(1);
63538589Sabial	}
63638589Sabial	/* Wait for child to exit */
63738589Sabial	while(pid!=waitpid(pid,(int *)0,0)) continue;
63838589Sabial	return;
63938589Sabial}
64038589Sabial#else
64138589Sabial/* Alternative /etc/rc - default is /etc/oinit.rc. Its format is as follows:
64238589Sabial * - each empty line or line beginning with a '#' is discarded
64338589Sabial * - any other line must contain a keyword, or a (nonblocking) command to run.
64438589Sabial *
64538589Sabial * Thus far, the following keywords are defined:
64638589Sabial * ncons <number>	number of virtual consoles to open
64738589Sabial * motd <pathname>	full path to motd file
64838589Sabial *
64938589Sabial * Examples of commands to run:
65038589Sabial *
65138589Sabial * ifconfig lo0 inet 127.0.0.1 netmask 255.0.0.0
65238589Sabial * ifconfig ed0 inet 148.81.168.10 netmask 255.255.255.0
65338589Sabial * kbdcontrol -l /usr/share/syscons/my_map.kbd
65438589Sabial */
65538589Sabialvoid
65638589Sabialruncom(char *fname)
65738589Sabial{
65841191Sabial	int fd;
65941191Sabial
66041191Sabial	close(0);
66141191Sabial	close(1);
66241191Sabial	close(2);
66341191Sabial	fd=open(_PATH_CONSOLE,O_RDWR);
66441191Sabial	dup2(fd,0);
66541191Sabial	dup2(fd,1);
66641191Sabial	dup2(fd,2);
66741191Sabial	if(fd>2) close(fd);
66838589Sabial	sourcer(fname);
66938589Sabial}
67038589Sabial#endif
67138589Sabial
67238589Sabialint
67338589Sabialrun_multi()
67438589Sabial{
67538589Sabial	int i,j;
67638589Sabial	pid_t pid;
67738589Sabial	int found;
67838589Sabial
67938589Sabial	/* Run /etc/rc if not in single user */
68038589Sabial#ifndef OINIT_RC
68138589Sabial	if(prevtrans==SINGLE) runcom();
68238589Sabial#else
68338589Sabial	if(prevtrans==SINGLE) runcom(OINIT_RC);
68438589Sabial#endif
68538589Sabial	if(transition!=MULTI) return(-1);
68638589Sabial
68738589Sabial	syslog(LOG_EMERG,"*** Starting multi-user mode ***");
68838589Sabial
68938589Sabial	/* Fork shell interface for each console */
69038589Sabial	for(i=0;i<ncons;i++) {
69138589Sabial		if(ttys[i].pid==0) {
69238589Sabial			switch(pid=fork()) {
69338589Sabial			case 0:
69438589Sabial				start_session(i,0,NULL);
69538589Sabial				break;
69638589Sabial			case -1:
69738589Sabial				printf("%s: %s\n",progname,strerror(errno));
69838589Sabial				break;
69938589Sabial			default:
70038589Sabial				ttys[i].pid=pid;
70138589Sabial				break;
70238589Sabial			}
70338589Sabial		}
70438589Sabial	}
70538589Sabial	/* Initialize any other services we'll use - most probably this will
70638589Sabial	 * be a 'telnet' service (some day...).
70738589Sabial	 */
70838589Sabial	/* */
70938589Sabial
71038589Sabial	/* Emulate multi-user */
71138589Sabial	while(transition==MULTI) {
71238589Sabial		/* XXX Modify this to allow for checking for the input on
71338589Sabial		 * XXX listening sockets, and forking a 'telnet' service.
71438589Sabial		 */
71538589Sabial		/* */
71638589Sabial
71738589Sabial		/* Wait for any process to exit */
71838589Sabial		pid=waitpid(-1,(int *)0,0);
71938589Sabial		if(pid==-1) continue;
72038589Sabial		found=0;
72138589Sabial		j=-1;
72238589Sabial		/* search if it's one of our sessions */
72338589Sabial		for(i=0;i<ncons;i++) {
72438589Sabial			if(ttys[i].pid==pid) {
72538589Sabial				found++;
72638589Sabial				j=i;
72738589Sabial				ttys[j].pid=0;
72838589Sabial				break;
72938589Sabial			}
73038589Sabial		}
73138589Sabial		if(!found) {
73238589Sabial			/* Just collect the process's status */
73338589Sabial			continue;
73438589Sabial		} else {
73538589Sabial			/* restart shell() on a console, if it died */
73638589Sabial			if(transition!=MULTI) return(0);
73738589Sabial			switch(pid=fork()) {
73838589Sabial			case 0:
73938589Sabial				sleep(1);
74038589Sabial				start_session(j,0,NULL);
74138589Sabial				break;
74238589Sabial			case -1:
74338589Sabial				printf("%s: %s\n",progname,strerror(errno));
74438589Sabial				break;
74538589Sabial			default:
74638589Sabial				ttys[j].pid=pid;
74738589Sabial				break;
74838589Sabial			}
74938589Sabial		}
75038589Sabial	}
75138589Sabial}
75238589Sabial
75338589Sabialint clang;
75438589Sabial
75538589Sabialvoid
75638589Sabialkill_timer(int sig)
75738589Sabial{
75838589Sabial	clang=1;
75938589Sabial}
76038589Sabial
76138589Sabialkill_ttys()
76238589Sabial{
76338589Sabial}
76438589Sabial
76538589Sabial/*
76638589Sabial * Start a shell on ttyv0 (i.e. the console).
76738589Sabial */
76838589Sabial
76938589Sabialint
77038589Sabialrun_single()
77138589Sabial{
77238589Sabial	int i;
77338589Sabial	pid_t pid,wpid;
77438589Sabial	static int sigs[2]={SIGTERM,SIGKILL};
77538589Sabial
77638589Sabial	syslog(LOG_EMERG,"*** Starting single-user mode ***");
77738589Sabial	/* Kill all existing sessions */
77838589Sabial	syslog(LOG_EMERG,"Killing all existing sessions...");
77938589Sabial	for(i=0;i<MAX_CONS;i++) {
78038589Sabial		kill(ttys[i].pid,SIGHUP);
78138589Sabial		ttys[i].pid=0;
78238589Sabial	}
78338589Sabial	for(i=0;i<2;i++) {
78438589Sabial		if(kill(-1,sigs[i])==-1 && errno==ESRCH) break;
78538589Sabial		clang=0;
78638589Sabial		alarm(10);
78738589Sabial		do {
78838589Sabial			pid=waitpid(-1,(int *)0,WUNTRACED);
78938589Sabial			if(errno==EINTR) continue;
79038589Sabial			else break;
79138589Sabial		} while (clang==0);
79238589Sabial	}
79338589Sabial	if(errno!=ECHILD) {
79438589Sabial		syslog(LOG_EMERG,"Some processes would not die; ps -axl advised");
79538589Sabial	}
79638589Sabial	/* Single-user */
79738589Sabial	switch(pid=fork()) {
79838589Sabial	case 0:
79938589Sabial		start_session(0,0,NULL);
80038589Sabial		break;
80138589Sabial	case -1:
80238589Sabial		printf("%s: %s\n",progname,strerror(errno));
80338589Sabial		printf("The system is seriously hosed. I'm dying...\n");
80438589Sabial		transition=DEATH;
80538589Sabial		return(-1);
80638589Sabial		break;
80738589Sabial	default:
80838589Sabial		do {
80938589Sabial			wpid=waitpid(pid,(int *)0,WUNTRACED);
81038589Sabial		} while(wpid!=pid && transition==SINGLE);
81138589Sabial		if(transition!=DEATH) {
81238589Sabial			prevtrans=transition;
81338589Sabial			transition=MULTI;
81438589Sabial		}
81538589Sabial		break;
81638589Sabial	}
81738589Sabial	return(0);
81838589Sabial}
81938589Sabial
82038589Sabial/*
82138589Sabial * Transition handler - installed as signal handler.
82238589Sabial */
82338589Sabial
82438589Sabialvoid
82538589Sabialtransition_handler(int sig)
82638589Sabial{
82738589Sabial
82838589Sabial	switch(sig) {
82938589Sabial	case SIGHUP:
83038589Sabial	case SIGTERM:
83138589Sabial		prevtrans=transition;
83238589Sabial		transition=SINGLE;
83338589Sabial		syslog(LOG_EMERG,"*** Going from %s -> %s\n",trans[prevtrans],trans[transition]);
83438589Sabial		if(prevtrans!=transition) longjmp(machine,sig);
83538589Sabial		break;
83638589Sabial	case SIGINT:
83738589Sabial	case SIGQUIT:
83838589Sabial		prevtrans=transition;
83938589Sabial		transition=DEATH;
84038589Sabial		syslog(LOG_EMERG,"*** Going from %s -> %s\n",trans[prevtrans],trans[transition]);
84138589Sabial		if(prevtrans!=transition) longjmp(machine,sig);
84238589Sabial		break;
84338589Sabial	default:
84438589Sabial		syslog(LOG_EMERG,"pid=%d sig=%s (ignored)\n",getpid(),sys_siglist[sig]);
84538589Sabial		break;
84638589Sabial	}
84738589Sabial}
84838589Sabial
84938589Sabial/*
85038589Sabial * Change system state appropriately to the signals
85138589Sabial */
85238589Sabial
85338589Sabialint
85438589Sabialtransition_machine()
85538589Sabial{
85638589Sabial	int i;
85738589Sabial
85838589Sabial	while(transition!=DEATH) {
85938589Sabial		switch(transition) {
86038589Sabial		case MULTI:
86138589Sabial			run_multi();
86238589Sabial			break;
86338589Sabial		case SINGLE:
86438589Sabial			run_single();
86538589Sabial			break;
86638589Sabial		}
86738589Sabial	}
86838589Sabial	syslog(LOG_EMERG,"Killing all existing sessions...");
86938589Sabial	/* Kill all sessions */
87038589Sabial	kill(-1,SIGKILL);
87138589Sabial	/* Be nice and wait for them */
87238589Sabial	while(waitpid(-1,(int *)0,WNOHANG|WUNTRACED)>0) continue;
87338589Sabial	unmount("/",0);
87438589Sabial	reboot(RB_AUTOBOOT);
87538589Sabial	/* NOTREACHED */
87638589Sabial}
87738589Sabial
87838589Sabialint
87938589Sabialmain(int argc, char **argv)
88038589Sabial{
88138589Sabial	int devfs=0,c,i;
88238589Sabial
88338589Sabial	/* These are copied from real init(8) */
88438589Sabial	if(getuid()!=0)
88538589Sabial		errx(1,"%s",strerror(EPERM));
88638589Sabial	openlog("init",LOG_CONS|LOG_ODELAY,LOG_AUTH);
88738589Sabial	if(setsid()<0)
88838589Sabial		warn("initial setsid() failed");
88938589Sabial	if(setlogin("root")<0)
89038589Sabial		warn("setlogin() failed");
89138589Sabial
89238589Sabial	close(0);
89338589Sabial	close(1);
89438589Sabial	close(2);
89538589Sabial	chdir("/");
89638589Sabial
89738589Sabial	progname=rindex(argv[0],'/');
89838589Sabial	if(progname==NULL) {
89938589Sabial		progname=argv[0];
90038589Sabial	} else progname++;
90138589Sabial
90238589Sabial	transition=MULTI;
90338589Sabial
90438589Sabial	/* We must recognize the same options as real init does */
90538589Sabial	while((c=getopt(argc,argv,"dsf"))!=-1) {
90638589Sabial		switch(c) {
90738589Sabial		case 'd':
90838589Sabial			devfs=1;
90938589Sabial			break;
91038589Sabial		case 's':
91138589Sabial			transition=SINGLE;
91238589Sabial			break;
91338589Sabial		case 'f':
91438589Sabial			break;
91538589Sabial		default:
91638589Sabial			printf("%s: unrecognized flag '-%c'\n",progname,c);
91738589Sabial			break;
91838589Sabial		}
91938589Sabial	}
92038589Sabial	if(devfs)
92169793Sobrien		mount("devfs",_PATH_DEV,MNT_NOEXEC|MNT_RDONLY,0);
92238589Sabial
92338589Sabial	/* Fill in the sess structures. */
92441191Sabial	/* XXX Really, should be filled based upon config file. */
92538589Sabial	for(i=0;i<MAX_CONS;i++) {
92641191Sabial		if(i==0) {
92769793Sobrien			sprintf(ttys[i].tty,_PATH_CONSOLE);
92841191Sabial		} else {
92969793Sobrien			sprintf(ttys[i].tty,"%sv%c",_PATH_TTY,vty[i]);
93041191Sabial		}
93138589Sabial		ttys[i].pid=0;
93238589Sabial		ttys[i].func=shell;
93338589Sabial	}
93438589Sabial
93538589Sabial	getcwd(cwd,BUFSIZE);
93638589Sabial
93738589Sabial	signal(SIGINT,transition_handler);
93838589Sabial	signal(SIGQUIT,transition_handler);
93938589Sabial	signal(SIGTERM,transition_handler);
94038589Sabial	signal(SIGHUP,transition_handler);
94138589Sabial	signal(SIGALRM,kill_timer);
94238589Sabial
94338589Sabial	setjmp(machine);
94438589Sabial	transition_machine(transition);
94538589Sabial	/* NOTREACHED */
94638589Sabial	exit(100);
94738589Sabial}
948