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