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