Ultrix-3.1/src/cmd/init.c

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


/**********************************************************************
 *   Copyright (c) Digital Equipment Corporation 1984, 1985, 1986.    *
 *   All Rights Reserved. 					      *
 *   Reference "/usr/src/COPYRIGHT" for applicable restrictions.      *
 **********************************************************************/

/*
 * ULTRIX-11 initialization program - init.c
 *
 * This version of init has been modified for
 * local/remote terminal operation and to reread
 * the ttys file when a hangup signal is received.
 * This allows local terminals to operate without
 * a null modem and for terminals to be placed
 * on-line and taken off-line, and/or change their getty
 * character without rebooting the system.
 *
 * Fred Canter
 *
 * Thanks to Bill Shannon for many of the code changes.
 */
static char Sccsid[] = "@(#)init.c	3.1	10/9/87";
#include <sys/localopts.h>
#include <signal.h>
#include <sys/types.h>
#include <utmp.h>
#include <setjmp.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <fcntl.h>

#define	LINSIZ	sizeof(wtmp.ut_line)
#define	TABSIZ	100
#define	ALL	p = &itab[0]; p < &itab[TABSIZ]; p++
#define	EVER	;;
#define SCPYN(a, b)	strncpy(a, b, sizeof(a))
#define SCMPN(a, b)	strncmp(a, b, sizeof(a))

char	shell[]	= "/bin/sh";
char	getty[]	 = "/etc/getty";
char	minus[]	= "-";
char	runc[]	= "/etc/rc";
char	ifile[]	= "/etc/ttys";
char	utmp[]	= UTMP_FILE;
char	wtmpf[]	= WTMP_FILE;
char	ctty[]	= "/dev/console";
char	dev[]	= "/dev/";
char	elc[]	= "/etc/elc";
unsigned sumcheck = 0;

struct utmp wtmp;
struct
{
	char	line[LINSIZ];
	char	comn;
	char	flag;
} line;
struct	tab
{
	char	line[LINSIZ];
	char	flag;
	char	comn;
	char	xflag;
	int	pid;
	time_t	gettytime;
	int	gettycnt;
} itab[TABSIZ];

extern	errno;

int	fi;
int	mergflag;
char	tty[20];
jmp_buf	sjbuf, shutpass;
time_t	time0;

time_t	time();
int	reset();

/*
 * default characters for single user mode.  This is
 * because ^Z and ^Y will goof you up if you aren't
 * running /bin/csh.
 */
struct	stat statb;
struct ltchars lchars =
{
/*	susp  dsusp rprntc  oflushc werasec  lnextc  iflush   status */
	0377, 0377, CRPRNT, CFLUSH, CWERASE, CLNEXT, CIFLUSH, 0
};

main(argc, argv)
int	argc;
char	*argv[];
{
	register int i, j, elpid;

	time(&time0);
	if((j = argc) == 2) {
		i = argv[1][1] << 8;
		i |= argv[1][0];
		argv[1][0] = ' ';
		argv[1][1] = ' ';
	}
	if ((elpid = fork()) == 0) {	/* Start error log copy process */
		open(ctty, 2);
		dup(0);
		dup(0);
		zaptty();
		execl(elc, elc, (char *)0);
		cmesg("init: can't exec", elc, 0);
		exit(0);
	}
	setjmp(sjbuf);
	signal(SIGINT, reset);
	signal(SIGHUP,SIG_IGN);
	for(EVER) {
		shutdown();
		single();
		if (j != 2 || cksum("/bin/login") || setlimit(i)) {
			cmesg("init: Cannot go multi-user.", 
				"Returning to single user mode", 0);
			sync();
			longjmp(sjbuf, 1);
		}
		runcom();
		merge();
		signal(SIGINT, reset);
		multiple(i);
	}
}

cksum(s)
char *s;
{
	register unsigned int i, j, k;
	int sizes[8], buf[512];
	int fd;

	fd = open(s, 0);
	if (fd < 0) {
		return(-1);
	}
	if (read(fd, sizes, 16) < 0) {
		close(fd);
		return(-1);
	}
	k = 0;
	for (j = sizes[1]/2; j > 0; ) {
		if (read(fd, buf, (j > 512) ? 1024 : (j*2)) < 0) {
			close(fd);
			return(-1);
		}
		for (i = 0; (i < 512) && (j > 0); i++, j--)
			k += buf[i];
	}
	for (j = sizes[2]/2; j > 0; ) {
		if (read(fd, buf, (j > 512) ? 1024 : (j*2)) < 0) {
			close(fd);
			return(-1);
		}
		for (i = 0; (i < 512) && (j > 0); i++, j--)
			k += buf[i];
	}
	close(fd);
	return(k);
}

int	shutreset();

