1 /*-
2  * Copyright (c) 1998 Andrzej Bialecki
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 /*
30  * A primitive version of init(8) with simplistic user interface
31  */
32 
33 #include <sys/types.h>
34 #include <sys/param.h>
35 #include <sys/mount.h>
36 #include <sys/reboot.h>
37 #include <sys/time.h>
38 #include <sys/resource.h>
39 #include <sys/wait.h>
40 #include <ctype.h>
41 #include <err.h>
42 
43 #ifdef USE_HISTORY
44 #error "Not yet. But it's quite simple to add - patches are welcome!"
45 #endif
46 
47 #include <errno.h>
48 #include <fcntl.h>
49 #include <libutil.h>
50 #include <paths.h>
51 #include <setjmp.h>
52 #include <signal.h>
53 #include <stdio.h>
54 #include <string.h>
55 #include <syslog.h>
56 #include <unistd.h>
57 #include <varargs.h>
58 
59 #define BUFSIZE 1024
60 #define MAX_CONS 12
61 
62 #define	NONE	0
63 #define	SINGLE	1
64 #define	MULTI	2
65 #define	DEATH	3
66 
67 #define	FALSE	0
68 #define TRUE	1
69 
70 char cwd[BUFSIZE];
71 char vty[]="0123456789abcdef";
72 char *progname;
73 char *motd=NULL;
74 int ncons=MAX_CONS;
75 int Reboot=FALSE;
76 int transition=MULTI;
77 int prevtrans=SINGLE;
78 jmp_buf machine;
79 
80 char *trans[]={ "NONE", "SINGLE", "MULTI", "DEATH" };
81 
82 extern char **environ;
83 
84 /* Struct for holding session state */
85 struct sess {
86 	char tty[16];	/* vty device path */
87 	pid_t pid;	/* pid of process running on it */
88 	int (*func)(int argc, char **argv);
89 			/* internal function to run on it (after forking) */
90 } ttys[MAX_CONS];
91 
92 /* Struct for built-in command */
93 struct command {
94 	char *cmd;		/* command name */
95 	char *descr;		/* command description */
96 	char *usage;		/* usage */
97 	char *example;		/* example of usage */
98 	int (*func)(char *);	/* callback function */
99 };
100 
101 /* Prototypes */
102 int cd(char *);
103 int pwd(char *);
104 int echo(char *);
105 int xit(char *);
106 int set(char *);
107 int unset(char *);
108 int env(char *);
109 int help(char *);
110 int sourcer(char *);
111 void do_command(int shell, char *cmdline);
112 void transition_handler(int);
113 
114 /* Table of built-in functions */
115 struct command bltins[]={
116 	{"cd","Change working directory","cd [dir]","cd /etc",cd},
117 	{"pwd","Print current directory","pwd","pwd",pwd},
118 	{"exit","Exit from shell()","exit","exit",xit},
119 	{"set","Set environment variable","set [VAR=value]","set TERM=xterm",set},
120 	{"unset","Unset environment variable","unset VAR","unset EDITOR",unset},
121 	{"echo","Echo arguments on stdout","echo arg1 arg2 ...","echo Hello World!",echo},
122 	{"env","Print all environment variables","env","env",env},
123 	{".","Source-in a file with commands",". filename",". /etc/rc",sourcer},
124 	{"?","Print this help :-)","? [command]","? set",help},
125 	{NULL,NULL,NULL,NULL,NULL}
126 };
127 
128 /*
129  * Built-in 'cd <path>' handler
130  */
131 int
cd(char * path)132 cd(char *path)
133 {
134 	if(chdir(path)) return(-1);
135 	getcwd(cwd,BUFSIZE);
136 	return(0);
137 }
138 
139 /*
140  * Built-in 'pwd' handler
141  */
142 int
pwd(char * dummy)143 pwd(char *dummy)
144 {
145 
146 	if(getcwd(cwd,BUFSIZE)==NULL) return(-1);
147 	printf("%s\n",cwd);
148 	return(0);
149 }
150 
151 /*
152  * Built-in 'exit' handler
153  */
154 int
xit(char * dummy)155 xit(char *dummy)
156 {
157 	_exit(0);
158 }
159 
160 /*
161  * Built-in 'echo' handler
162  */
163 int
echo(char * args)164 echo(char *args)
165 {
166 	int i=0,j;
167 	int len;
168 	char c;
169 	int s_quote=0,d_quote=0;
170 	int sep=0,no_lf=0;
171 
172 	if(args==NULL) {
173 		printf("\n");
174 		return;
175 	}
176 	len=strlen(args);
177 	if(len>=2) {
178 		if(args[0]=='-' && args[1]=='n') {
179 			no_lf++;
180 			i=2;
181 			while(i<len && (args[i]==' ' || args[i]=='\t')) i++;
182 		}
183 	}
184 	while(i<len) {
185 		c=args[i];
186 		switch(c) {
187 		case ' ':
188 		case '\t':
189 			if(s_quote||d_quote) {
190 				putchar(c);
191 			} else if(!sep) {
192 				putchar(' ');
193 				sep=1;
194 			}
195 			break;
196 		case '\\':
197 			i++;
198 			c=args[i];
199 			switch(c) {
200 			case 'n':
201 				putchar('\n');
202 				break;
203 			case 'b':
204 				putchar('\b');
205 				break;
206 			case 't':
207 				putchar('\t');
208 				break;
209 			case 'r':
210 				putchar('\r');
211 				break;
212 			default:
213 				putchar(c);
214 				break;
215 			}
216 			break;
217 		case '"':
218 			if(!d_quote) {
219 				d_quote=1;
220 				for(j=i+1;j<len;j++) {
221 					if(args[j]=='\\') {
222 						j++;
223 						continue;
224 					}
225 					if(args[j]=='"') {
226 						d_quote=2;
227 						break;
228 					}
229 				}
230 				if(d_quote!=2) {
231 					printf("\necho(): unmatched \"\n");
232 					return;
233 				}
234 			} else d_quote=0;
235 			break;
236 		case '\'':
237 			if(!s_quote) {
238 				s_quote=1;
239 				for(j=i+1;j<len;j++) {
240 					if(args[j]=='\\') {
241 						j++;
242 						continue;
243 					}
244 					if(args[j]=='\'') {
245 						s_quote=2;
246 						break;
247 					}
248 				}
249 				if(s_quote!=2) {
250 					printf("\necho(): unmatched '\n");
251 					return;
252 				}
253 			} else s_quote=0;
254 			break;
255 		case '`':
256 			printf("echo(): backquote not implemented yet!\n");
257 			break;
258 		default:
259 			sep=0;
260 			putchar(c);
261 			break;
262 		}
263 		i++;
264 	}
265 	if(!no_lf) putchar('\n');
266 	fflush(stdout);
267 }
268 
269 /*
270  * Built-in 'set VAR=value' handler
271  */
272 int
set(char * var)273 set(char *var)
274 {
275 	int res;
276 
277 	if(var==NULL) return(env(NULL));
278 	res=putenv(var);
279 	if(res) printf("set: %s\n",strerror(errno));
280 	return(res);
281 }
282 
283 /*
284  * Built-in 'env' handler
285  */
286 int
env(char * dummy)287 env(char *dummy)
288 {
289 	char **e;
290 
291 	e=environ;
292 	while(*e!=NULL) {
293 		printf("%s\n",*e++);
294 	}
295 	return(0);
296 }
297 
298 /*
299  * Built-in 'unset VAR' handler
300  */
301 int
unset(char * var)302 unset(char *var)
303 {
304 	if(var==NULL) {
305 		printf("%s: parameter required.\n",progname);
306 		return(-1);
307 	}
308 	return(unsetenv(var));
309 }
310 
311 /*
312  * Built-in '?' handler
313  */
314 int
help(char * cmd)315 help(char *cmd)
316 {
317 	struct command *x;
318 	int found=0;
319 
320 	if(cmd==NULL) {
321 		printf("\nBuilt-in commands:\n");
322 		printf("-------------------\n");
323 		x=bltins;
324 		while(x->cmd!=NULL) {
325 			printf("%s\t%s\n",x->cmd,x->descr);
326 			x++;
327 		}
328 		printf("\nEnter '? <cmd>' for details.\n\n");
329 		return(0);
330 	} else {
331 		x=bltins;
332 		while(x->cmd!=NULL) {
333 			if(strcmp(x->cmd,cmd)==0) {
334 				found++;
335 				break;
336 			}
337 			x++;
338 		}
339 		if(found) {
340 			printf("\n%s\t%s:\n",x->cmd,x->descr);
341 			printf("\tUsage:\n\t\t%s\n",x->usage);
342 			printf("\te.g:\n\t\t%s\n\n",x->example);
343 			return(0);
344 		} else {
345 			printf("\n%s: no such command.\n\n",cmd);
346 			return(-1);
347 		}
348 	}
349 }
350 
351 /*
352  * Signal handler for shell()
353  */
354 void
shell_sig(int sig)355 shell_sig(int sig)
356 {
357 	switch(sig) {
358 	case SIGINT:
359 	case SIGQUIT:
360 	case SIGTERM:
361 		/* ignore ? */
362 		break;
363 	default:
364 		break;
365 	}
366 }
367 
368 /*
369  * Built-in '.' handler (read-in and execute commands from file)
370  */
371 int
sourcer(char * fname)372 sourcer(char *fname)
373 {
374 	FILE *fd;
375 	char buf[512],*tok,*arg,**av;
376 	int ac,len,f,res,i;
377 	pid_t pid;
378 	char *sep=" \t";
379 
380 	fd=fopen(fname,"r");
381 	if(fd==NULL) {
382 		printf("Couldn't open file '%s'\n",fname);
383 		return(-1);
384 	}
385 	while(!feof(fd)) {
386 		memset(buf,0,512);
387 		if(fgets(buf,512,fd)==NULL) continue;
388 		if((*buf=='#') || (*buf=='\n')) continue;
389 		len=strlen(buf);
390 		buf[len-1]='\0';
391 		if(strncmp(buf,"ncons",5)==0) {
392 			tok=strtok(buf,sep);
393 			tok=strtok(NULL,sep);
394 			ncons=atoi(tok);
395 			if((ncons<1)||(ncons>MAX_CONS)) {
396 				syslog(LOG_EMERG,"%s: bad ncons value; defaulting to %d\n",fname,MAX_CONS);
397 				ncons=MAX_CONS;
398 			}
399 			continue;
400 		} else if(strncmp(buf,"motd",4)==0) {
401 			tok=strtok(buf,sep);
402 			motd=strdup(strtok(NULL,sep));
403 			continue;
404 		} else {
405 			do_command(0,buf);
406 		}
407 		/* Next command, please. */
408 	}
409 	fclose(fd);
410 	syslog(LOG_EMERG,"Done with %s",fname);
411 }
412 
413 void
do_command(int shell,char * cmdline)414 do_command(int shell, char *cmdline)
415 {
416 	char *tok,*c,*sep=" \t";
417 	char **av;
418 	struct command *x;
419 	int found,len;
420 	int ac,i,f,res;
421 	int bg=0;
422 	pid_t pid;
423 
424 	len=strlen(cmdline);
425 	if(cmdline[len-1]=='&') {
426 		bg++;
427 		cmdline[len-1]='\0';
428 		len--;
429 	} else bg=0;
430 	tok=strtok(cmdline,sep);
431 	x=bltins;
432 	found=0;
433 	while(x->cmd!=NULL) {
434 		if(strcmp(x->cmd,tok)==0) {
435 			found++;
436 			break;
437 		}
438 		x++;
439 	}
440 	if(found) {
441 		tok=cmdline+strlen(x->cmd)+1;
442 		while(*tok && isblank(*tok) && (tok<(cmdline+len))) tok++;
443 		if(*tok==NULL) tok=NULL;
444 		x->func(tok);
445 		return;
446 	}
447 	ac=0;
448 	av=(char **)calloc(((len+1)/2+1),sizeof(char *));
449 	av[ac++]=tok;
450 	while((av[ac++]=strtok(NULL,sep))!=NULL)
451 		continue;
452 	switch((pid=fork())) {
453 	case 0:
454 		if(shell) {
455 			signal(SIGINT,SIG_DFL);
456 			signal(SIGQUIT,SIG_DFL);
457 			signal(SIGTERM,SIG_DFL);
458 			signal(SIGHUP,SIG_DFL);
459 		} else {
460 			close(0);
461 			close(1);
462 			close(2);
463 			f=open(_PATH_CONSOLE,O_RDWR);
464 			dup2(f,0);
465 			dup2(f,1);
466 			dup2(f,2);
467 			if(f>2) close(f);
468 		}
469 		if(bg) {
470 			if(daemon(0,0)) {
471 				printf("do_command(%s): failed to run bg: %s\n",
472 				av[0],strerror(errno));
473 				_exit(100);
474 			}
475 		}
476 		execvp(av[0],av);
477 		/* Something went wrong... */
478 		printf("do_command(%s): %s\n",av[0],strerror(errno));
479 		_exit(100);
480 		break;
481 	case -1:
482 		printf("do_command(): %s\n",strerror(errno));
483 		break;
484 	default:
485 		while(waitpid(pid,&res,0)!=pid) continue;
486 		if(WEXITSTATUS(res)) {
487 			printf("do_command(%s): exit code=%d\n",
488 				av[0],WEXITSTATUS(res));
489 		}
490 		break;
491 	}
492 	free(av);
493 	return;
494 }
495 
496 /*
497  * This is the user interface. This routine gets executed on each
498  * virtual console serviced by init.
499  *
500  * It works as normal shell does - for each external command it forks
501  * and execs, for each internal command just executes a function.
502  */
503 
504 int
shell(int argc,char ** argv)505 shell(int argc, char **argv)
506 {
507 	char buf[BUFSIZE];
508 	char *prompt=" # ";
509 	int fd;
510 	int res;
511 	pid_t mypid;
512 
513 	if(motd!=NULL) {
514 		if((fd=open(motd,O_RDONLY))!=-1) {
515 			do {
516 				res=read(fd,buf,BUFSIZE);
517 				res=write(1,buf,res);
518 			} while(res>0);
519 			close(fd);
520 		}
521 	}
522 
523 	printf("\n\n+=========================================================+\n");
524 	printf("| Built-in shell() (enter '?' for short help on commands) |\n");
525 	printf("+=========================================================+\n\n");
526 	getcwd(cwd,BUFSIZE);
527 	mypid=getpid();
528 	signal(SIGINT,shell_sig);
529 	signal(SIGQUIT,shell_sig);
530 	signal(SIGTERM,shell_sig);
531 	while(!feof(stdin)) {
532 		memset(buf,0,BUFSIZE);
533 		printf("(%d)%s%s",mypid,cwd,prompt);
534 		fflush(stdout);
535 		if(fgets(buf,BUFSIZE-1,stdin)==NULL) continue;
536 		buf[strlen(buf)-1]='\0';
537 		if(strlen(buf)==0) continue;
538 		do_command(1,buf);
539 	}
540 	return(0);
541 }
542 
543 /*
544  * Stub for executing some external program on a console. This is called
545  * from previously forked copy of our process, so that exec is ok.
546  */
547 int
external_cmd(int argc,char ** argv)548 external_cmd(int argc, char **argv)
549 {
550 	execvp(argv[0],argv);
551 }
552 
553 /*
554  * Acquire vty and properly attach ourselves to it.
555  * Also, build basic environment for running user interface.
556  */
557 
558 int
start_session(int vty,int argc,char ** argv)559 start_session(int vty, int argc, char **argv)
560 {
561 	int fd;
562 	char *t;
563 
564 	close(0);
565 	close(1);
566 	close(2);
567 	revoke(ttys[vty].tty);
568 	fd=open(ttys[vty].tty,O_RDWR);
569 	dup2(fd,0);
570 	dup2(fd,1);
571 	dup2(fd,2);
572 	if(fd>2) close(fd);
573 	login_tty(fd);
574 	setpgid(0,getpid());
575 	putenv("TERM=xterm");
576 	putenv("HOME=/");
577 	putenv("PATH=/stand:/bin:/usr/bin:/sbin:.");
578 	signal(SIGHUP,SIG_DFL);
579 	signal(SIGINT,SIG_DFL);
580 	signal(SIGQUIT,SIG_DFL);
581 	signal(SIGTERM,SIG_DFL);
582 	chdir("/");
583 	t=(char *)(rindex(ttys[vty].tty,'/')+1);
584 	printf("\n\n\nStarting session on %s.\n",t);
585 	ttys[vty].func(argc,argv);
586 	_exit(0);
587 }
588 
589 /*
590  * Execute system startup script /etc/rc
591  *
592  * (Of course if you don't like it - I don't - you can run anything you
593  * want here. Perhaps it would be useful just to read some config DB and
594  * do these things ourselves, avoiding forking lots of shells and scripts.)
595  */
596 
597 /* If OINIT_RC is defined, oinit will use it's own configuration file,
598  * /etc/oinit.rc. It's format is described below. Otherwise, it will use
599  * normal /etc/rc interpreted by Bourne shell.
600  */
601 #ifndef OINIT_RC
602 #ifndef SH_NAME
603 #define SH_NAME	"-sh"
604 #endif
605 #ifndef SH_PATH
606 #define SH_PATH	_PATH_BSHELL
607 #endif
608 #ifndef SH_ARG
609 #define SH_ARG	"/etc/rc"
610 #endif
611 void
runcom()612 runcom()
613 {
614 	char *argv[3];
615 	pid_t pid;
616 	int st;
617 	int fd;
618 
619 	if((pid=fork())==0) {
620 		/* child */
621 		close(0);
622 		close(1);
623 		close(2);
624 		fd=open(_PATH_CONSOLE,O_RDWR);
625 		dup2(fd,0);
626 		dup2(fd,1);
627 		dup2(fd,2);
628 		if(fd>2) close(fd);
629 		argv[0]=SH_NAME;
630 		argv[1]=SH_ARG;
631 		argv[2]=0;
632 		execvp(SH_PATH,argv);
633 		printf("runcom(): %s\n",strerror(errno));
634 		_exit(1);
635 	}
636 	/* Wait for child to exit */
637 	while(pid!=waitpid(pid,(int *)0,0)) continue;
638 	return;
639 }
640 #else
641 /* Alternative /etc/rc - default is /etc/oinit.rc. Its format is as follows:
642  * - each empty line or line beginning with a '#' is discarded
643  * - any other line must contain a keyword, or a (nonblocking) command to run.
644  *
645  * Thus far, the following keywords are defined:
646  * ncons <number>	number of virtual consoles to open
647  * motd <pathname>	full path to motd file
648  *
649  * Examples of commands to run:
650  *
651  * ifconfig lo0 inet 127.0.0.1 netmask 255.0.0.0
652  * ifconfig ed0 inet 148.81.168.10 netmask 255.255.255.0
653  * kbdcontrol -l /usr/share/syscons/my_map.kbd
654  */
655 void
runcom(char * fname)656 runcom(char *fname)
657 {
658 	int fd;
659 
660 	close(0);
661 	close(1);
662 	close(2);
663 	fd=open(_PATH_CONSOLE,O_RDWR);
664 	dup2(fd,0);
665 	dup2(fd,1);
666 	dup2(fd,2);
667 	if(fd>2) close(fd);
668 	sourcer(fname);
669 }
670 #endif
671 
672 int
run_multi()673 run_multi()
674 {
675 	int i,j;
676 	pid_t pid;
677 	int found;
678 
679 	/* Run /etc/rc if not in single user */
680 #ifndef OINIT_RC
681 	if(prevtrans==SINGLE) runcom();
682 #else
683 	if(prevtrans==SINGLE) runcom(OINIT_RC);
684 #endif
685 	if(transition!=MULTI) return(-1);
686 
687 	syslog(LOG_EMERG,"*** Starting multi-user mode ***");
688 
689 	/* Fork shell interface for each console */
690 	for(i=0;i<ncons;i++) {
691 		if(ttys[i].pid==0) {
692 			switch(pid=fork()) {
693 			case 0:
694 				start_session(i,0,NULL);
695 				break;
696 			case -1:
697 				printf("%s: %s\n",progname,strerror(errno));
698 				break;
699 			default:
700 				ttys[i].pid=pid;
701 				break;
702 			}
703 		}
704 	}
705 	/* Initialize any other services we'll use - most probably this will
706 	 * be a 'telnet' service (some day...).
707 	 */
708 	/* */
709 
710 	/* Emulate multi-user */
711 	while(transition==MULTI) {
712 		/* XXX Modify this to allow for checking for the input on
713 		 * XXX listening sockets, and forking a 'telnet' service.
714 		 */
715 		/* */
716 
717 		/* Wait for any process to exit */
718 		pid=waitpid(-1,(int *)0,0);
719 		if(pid==-1) continue;
720 		found=0;
721 		j=-1;
722 		/* search if it's one of our sessions */
723 		for(i=0;i<ncons;i++) {
724 			if(ttys[i].pid==pid) {
725 				found++;
726 				j=i;
727 				ttys[j].pid=0;
728 				break;
729 			}
730 		}
731 		if(!found) {
732 			/* Just collect the process's status */
733 			continue;
734 		} else {
735 			/* restart shell() on a console, if it died */
736 			if(transition!=MULTI) return(0);
737 			switch(pid=fork()) {
738 			case 0:
739 				sleep(1);
740 				start_session(j,0,NULL);
741 				break;
742 			case -1:
743 				printf("%s: %s\n",progname,strerror(errno));
744 				break;
745 			default:
746 				ttys[j].pid=pid;
747 				break;
748 			}
749 		}
750 	}
751 }
752 
753 int clang;
754 
755 void
kill_timer(int sig)756 kill_timer(int sig)
757 {
758 	clang=1;
759 }
760 
kill_ttys()761 kill_ttys()
762 {
763 }
764 
765 /*
766  * Start a shell on ttyv0 (i.e. the console).
767  */
768 
769 int
run_single()770 run_single()
771 {
772 	int i;
773 	pid_t pid,wpid;
774 	static int sigs[2]={SIGTERM,SIGKILL};
775 
776 	syslog(LOG_EMERG,"*** Starting single-user mode ***");
777 	/* Kill all existing sessions */
778 	syslog(LOG_EMERG,"Killing all existing sessions...");
779 	for(i=0;i<MAX_CONS;i++) {
780 		kill(ttys[i].pid,SIGHUP);
781 		ttys[i].pid=0;
782 	}
783 	for(i=0;i<2;i++) {
784 		if(kill(-1,sigs[i])==-1 && errno==ESRCH) break;
785 		clang=0;
786 		alarm(10);
787 		do {
788 			pid=waitpid(-1,(int *)0,WUNTRACED);
789 			if(errno==EINTR) continue;
790 			else break;
791 		} while (clang==0);
792 	}
793 	if(errno!=ECHILD) {
794 		syslog(LOG_EMERG,"Some processes would not die; ps -axl advised");
795 	}
796 	/* Single-user */
797 	switch(pid=fork()) {
798 	case 0:
799 		start_session(0,0,NULL);
800 		break;
801 	case -1:
802 		printf("%s: %s\n",progname,strerror(errno));
803 		printf("The system is seriously hosed. I'm dying...\n");
804 		transition=DEATH;
805 		return(-1);
806 		break;
807 	default:
808 		do {
809 			wpid=waitpid(pid,(int *)0,WUNTRACED);
810 		} while(wpid!=pid && transition==SINGLE);
811 		if(transition!=DEATH) {
812 			prevtrans=transition;
813 			transition=MULTI;
814 		}
815 		break;
816 	}
817 	return(0);
818 }
819 
820 /*
821  * Transition handler - installed as signal handler.
822  */
823 
824 void
transition_handler(int sig)825 transition_handler(int sig)
826 {
827 
828 	switch(sig) {
829 	case SIGHUP:
830 	case SIGTERM:
831 		prevtrans=transition;
832 		transition=SINGLE;
833 		syslog(LOG_EMERG,"*** Going from %s -> %s\n",trans[prevtrans],trans[transition]);
834 		if(prevtrans!=transition) longjmp(machine,sig);
835 		break;
836 	case SIGINT:
837 	case SIGQUIT:
838 		prevtrans=transition;
839 		transition=DEATH;
840 		syslog(LOG_EMERG,"*** Going from %s -> %s\n",trans[prevtrans],trans[transition]);
841 		if(prevtrans!=transition) longjmp(machine,sig);
842 		break;
843 	default:
844 		syslog(LOG_EMERG,"pid=%d sig=%s (ignored)\n",getpid(),sys_siglist[sig]);
845 		break;
846 	}
847 }
848 
849 /*
850  * Change system state appropriately to the signals
851  */
852 
853 int
transition_machine()854 transition_machine()
855 {
856 	int i;
857 
858 	while(transition!=DEATH) {
859 		switch(transition) {
860 		case MULTI:
861 			run_multi();
862 			break;
863 		case SINGLE:
864 			run_single();
865 			break;
866 		}
867 	}
868 	syslog(LOG_EMERG,"Killing all existing sessions...");
869 	/* Kill all sessions */
870 	kill(-1,SIGKILL);
871 	/* Be nice and wait for them */
872 	while(waitpid(-1,(int *)0,WNOHANG|WUNTRACED)>0) continue;
873 	unmount("/",0);
874 	reboot(RB_AUTOBOOT);
875 	/* NOTREACHED */
876 }
877 
878 int
main(int argc,char ** argv)879 main(int argc, char **argv)
880 {
881 	int devfs=0,c,i;
882 
883 	/* These are copied from real init(8) */
884 	if(getuid()!=0)
885 		errx(1,"%s",strerror(EPERM));
886 	openlog("init",LOG_CONS|LOG_ODELAY,LOG_AUTH);
887 	if(setsid()<0)
888 		warn("initial setsid() failed");
889 	if(setlogin("root")<0)
890 		warn("setlogin() failed");
891 
892 	close(0);
893 	close(1);
894 	close(2);
895 	chdir("/");
896 
897 	progname=rindex(argv[0],'/');
898 	if(progname==NULL) {
899 		progname=argv[0];
900 	} else progname++;
901 
902 	transition=MULTI;
903 
904 	/* We must recognize the same options as real init does */
905 	while((c=getopt(argc,argv,"dsf"))!=-1) {
906 		switch(c) {
907 		case 'd':
908 			devfs=1;
909 			break;
910 		case 's':
911 			transition=SINGLE;
912 			break;
913 		case 'f':
914 			break;
915 		default:
916 			printf("%s: unrecognized flag '-%c'\n",progname,c);
917 			break;
918 		}
919 	}
920 	if(devfs)
921 		mount("devfs",_PATH_DEV,MNT_NOEXEC|MNT_RDONLY,0);
922 
923 	/* Fill in the sess structures. */
924 	/* XXX Really, should be filled based upon config file. */
925 	for(i=0;i<MAX_CONS;i++) {
926 		if(i==0) {
927 			sprintf(ttys[i].tty,_PATH_CONSOLE);
928 		} else {
929 			sprintf(ttys[i].tty,"%sv%c",_PATH_TTY,vty[i]);
930 		}
931 		ttys[i].pid=0;
932 		ttys[i].func=shell;
933 	}
934 
935 	getcwd(cwd,BUFSIZE);
936 
937 	signal(SIGINT,transition_handler);
938 	signal(SIGQUIT,transition_handler);
939 	signal(SIGTERM,transition_handler);
940 	signal(SIGHUP,transition_handler);
941 	signal(SIGALRM,kill_timer);
942 
943 	setjmp(machine);
944 	transition_machine(transition);
945 	/* NOTREACHED */
946 	exit(100);
947 }
948