/*
 *	init.c - start getty procs on ttys, and start system procs
 *
 *	Modfied 12/11/78 by H. Yen and F. Richter to change
 *		single user number to something semi-secret...
 *
 *	Modified 12/29/81 by J. Buck in several ways:
 *		Have two alternate booting terminals, ttyf1 and ttyN
 *		based on getcsw() & 07: (4=ttyN, 6=ttyf1, all other=console)
 *
 *		Changed single user number from 127004 to 127040 for
 *		obvious reasons.
 *
 *		Have init write out a logout message to the terminals
 *		as they logout.  (This involves forking a proc to
 *		do this, waiting for this proc to finish, then
 *		starting the getty on the tty.  The reason for the
 *		fork() is obvious (init would inherit the controlling
 *		tty ptr))
 *
 *		Also, /etc/rc had to be divided into two sections:
 *			/etc/rc - stuff that is run once at bootime
 *				at the boot terminal.
 *			/etc/rc.notty - stuff like cron, update that
 *				should not have a tty associated with
 *				them.
 *
 *		louttab is a table of processes that have logged
 *		out, (had a message printed to them), but have
 *		not had the message proc finish, and therefore
 *		have not had a getty started on them yet.
 *
 *	Modified 08/82 by J. Buck
 *		change single user back to 017303x, and other changes to
 *		conform with UNIX v7.
 *
 *	Modified 7/83 by John Buck
 *		make changes to stty stuff to conform with UNIX system V
 *
 *	Modified 2/84 by John Buck
 *		make getcsw()& 07 handle the following values:
 *		0: console, 1: /dev/ttyf1 2: /dev/ttyf2 3: /dev/ttyf3,
 *		4: /dev/ttyN 5: /dev/ttyf5 6: /dev/ttyf5!!! 7: /dev/ttyf7
 *
 */

#include	"sys/types.h"
#include	"utmp.h"
#include	"setjmp.h"
#include	"ttystab.h"
#include	"info.h"
#include	"sys/stat.h"

#define	tabsize	64
#define	all	p = &ltab[0]; p < &ltab[tabsize]; p++
#define	ever	;;
#define	single	0173030
#define	reboot	0173040

char	shell[]		"/bin/sh";
char	oldshell[]	"/bin/sh.old";
char	minus[]		"-";
char	runc[]		"/etc/rc";
char	rnotty[]	"/etc/rc.notty"; /* stuff that does not have */
char	rnotty2[]	"/etc/rc.notty2"; /* a cont. tty attached to it */
char	init[]		"/etc/init";
char	ifile[]		"/etc/ttys";
char	utmp[]		"/etc/utmp";
char	wtmpf[]		"/etc/wtmp";
char	ulock[]		"/etc/utmp.lck";
char	dev[]		"/dev/";
char	ctty[20];
char	tty[20];
char	unam[16];
int	fi;
int	ulen;

struct	termio	cmode = {
	0, 0, 0, 0, 0,
	TCHARS
};
struct info info;

struct	lstand {
	int l_pid;
	char l_line[8];
} louttab[tabsize + 1];

struct
{
	char	line[8];
	char	coms[2];
	int	flag;
} line;
struct	ltab
{
	char	line[8];
	int	comn;
	int	pid;
} ltab[tabsize];

struct	utmp	wtmp;

jmp_buf	sjbuf;
int	wstatus;

