AUSAM/source/S/init.c

Compare this file to the similar file:
Show the results in this format:

#include	<local-system>

#define	TALK
/*
 *	ian johnstone 	july 76
 *
 *	allow	"/etc/rc" to  have tty8 as fd 0 1 2
 *	useful for communicating etc during startup
 */ 

/* (not	SINGLE_USER)
#define	SINGLE_USER
/*
 *	chris maltby		'75
 *
 *	for single user invoke getty - must still know the passwd
 */ 

#define	SHUTDOWN
/*
 *	ian johnstone 		aug 77
 *
 *	on receiving a signal 14 init will not restart
 *	terminating shells.  The editor on receiving a signal 14
 *	will write current temp file to	"saved.file".
 *	see shtdwn() for more details.
 */ 

#define	AUSAM
/*
 *	Piers Lauder	oct '77
 *
 *	write more comprehensive info. into wtmp for
 *	each process collected here. Uses new wait for proc 1
 *	which returns process "zombie" & "limits" structures.
 *
 *	Piers Lauder	Feb '78 - May '78
 *
 *	The whole AUSAM works ...
 */ 

#define	SYS_SUP_PROCS
/*
 *	Piers Lauder	May '78
 *
 *	start up known system support processes
 *	i.e.: update, cron ...
 */ 

/* (not	DEBUG)
#define	DEBUG
/*
 *	Piers Lauder	Apr '78
 *
 *	Use "prs" to complain about problems ...
 */ 

/* (not	TEST)
#define	TEST
/*
 *	give alternate names for programs in test mode
 */ 

/* (not	TRACE)
#define	TRACE
/*
 **	trace bugs (needs DEBUG)
 */ 

#define	TTY_GROUP
/*
 **	Ian Johnstone	Sep '78
 **
 **	Each line in /etc/ttys is at least 4 chars long.
 **	The fourth char designates the terminal group to
 **	which a terminal belongs. Needs getty to have
 **	TTY_GROUP defined as well for full control
 **	to be exercised.
 */ 



#ifdef	AUSAM

#define	FORKTRYS 2	/* maximum fork attempts to be made */

#include	<wtmp.h>
struct stmp wtmp;

#include	<defines.h>
#include	<param.h>
#include	<passwd.h>
#include	<lnode.h>
#include	<stat16.h>
struct pwent pe;

#include	<pzomb.h>
struct corpse
{
	struct pzomb ppzomb;
	struct lnode limits;
} waitbuf;

char	homed[]		"/";
char	tmpd[]		"/tmp";
char	rmtree[]	"/etc/rmtree";
#define	rmtree_args	rmtree+5, "-f"
char	chkptf[]	"/etc/lnodes.chkpt";

int resetflg;

#endif	AUSAM

#ifdef	SYS_SUP_PROCS
/*
 *	system support processes
 */ 
char *sysprocs[]
{
	"/etc/update",
	"/etc/cron",
	0
};
#endif	SYS_SUP_PROCS

#define	SYSPRI	-90	/* run system processes at high priority */
#define	USERPRI	0	/* but not users */

#define	tabsize	NTTYS
#define	all	p = &itab[0]; p < &itab[tabsize]; p++
#define	ever	;;
#define	SINGLE	0173030
#define	REBOOT	0173040

long time();

char	shell[]	"/bin/sh";
char	getty[]	"/etc/getty";
char	runc[]	"/etc/rc";
char	init[]	"/etc/init";
char	ifile[]	"/etc/ttys";
char	utmpf[]	"/etc/utmp";
char	wtmpf[]	"/usr/adm/wtmp";
char	ctty[]	"/dev/tty8";

#ifdef	SINGLE_USER | AUSAM
#ifdef	BASSER
#define	CTTYS	"l"	/* console type for getty */
#define	CTTYC	'l'	/* console type for getty */
#else	BASSER
#define	CTTYS	"d"	/* console type for getty */
#define	CTTYC	'd'	/* console type for getty */
#endif	BASSER
#endif	SINGLE_USER | AUSAM

#ifndef	SINGLE_USER
char	minus[]	"-";
#endif