shutdown()
{
	register i, f;
	register struct tab *p;

	signal(SIGINT, SIG_IGN);
	for(ALL) {
		term(p);
		p->line[0] = 0;
	}
	close(creat(utmp, 0644));
	signal(SIGALRM, shutreset);
	if (setjmp(shutpass) == 0) {
		alarm(30);
		for(i=0; i<5; i++)
			kill(-1, SIGKILL);
		while(wait((int *)0) != -1)
			;
		alarm(0);
	}
	acct(0);
	signal(SIGALRM, SIG_DFL);
	for(i=0; i<10; i++)
		close(i);
	f = open(wtmpf, 1);
	if (f >= 0) {
		lseek(f, 0L, 2);
		SCPYN(wtmp.ut_line, "~");
		SCPYN(wtmp.ut_name, "shutdown");
		time(&wtmp.ut_time);
		write(f, (char *)&wtmp, sizeof(wtmp));
		close(f);
	}
}

shutreset()
{
	cmesg("WARNING: Something is hung (won't die); ps axl advised", 0);
	longjmp(shutpass, 1);
}

single()
{
	register pid;
	register xpid;
	extern	errno;

   do {
	pid = fork();
	if(pid == 0) {
		signal(SIGHUP, SIG_DFL);
		signal(SIGINT, SIG_DFL);
		signal(SIGALRM, SIG_DFL);
		open(ctty, 2);
		dup(0);
		dup(0);
		ioctl(0, TIOCSLTC, &lchars);
		execl(shell, minus, (char *)0);
		cmesg("init: can't exec ", shell, 0);
		exit(0);
	}
	sync();
	while((xpid = wait((int *)0)) != pid)
		if (xpid == -1 && errno == ECHILD)
			break;
   } while (xpid == -1);
}

runcom()
{
	register pid, f;

	pid = fork();
	if(pid == 0) {
		open("/", 0);
		dup(0);
		dup(0);
		execl(shell, shell, runc, (char *)0);
		exit(1);
	}
	while(wait((int *)0) != pid)
		;
	f = open(wtmpf, 1);
	if (f >= 0) {
		lseek(f, 0L, 2);
		SCPYN(wtmp.ut_line, "~");
		SCPYN(wtmp.ut_name, "reboot");
		if (time0) {
			wtmp.ut_time = time0;
			time0 = 0;
		} else
			time(&wtmp.ut_time);
		write(f, (char *)&wtmp, sizeof(wtmp));
		close(f);
	}
}

setmerge()
{
	signal(SIGHUP, SIG_IGN);
	mergflag = 1;
}

multiple(lim)
register lim;
{
	register struct tab *p;
	register pid;

loop:
	mergflag = 0;
	signal(SIGHUP, setmerge);
	for(EVER) {
		/*
		 * Keep resetting the limit to keep someone from
		 * adb'ing the kernel.  If we can't set the limit,
		 * then we go back to single user mode.
		 */
		if(setlimit(lim)) {
			cmesg("init: returning to single user mode", 0);
			return;
		}
		pid = wait((int *)0);
		if(mergflag) {
			merge();
			goto loop;
		}
		if(pid == -1) {
			if (errno == ECHILD) {
				cmesg("init: no children left", 0);
				return;
			}
			goto loop;
		}
		for(ALL)
			if(p->pid == pid || p->pid == -1) {
				rmut(p);
				dfork(p);
			}
	}
}

term(p)
register struct tab *p;
{

	if(p->pid != 0 && p->pid != -1) {
		rmut(p);
		kill(p->pid, SIGKILL);
	}
	p->pid = 0;
}

rline()
{
	register c, i;

loop:
	c = get();
	if(c < 0)
		return(0);
	if(c == 0)
		goto loop;
	line.flag = c;
	c = get();
	if(c <= 0)
		goto loop;
	line.comn = c;
	SCPYN(line.line, "");
	for (i=0; i<LINSIZ; i++) {
		c = get();
		if(c <= 0 || c == ' ' || c == '\t')
			break;
		line.line[i] = c;
	}

	while(c > 0)
		c = get();
	if (i == 0 || line.flag == '#')	/* skip blank lines and comments */
		goto loop;
	if(line.line[0] == 0)
		goto loop;
	strcpy(tty, dev);
	strncat(tty, line.line, LINSIZ);
	if(access(tty, 06) < 0)
		goto loop;
	return(1);
}

