xref: /original-bsd/sbin/init/init.c (revision 6f815012)
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.19 (Berkeley) 03/25/91";
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 	/* insure proper semantics for setjmp/longjmp */
65 	static int howto, oldhowto, started = 0;
66 #if defined(tahoe)
67 	register int r12;		/* make sure r11 gets bootflags */
68 #endif
69 #if defined(vax) || defined(tahoe) || defined(hp300)
70 	/* howto passed in high-order register XXX */
71 	register int r11;		/* passed thru from boot */
72 #ifdef __GNUC__
73 #ifdef hp300
74 	asm("movl d7,%0" : "=rm" (howto));
75 #else
76 	asm("movl r11,%0" : "=rm" (howto));
77 #endif
78 #else
79 	howto = r11;
80 #endif /* __GNUC__ */
81 #else  /* vax || tahoe || hp300 */
82 	/* howto passed as argument */
83 	howto = 0;
84 #endif  /* ! (vax || tahoe || hp300) */
85 
86 	/*
87 	 * We expect a single options argument from the kernel.
88 	 * If it is present, we ignore anything in registers from above.
89 	 */
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 #ifdef notyet
97 		case 'f':
98 			howto |= RB_FASTBOOT;
99 			break;
100 #endif
101 		case 's':
102 			howto |= RB_SINGLE;
103 			break;
104 		}
105 	}
106 	if (getuid() != 0)
107 		exit(1);
108 	openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH);
109 	if (setsid() < 0)
110 		syslog(LOG_ERR, "setsid failed (initial) %m");
111 	sigvec(SIGTERM, &rvec, (struct sigvec *)0);
112 	signal(SIGTSTP, idle);
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 fd, status;
230 
231 	pid = fork();
232 	if (pid == 0) {
233 		signal(SIGTSTP, SIG_IGN);
234 		signal(SIGHUP, SIG_IGN);
235 		if ((fd = open(ctty, O_RDWR)) < 0)
236 			syslog(LOG_ERR, "open %s: %m", ctty);
237 		else {
238 			dup2(fd, 0);
239 			dup2(fd, 1);
240 			dup2(fd, 2);
241 			if (fd > 2)
242 				close(fd);
243 		}
244 		if (setsid() < 0)
245 			syslog(LOG_ERR, "setsid failed (runcom) %m");
246 		if (ioctl(0, TIOCSCTTY, 0) < 0)
247 			syslog(LOG_ERR, "TIOCSCTTY failed (runcom) %m");
248 		if (oldhowto & RB_SINGLE)
249 			execl(shell, shell, runc, (char *)0);
250 		else
251 			execl(shell, shell, runc, "autoboot", (char *)0);
252 		exit(1);
253 	}
254 	while (wait(&status) != pid)
255 		;
256 	if (status)
257 		return (0);
258 	logwtmp("~", "reboot", "");
259 	return (1);
260 }
261 
262 struct	sigvec	mvec = { merge, sigmask(SIGTERM), 0 };
263 /*
264  * Multi-user.  Listen for users leaving, SIGHUP's
265  * which indicate ttys has changed, and SIGTERM's which
266  * are used to shutdown the system.
267  */
268 multiple()
269 {
270 	extern int errno;
271 	register struct tab *p;
272 	register pid;
273 	int omask;
274 
275 	sigvec(SIGHUP, &mvec, (struct sigvec *)0);
276 	for (EVER) {
277 		pid = wait((int *)0);
278 /* SHOULD FIX THIS IN THE KERNEL */
279 		if (pid == -1 && errno != EINTR)
280 			return;
281 		omask = sigblock(sigmask(SIGHUP));
282 		for (ALL) {
283 			/* must restart window system BEFORE emulator */
284 			if (p->wpid == pid || p->wpid == -1)
285 				wstart(p);
286 			if (p->pid == pid || p->pid == -1) {
287 				/* disown the window system */
288 				if (p->wpid)
289 					kill(p->wpid, SIGHUP);
290 				cleanutmp(p);
291 				dfork(p);
292 			}
293 		}
294 		sigsetmask(omask);
295 	}
296 }
297 
298 /*
299  * Merge current contents of ttys file
300  * into in-core table of configured tty lines.
301  * Entered as signal handler for SIGHUP.
302  */
303 #define	FOUND	1
304 #define	CHANGE	2
305 #define WCHANGE 4
306 
307 void
308 merge()
309 {
310 	register struct tab *p;
311 	register struct ttyent *t;
312 	register struct tab *p1;
313 
314 	for (ALL)
315 		p->xflag = 0;
316 	setttyent();
317 	while (t = getttyent()) {
318 		if ((t->ty_status & TTY_ON) == 0)
319 			continue;
320 		for (ALL) {
321 			if (SCMPN(p->line, t->ty_name))
322 				continue;
323 			p->xflag |= FOUND;
324 			if (SCMPN(p->comn, t->ty_getty)) {
325 				p->xflag |= CHANGE;
326 				SCPYN(p->comn, t->ty_getty);
327 			}
328 			if (SCMPN(p->wcmd, t->ty_window ? t->ty_window : "")) {
329 				p->xflag |= WCHANGE|CHANGE;
330 				SCPYN(p->wcmd, t->ty_window);
331 			}
332 			goto contin1;
333 		}
334 
335 		/*
336 		 * Make space for a new one
337 		 */
338 		p1 = (struct tab *)calloc(1, sizeof(*p1));
339 		if (!p1) {
340 			syslog(LOG_ERR, "no space for '%s' !?!", t->ty_name);
341 			goto contin1;
342 		}
343 		/*
344 		 * Put new terminal at the end of the linked list.
345 		 */
346 		if (itab) {
347 			for (p = itab; p->next ; p = p->next)
348 				;
349 			p->next = p1;
350 		} else
351 			itab = p1;
352 
353 		p = p1;
354 		SCPYN(p->line, t->ty_name);
355 		p->xflag |= FOUND|CHANGE;
356 		SCPYN(p->comn, t->ty_getty);
357 		if (t->ty_window && strcmp(t->ty_window, "") != 0) {
358 			p->xflag |= WCHANGE;
359 			SCPYN(p->wcmd, t->ty_window);
360 		}
361 	contin1:
362 		;
363 	}
364 	endttyent();
365 	p1 = (struct tab *)0;
366 	for (ALL) {
367 		if ((p->xflag&FOUND) == 0) {
368 			term(p);
369 			wterm(p);
370 			if (p1)
371 				p1->next = p->next;
372 			else
373 				itab = p->next;
374 			free(p);
375 			p = p1 ? p1 : itab;
376 		} else {
377 			/* window system should be started first */
378 			if (p->xflag&WCHANGE) {
379 				wterm(p);
380 				wstart(p);
381 			}
382 			if (p->xflag&CHANGE) {
383 				term(p);
384 				dfork(p);
385 			}
386 		}
387 		p1 = p;
388 	}
389 }
390 
391 term(p)
392 	register struct tab *p;
393 {
394 
395 	if (p->pid != 0) {
396 		cleanutmp(p);
397 		kill(p->pid, SIGKILL);
398 	}
399 	p->pid = 0;
400 	/* send SIGHUP to get rid of connections */
401 	if (p->wpid > 0)
402 		kill(p->wpid, SIGHUP);
403 }
404 
405 dfork(p)
406 	struct tab *p;
407 {
408 	register pid;
409 	time_t t;
410 	int dowait = 0;
411 
412 	time(&t);
413 	p->gettycnt++;
414 	if ((t - p->gettytime) >= 60) {
415 		p->gettytime = t;
416 		p->gettycnt = 1;
417 	} else if (p->gettycnt >= 5) {
418 		dowait = 1;
419 		p->gettytime = t;
420 		p->gettycnt = 1;
421 	}
422 	pid = fork();
423 	if (pid == 0) {
424 		signal(SIGTERM, SIG_DFL);
425 		signal(SIGHUP, SIG_IGN);
426 		sigsetmask(0);	/* since can be called from masked code */
427 		if (dowait) {
428 			syslog(LOG_ERR, "'%s %s' failing, sleeping", p->comn, p->line);
429 			closelog();
430 			sleep(30);
431 		}
432 		if (setsid() < 0)
433 			syslog(LOG_ERR, "setsid failed(dfork) %m");
434 		execit(p->comn, p->line);
435 		exit(0);
436 	}
437 	p->pid = pid;
438 }
439 
440 cleanutmp(p)
441 	register struct tab *p;
442 {
443 	if (logout(p->line)) {
444 		logwtmp(p->line, "", "");
445 		/*
446 		 * After a proper login force reset
447 		 * of error detection code in dfork.
448 		 */
449 		p->gettytime = 0;
450 		p->windtime = 0;
451 	}
452 }
453 
454 void
455 reset()
456 {
457 	longjmp(sjbuf, 1);
458 }
459 
460 jmp_buf	idlebuf;
461 
462 void
463 idlehup()
464 {
465 	longjmp(idlebuf, 1);
466 }
467 
468 void
469 idle()
470 {
471 	register struct tab *p;
472 	register pid;
473 
474 	signal(SIGHUP, idlehup);
475 	for (EVER) {
476 		if (setjmp(idlebuf))
477 			return;
478 		pid = wait((int *) 0);
479 		if (pid == -1) {
480 			sigpause(0);
481 			continue;
482 		}
483 		for (ALL) {
484 			/* if window system dies, mark it for restart */
485 			if (p->wpid == pid)
486 				p->wpid = -1;
487 			if (p->pid == pid) {
488 				cleanutmp(p);
489 				p->pid = -1;
490 			}
491 		}
492 	}
493 }
494 
495 wterm(p)
496 	register struct tab *p;
497 {
498 	if (p->wpid != 0) {
499 		kill(p->wpid, SIGKILL);
500 	}
501 	p->wpid = 0;
502 }
503 
504 wstart(p)
505 	register struct tab *p;
506 {
507 	register pid;
508 	time_t t;
509 	int dowait = 0;
510 
511 	time(&t);
512 	p->windcnt++;
513 	if ((t - p->windtime) >= 60) {
514 		p->windtime = t;
515 		p->windcnt = 1;
516 	} else if (p->windcnt >= 5) {
517 		dowait = 1;
518 		p->windtime = t;
519 		p->windcnt = 1;
520 	}
521 
522 	pid = fork();
523 
524 	if (pid == 0) {
525 		signal(SIGTERM, SIG_DFL);
526 		signal(SIGHUP,  SIG_IGN);
527 		sigsetmask(0);	/* since can be called from masked code */
528 		if (dowait) {
529 			syslog(LOG_ERR, "'%s %s' failing, sleeping", p->wcmd, p->line);
530 			closelog();
531 			sleep(30);
532 		}
533 		if (setsid() < 0)
534 			syslog(LOG_ERR, "setsid failed (window) %m");
535 		execit(p->wcmd, p->line);
536 		exit(0);
537 	}
538 	p->wpid = pid;
539 }
540 
541 #define NARGS	20	/* must be at least 4 */
542 #define ARGLEN	512	/* total size for all the argument strings */
543 
544 execit(s, arg)
545 	char *s;
546 	char *arg;	/* last argument on line */
547 {
548 	char *argv[NARGS], args[ARGLEN], *envp[1];
549 	register char *sp = s;
550 	register char *ap = args;
551 	register char c;
552 	register int i;
553 
554 	/*
555 	 * First we have to set up the argument vector.
556 	 * "prog arg1 arg2" maps to exec("prog", "-", "arg1", "arg2").
557 	 */
558 	for (i = 1; i < NARGS - 2; i++) {
559 		argv[i] = ap;
560 		for (EVER) {
561 			if ((c = *sp++) == '\0' || ap >= &args[ARGLEN-1]) {
562 				*ap = '\0';
563 				goto done;
564 			}
565 			if (c == ' ') {
566 				*ap++ = '\0';
567 				while (*sp == ' ')
568 					sp++;
569 				if (*sp == '\0')
570 					goto done;
571 				break;
572 			}
573 			*ap++ = c;
574 		}
575 	}
576 done:
577 	argv[0] = argv[1];
578 	argv[1] = "-";
579 	argv[i+1] = arg;
580 	argv[i+2] = 0;
581 	envp[0] = 0;
582 	execve(argv[0], &argv[1], envp);
583 	/* report failure of exec */
584 	syslog(LOG_ERR, "%s: %m", argv[0]);
585 	closelog();
586 	sleep(10);	/* prevent failures from eating machine */
587 }
588