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