int fi;
struct
{
	int flag;
	char lins[2];
	char coms[2];
#	ifdef	TTY_GROUP
	char tgroup[2];	/* terminal group */ 
#	endif	TTY_GROUP
} line;

struct tab
{
	int pid;
	int line;
	int comn;
#	ifdef	TTY_GROUP
	int tgp;
#	endif	TTY_GROUP
} itab[tabsize];

#ifndef	AUSAM
struct
{
	char name[8];
	char tty;
	char fill;
	long time;
	int wfill;
} wtmp;

int status;
#endif	AUSAM

/*
 * the main init:	run start-up file "rc"
 *			fork off "getty" for each valid tty
 *			loop, catching dead shells and restarting "getty"
 */ 
main()
{
	register i;
	register struct tab *p, *q;
#ifdef	SHUTDOWN
	int shtdwn();
#endif	SHUTDOWN
#ifdef	AUSAM
	extern char *initwait();
#else
	int reset();
#endif	AUSAM

	nice(SYSPRI);
#ifdef	AUSAM
Reboot:
	/*
	 * write system boot time to wtmp
	 */ 
	wtmp.s_type = S_TYPE;
	wtmp.s_boottime = time();
	wrwtmp();
#endif	AUSAM
	/*
	 * if not single user,
	 * run shell sequence
	 */ 
	if(getcsw() != SINGLE)
	{
#ifndef	AUSAM
		if((i = open(wtmpf, 1)) >= 0)
		{
			seek(i, 0, 2);
			wtmp.tty = '~';
			wtmp.time = time();
			write(i, &wtmp, (sizeof wtmp));
			close(i);
		}
#endif	AUSAM
#ifdef	AUSAML
		/*
		 * if system crashed - do checkpointing
		 */ 
		{
			struct statbuf statb;

			if(stat(chkptf, &statb) != -1 && statb.sb_size1)
			{
				struct lnode ln;

				i = open(chkptf, 0);
				while(read(i, &ln, (sizeof ln)) == (sizeof ln))
				{
					pe.pw_uid = ln.l_uid;
					if(getpwlog(&pe, 0, 0) >= 0)
					{
						pe.pw_usage = ln.l_usage;
						pe.pw_extime = wtmp.s_boottime;
						updtpwent(&pe);
					}
				}
				close(i);
				pwclose();
			}
		}
#endif	AUSAML
		/*
		 *	run shell start up file "rc".
#		ifdef	TALK
		 *
		 *  N.B. -- it should ask if you really
		 *	want to run it!
#		endif	TALK
		 */ 
		i = fork();
		if(i == 0)
		{
			nice(USERPRI);
#ifdef	TALK
			open(ctty, 2);
#else
			open("/", 0);
#endif	TALK
			dup(0);
			dup(1);
			execl(shell, shell+5, runc, 0);
#			ifdef	DEBUG
			prs("init: no ");
			prs(shell);
			prs("!\n");
#			endif	DEBUG
			exit(100);
		}
#ifndef	AUSAM
		while(waitx(&status) != i);
#else
		itab[0].pid = i;
		itab[0].line = ctty[8];
		while(initwait(1) != &itab[0]);
#endif	AUSAM
#ifdef	SYS_SUP_PROCS
		/*
		 * start up known system support processes
		 */ 
		{
			struct
			{
				char **cp;
			};

			for(p.cp = sysprocs; *p.cp; p.cp++)
			{
				switch(fork())
				{
				case 0:
					signal(2, 1);
					signal(3, 1);
					signal(14, 0);
					open(ctty, 2);
					dup(0);
					dup(1);
					execl(*p.cp, *p.cp+5, 0);
#					ifdef	DEBUG
						prs("init: no ");
						prs(*p.cp);
						prs("!\n");
#					endif
					exit(1);
				case -1:
#					ifdef	DEBUG
						prs("init: fork failed\n");
#					endif	DEBUG
					continue;
				default:
					sleep(1);
				}
			}
		}
#endif	SYS_SUP_PROCS
		close(creat(utmpf, 0604));	/* ensure utmp exists */ 
	}
	/*
	 * main loop for hangup signal
	 * close all files and
	 * check switches for magic values
	 */ 
#ifndef	AUSAM
	setexit();
	signal(1, reset);
#else
	signal(1, 1);
Reset:
	resetflg = 0;
#endif	AUSAM
#ifdef	SHUTDOWN
	if(getpid() == 1)
		signal(14, shtdwn);	/*  only process one (the real init) */ 
#endif	SHUTDOWN
	for(i = 0; i < 10; i++)
		close(i);
	switch(getcsw())
	{
	case SINGLE:
	error:
		termall();
#ifdef	!AUSAM | !SINGLE_USER
		i = fork();
		if(i == 0)
		{
			nice(USERPRI);
#ifdef	AUSAM
			signal(2, 1);
			signal(3, 1);
#endif	AUSAM
			open(ctty, 2);
			dup(0);
			dup(1);
#ifndef	SINGLE_USER
			execl(shell, minus, 0);
#else	SINGLE_USER
#			ifndef	TTY_GROUP
			execl(getty, getty+5, CTTYS, 0);
#			else	TTY_GROUP
			execl(getty, getty+5, CTTYS, p->tgroup, 0);
#			endif	TTY_GROUP
#endif	SINGLE_USER
			exit(100);
		}
#ifndef	AUSAM
		while(waitx(&status) != i);
#else
		itab[0].pid = i;
		itab[0].line = ctty[8];
		while(initwait(1) != &itab[0] && !resetflg);
		if(resetflg)
			goto Reset;
#endif	AUSAM
#else	!AUSAM | !SINGLE_USER
		itab[0].line = ctty[8];
		itab[0].comn = CTTYC;
		dfork(&itab[0]);
		while(initwait(1) != &itab[0] && !resetflg);
		if(resetflg)
			goto Reset;
#endif	!AUSAM | !SINGLE_USER

	case REBOOT:
		termall();
		execl(init, init+5, 0);
#ifndef	AUSAM
		reset();
#else
		goto Reboot;
#endif	AUSAM
	}
	/*
	 * open and merge in init file
	 */ 
	fi = open(ifile, 0);
	q = &itab[0];
	while(rline())
	{
		if(line.flag == '0')
			continue;
		for( all)
			if(p->line == line.line || p->line == 0)
			{
				if(p >= q)
				{
					i = p->pid;
					p->pid = q->pid;
					q->pid = i;
					p->line = q->line;
					p->comn = q->comn;
#					ifdef	TTY_GROUP
					p->tgp = q->tgp;
#					endif	TTY_GROUP
					q->line = line.line;
					q->comn = line.comn;
#					ifdef	TTY_GROUP
					q->tgp = line.tgp;
#					endif	TTY_GROUP
					q++;
				}
				break;
			}
	}
	close(fi);
	if(q == &itab[0])
		goto error;
	for(; q < &itab[tabsize]; q++)
		term(q);
	for( all)
		if(p->line != 0 && p->pid == 0)
			dfork(p);
	for(ever)
	{
#ifndef	AUSAM
		i = waitx(&status);
		for( all)
			if(p->pid == i)
			{
				rmut(p);
				dfork(p);
			}
#else	AUSAM
		initwait(0);
		if(resetflg)
			goto Reset;
#endif	AUSAM
	}
}

