121135Sdist /* 228801Skarels * Copyright (c) 1980,1986 Regents of the University of California. 321135Sdist * All rights reserved. The Berkeley software License Agreement 421135Sdist * specifies the terms and conditions for redistribution. 521135Sdist */ 621135Sdist 712682Ssam #ifndef lint 8*42408Smckusick static char sccsid[] = "@(#)init.c 5.16 (Berkeley) 05/27/90"; 921135Sdist #endif not lint 1012682Ssam 111029Sbill #include <sys/types.h> 1237284Sbostic #include <sys/file.h> 1337284Sbostic #include <sys/signal.h> 1437284Sbostic #include <sys/reboot.h> 1537284Sbostic #include <sys/syslog.h> 1637284Sbostic #include <sys/stat.h> 1742407Smarc #include <sys/ioctl.h> 181029Sbill #include <setjmp.h> 1935606Sbostic #include <utmp.h> 202821Swnj #include <errno.h> 2116452Sroot #include <ttyent.h> 2237284Sbostic #include "pathnames.h" 231029Sbill 2424494Skarels #define CMDSIZ 200 /* max string length for getty or window command*/ 2523147Sbloom #define ALL p = itab; p ; p = p->next 261029Sbill #define EVER ;; 271029Sbill #define SCPYN(a, b) strncpy(a, b, sizeof(a)) 281029Sbill #define SCMPN(a, b) strncmp(a, b, sizeof(a)) 291029Sbill 3037284Sbostic char shell[] = _PATH_BSHELL; 311029Sbill char minus[] = "-"; 3237284Sbostic char runc[] = _PATH_RC; 3337284Sbostic char ctty[] = _PATH_CONSOLE; 341029Sbill 351029Sbill struct tab 361029Sbill { 3735670Sbostic char line[UT_LINESIZE]; 3818542Sralph char comn[CMDSIZ]; 391029Sbill char xflag; 401029Sbill int pid; 4118542Sralph int wpid; /* window system pid for SIGHUP */ 4218542Sralph char wcmd[CMDSIZ]; /* command to start window system process */ 435971Sroot time_t gettytime; 445971Sroot int gettycnt; 4522181Skarels time_t windtime; 4622181Skarels int windcnt; 4723147Sbloom struct tab *next; 4823147Sbloom } *itab; 491029Sbill 501029Sbill int fi; 511029Sbill int mergflag; 521029Sbill char tty[20]; 531429Sbill jmp_buf sjbuf, shutpass; 541029Sbill 551029Sbill char *strcpy(), *strcat(); 561029Sbill long lseek(); 5742407Smarc void idle(), merge(), reset(); 581029Sbill 5918542Sralph struct sigvec rvec = { reset, sigmask(SIGHUP), 0 }; 6013021Ssam 61*42408Smckusick #if defined(vax) || defined(tahoe) || defined(hp300) 621029Sbill main() 631029Sbill { 6429836Ssam #if defined(tahoe) 6529836Ssam register int r12; /* make sure r11 gets bootflags */ 6629836Ssam #endif 671403Sbill register int r11; /* passed thru from boot */ 6813021Ssam #else 699869Spugs main(argc, argv) 709869Spugs char **argv; 719869Spugs { 7213021Ssam #endif 73*42408Smckusick #ifdef __GNUC__ 74*42408Smckusick /* insure proper semantics for setjmp/longjmp */ 75*42408Smckusick static 76*42408Smckusick #endif 7742407Smarc int howto, oldhowto, started; 781403Sbill 79*42408Smckusick #ifdef __GNUC__ 80*42408Smckusick #ifdef hp300 81*42408Smckusick asm("movl d7,%0" : "=rm" (howto)); 82*42408Smckusick #else 83*42408Smckusick asm("movl r11,%0" : "=rm" (howto)); 84*42408Smckusick #endif 85*42408Smckusick #else 86*42408Smckusick #if defined(vax) || defined(tahoe) || defined(hp300) 871403Sbill howto = r11; 8813021Ssam #else 8942407Smarc howto = 0; 909869Spugs if (argc > 1 && argv[1][0] == '-') { 919869Spugs char *cp; 929869Spugs 93*42408Smckusick howto = 0; 949869Spugs cp = &argv[1][1]; 959869Spugs while (*cp) switch (*cp++) { 969869Spugs case 'a': 979869Spugs howto |= RB_ASKNAME; 989869Spugs break; 999869Spugs case 's': 1009869Spugs howto |= RB_SINGLE; 1019869Spugs break; 1029869Spugs } 1039869Spugs } else { 1049869Spugs howto = RB_SINGLE; 1059869Spugs } 10613021Ssam #endif 107*42408Smckusick #endif /* !__GNUC__ */ 10842407Smarc if (getuid() != 0) 10942407Smarc exit(1); 11024854Seric openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); 11142407Smarc if (setsid() < 0) 11242407Smarc syslog(LOG_ERR, "setsid failed (initial) %m"); 11313021Ssam sigvec(SIGTERM, &rvec, (struct sigvec *)0); 1142821Swnj signal(SIGTSTP, idle); 1151029Sbill signal(SIGSTOP, SIG_IGN); 1161029Sbill signal(SIGTTIN, SIG_IGN); 1171029Sbill signal(SIGTTOU, SIG_IGN); 11813021Ssam (void) setjmp(sjbuf); 11942407Smarc for (started = 0; ; ) { 1201403Sbill oldhowto = howto; 1211403Sbill howto = RB_SINGLE; 12242407Smarc if (started && setjmp(shutpass) == 0) 1231429Sbill shutdown(); 12442407Smarc started = 1; 1251403Sbill if (oldhowto & RB_SINGLE) 1261403Sbill single(); 1271403Sbill if (runcom(oldhowto) == 0) 1281403Sbill continue; 1291029Sbill merge(); 1301029Sbill multiple(); 1311029Sbill } 1321029Sbill } 1331029Sbill 13442407Smarc void shutreset(); 1351429Sbill 1361029Sbill shutdown() 1371029Sbill { 1381029Sbill register i; 13923147Sbloom register struct tab *p, *p1; 1401029Sbill 1411029Sbill signal(SIGHUP, SIG_IGN); 14223147Sbloom for (p = itab; p ; ) { 1431029Sbill term(p); 14423147Sbloom p1 = p->next; 14523147Sbloom free(p); 14623147Sbloom p = p1; 1471029Sbill } 14823147Sbloom itab = (struct tab *)0; 1491429Sbill signal(SIGALRM, shutreset); 15028801Skarels (void) kill(-1, SIGTERM); /* one chance to catch it */ 15128801Skarels sleep(5); 1521429Sbill alarm(30); 15313021Ssam for (i = 0; i < 5; i++) 1541029Sbill kill(-1, SIGKILL); 15513021Ssam while (wait((int *)0) != -1) 1561029Sbill ; 1571029Sbill alarm(0); 1581429Sbill shutend(); 1591429Sbill } 1601429Sbill 16142407Smarc char shutfailm[] = "init: WARNING: something is hung (won't die); ps axl advised\n"; 1621429Sbill 16342407Smarc void 1641429Sbill shutreset() 1651429Sbill { 1661429Sbill int status; 1671429Sbill 1681429Sbill if (fork() == 0) { 1691429Sbill int ct = open(ctty, 1); 1701429Sbill write(ct, shutfailm, sizeof (shutfailm)); 1711429Sbill sleep(5); 1721429Sbill exit(1); 1731429Sbill } 1741429Sbill sleep(5); 1751429Sbill shutend(); 1761429Sbill longjmp(shutpass, 1); 1771429Sbill } 1781429Sbill 1791429Sbill shutend() 1801429Sbill { 181*42408Smckusick register i; 1821429Sbill 1832821Swnj acct(0); 1841029Sbill signal(SIGALRM, SIG_DFL); 18513021Ssam for (i = 0; i < 10; i++) 1861029Sbill close(i); 18735606Sbostic logwtmp("~", "shutdown", ""); 1881029Sbill } 1891029Sbill 1901029Sbill single() 1911029Sbill { 1921029Sbill register pid; 1932821Swnj register xpid; 19435606Sbostic extern int errno; 1951029Sbill 19613021Ssam do { 19713021Ssam pid = fork(); 19813021Ssam if (pid == 0) { 19913021Ssam signal(SIGTERM, SIG_DFL); 20013021Ssam signal(SIGHUP, SIG_DFL); 20113021Ssam signal(SIGALRM, SIG_DFL); 20216452Sroot signal(SIGTSTP, SIG_IGN); 20342407Smarc if (setsid() < 0) 20442407Smarc syslog(LOG_ERR, "setsid failed (single): %m"); 20513021Ssam (void) open(ctty, O_RDWR); 20642407Smarc if (ioctl(0, TIOCSCTTY, 0) < 0) 20742407Smarc syslog(LOG_ERR, "TIOCSCTTY failed: %m"); 20813021Ssam dup2(0, 1); 20913021Ssam dup2(0, 2); 21013021Ssam execl(shell, minus, (char *)0); 21130516Sbostic perror(shell); 21213021Ssam exit(0); 21313021Ssam } 21413021Ssam while ((xpid = wait((int *)0)) != pid) 21513021Ssam if (xpid == -1 && errno == ECHILD) 21613021Ssam break; 21713021Ssam } while (xpid == -1); 2181029Sbill } 2191029Sbill 2201403Sbill runcom(oldhowto) 2211403Sbill int oldhowto; 2221029Sbill { 223*42408Smckusick register pid; 2241403Sbill int status; 2251029Sbill 2261029Sbill pid = fork(); 22713021Ssam if (pid == 0) { 22842407Smarc (void) open(ctty, O_RDONLY); 22913021Ssam dup2(0, 1); 23013021Ssam dup2(0, 2); 23142407Smarc if (setsid() < 0) 23242407Smarc syslog(LOG_ERR, "setsid failed (runcom) %m"); 23342407Smarc if (ioctl(0, TIOCSCTTY, 0) < 0) 23442407Smarc syslog(LOG_ERR, "TIOCSCTTY failed (runcom) %m"); 2351403Sbill if (oldhowto & RB_SINGLE) 2361403Sbill execl(shell, shell, runc, (char *)0); 2371403Sbill else 2381403Sbill execl(shell, shell, runc, "autoboot", (char *)0); 2391403Sbill exit(1); 2401029Sbill } 24113021Ssam while (wait(&status) != pid) 2421029Sbill ; 24313021Ssam if (status) 24413021Ssam return (0); 24535606Sbostic logwtmp("~", "reboot", ""); 24613021Ssam return (1); 2471029Sbill } 2481029Sbill 24918542Sralph struct sigvec mvec = { merge, sigmask(SIGTERM), 0 }; 25013021Ssam /* 25113021Ssam * Multi-user. Listen for users leaving, SIGHUP's 25213021Ssam * which indicate ttys has changed, and SIGTERM's which 25313021Ssam * are used to shutdown the system. 25413021Ssam */ 2551029Sbill multiple() 2561029Sbill { 25742407Smarc extern int errno; 2581029Sbill register struct tab *p; 2591029Sbill register pid; 26023147Sbloom int omask; 2611029Sbill 26213021Ssam sigvec(SIGHUP, &mvec, (struct sigvec *)0); 26313021Ssam for (EVER) { 2641029Sbill pid = wait((int *)0); 26542407Smarc /* SHOULD FIX THIS IN THE KERNEL */ 26642407Smarc if (pid == -1 && errno != EINTR) 2671029Sbill return; 26830516Sbostic omask = sigblock(sigmask(SIGHUP)); 26918542Sralph for (ALL) { 27018542Sralph /* must restart window system BEFORE emulator */ 27118542Sralph if (p->wpid == pid || p->wpid == -1) 27218542Sralph wstart(p); 27313021Ssam if (p->pid == pid || p->pid == -1) { 27418542Sralph /* disown the window system */ 27518542Sralph if (p->wpid) 27618542Sralph kill(p->wpid, SIGHUP); 27735445Sbostic cleanutmp(p); 2781029Sbill dfork(p); 2791029Sbill } 28018542Sralph } 28123147Sbloom sigsetmask(omask); 2821029Sbill } 2831029Sbill } 2841029Sbill 28513021Ssam /* 28613021Ssam * Merge current contents of ttys file 28713021Ssam * into in-core table of configured tty lines. 28813021Ssam * Entered as signal handler for SIGHUP. 28913021Ssam */ 29013021Ssam #define FOUND 1 29113021Ssam #define CHANGE 2 29218542Sralph #define WCHANGE 4 29313021Ssam 29442407Smarc void 29513021Ssam merge() 29613021Ssam { 29713021Ssam register struct tab *p; 29816452Sroot register struct ttyent *t; 29923147Sbloom register struct tab *p1; 30013021Ssam 30113021Ssam for (ALL) 30213021Ssam p->xflag = 0; 30316452Sroot setttyent(); 30416452Sroot while (t = getttyent()) { 30516452Sroot if ((t->ty_status & TTY_ON) == 0) 30616452Sroot continue; 30713021Ssam for (ALL) { 30816452Sroot if (SCMPN(p->line, t->ty_name)) 30913021Ssam continue; 31013021Ssam p->xflag |= FOUND; 31116452Sroot if (SCMPN(p->comn, t->ty_getty)) { 31213021Ssam p->xflag |= CHANGE; 31316452Sroot SCPYN(p->comn, t->ty_getty); 31413021Ssam } 31530516Sbostic if (SCMPN(p->wcmd, t->ty_window ? t->ty_window : "")) { 31618542Sralph p->xflag |= WCHANGE|CHANGE; 31718542Sralph SCPYN(p->wcmd, t->ty_window); 31818542Sralph } 31913021Ssam goto contin1; 32013021Ssam } 32118542Sralph 32223147Sbloom /* 32323147Sbloom * Make space for a new one 32423147Sbloom */ 32523147Sbloom p1 = (struct tab *)calloc(1, sizeof(*p1)); 32623147Sbloom if (!p1) { 32723147Sbloom syslog(LOG_ERR, "no space for '%s' !?!", t->ty_name); 32813021Ssam goto contin1; 32913021Ssam } 33023147Sbloom /* 33123147Sbloom * Put new terminal at the end of the linked list. 33223147Sbloom */ 33323147Sbloom if (itab) { 33423147Sbloom for (p = itab; p->next ; p = p->next) 33523147Sbloom ; 33623147Sbloom p->next = p1; 33723147Sbloom } else 33823147Sbloom itab = p1; 33923147Sbloom 34023147Sbloom p = p1; 34123147Sbloom SCPYN(p->line, t->ty_name); 34223147Sbloom p->xflag |= FOUND|CHANGE; 34323147Sbloom SCPYN(p->comn, t->ty_getty); 34430516Sbostic if (t->ty_window && strcmp(t->ty_window, "") != 0) { 34523147Sbloom p->xflag |= WCHANGE; 34623147Sbloom SCPYN(p->wcmd, t->ty_window); 34723147Sbloom } 34813021Ssam contin1: 34913021Ssam ; 35013021Ssam } 35116452Sroot endttyent(); 35223147Sbloom p1 = (struct tab *)0; 35313021Ssam for (ALL) { 35413021Ssam if ((p->xflag&FOUND) == 0) { 35513021Ssam term(p); 35618542Sralph wterm(p); 35723147Sbloom if (p1) 35823147Sbloom p1->next = p->next; 35923147Sbloom else 36023147Sbloom itab = p->next; 36123147Sbloom free(p); 36223147Sbloom p = p1 ? p1 : itab; 36323147Sbloom } else { 36423147Sbloom /* window system should be started first */ 36523147Sbloom if (p->xflag&WCHANGE) { 36623147Sbloom wterm(p); 36723147Sbloom wstart(p); 36823147Sbloom } 36923147Sbloom if (p->xflag&CHANGE) { 37023147Sbloom term(p); 37123147Sbloom dfork(p); 37223147Sbloom } 37313021Ssam } 37423147Sbloom p1 = p; 37513021Ssam } 37613021Ssam } 37713021Ssam 3781029Sbill term(p) 37913021Ssam register struct tab *p; 3801029Sbill { 3811029Sbill 38213021Ssam if (p->pid != 0) { 38335445Sbostic cleanutmp(p); 3841029Sbill kill(p->pid, SIGKILL); 3851029Sbill } 3861029Sbill p->pid = 0; 38718542Sralph /* send SIGHUP to get rid of connections */ 38818542Sralph if (p->wpid > 0) 38918542Sralph kill(p->wpid, SIGHUP); 3901029Sbill } 3911029Sbill 3921029Sbill dfork(p) 39313021Ssam struct tab *p; 3941029Sbill { 3951029Sbill register pid; 3965971Sroot time_t t; 3975971Sroot int dowait = 0; 3981029Sbill 3995971Sroot time(&t); 4005971Sroot p->gettycnt++; 4015971Sroot if ((t - p->gettytime) >= 60) { 4025971Sroot p->gettytime = t; 4035971Sroot p->gettycnt = 1; 40418542Sralph } else if (p->gettycnt >= 5) { 40518542Sralph dowait = 1; 40618542Sralph p->gettytime = t; 40718542Sralph p->gettycnt = 1; 4085971Sroot } 4091029Sbill pid = fork(); 41013021Ssam if (pid == 0) { 4116816Ssam signal(SIGTERM, SIG_DFL); 4126816Ssam signal(SIGHUP, SIG_IGN); 41322181Skarels sigsetmask(0); /* since can be called from masked code */ 4145971Sroot if (dowait) { 41518542Sralph syslog(LOG_ERR, "'%s %s' failing, sleeping", p->comn, p->line); 41618542Sralph closelog(); 4175971Sroot sleep(30); 4185971Sroot } 41942407Smarc if (setsid() < 0) 42042407Smarc syslog(LOG_ERR, "setsid failed(dfork) %m"); 42118542Sralph execit(p->comn, p->line); 4221029Sbill exit(0); 4231029Sbill } 4241029Sbill p->pid = pid; 4251029Sbill } 4261029Sbill 42735445Sbostic cleanutmp(p) 42813021Ssam register struct tab *p; 4291029Sbill { 43035445Sbostic if (logout(p->line)) { 43142407Smarc logwtmp(p->line, "", ""); 43217582Ssam /* 43317582Ssam * After a proper login force reset 43417582Ssam * of error detection code in dfork. 43517582Ssam */ 43617582Ssam p->gettytime = 0; 43722181Skarels p->windtime = 0; 4381029Sbill } 4391029Sbill } 4401029Sbill 44142407Smarc void 4421029Sbill reset() 4431029Sbill { 4441029Sbill longjmp(sjbuf, 1); 4451029Sbill } 4462821Swnj 44713021Ssam jmp_buf idlebuf; 44813021Ssam 44942407Smarc void 45013021Ssam idlehup() 45113021Ssam { 45213021Ssam longjmp(idlebuf, 1); 45313021Ssam } 45413021Ssam 45542407Smarc void 4562821Swnj idle() 4572821Swnj { 4582821Swnj register struct tab *p; 4592821Swnj register pid; 4602821Swnj 46113021Ssam signal(SIGHUP, idlehup); 46218542Sralph for (EVER) { 46313021Ssam if (setjmp(idlebuf)) 46413021Ssam return; 4652821Swnj pid = wait((int *) 0); 46613021Ssam if (pid == -1) { 46713021Ssam sigpause(0); 46813021Ssam continue; 4692821Swnj } 47018542Sralph for (ALL) { 47118542Sralph /* if window system dies, mark it for restart */ 47218542Sralph if (p->wpid == pid) 47318542Sralph p->wpid = -1; 47413021Ssam if (p->pid == pid) { 47535445Sbostic cleanutmp(p); 47613021Ssam p->pid = -1; 47713021Ssam } 47818542Sralph } 4792821Swnj } 4802821Swnj } 48118542Sralph 48218542Sralph wterm(p) 48318542Sralph register struct tab *p; 48418542Sralph { 48518542Sralph if (p->wpid != 0) { 48618542Sralph kill(p->wpid, SIGKILL); 48718542Sralph } 48818542Sralph p->wpid = 0; 48918542Sralph } 49018542Sralph 49118542Sralph wstart(p) 49218542Sralph register struct tab *p; 49318542Sralph { 49422181Skarels register pid; 49522181Skarels time_t t; 49622181Skarels int dowait = 0; 49718542Sralph 49822181Skarels time(&t); 49922181Skarels p->windcnt++; 50022181Skarels if ((t - p->windtime) >= 60) { 50122181Skarels p->windtime = t; 50222181Skarels p->windcnt = 1; 50322181Skarels } else if (p->windcnt >= 5) { 50422181Skarels dowait = 1; 50522181Skarels p->windtime = t; 50622181Skarels p->windcnt = 1; 50722181Skarels } 50822181Skarels 50922181Skarels pid = fork(); 51022181Skarels 51122181Skarels if (pid == 0) { 51218542Sralph signal(SIGTERM, SIG_DFL); 51322181Skarels signal(SIGHUP, SIG_IGN); 51422181Skarels sigsetmask(0); /* since can be called from masked code */ 51522181Skarels if (dowait) { 51622181Skarels syslog(LOG_ERR, "'%s %s' failing, sleeping", p->wcmd, p->line); 51722181Skarels closelog(); 51822181Skarels sleep(30); 51922181Skarels } 52042407Smarc if (setsid() < 0) 52142407Smarc syslog(LOG_ERR, "setsid failed (window) %m"); 52218542Sralph execit(p->wcmd, p->line); 52318542Sralph exit(0); 52418542Sralph } 52522181Skarels p->wpid = pid; 52618542Sralph } 52718542Sralph 52822181Skarels #define NARGS 20 /* must be at least 4 */ 52918542Sralph #define ARGLEN 512 /* total size for all the argument strings */ 53018542Sralph 53118542Sralph execit(s, arg) 53218542Sralph char *s; 53318542Sralph char *arg; /* last argument on line */ 53418542Sralph { 53518542Sralph char *argv[NARGS], args[ARGLEN], *envp[1]; 53618542Sralph register char *sp = s; 53718542Sralph register char *ap = args; 53818542Sralph register char c; 53918542Sralph register int i; 54018542Sralph 54118542Sralph /* 54218542Sralph * First we have to set up the argument vector. 54318542Sralph * "prog arg1 arg2" maps to exec("prog", "-", "arg1", "arg2"). 54418542Sralph */ 54518542Sralph for (i = 1; i < NARGS - 2; i++) { 54618542Sralph argv[i] = ap; 54718542Sralph for (EVER) { 54818542Sralph if ((c = *sp++) == '\0' || ap >= &args[ARGLEN-1]) { 54918542Sralph *ap = '\0'; 55018542Sralph goto done; 55118542Sralph } 55218542Sralph if (c == ' ') { 55318542Sralph *ap++ = '\0'; 55418542Sralph while (*sp == ' ') 55518542Sralph sp++; 55618542Sralph if (*sp == '\0') 55718542Sralph goto done; 55818542Sralph break; 55918542Sralph } 56018542Sralph *ap++ = c; 56118542Sralph } 56218542Sralph } 56318542Sralph done: 56418542Sralph argv[0] = argv[1]; 56518542Sralph argv[1] = "-"; 56618542Sralph argv[i+1] = arg; 56718542Sralph argv[i+2] = 0; 56818542Sralph envp[0] = 0; 56918542Sralph execve(argv[0], &argv[1], envp); 57018542Sralph /* report failure of exec */ 57118542Sralph syslog(LOG_ERR, "%s: %m", argv[0]); 57218542Sralph closelog(); 57318542Sralph sleep(10); /* prevent failures from eating machine */ 57418542Sralph } 575