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