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