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