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