main()
{
	register i;
	register struct ltab *p, *q;
	int reset();

	if(getpid() != 1){
		write(2,"/etc/init: Will not re-run\n", 27);
		exit(1);
	}

	/*
	 * if not single user,
	 * run shell sequence
	 */

	postme("(GOD)", "/", "?");
	for(i = 0; i < tabsize; i++)
		louttab[i].l_pid = louttab[i].l_line[0] = 0;
	louttab[tabsize].l_pid = -1;

	if((getcsw() & 0177770) != single) {
		i = fork();
		if(i == 0) {
			getcon();
			ioctl(open(ctty, 2), TCSETA, &cmode);
			dup(0);
			dup(0);
			postme("/ETC/RC", "/", ctty);
			execl(shell, shell, runc, 0);
			exit();
		}
		while(wait(0) != i);
		i = fork();
		if(i == 0){
			setpgrp(-1);
			open("/", 0);
			dup(0);
			dup(0);
			postme("DAEMON", "/", "?");
			execl(shell, shell, rnotty, 0);
			exit();
		}
		while(wait(0) != i);
		if ((i = open(wtmpf, 1)) >= 0) {
			lseek(i, 0L, 2);
			wtmp.ut_line[0] = '~';
			wtmp.ut_pid = 1;
			wtmp.ut_type = BOOT_TIME;
			wtmp.ut_line[1] = 0;
			time(&wtmp.ut_time);
			write(i, &wtmp, sizeof wtmp);
			close(i);
		}
		unlink(ulock);
		i = creat(utmp, 0644);
		if(i != -1){
			write(i, &wtmp, sizeof wtmp);
			close(i);
		}
	}

	/*
	 * main loop for hangup signal
	 * close all files and
	 * check switches for magic values
	 */

	setpgrp(0);
	setjmp(sjbuf);
	signal(1, reset);
	for(i=0; i<20; i++)
		close(i);
	switch(getcsw() & 0177770) {

	case single:
	error:
		termall();
		i = fork();
		if(i == 0) {
			getcon();
			ioctl(open(ctty, 2), TCSETA, &cmode);
			dup(0);
			dup(0);
			postme("(S/U)", "/", ctty);
			execl(shell, minus, 0);
			execl(oldshell, minus,0);
			exit();
		}
		while(wait(0) != i);

	case reboot:
		termall();
		execl(init, minus, 0);
		reset();
	}

	/*
	 * open and merge in init file
	 */

	fi = open(ifile, 0);
	q = &ltab[0];
	while(rline()) {
		if(line.flag == '0')
			continue;
		for(all)
			if(p->line[0] == 0 || sameline(p->line, line.line)){
				if(p >= q) {
					i = p->pid;
					p->pid = q->pid;
					q->pid = i;
					for(i = 0; i < 8; i++)
						p->line[i] = q->line[i];
					p->comn = q->comn;
					for(i=0; i < 8; i++)
						q->line[i] = line.line[i];
					q->coms[0] = line.comn;
					q++;
				}
				break;
			}
	}
	close(fi);
	if(q == &ltab[0])
		goto error;
	for(; q < &ltab[tabsize]; q++)
		term(q);
	for(all)
		if(p->line[0] != 0 && p->pid == 0)
			dfork(p);
	for(ever) {
		i = wait(&wstatus);
		if(logdout(i))i = -2;
		for(all)
			if(p->pid == i) {
				rmut(p);
				p->pid = -1;
			}
			else	if(p->pid == -1)
					dfork(p);
	}
}

termall()
{
	register struct ltab *p;

	for(all)
		term(p);
}

term(ap)
struct ltab *ap;
{
	register struct ltab *p;

	p = ap;
	if(p->pid > 0) {
		wstatus = -1;
		rmut(p);
		kill(p->pid, 9);
	}
	p->pid = 0;
	p->line[0] = 0;
}

rline()
{
	register c, i;

	c = get();
	if(c < 0)
		return(0);
	if(c == 0)
		goto bad;
	line.flag = c;
	c = get();
	if(c <= 0)
		goto bad;
	line.comn = c;
	for(i=0; i<8; i++)
		line.line[i] = 0;
	for(i=0; i<7; i++) {
		c = get();
		if(c <= 0)
			break;
		line.line[i] = c;
	}
	while(c > 0)
		c = get();
	maktty(tty, line.line);
	if(access(tty, 06) < 0)
		goto bad;
	return(1);

bad:
	line.flag = '0';
	return(1);
}

maktty(des, lin)
char *des;
char *lin;
{
	register i, j;

	for(i=0; dev[i]; i++)
		des[i] = dev[i];
	for(j=0; lin[j]; j++) {
		des[i] = lin[j];
		i++;
	}
	des[i] = 0;
}

get()
{
	char b;

	if(read(fi, &b, 1) != 1)
		return(-1);
	if(b == '\n')
		return(0);
	return(b);
}

dfork(ap)
struct ltab *ap;
{
	register struct lstand *pl;
	register struct ltab *p;
	register int i;
	short j;
	short urd;