/*
 * terminate all logged in users
 */ 
termall()
{
	register struct tab *p;

	for( all)
		term(p);
}

/*
 * terminate known process started by init
 */ 
term(ap)
struct tab *ap;
{
	register struct tab *p;

	p = ap;
	if(p->pid != 0)
	{
#ifndef	AUSAM
		rmut(p);
#endif	AUSAM
		kill(p->pid, 9);
#ifndef	AUSAM
		p->pid = 0;
#else
		p->comn = 0;	/* flag for wait to clear pid & line */ 
#endif	AUSAM
	}
#ifdef	AUSAM
	else
#endif	AUSAM
		p->line = 0;
}

/*
 * read a line from "ttys" file
 *	char 0 = '0' or '1' - tty valid flag
 *	char 1 = '0'-'9','a'-'z','A'-'Z' - tty id
 *	char 2 = tty type for getty
#ifdef	TTY_GROUP
 *	char 3 = tty group
#endif	TTY_GROUP
 *	char ... anything you like
 */ 
rline()
{
	static char c[4];

#	ifndef	TTY_GROUP
	if(read(fi, c, 3) != 3)
#	else	TTY_GROUP
	if(read(fi, c, 4) != 4)
#	endif	TTY_GROUP
		return(0);
	line.flag = c[0];
	line.lins[0] = c[1];
	line.coms[0] = c[2];
#	ifdef	TTY_GROUP
	line.tgroup[0] = c[3];
#	endif	TTY_GROUP
	while(read(fi, c, 1) == 1 && c[0] != '\n');
	return(c[0] == '\n');
}

