#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