get()
{
	char b;

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

#define	FOUND	1
#define	CHANGE	2

merge()
{
	register struct tab *p;

	fi = open(ifile, 0);
	if(fi < 0)
		return;
	for(ALL)
		p->xflag = 0;
	while(rline()) {
		for(ALL) {
			if (SCMPN(p->line, line.line))
				continue;
			p->xflag |= FOUND;
			if((line.comn != p->comn) || (line.flag != p->flag)) {
				p->xflag |= CHANGE;
				p->flag = line.flag;
				p->comn = line.comn;
			}
			goto contin1;
		}
		if(line.flag == '0')
			goto contin1;
		for(ALL) {
			if(p->line[0] != 0)
				continue;
			SCPYN(p->line, line.line);
			p->xflag |= FOUND|CHANGE;
			p->flag = line.flag;
			p->comn = line.comn;
			goto contin1;
		}
	contin1:
		;
	}
	close(fi);
	for(ALL) {
		if((p->xflag&FOUND) == 0) {
			term(p);
			clrtty(p);
			p->line[0] = 0;
		}
		if((p->xflag&CHANGE) != 0) {
			term(p);
			clrtty(p);
			if((p->flag == '1') || (p->flag == '2'))
				dfork(p);
		}
	}
}

/*
 * Attempt to force the terminal to a known state
 * by doing a non blocking open then a close.
 * This forces DTR to drop when the state of a line
 * is changed from modem to any other state.
 * Also, set the line to local or remote.
 *
 * Added 9/12/87 -- Fred Canter
 */

clrtty(p)
register struct tab *p;
{
	register int fd;

	/*
	 * Check for bogus tty name.
	 * This should not happen.
	 */
	if(p->line[0] == 0)
		return;
	strcpy(tty, dev);
	strncat(tty, p->line, LINSIZ);
	fd = open(tty, O_RDONLY|O_NDELAY);
	if(fd >= 0)
		close(fd);
	zaptty();
	if(stat(&tty, &statb) < 0)
		return;
	/*
	 * Set line to local or remote.
	 */
	ttlocl(statb.st_rdev, (((p->flag - '0') & 02) >> 1));
}

setlimit(n)
{
	switch(n) {
	case '!&':
		lim(8*17);
		break;
	case '.:':
		lim(16*17);
		break;
	case '%$':
		lim(32*17);
		break;
	case ';#':
		lim(100*17);
		break;
	default:
		lim(-1);
		return(-1);
	}
	return(0);
}

dfork(p)
struct tab *p;
{
	register pid;
	time_t	t;
	int dowait = 0;

	time(&t);
	p->gettycnt++;
	if ((t - p->gettytime) >= 60) {
		p->gettytime = t;
		p->gettycnt = 1;
	} else if (p->gettycnt >= 5) {
		dowait = 1;
		p->gettytime = t;
		p->gettycnt = 1;
	}
	pid = fork();
	if(pid == 0) {
		signal(SIGHUP, SIG_DFL);
		signal(SIGINT, SIG_DFL);
		strcpy(tty, dev);
		strncat(tty, p->line, LINSIZ);
		if (dowait) {
			cmesg("init: ", getty, tty," failing, sleeping");
			sleep(30);
		}
		chown(tty, 0, 0);
		chmod(tty, 0622);
		if (open(tty, 2) < 0) {
			int repcnt = 0;
			do {
				if (repcnt % 10 == 0)
				    cmesg("init: cannot open", tty, 0);
				repcnt++;
				sleep(60);
			} while (open(tty, 2) < 0);
		}
		dup(0);
		dup(0);
		tty[0] = p->comn;
		tty[1] = 0;
		execl(getty, minus, tty, (char *)0);
		cmesg("init: can't exec ", getty, tty, 0);
		exit(0);
	}
	p->pid = pid;
}

rmut(p)
register struct tab *p;
{
	register f;
	int found = 0;

	f = open(utmp, 2);
	if(f >= 0) {
		while(read(f, (char *)&wtmp, sizeof(wtmp)) == sizeof(wtmp)) {
			if (SCMPN(wtmp.ut_line, p->line) || wtmp.ut_name[0]==0)
				continue;
			lseek(f, -(long)sizeof(wtmp), 1);
			SCPYN(wtmp.ut_name, "");
			time(&wtmp.ut_time);
			write(f, (char *)&wtmp, sizeof(wtmp));
			found++;
		}
		close(f);
	}
	if (found) {
		f = open(wtmpf, 1);
		if (f >= 0) {
			SCPYN(wtmp.ut_line, p->line);
			SCPYN(wtmp.ut_name, "");
			time(&wtmp.ut_time);
			lseek(f, (long)0, 2);
			write(f, (char *)&wtmp, sizeof(wtmp));
			close(f);
		}
		/*
		 * After a proper login force reset
		 * of error detection code in dfork.
		 */
		p->gettytime = 0;
	}
}

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

cmesg(s1, s2, s3, s4)
char *s1, *s2, *s3, *s4;
{
	register int pid;
	char c = ' ';

	pid = fork();
	if (pid == 0) {
		int fd = open(ctty, 2);
		write(fd, s1, strlen(s1));
		if (s2) {
			write(fd, &c, 1);
			write(fd, s2, strlen(s2));
			if (s3) {
				write(fd, &c, 1);
				write(fd, s3, strlen(s3));
				if (s4) {
					write(fd, &c, 1);
					write(fd, s4, strlen(s4));
				}
			}
		}
		write(fd, "\r\n", 2);
		close(fd);
		exit(0);
	}
	while (wait((int *)0) != pid)
		;
}