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