2 * Copyright (c) 1998 Andrzej Bialecki
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
26 * $FreeBSD: src/release/picobsd/tinyware/oinit/oinit.c,v 1.4.2.2 2001/07/25 15:57:43 dd Exp $
27 * $DragonFly: src/release/picobsd/tinyware/oinit/Attic/oinit.c,v 1.2 2003/06/17 04:27:20 dillon Exp $
31 * A primitive version of init(8) with simplistic user interface
34 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/mount.h>
37 #include <sys/reboot.h>
39 #include <sys/resource.h>
45 #error "Not yet. But it's quite simple to add - patches are welcome!"
72 char vty[]="0123456789abcdef";
81 char *trans[]={ "NONE", "SINGLE", "MULTI", "DEATH" };
83 extern char **environ;
85 /* Struct for holding session state */
87 char tty[16]; /* vty device path */
88 pid_t pid; /* pid of process running on it */
89 int (*func)(int argc, char **argv);
90 /* internal function to run on it (after forking) */
93 /* Struct for built-in command */
95 char *cmd; /* command name */
96 char *descr; /* command description */
97 char *usage; /* usage */
98 char *example; /* example of usage */
99 int (*func)(char *); /* callback function */
103 int cd __P((char *));
104 int pwd __P((char *));
105 int echo __P((char *));
106 int xit __P((char *));
107 int set __P((char *));
108 int unset __P((char *));
109 int env __P((char *));
110 int help __P((char *));
111 int sourcer __P((char *));
112 void do_command __P((int shell, char *cmdline));
113 void transition_handler __P((int));
115 /* Table of built-in functions */
116 struct command bltins[]={
117 {"cd","Change working directory","cd [dir]","cd /etc",cd},
118 {"pwd","Print current directory","pwd","pwd",pwd},
119 {"exit","Exit from shell()","exit","exit",xit},
120 {"set","Set environment variable","set [VAR=value]","set TERM=cons25",set},
121 {"unset","Unset environment variable","unset VAR","unset EDITOR",unset},
122 {"echo","Echo arguments on stdout","echo arg1 arg2 ...","echo Hello World!",echo},
123 {"env","Print all environment variables","env","env",env},
124 {".","Source-in a file with commands",". filename",". /etc/rc",sourcer},
125 {"?","Print this help :-)","? [command]","? set",help},
126 {NULL,NULL,NULL,NULL,NULL}
130 * Built-in 'cd <path>' handler
135 if(chdir(path)) return(-1);
141 * Built-in 'pwd' handler
147 if(getcwd(cwd,BUFSIZE)==NULL) return(-1);
153 * Built-in 'exit' handler
162 * Built-in 'echo' handler
170 int s_quote=0,d_quote=0;
179 if(args[0]=='-' && args[1]=='n') {
182 while(i<len && (args[i]==' ' || args[i]=='\t')) i++;
190 if(s_quote||d_quote) {
221 for(j=i+1;j<len;j++) {
232 printf("\necho(): unmatched \"\n");
240 for(j=i+1;j<len;j++) {
251 printf("\necho(): unmatched '\n");
257 printf("echo(): backquote not implemented yet!\n");
266 if(!no_lf) putchar('\n');
271 * Built-in 'set VAR=value' handler
278 if(var==NULL) return(env(NULL));
280 if(res) printf("set: %s\n",strerror(errno));
285 * Built-in 'env' handler
300 * Built-in 'unset VAR' handler
306 printf("%s: parameter required.\n",progname);
309 return(unsetenv(var));
313 * Built-in '?' handler
322 printf("\nBuilt-in commands:\n");
323 printf("-------------------\n");
325 while(x->cmd!=NULL) {
326 printf("%s\t%s\n",x->cmd,x->descr);
329 printf("\nEnter '? <cmd>' for details.\n\n");
333 while(x->cmd!=NULL) {
334 if(strcmp(x->cmd,cmd)==0) {
341 printf("\n%s\t%s:\n",x->cmd,x->descr);
342 printf("\tUsage:\n\t\t%s\n",x->usage);
343 printf("\te.g:\n\t\t%s\n\n",x->example);
346 printf("\n%s: no such command.\n\n",cmd);
353 * Signal handler for shell()
370 * Built-in '.' handler (read-in and execute commands from file)
376 char buf[512],*tok,*arg,**av;
383 printf("Couldn't open file '%s'\n",fname);
388 if(fgets(buf,512,fd)==NULL) continue;
389 if((*buf=='#') || (*buf=='\n')) continue;
392 if(strncmp(buf,"ncons",5)==0) {
394 tok=strtok(NULL,sep);
396 if((ncons<1)||(ncons>MAX_CONS)) {
397 syslog(LOG_EMERG,"%s: bad ncons value; defaulting to %d\n",fname,MAX_CONS);
401 } else if(strncmp(buf,"motd",4)==0) {
403 motd=strdup(strtok(NULL,sep));
408 /* Next command, please. */
411 syslog(LOG_EMERG,"Done with %s",fname);
415 do_command(int shell, char *cmdline)
417 char *tok,*c,*sep=" \t";
426 if(cmdline[len-1]=='&') {
431 tok=strtok(cmdline,sep);
434 while(x->cmd!=NULL) {
435 if(strcmp(x->cmd,tok)==0) {
442 tok=cmdline+strlen(x->cmd)+1;
443 while(*tok && isblank(*tok) && (tok<(cmdline+len))) tok++;
444 if(*tok==NULL) tok=NULL;
449 av=(char **)calloc(((len+1)/2+1),sizeof(char *));
451 while((av[ac++]=strtok(NULL,sep))!=NULL)
453 switch((pid=fork())) {
456 signal(SIGINT,SIG_DFL);
457 signal(SIGQUIT,SIG_DFL);
458 signal(SIGTERM,SIG_DFL);
459 signal(SIGHUP,SIG_DFL);
464 f=open(_PATH_CONSOLE,O_RDWR);
472 printf("do_command(%s): failed to run bg: %s\n",
473 av[0],strerror(errno));
478 /* Something went wrong... */
479 printf("do_command(%s): %s\n",av[0],strerror(errno));
483 printf("do_command(): %s\n",strerror(errno));
486 while(waitpid(pid,&res,0)!=pid) continue;
487 if(WEXITSTATUS(res)) {
488 printf("do_command(%s): exit code=%d\n",
489 av[0],WEXITSTATUS(res));
498 * This is the user interface. This routine gets executed on each
499 * virtual console serviced by init.
501 * It works as normal shell does - for each external command it forks
502 * and execs, for each internal command just executes a function.
506 shell(int argc, char **argv)
515 if((fd=open(motd,O_RDONLY))!=-1) {
517 res=read(fd,buf,BUFSIZE);
518 res=write(1,buf,res);
524 printf("\n\n+=========================================================+\n");
525 printf("| Built-in shell() (enter '?' for short help on commands) |\n");
526 printf("+=========================================================+\n\n");
529 signal(SIGINT,shell_sig);
530 signal(SIGQUIT,shell_sig);
531 signal(SIGTERM,shell_sig);
532 while(!feof(stdin)) {
533 memset(buf,0,BUFSIZE);
534 printf("(%d)%s%s",mypid,cwd,prompt);
536 if(fgets(buf,BUFSIZE-1,stdin)==NULL) continue;
537 buf[strlen(buf)-1]='\0';
538 if(strlen(buf)==0) continue;
545 * Stub for executing some external program on a console. This is called
546 * from previously forked copy of our process, so that exec is ok.
549 external_cmd(int argc, char **argv)
551 execvp(argv[0],argv);
555 * Acquire vty and properly attach ourselves to it.
556 * Also, build basic environment for running user interface.
560 start_session(int vty, int argc, char **argv)
568 revoke(ttys[vty].tty);
569 fd=open(ttys[vty].tty,O_RDWR);
576 putenv("TERM=cons25");
578 putenv("PATH=/stand:/bin:/usr/bin:/sbin:.");
579 signal(SIGHUP,SIG_DFL);
580 signal(SIGINT,SIG_DFL);
581 signal(SIGQUIT,SIG_DFL);
582 signal(SIGTERM,SIG_DFL);
584 t=(char *)(rindex(ttys[vty].tty,'/')+1);
585 printf("\n\n\nStarting session on %s.\n",t);
586 ttys[vty].func(argc,argv);
591 * Execute system startup script /etc/rc
593 * (Of course if you don't like it - I don't - you can run anything you
594 * want here. Perhaps it would be useful just to read some config DB and
595 * do these things ourselves, avoiding forking lots of shells and scripts.)
598 /* If OINIT_RC is defined, oinit will use it's own configuration file,
599 * /etc/oinit.rc. It's format is described below. Otherwise, it will use
600 * normal /etc/rc interpreted by Bourne shell.
611 if((pid=fork())==0) {
616 fd=open(_PATH_CONSOLE,O_RDWR);
624 execvp("/bin/sh",argv);
625 printf("runcom(): %s\n",strerror(errno));
628 /* Wait for child to exit */
629 while(pid!=waitpid(pid,(int *)0,0)) continue;
633 /* Alternative /etc/rc - default is /etc/oinit.rc. Its format is as follows:
634 * - each empty line or line beginning with a '#' is discarded
635 * - any other line must contain a keyword, or a (nonblocking) command to run.
637 * Thus far, the following keywords are defined:
638 * ncons <number> number of virtual consoles to open
639 * motd <pathname> full path to motd file
641 * Examples of commands to run:
643 * ifconfig lo0 inet 127.0.0.1 netmask 255.0.0.0
644 * ifconfig ed0 inet 148.81.168.10 netmask 255.255.255.0
645 * kbdcontrol -l /usr/share/syscons/my_map.kbd
655 fd=open(_PATH_CONSOLE,O_RDWR);
671 /* Run /etc/rc if not in single user */
673 if(prevtrans==SINGLE) runcom();
675 if(prevtrans==SINGLE) runcom(OINIT_RC);
677 if(transition!=MULTI) return(-1);
679 syslog(LOG_EMERG,"*** Starting multi-user mode ***");
681 /* Fork shell interface for each console */
682 for(i=0;i<ncons;i++) {
686 start_session(i,0,NULL);
689 printf("%s: %s\n",progname,strerror(errno));
697 /* Initialize any other services we'll use - most probably this will
698 * be a 'telnet' service (some day...).
702 /* Emulate multi-user */
703 while(transition==MULTI) {
704 /* XXX Modify this to allow for checking for the input on
705 * XXX listening sockets, and forking a 'telnet' service.
709 /* Wait for any process to exit */
710 pid=waitpid(-1,(int *)0,0);
711 if(pid==-1) continue;
714 /* search if it's one of our sessions */
715 for(i=0;i<ncons;i++) {
716 if(ttys[i].pid==pid) {
724 /* Just collect the process's status */
727 /* restart shell() on a console, if it died */
728 if(transition!=MULTI) return(0);
732 start_session(j,0,NULL);
735 printf("%s: %s\n",progname,strerror(errno));
758 * Start a shell on ttyv0 (i.e. the console).
766 static int sigs[2]={SIGTERM,SIGKILL};
768 syslog(LOG_EMERG,"*** Starting single-user mode ***");
769 /* Kill all existing sessions */
770 syslog(LOG_EMERG,"Killing all existing sessions...");
771 for(i=0;i<MAX_CONS;i++) {
772 kill(ttys[i].pid,SIGHUP);
776 if(kill(-1,sigs[i])==-1 && errno==ESRCH) break;
780 pid=waitpid(-1,(int *)0,WUNTRACED);
781 if(errno==EINTR) continue;
786 syslog(LOG_EMERG,"Some processes would not die; ps -axl advised");
791 start_session(0,0,NULL);
794 printf("%s: %s\n",progname,strerror(errno));
795 printf("The system is seriously hosed. I'm dying...\n");
801 wpid=waitpid(pid,(int *)0,WUNTRACED);
802 } while(wpid!=pid && transition==SINGLE);
803 if(transition!=DEATH) {
804 prevtrans=transition;
813 * Transition handler - installed as signal handler.
817 transition_handler(int sig)
823 prevtrans=transition;
825 syslog(LOG_EMERG,"*** Going from %s -> %s\n",trans[prevtrans],trans[transition]);
826 if(prevtrans!=transition) longjmp(machine,sig);
830 prevtrans=transition;
832 syslog(LOG_EMERG,"*** Going from %s -> %s\n",trans[prevtrans],trans[transition]);
833 if(prevtrans!=transition) longjmp(machine,sig);
836 syslog(LOG_EMERG,"pid=%d sig=%s (ignored)\n",getpid(),sys_siglist[sig]);
842 * Change system state appropriately to the signals
850 while(transition!=DEATH) {
860 syslog(LOG_EMERG,"Killing all existing sessions...");
861 /* Kill all sessions */
863 /* Be nice and wait for them */
864 while(waitpid(-1,(int *)0,WNOHANG|WUNTRACED)>0) continue;
871 main(int argc, char **argv)
875 /* These are copied from real init(8) */
877 errx(1,"%s",strerror(EPERM));
878 openlog("init",LOG_CONS|LOG_ODELAY,LOG_AUTH);
880 warn("initial setsid() failed");
881 if(setlogin("root")<0)
882 warn("setlogin() failed");
889 progname=rindex(argv[0],'/');
896 /* We must recognize the same options as real init does */
897 while((c=getopt(argc,argv,"dsf"))!=-1) {
907 printf("%s: unrecognized flag '-%c'\n",progname,c);
912 /* Fill in the sess structures. */
913 /* XXX Really, should be filled based upon config file. */
914 for(i=0;i<MAX_CONS;i++) {
916 sprintf(ttys[i].tty,_PATH_CONSOLE);
918 sprintf(ttys[i].tty,"%sv%c",_PATH_TTY,vty[i]);
926 signal(SIGINT,transition_handler);
927 signal(SIGQUIT,transition_handler);
928 signal(SIGTERM,transition_handler);
929 signal(SIGHUP,transition_handler);
930 signal(SIGALRM,kill_timer);
933 transition_machine(transition);