/*
 * fork, open a tty file, and exec getty
 */ 
dfork(ap)
struct tab *ap;
{
	register i;
	register char *tty;
	register struct tab *p;

	p = ap;
	i = fork();
	if(i == 0)
#	ifdef	AUSAM
		newgetty(p);
#	else	AUSAM
	{
		nice(USERPRI);
#		ifdef	SHUTDOWN
		signal(14, 0);
#		endif	SHUTDOWN
		tty = "/dev/ttyx";
		tty[8] = p->lins[0];
		chown(tty, 0);
		chmod(tty, 0600);
		open(tty, 2);
		dup(0);
		dup(1);
#		ifndef	TTY_GROUP
		execl(getty, getty+5, p->coms, p->lins, 0);
#		else	TTY_GROUP
		execl(getty, getty+5, p->coms, p->lins, p->tgroup, 0);
#		endif	TTY_GROUP
#		ifdef	DEBUG
		prs("init: no getty!\n");
#		endif
		sleep(10);	/* avoid thrashing */ 
		exit(100);
	}
#	endif	AUSAM
	if(i == -1)	/* fork failed */ 
		i = 0;
	p->pid = i;
}

#ifdef	AUSAM
/*
 *	set up tty and exec getty
 */ 
newgetty(p)
register struct tab *p;
{
	register char *tty;
#	ifdef	DEBUG
	register i;
#	endif	DEBUG

	nice(USERPRI);
	signal(1, 0);
	signal(2, 1);
	signal(3, 1);
#	ifdef	SHUTDOWN
	signal(14, 0);
#	endif	SHUTDOWN
	tty = "/dev/ttyx";
	tty[8] = p->lins[0];
	chown(tty, 0);
	chmod(tty, 0600);
#	ifdef	DEBUG
	if((i = open(tty, 2)) == 0)
#	else	DEBUG
	if(open(tty, 2) == 0)
#	endif	DEBUG
	{
		dup(0);
		dup(1);
#		ifndef	TTY_GROUP
		execl(getty, getty+5, p->coms, p->lins, 0);
#		else	TTY_GROUP
		execl(getty, getty+5, p->coms, p->lins, p->tgroup, 0);
#		endif	TTY_GROUP
#		ifdef	DEBUG
		prs("init: no getty!\n");
#		endif	DEBUG
	}
#	ifdef	DEBUG
	else if(i > 0)
		prs("init: tty not fd 0!\n");
	else
		prs("init: tty open error!\n");
#	endif	DEBUG
	sleep(10);	/* avoid thrashing */ 
	exit(100);
}

/*
 * initwait collects dead procs and updates various files;
 *
 *	there are three types of procs and the action taken is as follows:
 *	1. tty shells		-> update utmp
 *				-> update pwent with connect time etc.
 *	2. async procs		-> update pwent cpu times etc.
 *	3. last proc of a user	-> update pwent with extime etc.
 *				-> clean out /tmp for this user
 *
 *	all except async root processes cause termination entries in wtmp.
 *
 * N.B. the hangup signal is enabled during "iwait".
 */ 
char *initwait(nofork)
#else	AUSAM
/*
 * rmut removes utmp entry and writes logoff time to wtmp
 */ 