	p = ap;
	for(pl = louttab; pl->l_pid >= 0; pl++)
		if(sameline(pl->l_line, p->line))return;
	i = fork();
	if(i == 0) {
		signal(1, 0);
		signal(2, 1);
		signal(3, 1);
		postme("(login:)", "/", p->line);
		if((i = open(utmp, 2)) >= 0){
			while((urd = read(i, &wtmp, sizeof wtmp))
				== sizeof wtmp){
				if(sameline(wtmp.ut_line, p->line))
					break;
			}
			for(j = 0; j < 8; j++){
				wtmp.ut_line[j] = p->line[j];
				wtmp.ut_name[j] = '\0';
			}
			time(&wtmp.ut_time);
			wtmp.ut_type = INIT_PROCESS;
			wtmp.ut_exit.e_exit = 0;
			wtmp.ut_exit.e_termination = 0;
			wtmp.ut_pid = getpid();
			if(urd)	/* Not EOF */
				lseek(i, -(long)(sizeof wtmp), 1);
			write(i, &wtmp, sizeof wtmp);
			close(i);
		}
		maktty(tty, p->line);
		chown(tty, 0, 0);
		if(sameline(p->line, "ttyf1\0\0\0\0\0")
			|| sameline(p->line, "ttyw1\0\0\0\0\0"))chmod(tty, 0622);
		else	chmod(tty, 0611);
		open(tty, 2);
		dup(0);
		dup(0);
		p->coms[1] = 0;
		execl("/etc/getty", minus, p->coms, 0);
		exit(1);
	}
	p->pid = i;
}

rmut(p)
struct ltab *p;
{
	register i, f;
	register char *s;
	char *ctime();
	static char zero[16];
	int reset();

	unam[0] = 0;
	f = open(utmp, 2);
	if(f >= 0) {
		while(read(f, &wtmp, sizeof wtmp) == sizeof wtmp) {
			if(!sameline(wtmp.ut_line, p->line))continue;
			lseek(f, -(long)(sizeof wtmp), 1);
			for(i = 0; i < 8; i++){
				unam[i] = wtmp.ut_name[i];
				if(unam[i] == ' ')
					unam[i] = 0;
				wtmp.ut_name[i] = 0;
			}
			time(&wtmp.ut_time);
			wtmp.ut_type = DEAD_PROCESS;
			wtmp.ut_exit.e_exit = wstatus & 0377;
			wtmp.ut_exit.e_termination = (wstatus >> 8) & 0377;
			write(f, &wtmp, sizeof wtmp);
			break;
		}
		close(f);
	}
	else	{
		s = unam;
		for(i = 0; i < 8; i++)
			*s++ = '?';
		*s = 0;
	}

	time(&wtmp.ut_time);
	signal(1,1);
	if((f = fork()) == 0){
		lgtmsg(p, ctime(&wtmp.ut_time));
		exit(0);
	}
	slogout(f, p->line);
	signal(1, reset);
	f = open(wtmpf, 1);
	if (f >= 0) {
		for(i = 0; i < 8; i++){
			wtmp.ut_name[i] = unam[i];
			wtmp.ut_line[i] = p->line[i];
		}
		lseek(f, 0L, 2);
		wtmp.ut_pid = p->pid;
		wtmp.ut_type = DEAD_PROCESS;
		wtmp.ut_exit.e_exit = wstatus & 0377;
		wtmp.ut_exit.e_termination = (wstatus >> 8) &  0377;
		write(f, &wtmp, sizeof wtmp);
		close(f);
	}
}


