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