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