xref: /csrg-svn/old/init/init.c (revision 37284)
1 /*
2  * Copyright (c) 1980,1986 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 static char sccsid[] = "@(#)init.c	5.14 (Berkeley) 04/02/89";
9 #endif not lint
10 
11 #include <sys/types.h>
12 #include <sys/file.h>
13 #include <sys/signal.h>
14 #include <sys/reboot.h>
15 #include <sys/syslog.h>
16 #include <sys/stat.h>
17 #include <setjmp.h>
18 #include <utmp.h>
19 #include <errno.h>
20 #include <ttyent.h>
21 #include "pathnames.h"
22 
23 #define	CMDSIZ	200	/* max string length for getty or window command*/
24 #define	ALL	p = itab; p ; p = p->next
25 #define	EVER	;;
26 #define SCPYN(a, b)	strncpy(a, b, sizeof(a))
27 #define SCMPN(a, b)	strncmp(a, b, sizeof(a))
28 
29 char	shell[]	= _PATH_BSHELL;
30 char	minus[]	= "-";
31 char	runc[]	= _PATH_RC;
32 char	utmpf[]	= _PATH_UTMP;
33 char	ctty[]	= _PATH_CONSOLE;
34 
35 struct	tab
36 {
37 	char	line[UT_LINESIZE];
38 	char	comn[CMDSIZ];
39 	char	xflag;
40 	int	pid;
41 	int	wpid;		/* window system pid for SIGHUP	*/
42 	char	wcmd[CMDSIZ];	/* command to start window system process */
43 	time_t	gettytime;
44 	int	gettycnt;
45 	time_t	windtime;
46 	int	windcnt;
47 	struct	tab *next;
48 } *itab;
49 
50 int	fi;
51 int	mergflag;
52 char	tty[20];
53 jmp_buf	sjbuf, shutpass;
54 
55 int	reset();
56 int	idle();
57 char	*strcpy(), *strcat();
58 long	lseek();
59 
60 struct	sigvec rvec = { reset, sigmask(SIGHUP), 0 };
61 
62 
63 #if defined(vax) || defined(tahoe)
64 main()
65 {
66 #if defined(tahoe)
67 	register int r12;		/* make sure r11 gets bootflags */
68 #endif
69 	register int r11;		/* passed thru from boot */
70 #else
71 main(argc, argv)
72 	char **argv;
73 {
74 #endif
75 	int howto, oldhowto;
76 
77 #if defined(vax) || defined(tahoe)
78 	howto = r11;
79 #else
80 	if (argc > 1 && argv[1][0] == '-') {
81 		char *cp;
82 
83 		howto = 0;
84 		cp = &argv[1][1];
85 		while (*cp) switch (*cp++) {
86 		case 'a':
87 			howto |= RB_ASKNAME;
88 			break;
89 		case 's':
90 			howto |= RB_SINGLE;
91 			break;
92 		}
93 	} else {
94 		howto = RB_SINGLE;
95 	}
96 #endif
97 	openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH);
98 	sigvec(SIGTERM, &rvec, (struct sigvec *)0);
99 	signal(SIGTSTP, idle);
100 	signal(SIGSTOP, SIG_IGN);
101 	signal(SIGTTIN, SIG_IGN);
102 	signal(SIGTTOU, SIG_IGN);
103 	(void) setjmp(sjbuf);
104 	for (EVER) {
105 		oldhowto = howto;
106 		howto = RB_SINGLE;
107 		if (setjmp(shutpass) == 0)
108 			shutdown();
109 		if (oldhowto & RB_SINGLE)
110 			single();
111 		if (runcom(oldhowto) == 0)
112 			continue;
113 		merge();
114 		multiple();
115 	}
116 }
117 
118 int	shutreset();
119 
120 shutdown()
121 {
122 	register i;
123 	register struct tab *p, *p1;
124 
125 	close(creat(utmpf, 0644));
126 	signal(SIGHUP, SIG_IGN);
127 	for (p = itab; p ; ) {
128 		term(p);
129 		p1 = p->next;
130 		free(p);
131 		p = p1;
132 	}
133 	itab = (struct tab *)0;
134 	signal(SIGALRM, shutreset);
135 	(void) kill(-1, SIGTERM);	/* one chance to catch it */
136 	sleep(5);
137 	alarm(30);
138 	for (i = 0; i < 5; i++)
139 		kill(-1, SIGKILL);
140 	while (wait((int *)0) != -1)
141 		;
142 	alarm(0);
143 	shutend();
144 }
145 
146 char shutfailm[] = "WARNING: Something is hung (won't die); ps axl advised\n";
147 
148 shutreset()
149 {
150 	int status;
151 
152 	if (fork() == 0) {
153 		int ct = open(ctty, 1);
154 		write(ct, shutfailm, sizeof (shutfailm));
155 		sleep(5);
156 		exit(1);
157 	}
158 	sleep(5);
159 	shutend();
160 	longjmp(shutpass, 1);
161 }
162 
163 shutend()
164 {
165 	register i, f;
166 
167 	acct(0);
168 	signal(SIGALRM, SIG_DFL);
169 	for (i = 0; i < 10; i++)
170 		close(i);
171 	logwtmp("~", "shutdown", "");
172 }
173 
174 single()
175 {
176 	register pid;
177 	register xpid;
178 	extern int errno;
179 
180 	do {
181 		pid = fork();
182 		if (pid == 0) {
183 			signal(SIGTERM, SIG_DFL);
184 			signal(SIGHUP, SIG_DFL);
185 			signal(SIGALRM, SIG_DFL);
186 			signal(SIGTSTP, SIG_IGN);
187 			(void) open(ctty, O_RDWR);
188 			dup2(0, 1);
189 			dup2(0, 2);
190 			execl(shell, minus, (char *)0);
191 			perror(shell);
192 			exit(0);
193 		}
194 		while ((xpid = wait((int *)0)) != pid)
195 			if (xpid == -1 && errno == ECHILD)
196 				break;
197 	} while (xpid == -1);
198 }
199 
200 runcom(oldhowto)
201 	int oldhowto;
202 {
203 	register pid, f;
204 	int status;
205 
206 	pid = fork();
207 	if (pid == 0) {
208 		(void) open("/", O_RDONLY);
209 		dup2(0, 1);
210 		dup2(0, 2);
211 		if (oldhowto & RB_SINGLE)
212 			execl(shell, shell, runc, (char *)0);
213 		else
214 			execl(shell, shell, runc, "autoboot", (char *)0);
215 		exit(1);
216 	}
217 	while (wait(&status) != pid)
218 		;
219 	if (status)
220 		return (0);
221 	logwtmp("~", "reboot", "");
222 	return (1);
223 }
224 
225 int merge();
226 struct	sigvec	mvec = { merge, sigmask(SIGTERM), 0 };
227 /*
228  * Multi-user.  Listen for users leaving, SIGHUP's
229  * which indicate ttys has changed, and SIGTERM's which
230  * are used to shutdown the system.
231  */
232 multiple()
233 {
234 	register struct tab *p;
235 	register pid;
236 	int omask;
237 
238 	sigvec(SIGHUP, &mvec, (struct sigvec *)0);
239 	for (EVER) {
240 		pid = wait((int *)0);
241 		if (pid == -1)
242 			return;
243 		omask = sigblock(sigmask(SIGHUP));
244 		for (ALL) {
245 			/* must restart window system BEFORE emulator */
246 			if (p->wpid == pid || p->wpid == -1)
247 				wstart(p);
248 			if (p->pid == pid || p->pid == -1) {
249 				/* disown the window system */
250 				if (p->wpid)
251 					kill(p->wpid, SIGHUP);
252 				cleanutmp(p);
253 				dfork(p);
254 			}
255 		}
256 		sigsetmask(omask);
257 	}
258 }
259 
260 /*
261  * Merge current contents of ttys file
262  * into in-core table of configured tty lines.
263  * Entered as signal handler for SIGHUP.
264  */
265 #define	FOUND	1
266 #define	CHANGE	2
267 #define WCHANGE 4
268 
269 merge()
270 {
271 	register struct tab *p;
272 	register struct ttyent *t;
273 	register struct tab *p1;
274 
275 	for (ALL)
276 		p->xflag = 0;
277 	setttyent();
278 	while (t = getttyent()) {
279 		if ((t->ty_status & TTY_ON) == 0)
280 			continue;
281 		for (ALL) {
282 			if (SCMPN(p->line, t->ty_name))
283 				continue;
284 			p->xflag |= FOUND;
285 			if (SCMPN(p->comn, t->ty_getty)) {
286 				p->xflag |= CHANGE;
287 				SCPYN(p->comn, t->ty_getty);
288 			}
289 			if (SCMPN(p->wcmd, t->ty_window ? t->ty_window : "")) {
290 				p->xflag |= WCHANGE|CHANGE;
291 				SCPYN(p->wcmd, t->ty_window);
292 			}
293 			goto contin1;
294 		}
295 
296 		/*
297 		 * Make space for a new one
298 		 */
299 		p1 = (struct tab *)calloc(1, sizeof(*p1));
300 		if (!p1) {
301 			syslog(LOG_ERR, "no space for '%s' !?!", t->ty_name);
302 			goto contin1;
303 		}
304 		/*
305 		 * Put new terminal at the end of the linked list.
306 		 */
307 		if (itab) {
308 			for (p = itab; p->next ; p = p->next)
309 				;
310 			p->next = p1;
311 		} else
312 			itab = p1;
313 
314 		p = p1;
315 		SCPYN(p->line, t->ty_name);
316 		p->xflag |= FOUND|CHANGE;
317 		SCPYN(p->comn, t->ty_getty);
318 		if (t->ty_window && strcmp(t->ty_window, "") != 0) {
319 			p->xflag |= WCHANGE;
320 			SCPYN(p->wcmd, t->ty_window);
321 		}
322 	contin1:
323 		;
324 	}
325 	endttyent();
326 	p1 = (struct tab *)0;
327 	for (ALL) {
328 		if ((p->xflag&FOUND) == 0) {
329 			term(p);
330 			wterm(p);
331 			if (p1)
332 				p1->next = p->next;
333 			else
334 				itab = p->next;
335 			free(p);
336 			p = p1 ? p1 : itab;
337 		} else {
338 			/* window system should be started first */
339 			if (p->xflag&WCHANGE) {
340 				wterm(p);
341 				wstart(p);
342 			}
343 			if (p->xflag&CHANGE) {
344 				term(p);
345 				dfork(p);
346 			}
347 		}
348 		p1 = p;
349 	}
350 }
351 
352 term(p)
353 	register struct tab *p;
354 {
355 
356 	if (p->pid != 0) {
357 		cleanutmp(p);
358 		kill(p->pid, SIGKILL);
359 	}
360 	p->pid = 0;
361 	/* send SIGHUP to get rid of connections */
362 	if (p->wpid > 0)
363 		kill(p->wpid, SIGHUP);
364 }
365 
366 dfork(p)
367 	struct tab *p;
368 {
369 	register pid;
370 	time_t t;
371 	int dowait = 0;
372 
373 	time(&t);
374 	p->gettycnt++;
375 	if ((t - p->gettytime) >= 60) {
376 		p->gettytime = t;
377 		p->gettycnt = 1;
378 	} else if (p->gettycnt >= 5) {
379 		dowait = 1;
380 		p->gettytime = t;
381 		p->gettycnt = 1;
382 	}
383 	pid = fork();
384 	if (pid == 0) {
385 		signal(SIGTERM, SIG_DFL);
386 		signal(SIGHUP, SIG_IGN);
387 		sigsetmask(0);	/* since can be called from masked code */
388 		if (dowait) {
389 			syslog(LOG_ERR, "'%s %s' failing, sleeping", p->comn, p->line);
390 			closelog();
391 			sleep(30);
392 		}
393 		execit(p->comn, p->line);
394 		exit(0);
395 	}
396 	p->pid = pid;
397 }
398 
399 cleanutmp(p)
400 	register struct tab *p;
401 {
402 	if (logout(p->line)) {
403 		logwtmp(p->line);
404 		/*
405 		 * After a proper login force reset
406 		 * of error detection code in dfork.
407 		 */
408 		p->gettytime = 0;
409 		p->windtime = 0;
410 	}
411 }
412 
413 reset()
414 {
415 
416 	longjmp(sjbuf, 1);
417 }
418 
419 jmp_buf	idlebuf;
420 
421 idlehup()
422 {
423 
424 	longjmp(idlebuf, 1);
425 }
426 
427 idle()
428 {
429 	register struct tab *p;
430 	register pid;
431 
432 	signal(SIGHUP, idlehup);
433 	for (EVER) {
434 		if (setjmp(idlebuf))
435 			return;
436 		pid = wait((int *) 0);
437 		if (pid == -1) {
438 			sigpause(0);
439 			continue;
440 		}
441 		for (ALL) {
442 			/* if window system dies, mark it for restart */
443 			if (p->wpid == pid)
444 				p->wpid = -1;
445 			if (p->pid == pid) {
446 				cleanutmp(p);
447 				p->pid = -1;
448 			}
449 		}
450 	}
451 }
452 
453 wterm(p)
454 	register struct tab *p;
455 {
456 	if (p->wpid != 0) {
457 		kill(p->wpid, SIGKILL);
458 	}
459 	p->wpid = 0;
460 }
461 
462 wstart(p)
463 	register struct tab *p;
464 {
465 	register pid;
466 	time_t t;
467 	int dowait = 0;
468 
469 	time(&t);
470 	p->windcnt++;
471 	if ((t - p->windtime) >= 60) {
472 		p->windtime = t;
473 		p->windcnt = 1;
474 	} else if (p->windcnt >= 5) {
475 		dowait = 1;
476 		p->windtime = t;
477 		p->windcnt = 1;
478 	}
479 
480 	pid = fork();
481 
482 	if (pid == 0) {
483 		signal(SIGTERM, SIG_DFL);
484 		signal(SIGHUP,  SIG_IGN);
485 		sigsetmask(0);	/* since can be called from masked code */
486 		if (dowait) {
487 			syslog(LOG_ERR, "'%s %s' failing, sleeping", p->wcmd, p->line);
488 			closelog();
489 			sleep(30);
490 		}
491 		execit(p->wcmd, p->line);
492 		exit(0);
493 	}
494 	p->wpid = pid;
495 }
496 
497 #define NARGS	20	/* must be at least 4 */
498 #define ARGLEN	512	/* total size for all the argument strings */
499 
500 execit(s, arg)
501 	char *s;
502 	char *arg;	/* last argument on line */
503 {
504 	char *argv[NARGS], args[ARGLEN], *envp[1];
505 	register char *sp = s;
506 	register char *ap = args;
507 	register char c;
508 	register int i;
509 
510 	/*
511 	 * First we have to set up the argument vector.
512 	 * "prog arg1 arg2" maps to exec("prog", "-", "arg1", "arg2").
513 	 */
514 	for (i = 1; i < NARGS - 2; i++) {
515 		argv[i] = ap;
516 		for (EVER) {
517 			if ((c = *sp++) == '\0' || ap >= &args[ARGLEN-1]) {
518 				*ap = '\0';
519 				goto done;
520 			}
521 			if (c == ' ') {
522 				*ap++ = '\0';
523 				while (*sp == ' ')
524 					sp++;
525 				if (*sp == '\0')
526 					goto done;
527 				break;
528 			}
529 			*ap++ = c;
530 		}
531 	}
532 done:
533 	argv[0] = argv[1];
534 	argv[1] = "-";
535 	argv[i+1] = arg;
536 	argv[i+2] = 0;
537 	envp[0] = 0;
538 	execve(argv[0], &argv[1], envp);
539 	/* report failure of exec */
540 	syslog(LOG_ERR, "%s: %m", argv[0]);
541 	closelog();
542 	sleep(10);	/* prevent failures from eating machine */
543 }
544