/* * init.c */ #include "whoami.h" #include #include #include #include #ifdef UCB_AUTOBOOT #include #endif #include #include #define VHANGUP /* undefine if you don't have vhangup */ #define CONFIGOPTS "-vc" #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 plus[] = "+"; /* this tells getty we are in sp. sess. */ char runc[] = "/etc/rc"; char ifile[] = "/etc/ttys"; char utmp[] = "/etc/utmp"; char wtmpf[] = "/usr/adm/wtmp"; char ctty[] = "/dev/console"; char dev[] = "/dev/"; char config[]= "/etc/autoconfig"; struct utmp wtmp; struct { char line[LINSIZ]; char comn; char flag; } line; struct tab { char line[LINSIZ]; char comn; char xflag; int pid; } itab[TABSIZ]; extern errno; int fi; int mergflag; int multiuser; /* * Modes: bits in first char of /etc/ttys line */ #define NORMAL 1 /* anyone can login */ #define SP_SESS 2 /* only root can login */ int mode = NORMAL; char tty[20]; jmp_buf sjbuf, shutpass; time_t time0; time_t time(); int reset(); int idle(), sp_ss(), setmerge(); char *strcpy(), *strcat(); long lseek(); #ifdef UCB_AUTOBOOT main(ac, av) char **av; #else main () #endif { int howto, oldhowto; time0 = time((time_t *) 0); #ifdef UCB_AUTOBOOT signal(SIGQUIT, idle); if (ac > 1) { howto = * ((int *) av[1]); * ((int *) av[1]) = 0; /* don't confuse ps with binary args */ } else howto = RB_SINGLE; if (autoconfig() == 0) howto = RB_SINGLE; #else autoconfig(); #endif setjmp(sjbuf); signal(SIGTERM, reset); signal(SIGINT, sp_ss); signal(SIGHUP, setmerge); for(EVER) { shutdown(); #ifdef UCB_AUTOBOOT oldhowto = howto; howto = RB_SINGLE; if (oldhowto & RB_SINGLE) single(); if (runcom(oldhowto) == 0) mode = SP_SESS; #else single(); runcom(); #endif merge(); multiple(); } } int shutreset(); shutdown() { register i, f; register struct tab *p; multiuser = 0; 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\n", 0, 0); longjmp(shutpass, 1); } single() { register pid; register xpid; extern errno; multiuser = 0; do { pid = fork(); if(pid == 0) { /* alarm(300); */ signal(SIGTERM, SIG_DFL); signal(SIGHUP, SIG_DFL); signal(SIGALRM, SIG_DFL); open(ctty, 2); dup(0); dup(0); execl(shell, minus, (char *)0); cmesg("Init: can't exec ", shell, "\r\n"); exit(0); } while((xpid = wait((int *)0)) != pid) if (xpid == -1 && errno == ECHILD) break; } while (xpid == -1); } #ifdef UCB_AUTOBOOT runcom(howto) int howto; #else runcom() #endif { register pid, f; int status; char *arg1, *arg2; pid = fork(); if(pid == 0) { open("/", 0); dup(0); dup(0); #ifdef UCB_AUTOBOOT if ((howto & RB_SINGLE) || (howto & RB_NOFSCK)) arg1 = "fastboot"; else arg1 = "autoboot"; if (howto & RB_POWRFAIL) arg2 = "powerfail"; else arg2 = (char *)0; execl(shell, shell, runc, arg1, arg2, (char *)0); exit(1); #else execl(shell, shell, runc, (char *)0); exit(1); #endif } while(wait(&status) != pid) ; #ifdef UCB_AUTOBOOT if(status) return(0); #endif 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); } return(1); } setmerge() { signal(SIGHUP, SIG_IGN); mergflag = 1; } multiple() { register struct tab *p; register pid; loop: multiuser = 1; mergflag = 0; signal(SIGHUP, setmerge); for(EVER) { pid = wait((int *)0); if(mergflag) { merge(); goto loop; } if(pid == -1) { if (errno == ECHILD) { cmesg("Init: ", "no children left", "\r\n"); return; } goto loop; } for(ALL) if(p->pid == pid || p->pid == -1) { #ifdef UCB_SUBMIT if (p->pid != -1) killbkg(p->pid, SIGKILL); #endif rmut(p); dfork(p); } } } term(p) register struct tab *p; { if(p->pid != 0 && p->pid != -1) { rmut(p); kill(p->pid, SIGKILL); #ifdef UCB_SUBMIT killbkg(p->pid, SIGKILL); #endif } 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 0) c = get(); if(line.line[0] == 0) goto loop; if(line.flag == '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()) { if ((line.flag < '1') || (line.flag > '9')) continue; if (((line.flag-'0') & mode) == 0) continue; for(ALL) { if (SCMPN(p->line, line.line)) continue; p->xflag |= FOUND; if(line.comn != p->comn) { p->xflag |= CHANGE; p->comn = line.comn; } goto contin1; } for(ALL) { if(p->line[0] != 0) continue; SCPYN(p->line, line.line); p->xflag |= FOUND|CHANGE; p->comn = line.comn; goto contin1; } contin1: ; } close(fi); for(ALL) { if((p->xflag&FOUND) == 0) { term(p); p->line[0] = 0; } if((p->xflag&CHANGE) != 0) { term(p); dfork(p); } #ifdef UCB_AUTOBOOT /* * If we are resuming after an idle (possibly a failed reboot) * we need to restart the lines that shut down. */ if (p->pid == -1) dfork(p); #endif } } dfork(p) struct tab *p; { register pid; pid = fork(); if(pid == 0) { signal(SIGTERM, SIG_DFL); signal(SIGHUP, SIG_IGN); strcpy(tty, dev); strncat(tty, p->line, LINSIZ); chown(tty, 0, 0); chmod(tty, 0622); if (open(tty, 2) < 0) { int repcnt = 0; do { if (repcnt % 10 == 0) cmesg("init: ",tty,": cannot open\n\r"); repcnt++; sleep(60); } while (open(tty, 2) < 0); } #ifdef VHANGUP vhangup(); #endif signal(SIGHUP, SIG_DFL); open(tty, 2); close(0); dup(1); dup(0); tty[0] = p->comn; tty[1] = 0; if(mode == SP_SESS) execl(getty, plus, tty, (char *)0); else execl(getty, minus, tty, (char *)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); } } } reset() { longjmp(sjbuf, 1); } /* * Toggle special-session mode. * Do a shutdown() so that getty's are reissued in the new mode. */ sp_ss() { signal(SIGINT, SIG_IGN); mergflag++; shutdown(); if (mode == NORMAL) mode = SP_SESS; else { /* * Returning to normal operation. * Run the rc file; either it hasn't been finished * since we failed a filesystem check, or we shut down. */ #ifdef UCB_AUTOBOOT (void) runcom(RB_NOFSCK); #else (void) runcom(); #endif mode = NORMAL; } signal(SIGINT, sp_ss); } #ifdef UCB_AUTOBOOT idle() { register struct tab *p; register pid; signal(SIGQUIT,idle); for (;;) { pid = wait((int *) 0); if (mergflag) { if (!multiuser) reset(); else return; } if (pid == -1) pause(); else { for (ALL) if (p->pid == pid) { rmut(p); p->pid = -1; } } } } #endif cmesg(s1, s2, s3) char *s1, *s2, *s3; { register int pid; pid = fork(); if (pid == 0) { int fd = open(ctty, 2); write(fd, s1, strlen(s1)); if (s2) write(fd, s2, strlen(s2)); if (s3) write(fd, s3, strlen(s3)); close(fd); exit(0); } while (wait((int *)0) != pid) ; } autoconfig() { int pid, status; cmesg("\r\nCONFIGURE SYSTEM:\n", 0, 0); if ((pid = fork()) == 0) { open(ctty, 2); dup(0); dup(0); execl(config, "autoconfig", CONFIGOPTS, 0); printf("Couldn't exec %s\n", config); exit(AC_SETUP); } while (wait(&status) != pid) ; if ((status & 0377) == 0) status >>= 8; else status = AC_SINGLE; switch (status) { case AC_SETUP: cmesg("Configuration setup error\n", 0, 0); return 0; case AC_SINGLE: cmesg("SERIOUS CONFIGURATION ERROR\n", 0, 0); return 0; case AC_OK: return 1; default: cmesg("Unrecognized return from configure\n", 0, 0); return 0; } }