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*44284Skarels static char sccsid[] = "@(#)init.c 5.18 (Berkeley) 06/26/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 6143633Skarels main(argc, argv) 6243633Skarels char **argv; 631029Sbill { 6429836Ssam #if defined(tahoe) 6529836Ssam register int r12; /* make sure r11 gets bootflags */ 6629836Ssam #endif 6743633Skarels #if defined(vax) || defined(tahoe) || defined(hp300) 681403Sbill register int r11; /* passed thru from boot */ 6913021Ssam #endif 7042408Smckusick #ifdef __GNUC__ 7142408Smckusick /* insure proper semantics for setjmp/longjmp */ 7242408Smckusick static 7342408Smckusick #endif 7443633Skarels int howto, oldhowto, started = 0; 751403Sbill 7643633Skarels #if defined(vax) || defined(tahoe) || defined(hp300) 7743633Skarels /* howto passed in high-order register XXX */ 7842408Smckusick #ifdef __GNUC__ 7942408Smckusick #ifdef hp300 8042408Smckusick asm("movl d7,%0" : "=rm" (howto)); 8142408Smckusick #else 8242408Smckusick asm("movl r11,%0" : "=rm" (howto)); 8342408Smckusick #endif 8442408Smckusick #else 851403Sbill howto = r11; 8643633Skarels #endif /* __GNUC__ */ 8743633Skarels #else /* defined(vax) || defined(tahoe) || defined(hp300) */ 8843633Skarels /* howto passed as argument */ 899869Spugs if (argc > 1 && argv[1][0] == '-') { 909869Spugs char *cp; 919869Spugs 9242408Smckusick howto = 0; 939869Spugs cp = &argv[1][1]; 949869Spugs while (*cp) switch (*cp++) { 959869Spugs case 'a': 969869Spugs howto |= RB_ASKNAME; 979869Spugs break; 989869Spugs case 's': 999869Spugs howto |= RB_SINGLE; 1009869Spugs break; 1019869Spugs } 10243633Skarels } else 1039869Spugs howto = RB_SINGLE; 10413021Ssam #endif 10542407Smarc if (getuid() != 0) 10642407Smarc exit(1); 10724854Seric openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); 10842407Smarc if (setsid() < 0) 10942407Smarc syslog(LOG_ERR, "setsid failed (initial) %m"); 11013021Ssam sigvec(SIGTERM, &rvec, (struct sigvec *)0); 1112821Swnj signal(SIGTSTP, idle); 1121029Sbill signal(SIGTTIN, SIG_IGN); 1131029Sbill signal(SIGTTOU, SIG_IGN); 11413021Ssam (void) setjmp(sjbuf); 11543633Skarels for (; ; ) { 1161403Sbill oldhowto = howto; 1171403Sbill howto = RB_SINGLE; 11842407Smarc if (started && setjmp(shutpass) == 0) 1191429Sbill shutdown(); 12042407Smarc started = 1; 1211403Sbill if (oldhowto & RB_SINGLE) 1221403Sbill single(); 1231403Sbill if (runcom(oldhowto) == 0) 1241403Sbill continue; 1251029Sbill merge(); 1261029Sbill multiple(); 1271029Sbill } 1281029Sbill } 1291029Sbill 13042407Smarc void shutreset(); 1311429Sbill 1321029Sbill shutdown() 1331029Sbill { 1341029Sbill register i; 13523147Sbloom register struct tab *p, *p1; 1361029Sbill 1371029Sbill signal(SIGHUP, SIG_IGN); 13823147Sbloom for (p = itab; p ; ) { 1391029Sbill term(p); 14023147Sbloom p1 = p->next; 14123147Sbloom free(p); 14223147Sbloom p = p1; 1431029Sbill } 14423147Sbloom itab = (struct tab *)0; 1451429Sbill signal(SIGALRM, shutreset); 14628801Skarels (void) kill(-1, SIGTERM); /* one chance to catch it */ 14728801Skarels sleep(5); 1481429Sbill alarm(30); 14913021Ssam for (i = 0; i < 5; i++) 1501029Sbill kill(-1, SIGKILL); 15113021Ssam while (wait((int *)0) != -1) 1521029Sbill ; 1531029Sbill alarm(0); 1541429Sbill shutend(); 1551429Sbill } 1561429Sbill 15742407Smarc char shutfailm[] = "init: WARNING: something is hung (won't die); ps axl advised\n"; 1581429Sbill 15942407Smarc void 1601429Sbill shutreset() 1611429Sbill { 1621429Sbill int status; 1631429Sbill 1641429Sbill if (fork() == 0) { 1651429Sbill int ct = open(ctty, 1); 1661429Sbill write(ct, shutfailm, sizeof (shutfailm)); 1671429Sbill sleep(5); 1681429Sbill exit(1); 1691429Sbill } 1701429Sbill sleep(5); 1711429Sbill shutend(); 1721429Sbill longjmp(shutpass, 1); 1731429Sbill } 1741429Sbill 1751429Sbill shutend() 1761429Sbill { 17742408Smckusick register i; 1781429Sbill 1792821Swnj acct(0); 1801029Sbill signal(SIGALRM, SIG_DFL); 18113021Ssam for (i = 0; i < 10; i++) 1821029Sbill close(i); 18335606Sbostic logwtmp("~", "shutdown", ""); 1841029Sbill } 1851029Sbill 1861029Sbill single() 1871029Sbill { 1881029Sbill register pid; 1892821Swnj register xpid; 19043633Skarels int fd; 19135606Sbostic extern int errno; 1921029Sbill 19313021Ssam do { 19413021Ssam pid = fork(); 19513021Ssam if (pid == 0) { 19613021Ssam signal(SIGTERM, SIG_DFL); 19713021Ssam signal(SIGHUP, SIG_DFL); 19813021Ssam signal(SIGALRM, SIG_DFL); 19916452Sroot signal(SIGTSTP, SIG_IGN); 20042407Smarc if (setsid() < 0) 20142407Smarc syslog(LOG_ERR, "setsid failed (single): %m"); 20243633Skarels (void) revoke(ctty); 20343633Skarels if ((fd = open(ctty, O_RDWR)) < 0) { 20443633Skarels syslog(LOG_ERR, "open %s: %m", ctty); 20543633Skarels exit(1); 20643633Skarels } 20743633Skarels if (ioctl(fd, TIOCSCTTY, 0) < 0) 20842407Smarc syslog(LOG_ERR, "TIOCSCTTY failed: %m"); 20943633Skarels dup2(fd, 0); 21043633Skarels dup2(fd, 1); 21143633Skarels dup2(fd, 2); 21243633Skarels if (fd > 2) 21343633Skarels close(fd); 21413021Ssam execl(shell, minus, (char *)0); 21530516Sbostic perror(shell); 21613021Ssam exit(0); 21713021Ssam } 21813021Ssam while ((xpid = wait((int *)0)) != pid) 21913021Ssam if (xpid == -1 && errno == ECHILD) 22013021Ssam break; 22113021Ssam } while (xpid == -1); 2221029Sbill } 2231029Sbill 2241403Sbill runcom(oldhowto) 2251403Sbill int oldhowto; 2261029Sbill { 22742408Smckusick register pid; 228*44284Skarels int fd, status; 2291029Sbill 2301029Sbill pid = fork(); 23113021Ssam if (pid == 0) { 232*44284Skarels signal(SIGTSTP, SIG_IGN); 233*44284Skarels signal(SIGHUP, SIG_IGN); 234*44284Skarels if ((fd = open(ctty, O_RDWR)) < 0) 235*44284Skarels syslog(LOG_ERR, "open %s: %m", ctty); 236*44284Skarels else { 237*44284Skarels dup2(fd, 0); 238*44284Skarels dup2(fd, 1); 239*44284Skarels dup2(fd, 2); 240*44284Skarels if (fd > 2) 241*44284Skarels close(fd); 242*44284Skarels } 24342407Smarc if (setsid() < 0) 24442407Smarc syslog(LOG_ERR, "setsid failed (runcom) %m"); 24542407Smarc if (ioctl(0, TIOCSCTTY, 0) < 0) 24642407Smarc syslog(LOG_ERR, "TIOCSCTTY failed (runcom) %m"); 2471403Sbill if (oldhowto & RB_SINGLE) 2481403Sbill execl(shell, shell, runc, (char *)0); 2491403Sbill else 2501403Sbill execl(shell, shell, runc, "autoboot", (char *)0); 2511403Sbill exit(1); 2521029Sbill } 25313021Ssam while (wait(&status) != pid) 2541029Sbill ; 25513021Ssam if (status) 25613021Ssam return (0); 25735606Sbostic logwtmp("~", "reboot", ""); 25813021Ssam return (1); 2591029Sbill } 2601029Sbill 26118542Sralph struct sigvec mvec = { merge, sigmask(SIGTERM), 0 }; 26213021Ssam /* 26313021Ssam * Multi-user. Listen for users leaving, SIGHUP's 26413021Ssam * which indicate ttys has changed, and SIGTERM's which 26513021Ssam * are used to shutdown the system. 26613021Ssam */ 2671029Sbill multiple() 2681029Sbill { 26942407Smarc extern int errno; 2701029Sbill register struct tab *p; 2711029Sbill register pid; 27223147Sbloom int omask; 2731029Sbill 27413021Ssam sigvec(SIGHUP, &mvec, (struct sigvec *)0); 27513021Ssam for (EVER) { 2761029Sbill pid = wait((int *)0); 27742407Smarc /* SHOULD FIX THIS IN THE KERNEL */ 27842407Smarc if (pid == -1 && errno != EINTR) 2791029Sbill return; 28030516Sbostic omask = sigblock(sigmask(SIGHUP)); 28118542Sralph for (ALL) { 28218542Sralph /* must restart window system BEFORE emulator */ 28318542Sralph if (p->wpid == pid || p->wpid == -1) 28418542Sralph wstart(p); 28513021Ssam if (p->pid == pid || p->pid == -1) { 28618542Sralph /* disown the window system */ 28718542Sralph if (p->wpid) 28818542Sralph kill(p->wpid, SIGHUP); 28935445Sbostic cleanutmp(p); 2901029Sbill dfork(p); 2911029Sbill } 29218542Sralph } 29323147Sbloom sigsetmask(omask); 2941029Sbill } 2951029Sbill } 2961029Sbill 29713021Ssam /* 29813021Ssam * Merge current contents of ttys file 29913021Ssam * into in-core table of configured tty lines. 30013021Ssam * Entered as signal handler for SIGHUP. 30113021Ssam */ 30213021Ssam #define FOUND 1 30313021Ssam #define CHANGE 2 30418542Sralph #define WCHANGE 4 30513021Ssam 30642407Smarc void 30713021Ssam merge() 30813021Ssam { 30913021Ssam register struct tab *p; 31016452Sroot register struct ttyent *t; 31123147Sbloom register struct tab *p1; 31213021Ssam 31313021Ssam for (ALL) 31413021Ssam p->xflag = 0; 31516452Sroot setttyent(); 31616452Sroot while (t = getttyent()) { 31716452Sroot if ((t->ty_status & TTY_ON) == 0) 31816452Sroot continue; 31913021Ssam for (ALL) { 32016452Sroot if (SCMPN(p->line, t->ty_name)) 32113021Ssam continue; 32213021Ssam p->xflag |= FOUND; 32316452Sroot if (SCMPN(p->comn, t->ty_getty)) { 32413021Ssam p->xflag |= CHANGE; 32516452Sroot SCPYN(p->comn, t->ty_getty); 32613021Ssam } 32730516Sbostic if (SCMPN(p->wcmd, t->ty_window ? t->ty_window : "")) { 32818542Sralph p->xflag |= WCHANGE|CHANGE; 32918542Sralph SCPYN(p->wcmd, t->ty_window); 33018542Sralph } 33113021Ssam goto contin1; 33213021Ssam } 33318542Sralph 33423147Sbloom /* 33523147Sbloom * Make space for a new one 33623147Sbloom */ 33723147Sbloom p1 = (struct tab *)calloc(1, sizeof(*p1)); 33823147Sbloom if (!p1) { 33923147Sbloom syslog(LOG_ERR, "no space for '%s' !?!", t->ty_name); 34013021Ssam goto contin1; 34113021Ssam } 34223147Sbloom /* 34323147Sbloom * Put new terminal at the end of the linked list. 34423147Sbloom */ 34523147Sbloom if (itab) { 34623147Sbloom for (p = itab; p->next ; p = p->next) 34723147Sbloom ; 34823147Sbloom p->next = p1; 34923147Sbloom } else 35023147Sbloom itab = p1; 35123147Sbloom 35223147Sbloom p = p1; 35323147Sbloom SCPYN(p->line, t->ty_name); 35423147Sbloom p->xflag |= FOUND|CHANGE; 35523147Sbloom SCPYN(p->comn, t->ty_getty); 35630516Sbostic if (t->ty_window && strcmp(t->ty_window, "") != 0) { 35723147Sbloom p->xflag |= WCHANGE; 35823147Sbloom SCPYN(p->wcmd, t->ty_window); 35923147Sbloom } 36013021Ssam contin1: 36113021Ssam ; 36213021Ssam } 36316452Sroot endttyent(); 36423147Sbloom p1 = (struct tab *)0; 36513021Ssam for (ALL) { 36613021Ssam if ((p->xflag&FOUND) == 0) { 36713021Ssam term(p); 36818542Sralph wterm(p); 36923147Sbloom if (p1) 37023147Sbloom p1->next = p->next; 37123147Sbloom else 37223147Sbloom itab = p->next; 37323147Sbloom free(p); 37423147Sbloom p = p1 ? p1 : itab; 37523147Sbloom } else { 37623147Sbloom /* window system should be started first */ 37723147Sbloom if (p->xflag&WCHANGE) { 37823147Sbloom wterm(p); 37923147Sbloom wstart(p); 38023147Sbloom } 38123147Sbloom if (p->xflag&CHANGE) { 38223147Sbloom term(p); 38323147Sbloom dfork(p); 38423147Sbloom } 38513021Ssam } 38623147Sbloom p1 = p; 38713021Ssam } 38813021Ssam } 38913021Ssam 3901029Sbill term(p) 39113021Ssam register struct tab *p; 3921029Sbill { 3931029Sbill 39413021Ssam if (p->pid != 0) { 39535445Sbostic cleanutmp(p); 3961029Sbill kill(p->pid, SIGKILL); 3971029Sbill } 3981029Sbill p->pid = 0; 39918542Sralph /* send SIGHUP to get rid of connections */ 40018542Sralph if (p->wpid > 0) 40118542Sralph kill(p->wpid, SIGHUP); 4021029Sbill } 4031029Sbill 4041029Sbill dfork(p) 40513021Ssam struct tab *p; 4061029Sbill { 4071029Sbill register pid; 4085971Sroot time_t t; 4095971Sroot int dowait = 0; 4101029Sbill 4115971Sroot time(&t); 4125971Sroot p->gettycnt++; 4135971Sroot if ((t - p->gettytime) >= 60) { 4145971Sroot p->gettytime = t; 4155971Sroot p->gettycnt = 1; 41618542Sralph } else if (p->gettycnt >= 5) { 41718542Sralph dowait = 1; 41818542Sralph p->gettytime = t; 41918542Sralph p->gettycnt = 1; 4205971Sroot } 4211029Sbill pid = fork(); 42213021Ssam if (pid == 0) { 4236816Ssam signal(SIGTERM, SIG_DFL); 4246816Ssam signal(SIGHUP, SIG_IGN); 42522181Skarels sigsetmask(0); /* since can be called from masked code */ 4265971Sroot if (dowait) { 42718542Sralph syslog(LOG_ERR, "'%s %s' failing, sleeping", p->comn, p->line); 42818542Sralph closelog(); 4295971Sroot sleep(30); 4305971Sroot } 43142407Smarc if (setsid() < 0) 43242407Smarc syslog(LOG_ERR, "setsid failed(dfork) %m"); 43318542Sralph execit(p->comn, p->line); 4341029Sbill exit(0); 4351029Sbill } 4361029Sbill p->pid = pid; 4371029Sbill } 4381029Sbill 43935445Sbostic cleanutmp(p) 44013021Ssam register struct tab *p; 4411029Sbill { 44235445Sbostic if (logout(p->line)) { 44342407Smarc logwtmp(p->line, "", ""); 44417582Ssam /* 44517582Ssam * After a proper login force reset 44617582Ssam * of error detection code in dfork. 44717582Ssam */ 44817582Ssam p->gettytime = 0; 44922181Skarels p->windtime = 0; 4501029Sbill } 4511029Sbill } 4521029Sbill 45342407Smarc void 4541029Sbill reset() 4551029Sbill { 4561029Sbill longjmp(sjbuf, 1); 4571029Sbill } 4582821Swnj 45913021Ssam jmp_buf idlebuf; 46013021Ssam 46142407Smarc void 46213021Ssam idlehup() 46313021Ssam { 46413021Ssam longjmp(idlebuf, 1); 46513021Ssam } 46613021Ssam 46742407Smarc void 4682821Swnj idle() 4692821Swnj { 4702821Swnj register struct tab *p; 4712821Swnj register pid; 4722821Swnj 47313021Ssam signal(SIGHUP, idlehup); 47418542Sralph for (EVER) { 47513021Ssam if (setjmp(idlebuf)) 47613021Ssam return; 4772821Swnj pid = wait((int *) 0); 47813021Ssam if (pid == -1) { 47913021Ssam sigpause(0); 48013021Ssam continue; 4812821Swnj } 48218542Sralph for (ALL) { 48318542Sralph /* if window system dies, mark it for restart */ 48418542Sralph if (p->wpid == pid) 48518542Sralph p->wpid = -1; 48613021Ssam if (p->pid == pid) { 48735445Sbostic cleanutmp(p); 48813021Ssam p->pid = -1; 48913021Ssam } 49018542Sralph } 4912821Swnj } 4922821Swnj } 49318542Sralph 49418542Sralph wterm(p) 49518542Sralph register struct tab *p; 49618542Sralph { 49718542Sralph if (p->wpid != 0) { 49818542Sralph kill(p->wpid, SIGKILL); 49918542Sralph } 50018542Sralph p->wpid = 0; 50118542Sralph } 50218542Sralph 50318542Sralph wstart(p) 50418542Sralph register struct tab *p; 50518542Sralph { 50622181Skarels register pid; 50722181Skarels time_t t; 50822181Skarels int dowait = 0; 50918542Sralph 51022181Skarels time(&t); 51122181Skarels p->windcnt++; 51222181Skarels if ((t - p->windtime) >= 60) { 51322181Skarels p->windtime = t; 51422181Skarels p->windcnt = 1; 51522181Skarels } else if (p->windcnt >= 5) { 51622181Skarels dowait = 1; 51722181Skarels p->windtime = t; 51822181Skarels p->windcnt = 1; 51922181Skarels } 52022181Skarels 52122181Skarels pid = fork(); 52222181Skarels 52322181Skarels if (pid == 0) { 52418542Sralph signal(SIGTERM, SIG_DFL); 52522181Skarels signal(SIGHUP, SIG_IGN); 52622181Skarels sigsetmask(0); /* since can be called from masked code */ 52722181Skarels if (dowait) { 52822181Skarels syslog(LOG_ERR, "'%s %s' failing, sleeping", p->wcmd, p->line); 52922181Skarels closelog(); 53022181Skarels sleep(30); 53122181Skarels } 53242407Smarc if (setsid() < 0) 53342407Smarc syslog(LOG_ERR, "setsid failed (window) %m"); 53418542Sralph execit(p->wcmd, p->line); 53518542Sralph exit(0); 53618542Sralph } 53722181Skarels p->wpid = pid; 53818542Sralph } 53918542Sralph 54022181Skarels #define NARGS 20 /* must be at least 4 */ 54118542Sralph #define ARGLEN 512 /* total size for all the argument strings */ 54218542Sralph 54318542Sralph execit(s, arg) 54418542Sralph char *s; 54518542Sralph char *arg; /* last argument on line */ 54618542Sralph { 54718542Sralph char *argv[NARGS], args[ARGLEN], *envp[1]; 54818542Sralph register char *sp = s; 54918542Sralph register char *ap = args; 55018542Sralph register char c; 55118542Sralph register int i; 55218542Sralph 55318542Sralph /* 55418542Sralph * First we have to set up the argument vector. 55518542Sralph * "prog arg1 arg2" maps to exec("prog", "-", "arg1", "arg2"). 55618542Sralph */ 55718542Sralph for (i = 1; i < NARGS - 2; i++) { 55818542Sralph argv[i] = ap; 55918542Sralph for (EVER) { 56018542Sralph if ((c = *sp++) == '\0' || ap >= &args[ARGLEN-1]) { 56118542Sralph *ap = '\0'; 56218542Sralph goto done; 56318542Sralph } 56418542Sralph if (c == ' ') { 56518542Sralph *ap++ = '\0'; 56618542Sralph while (*sp == ' ') 56718542Sralph sp++; 56818542Sralph if (*sp == '\0') 56918542Sralph goto done; 57018542Sralph break; 57118542Sralph } 57218542Sralph *ap++ = c; 57318542Sralph } 57418542Sralph } 57518542Sralph done: 57618542Sralph argv[0] = argv[1]; 57718542Sralph argv[1] = "-"; 57818542Sralph argv[i+1] = arg; 57918542Sralph argv[i+2] = 0; 58018542Sralph envp[0] = 0; 58118542Sralph execve(argv[0], &argv[1], envp); 58218542Sralph /* report failure of exec */ 58318542Sralph syslog(LOG_ERR, "%s: %m", argv[0]); 58418542Sralph closelog(); 58518542Sralph sleep(10); /* prevent failures from eating machine */ 58618542Sralph } 587