rmut(p)
register struct tab *p;
#endif	AUSAM
{
	register i, f;
	static char zero[(sizeof wtmp)];
#ifdef	AUSAM
	register struct tab *p;
	long ttime, time();
	int reset();
#	ifdef	TRACE
	char tbuf[8];
#	endif	TRACE

	waitbuf.limits.l_refcount = 0;
	waitbuf.limits.l_uid = 0;
	signal(1, reset);
	i = iwait(&waitbuf);
	signal(1, 1);
	if(i == -1)
	{
		if(resetflg)
			return(0);
		else
			return(i);
	}
	for( all)
		if(p->pid == i)	/* tty shell */ 
		{
			p->pid = 0;
			goto out;
		}
	p = 0;	/* async proc */ 
	if(waitbuf.limits.l_uid == 0)	/* ignore async root processes */ 
		return(p);
out:
	if(!nofork)
	{
		/*
		 **	fork off a proc to do accounting and start new getty
		 */ 
		f = 0;
		while(i = fork())
		{
			/** Parent -- return to main **/ 
			if(i == -1)	/* fork failed */ 
			{
				if(++f < FORKTRYS)
				{
					sleep(10);
					continue;
				}
				else
					i = 0;
			}
			if(p && p->comn)
				p->pid = i;	/* mark new tty shell id */ 
			return(p);
		}
	}
	/*
	 **	This is (possibly) the child
	 */ 
#	ifdef	SHUTDOWN
	signal(14, 1);
#	endif	SHUTDOWN
	ttime = time();
#	ifdef	TRACE
		prs(ctime(ttime));
#	endif	TRACE
	if(waitbuf.limits.l_refcount)
		pe.pw_uid = waitbuf.limits.l_uid;
	else
		pe.pw_uid = waitbuf.ppzomb.p_uid;
	if(getpwlog(&pe, 0, 0) < 0)
	{
#		ifdef	DEBUG
		prs("getpwlog fails\n");
#		endif	DEBUG
		pe.pw_uid = 0;
		getpwlog(&pe, 0, 0);
	}
#endif	AUSAM
	/*
	 **	update utmp login record
	 */ 
	if(p && (f = open(utmpf, 2)) >= 0)
	{
		i = p->line;
		if(i >= '0' && i <= '9')
			i = i-'0';
		else if(i >= 'a' && i <= 'z')
			i = 10+i-'a';
		else if(i >= 'A' && i <= 'Z')
			i = 36+i-'A';
		else
			i =+ 62;
		i =* (sizeof wtmp);
#		ifdef	AUSAM
		seek(f, i, 0);
		read(f, &wtmp, (sizeof wtmp));	/* get login time */ 
#		ifdef	TRACE
			prs(ptime(pe.pw_contime, tbuf));
			prs(ctime(wtmp.u_logintime));
#		endif	TRACE
		if(wtmp.u_logintime)
			pe.pw_contime =+ (ttime-wtmp.u_logintime);
		else
		{
			if(pe.pw_uid == 0)
			{
				/* almost certainly a terminating getty */ 
				pwclose();
				close(f);
				goto skipac;
			}
		}
#		ifdef	TRACE
			prs(ptime(pe.pw_contime, tbuf));
			prs("\n");
#		endif	TRACE
#		endif	AUSAM
		seek(f, i, 0);
		write(f, zero, (sizeof wtmp));	/* clear login time */ 
		close(f);
	}
#ifdef	AUSAM
	/*
	 **	update cpu times
	 */ 
	pe.pw_cputime =+ waitbuf.ppzomb.pz_utime+waitbuf.ppzomb.pz_stime;
#	ifdef	AUSAML
	/*
	 **	clean up after user's last proc
	 */ 
	if(pe.pw_uid && waitbuf.limits.l_refcount == 1)
	{
		tmpclean();
		pe.pw_usage = waitbuf.limits.l_usage;
		pe.pw_extime = ttime;
	}
#	endif	AUSAML
#	ifdef	TRACE
	prs(ptime(pe.pw_contime, tbuf));
	prs(ctime(pe.pw_extime));
#	endif	TRACE
	updtpwent(&pe);
	pwclose();
	/*
	 **	Write account record to wtmp
	 */ 
	wtmp.w_type = p?O_TYPE:W_TYPE;
	wtmp.w_ttyid = p?p->line:0;
	wtmp.w_uid = pe.pw_uid;
	wtmp.w_finishtime = ttime;
	wtmp.w_usertime = waitbuf.ppzomb.pz_utime;
	wtmp.w_systime = waitbuf.ppzomb.pz_stime;
	wrwtmp();
skipac:
	if(p && !p->comn)
		p->line = 0;	/* this tty terminated */ 
	/*
	 **	if we didn't fork, return to init
	 */ 
	if(nofork)
		return(p);
	/*
	 **	otherwise, start up a getty if a shell just died
	 */ 
	if(p && p->line)
		newgetty(p);
	exit(0);
#else	AUSAM
	f = open(wtmpf, 1);
	if(f >= 0)
	{
		wtmp.tty = p->line;
		wtmp.time = time();
		seek(f, 0, 2);
		write(f, &wtmp, (sizeof wtmp));
		close(f);
	}
#endif	AUSAM
}

