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