lgtmsg(p, outtime)
register struct ltab  *p;
char *outtime;
{
	register struct ttab *g;
	register int f;
	struct stat statb;
	short hupflg;

	static char ttynam[20];
	static char mbuf[128];
	short i;

	maktty(ttynam, p->line);
	signal(2, 1);
	signal(3, 1);
	if((f = open(ttynam, 1)) == -1)
		return;

	for(g = &gtab[0]; g < &gtab[NGTAB]; g++)
		if(g->tname == p->comn)
			break;
	hupflg = 0;
	if(g < &gtab[NGTAB]){
		ioctl(f, TCSETAF, &(g->fmode));
		if(strlen(unam)){
			i = sprintf(mbuf, "\n\n\rUser \"%s\" logged off %s -- %s\r\n",
				unam, p->line, outtime);
			if(hupflg = !(g->fmode.c_cflag & CLOCAL)){
				if(fstat(f, &statb) != -1 &&
					(statb.st_uid < 4 || statb.st_uid == 47))
						hupflg = 0;
			}
		}
		else	{
			i = sprintf(mbuf, "\n\n\rUNIX %s free -- %s\n\r",
				p->line, outtime);
			if(wtmp.ut_exit.e_termination == 77)	/* magic */
				hupflg = 1;
		}
		write(f, mbuf, i);
	}
	if(hupflg){
		g->fmode.c_cflag = (g->fmode.c_cflag & ~CBAUD) | (B0|HUPCL);
		ioctl(f, TCSETAW, &(g->fmode));
		close(f);
		sleep(3);
	}
	else close(f);
	return;
}



/*
 * Pick an alternate booting terminal
 *
 * getcsw() & 07:
	0: 	console
	1:	/dev/ttyf1
	2:	/dev/ttyf2
	3:	/dev/ttyf3
	4:	/dev/ttyN
	5:	/dev/ttyf5
	6:	/dev/ttyf5
	7:	/dev/ttyf7
 */

getcon()
{
	register i;
	register char *s;

	switch ((i = getcsw()) & 07){
		case 4:
			maktty(ctty, "ttyN");
			cmode.c_cflag = B4800|CS7|CREAD|PARENB|CLOCAL;
			break;

		case 6:
			maktty(ctty, "ttyf5");
modes:
			cmode.c_cflag = B1200|CS7|CREAD|PARENB|CLOCAL;
			break;

		case 0:
			maktty(ctty, "console");
			cmode.c_cflag = B1200|CS7|CREAD|PARENB|CLOCAL;
			break;
		default:
			s = "ttyfx";
			s[4] = i + '0';
			maktty(ctty, s);
			goto modes;
	}
	cmode.c_iflag = ICRNL|BRKINT|IGNPAR|ISTRIP|IXON;
	cmode.c_oflag = TAB3|ONLCR|OPOST;
	cmode.c_lflag = ECHO|ECHOK|ICANON|ISIG;
	if(i & 01){	/* Upper case only for some reason */
		cmode.c_iflag |= IUCLC;
		cmode.c_oflag |= OLCUC;
		cmode.c_lflag |= XCASE;
	}
}


logdout(proc)
register proc;
{
	register struct lstand *pl;

	for(pl = louttab; pl->l_pid >= 0; pl++)
		if(pl->l_pid == proc){
			pl->l_pid = pl->l_line[0] = 0;
			return(1);
		}
	return(0);
}


slogout(proc, lne)
register int proc;
register char *lne;
{
	register struct lstand *pl;
	int	reset();

	if(proc < 1)return;

	for(pl = louttab; pl < &louttab[tabsize+1] && pl->l_pid != 0; pl++);
	if(pl->l_pid){
		signal(1, 1);
		if(getcsw() && fork() == 0){
			maktty(ctty, "console");
			close(0);
			close(1);
			close(2);
			open(ctty, 2);
			dup(0);
			write(1,"\n\rINIT ERROR - louttab full\r\n\n", 36);
			exit();
		}
		signal(1, reset);
	}
	pl->l_pid = proc;
	for(proc = 0; proc < 8; proc++)
		pl->l_line[proc] = lne[proc];
}

sameline(s, t)
register char *s, *t;
{
	register i;

	for(i = 0; i < 8 && *s == *t; i++, s++, t++);
	return(i == 8);
}


reset()
{
	longjmp(sjbuf, 1);
}


postme(n, d, t)
char *n, *d, *t;
{
	register char *s, *p;
	register short i;

	p = t;
	while(*p)
		if(*p++ == '/')t = p;
	for(i = 0, p = n, s = info.l_lognam; i < 8 && *p; i++, *s++ = *p++);
	while(i++ < 8)*s++ = 0;
	for(i = 0, p = d, s = info.l_logdir; i < 32 && *p; i++, *s++ = *p++);
	while(i++ < 32)*s++ = 0;
	for(i = 0, p = t, s = info.l_logtty; i < 8 && *p; i++, *s++ = *p++);
	while(i++ < 8)*s++ = 0;
	logpost(&info);
}