#ifdef	AUSAM
/*
 *	write wtmp entry
 */ 
wrwtmp()
{
	register f;

	if((f = open(wtmpf, 1)) >= 0)
	{
		seek(f, 0, 2);
		write(f, &wtmp, (sizeof wtmp));
		close(f);
	}
}
#ifdef	AUSAML

/*
 * tmpclean removes all files belonging to user in the temp directory
 *	if the user has created any directories, these are removed by
 *	forking off a process to execute "rmtree"(VIII).
 */ 
tmpclean()
{
	register fd;
	static struct
	{
		int f_ino;
		char f_name[14];
		char zero;
	} dirb;
	static struct statbuf statb;
	register char *name = dirb.f_name;
	register dcount = 0;
#	define	dotdot(n)	((n[1]==0||(n[2]==0&&n[1]=='.'))&&n[0]=='.')

	if(chdir(tmpd) < 0)
		return;
	if(fd = open(tmpd, 0) >= 0)
	{
		while(read(fd, &dirb, 16) == 16)
		{
			if(dirb.f_ino
			   && !dotdot(name)
			      && newstat(name, &statb) >= 0
				 && waitbuf.limits.l_uid == statb.sb_uid)
			{
				if((statb.sb_flags&IFTYP) == IFDIR)
				{
					switch(fork())
					{
					case -1:
#						ifdef	DEBUG
							prs("fork failed\n");
#						endif	DEBUG
						continue;
					case 0:
						close(fd);
						pwclose();
						open(ctty, 2);
						dup(0);
						dup(1);
						execl(rmtree, rmtree_args, name, 0);
#						ifdef	DEBUG
						prs("init: no rmtree!\n");
#						endif	DEBUG
						exit(1);
					default:
						waitx(&dirb);
					}
				}
				else
				{
					unlink(name);
				}
			}
		}
		close(fd);
	}
	chdir(homed);
}
#endif	AUSAML

reset()
{
	signal(1, reset);
	resetflg++;
}
#endif	AUSAM

#ifdef	SHUTDOWN
shtdwn()
{
	register struct tab *p;
	register i;

	signal(14, 1);
#ifndef	AUSAM
	for(ever)
	{
		if((i = waitx(&status)) == -1)
			break;
		for( all)
			if(p->pid == i)
				rmut(p);
	}
#else
	resetflg = 0;
	while(initwait(1) != -1)
		if(resetflg)
		{
			for(i = 0; i < 15; i++)
				close(i);
			termall();
			execl(init, init+5, 0);
			sync();
#ifdef	DEBUG
			prs("init: no init!\n");
#endif	DEBUG
			exit(-1);
		}
#endif	AUSAM
	for(i = 0; i < 15; i++)
		close(i);
	sync();
	prs("\n\nall shells dead - shutdown complete\n\n");
	exit(0);
}
#endif	SHUTDOWN

#ifdef	SHUTDOWN | DEBUG
int fd;
/*
 *	WARNING - do not use in process 1
 *		  as this will attach init to the console tty !
 */ 
prs(s)
register char *s;
{
	fd = open(ctty, 1);
	while(*s)
		putc(*s++);
	close(fd);
}

putc(c)
{
	write(fd, &c, 1);
}
#endif	SHUTDOWN | DEBUG