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