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*47659Skarels static char sccsid[] = "@(#)init.c 5.19 (Berkeley) 03/25/91";
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
main(argc,argv)6143633Skarels main(argc, argv)
6243633Skarels char **argv;
631029Sbill {
64*47659Skarels /* insure proper semantics for setjmp/longjmp */
65*47659Skarels static int howto, oldhowto, started = 0;
6629836Ssam #if defined(tahoe)
6729836Ssam register int r12; /* make sure r11 gets bootflags */
6829836Ssam #endif
6943633Skarels #if defined(vax) || defined(tahoe) || defined(hp300)
70*47659Skarels /* howto passed in high-order register XXX */
711403Sbill register int r11; /* passed thru from boot */
7242408Smckusick #ifdef __GNUC__
7342408Smckusick #ifdef hp300
7442408Smckusick asm("movl d7,%0" : "=rm" (howto));
7542408Smckusick #else
7642408Smckusick asm("movl r11,%0" : "=rm" (howto));
7742408Smckusick #endif
7842408Smckusick #else
791403Sbill howto = r11;
8043633Skarels #endif /* __GNUC__ */
81*47659Skarels #else /* vax || tahoe || hp300 */
8243633Skarels /* howto passed as argument */
83*47659Skarels howto = 0;
84*47659Skarels #endif /* ! (vax || tahoe || hp300) */
85*47659Skarels
86*47659Skarels /*
87*47659Skarels * We expect a single options argument from the kernel.
88*47659Skarels * If it is present, we ignore anything in registers from above.
89*47659Skarels */
909869Spugs if (argc > 1 && argv[1][0] == '-') {
919869Spugs char *cp;
929869Spugs
9342408Smckusick howto = 0;
949869Spugs cp = &argv[1][1];
959869Spugs while (*cp) switch (*cp++) {
96*47659Skarels #ifdef notyet
97*47659Skarels case 'f':
98*47659Skarels howto |= RB_FASTBOOT;
999869Spugs break;
100*47659Skarels #endif
1019869Spugs case 's':
1029869Spugs howto |= RB_SINGLE;
1039869Spugs break;
1049869Spugs }
105*47659Skarels }
10642407Smarc if (getuid() != 0)
10742407Smarc exit(1);
10824854Seric openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH);
10942407Smarc if (setsid() < 0)
11042407Smarc syslog(LOG_ERR, "setsid failed (initial) %m");
11113021Ssam sigvec(SIGTERM, &rvec, (struct sigvec *)0);
1122821Swnj signal(SIGTSTP, idle);
1131029Sbill signal(SIGTTIN, SIG_IGN);
1141029Sbill signal(SIGTTOU, SIG_IGN);
11513021Ssam (void) setjmp(sjbuf);
11643633Skarels for (; ; ) {
1171403Sbill oldhowto = howto;
1181403Sbill howto = RB_SINGLE;
11942407Smarc if (started && setjmp(shutpass) == 0)
1201429Sbill shutdown();
12142407Smarc started = 1;
1221403Sbill if (oldhowto & RB_SINGLE)
1231403Sbill single();
1241403Sbill if (runcom(oldhowto) == 0)
1251403Sbill continue;
1261029Sbill merge();
1271029Sbill multiple();
1281029Sbill }
1291029Sbill }
1301029Sbill
13142407Smarc void shutreset();
1321429Sbill
shutdown()1331029Sbill shutdown()
1341029Sbill {
1351029Sbill register i;
13623147Sbloom register struct tab *p, *p1;
1371029Sbill
1381029Sbill signal(SIGHUP, SIG_IGN);
13923147Sbloom for (p = itab; p ; ) {
1401029Sbill term(p);
14123147Sbloom p1 = p->next;
14223147Sbloom free(p);
14323147Sbloom p = p1;
1441029Sbill }
14523147Sbloom itab = (struct tab *)0;
1461429Sbill signal(SIGALRM, shutreset);
14728801Skarels (void) kill(-1, SIGTERM); /* one chance to catch it */
14828801Skarels sleep(5);
1491429Sbill alarm(30);
15013021Ssam for (i = 0; i < 5; i++)
1511029Sbill kill(-1, SIGKILL);
15213021Ssam while (wait((int *)0) != -1)
1531029Sbill ;
1541029Sbill alarm(0);
1551429Sbill shutend();
1561429Sbill }
1571429Sbill
15842407Smarc char shutfailm[] = "init: WARNING: something is hung (won't die); ps axl advised\n";
1591429Sbill
16042407Smarc void
shutreset()1611429Sbill shutreset()
1621429Sbill {
1631429Sbill int status;
1641429Sbill
1651429Sbill if (fork() == 0) {
1661429Sbill int ct = open(ctty, 1);
1671429Sbill write(ct, shutfailm, sizeof (shutfailm));
1681429Sbill sleep(5);
1691429Sbill exit(1);
1701429Sbill }
1711429Sbill sleep(5);
1721429Sbill shutend();
1731429Sbill longjmp(shutpass, 1);
1741429Sbill }
1751429Sbill
shutend()1761429Sbill shutend()
1771429Sbill {
17842408Smckusick register i;
1791429Sbill
1802821Swnj acct(0);
1811029Sbill signal(SIGALRM, SIG_DFL);
18213021Ssam for (i = 0; i < 10; i++)
1831029Sbill close(i);
18435606Sbostic logwtmp("~", "shutdown", "");
1851029Sbill }
1861029Sbill
single()1871029Sbill single()
1881029Sbill {
1891029Sbill register pid;
1902821Swnj register xpid;
19143633Skarels int fd;
19235606Sbostic extern int errno;
1931029Sbill
19413021Ssam do {
19513021Ssam pid = fork();
19613021Ssam if (pid == 0) {
19713021Ssam signal(SIGTERM, SIG_DFL);
19813021Ssam signal(SIGHUP, SIG_DFL);
19913021Ssam signal(SIGALRM, SIG_DFL);
20016452Sroot signal(SIGTSTP, SIG_IGN);
20142407Smarc if (setsid() < 0)
20242407Smarc syslog(LOG_ERR, "setsid failed (single): %m");
20343633Skarels (void) revoke(ctty);
20443633Skarels if ((fd = open(ctty, O_RDWR)) < 0) {
20543633Skarels syslog(LOG_ERR, "open %s: %m", ctty);
20643633Skarels exit(1);
20743633Skarels }
20843633Skarels if (ioctl(fd, TIOCSCTTY, 0) < 0)
20942407Smarc syslog(LOG_ERR, "TIOCSCTTY failed: %m");
21043633Skarels dup2(fd, 0);
21143633Skarels dup2(fd, 1);
21243633Skarels dup2(fd, 2);
21343633Skarels if (fd > 2)
21443633Skarels close(fd);
21513021Ssam execl(shell, minus, (char *)0);
21630516Sbostic perror(shell);
21713021Ssam exit(0);
21813021Ssam }
21913021Ssam while ((xpid = wait((int *)0)) != pid)
22013021Ssam if (xpid == -1 && errno == ECHILD)
22113021Ssam break;
22213021Ssam } while (xpid == -1);
2231029Sbill }
2241029Sbill
runcom(oldhowto)2251403Sbill runcom(oldhowto)
2261403Sbill int oldhowto;
2271029Sbill {
22842408Smckusick register pid;
22944284Skarels int fd, status;
2301029Sbill
2311029Sbill pid = fork();
23213021Ssam if (pid == 0) {
23344284Skarels signal(SIGTSTP, SIG_IGN);
23444284Skarels signal(SIGHUP, SIG_IGN);
23544284Skarels if ((fd = open(ctty, O_RDWR)) < 0)
23644284Skarels syslog(LOG_ERR, "open %s: %m", ctty);
23744284Skarels else {
23844284Skarels dup2(fd, 0);
23944284Skarels dup2(fd, 1);
24044284Skarels dup2(fd, 2);
24144284Skarels if (fd > 2)
24244284Skarels close(fd);
24344284Skarels }
24442407Smarc if (setsid() < 0)
24542407Smarc syslog(LOG_ERR, "setsid failed (runcom) %m");
24642407Smarc if (ioctl(0, TIOCSCTTY, 0) < 0)
24742407Smarc syslog(LOG_ERR, "TIOCSCTTY failed (runcom) %m");
2481403Sbill if (oldhowto & RB_SINGLE)
2491403Sbill execl(shell, shell, runc, (char *)0);
2501403Sbill else
2511403Sbill execl(shell, shell, runc, "autoboot", (char *)0);
2521403Sbill exit(1);
2531029Sbill }
25413021Ssam while (wait(&status) != pid)
2551029Sbill ;
25613021Ssam if (status)
25713021Ssam return (0);
25835606Sbostic logwtmp("~", "reboot", "");
25913021Ssam return (1);
2601029Sbill }
2611029Sbill
26218542Sralph struct sigvec mvec = { merge, sigmask(SIGTERM), 0 };
26313021Ssam /*
26413021Ssam * Multi-user. Listen for users leaving, SIGHUP's
26513021Ssam * which indicate ttys has changed, and SIGTERM's which
26613021Ssam * are used to shutdown the system.
26713021Ssam */
multiple()2681029Sbill multiple()
2691029Sbill {
27042407Smarc extern int errno;
2711029Sbill register struct tab *p;
2721029Sbill register pid;
27323147Sbloom int omask;
2741029Sbill
27513021Ssam sigvec(SIGHUP, &mvec, (struct sigvec *)0);
27613021Ssam for (EVER) {
2771029Sbill pid = wait((int *)0);
27842407Smarc /* SHOULD FIX THIS IN THE KERNEL */
27942407Smarc if (pid == -1 && errno != EINTR)
2801029Sbill return;
28130516Sbostic omask = sigblock(sigmask(SIGHUP));
28218542Sralph for (ALL) {
28318542Sralph /* must restart window system BEFORE emulator */
28418542Sralph if (p->wpid == pid || p->wpid == -1)
28518542Sralph wstart(p);
28613021Ssam if (p->pid == pid || p->pid == -1) {
28718542Sralph /* disown the window system */
28818542Sralph if (p->wpid)
28918542Sralph kill(p->wpid, SIGHUP);
29035445Sbostic cleanutmp(p);
2911029Sbill dfork(p);
2921029Sbill }
29318542Sralph }
29423147Sbloom sigsetmask(omask);
2951029Sbill }
2961029Sbill }
2971029Sbill
29813021Ssam /*
29913021Ssam * Merge current contents of ttys file
30013021Ssam * into in-core table of configured tty lines.
30113021Ssam * Entered as signal handler for SIGHUP.
30213021Ssam */
30313021Ssam #define FOUND 1
30413021Ssam #define CHANGE 2
30518542Sralph #define WCHANGE 4
30613021Ssam
30742407Smarc void
merge()30813021Ssam merge()
30913021Ssam {
31013021Ssam register struct tab *p;
31116452Sroot register struct ttyent *t;
31223147Sbloom register struct tab *p1;
31313021Ssam
31413021Ssam for (ALL)
31513021Ssam p->xflag = 0;
31616452Sroot setttyent();
31716452Sroot while (t = getttyent()) {
31816452Sroot if ((t->ty_status & TTY_ON) == 0)
31916452Sroot continue;
32013021Ssam for (ALL) {
32116452Sroot if (SCMPN(p->line, t->ty_name))
32213021Ssam continue;
32313021Ssam p->xflag |= FOUND;
32416452Sroot if (SCMPN(p->comn, t->ty_getty)) {
32513021Ssam p->xflag |= CHANGE;
32616452Sroot SCPYN(p->comn, t->ty_getty);
32713021Ssam }
32830516Sbostic if (SCMPN(p->wcmd, t->ty_window ? t->ty_window : "")) {
32918542Sralph p->xflag |= WCHANGE|CHANGE;
33018542Sralph SCPYN(p->wcmd, t->ty_window);
33118542Sralph }
33213021Ssam goto contin1;
33313021Ssam }
33418542Sralph
33523147Sbloom /*
33623147Sbloom * Make space for a new one
33723147Sbloom */
33823147Sbloom p1 = (struct tab *)calloc(1, sizeof(*p1));
33923147Sbloom if (!p1) {
34023147Sbloom syslog(LOG_ERR, "no space for '%s' !?!", t->ty_name);
34113021Ssam goto contin1;
34213021Ssam }
34323147Sbloom /*
34423147Sbloom * Put new terminal at the end of the linked list.
34523147Sbloom */
34623147Sbloom if (itab) {
34723147Sbloom for (p = itab; p->next ; p = p->next)
34823147Sbloom ;
34923147Sbloom p->next = p1;
35023147Sbloom } else
35123147Sbloom itab = p1;
35223147Sbloom
35323147Sbloom p = p1;
35423147Sbloom SCPYN(p->line, t->ty_name);
35523147Sbloom p->xflag |= FOUND|CHANGE;
35623147Sbloom SCPYN(p->comn, t->ty_getty);
35730516Sbostic if (t->ty_window && strcmp(t->ty_window, "") != 0) {
35823147Sbloom p->xflag |= WCHANGE;
35923147Sbloom SCPYN(p->wcmd, t->ty_window);
36023147Sbloom }
36113021Ssam contin1:
36213021Ssam ;
36313021Ssam }
36416452Sroot endttyent();
36523147Sbloom p1 = (struct tab *)0;
36613021Ssam for (ALL) {
36713021Ssam if ((p->xflag&FOUND) == 0) {
36813021Ssam term(p);
36918542Sralph wterm(p);
37023147Sbloom if (p1)
37123147Sbloom p1->next = p->next;
37223147Sbloom else
37323147Sbloom itab = p->next;
37423147Sbloom free(p);
37523147Sbloom p = p1 ? p1 : itab;
37623147Sbloom } else {
37723147Sbloom /* window system should be started first */
37823147Sbloom if (p->xflag&WCHANGE) {
37923147Sbloom wterm(p);
38023147Sbloom wstart(p);
38123147Sbloom }
38223147Sbloom if (p->xflag&CHANGE) {
38323147Sbloom term(p);
38423147Sbloom dfork(p);
38523147Sbloom }
38613021Ssam }
38723147Sbloom p1 = p;
38813021Ssam }
38913021Ssam }
39013021Ssam
term(p)3911029Sbill term(p)
39213021Ssam register struct tab *p;
3931029Sbill {
3941029Sbill
39513021Ssam if (p->pid != 0) {
39635445Sbostic cleanutmp(p);
3971029Sbill kill(p->pid, SIGKILL);
3981029Sbill }
3991029Sbill p->pid = 0;
40018542Sralph /* send SIGHUP to get rid of connections */
40118542Sralph if (p->wpid > 0)
40218542Sralph kill(p->wpid, SIGHUP);
4031029Sbill }
4041029Sbill
4051029Sbill dfork(p)
40613021Ssam struct tab *p;
4071029Sbill {
4081029Sbill register pid;
4095971Sroot time_t t;
4105971Sroot int dowait = 0;
4111029Sbill
4125971Sroot time(&t);
4135971Sroot p->gettycnt++;
4145971Sroot if ((t - p->gettytime) >= 60) {
4155971Sroot p->gettytime = t;
4165971Sroot p->gettycnt = 1;
41718542Sralph } else if (p->gettycnt >= 5) {
41818542Sralph dowait = 1;
41918542Sralph p->gettytime = t;
42018542Sralph p->gettycnt = 1;
4215971Sroot }
4221029Sbill pid = fork();
42313021Ssam if (pid == 0) {
4246816Ssam signal(SIGTERM, SIG_DFL);
4256816Ssam signal(SIGHUP, SIG_IGN);
42622181Skarels sigsetmask(0); /* since can be called from masked code */
4275971Sroot if (dowait) {
42818542Sralph syslog(LOG_ERR, "'%s %s' failing, sleeping", p->comn, p->line);
42918542Sralph closelog();
4305971Sroot sleep(30);
4315971Sroot }
43242407Smarc if (setsid() < 0)
43342407Smarc syslog(LOG_ERR, "setsid failed(dfork) %m");
43418542Sralph execit(p->comn, p->line);
4351029Sbill exit(0);
4361029Sbill }
4371029Sbill p->pid = pid;
4381029Sbill }
4391029Sbill
cleanutmp(p)44035445Sbostic cleanutmp(p)
44113021Ssam register struct tab *p;
4421029Sbill {
44335445Sbostic if (logout(p->line)) {
44442407Smarc logwtmp(p->line, "", "");
44517582Ssam /*
44617582Ssam * After a proper login force reset
44717582Ssam * of error detection code in dfork.
44817582Ssam */
44917582Ssam p->gettytime = 0;
45022181Skarels p->windtime = 0;
4511029Sbill }
4521029Sbill }
4531029Sbill
45442407Smarc void
reset()4551029Sbill reset()
4561029Sbill {
4571029Sbill longjmp(sjbuf, 1);
4581029Sbill }
4592821Swnj
46013021Ssam jmp_buf idlebuf;
46113021Ssam
46242407Smarc void
idlehup()46313021Ssam idlehup()
46413021Ssam {
46513021Ssam longjmp(idlebuf, 1);
46613021Ssam }
46713021Ssam
46842407Smarc void
idle()4692821Swnj idle()
4702821Swnj {
4712821Swnj register struct tab *p;
4722821Swnj register pid;
4732821Swnj
47413021Ssam signal(SIGHUP, idlehup);
47518542Sralph for (EVER) {
47613021Ssam if (setjmp(idlebuf))
47713021Ssam return;
4782821Swnj pid = wait((int *) 0);
47913021Ssam if (pid == -1) {
48013021Ssam sigpause(0);
48113021Ssam continue;
4822821Swnj }
48318542Sralph for (ALL) {
48418542Sralph /* if window system dies, mark it for restart */
48518542Sralph if (p->wpid == pid)
48618542Sralph p->wpid = -1;
48713021Ssam if (p->pid == pid) {
48835445Sbostic cleanutmp(p);
48913021Ssam p->pid = -1;
49013021Ssam }
49118542Sralph }
4922821Swnj }
4932821Swnj }
49418542Sralph
wterm(p)49518542Sralph wterm(p)
49618542Sralph register struct tab *p;
49718542Sralph {
49818542Sralph if (p->wpid != 0) {
49918542Sralph kill(p->wpid, SIGKILL);
50018542Sralph }
50118542Sralph p->wpid = 0;
50218542Sralph }
50318542Sralph
wstart(p)50418542Sralph wstart(p)
50518542Sralph register struct tab *p;
50618542Sralph {
50722181Skarels register pid;
50822181Skarels time_t t;
50922181Skarels int dowait = 0;
51018542Sralph
51122181Skarels time(&t);
51222181Skarels p->windcnt++;
51322181Skarels if ((t - p->windtime) >= 60) {
51422181Skarels p->windtime = t;
51522181Skarels p->windcnt = 1;
51622181Skarels } else if (p->windcnt >= 5) {
51722181Skarels dowait = 1;
51822181Skarels p->windtime = t;
51922181Skarels p->windcnt = 1;
52022181Skarels }
52122181Skarels
52222181Skarels pid = fork();
52322181Skarels
52422181Skarels if (pid == 0) {
52518542Sralph signal(SIGTERM, SIG_DFL);
52622181Skarels signal(SIGHUP, SIG_IGN);
52722181Skarels sigsetmask(0); /* since can be called from masked code */
52822181Skarels if (dowait) {
52922181Skarels syslog(LOG_ERR, "'%s %s' failing, sleeping", p->wcmd, p->line);
53022181Skarels closelog();
53122181Skarels sleep(30);
53222181Skarels }
53342407Smarc if (setsid() < 0)
53442407Smarc syslog(LOG_ERR, "setsid failed (window) %m");
53518542Sralph execit(p->wcmd, p->line);
53618542Sralph exit(0);
53718542Sralph }
53822181Skarels p->wpid = pid;
53918542Sralph }
54018542Sralph
54122181Skarels #define NARGS 20 /* must be at least 4 */
54218542Sralph #define ARGLEN 512 /* total size for all the argument strings */
54318542Sralph
execit(s,arg)54418542Sralph execit(s, arg)
54518542Sralph char *s;
54618542Sralph char *arg; /* last argument on line */
54718542Sralph {
54818542Sralph char *argv[NARGS], args[ARGLEN], *envp[1];
54918542Sralph register char *sp = s;
55018542Sralph register char *ap = args;
55118542Sralph register char c;
55218542Sralph register int i;
55318542Sralph
55418542Sralph /*
55518542Sralph * First we have to set up the argument vector.
55618542Sralph * "prog arg1 arg2" maps to exec("prog", "-", "arg1", "arg2").
55718542Sralph */
55818542Sralph for (i = 1; i < NARGS - 2; i++) {
55918542Sralph argv[i] = ap;
56018542Sralph for (EVER) {
56118542Sralph if ((c = *sp++) == '\0' || ap >= &args[ARGLEN-1]) {
56218542Sralph *ap = '\0';
56318542Sralph goto done;
56418542Sralph }
56518542Sralph if (c == ' ') {
56618542Sralph *ap++ = '\0';
56718542Sralph while (*sp == ' ')
56818542Sralph sp++;
56918542Sralph if (*sp == '\0')
57018542Sralph goto done;
57118542Sralph break;
57218542Sralph }
57318542Sralph *ap++ = c;
57418542Sralph }
57518542Sralph }
57618542Sralph done:
57718542Sralph argv[0] = argv[1];
57818542Sralph argv[1] = "-";
57918542Sralph argv[i+1] = arg;
58018542Sralph argv[i+2] = 0;
58118542Sralph envp[0] = 0;
58218542Sralph execve(argv[0], &argv[1], envp);
58318542Sralph /* report failure of exec */
58418542Sralph syslog(LOG_ERR, "%s: %m", argv[0]);
58518542Sralph closelog();
58618542Sralph sleep(10); /* prevent failures from eating machine */
58718542Sralph }
588