1 /* simpleinit.c - poe@daimi.aau.dk */
4 /* gerg@snapgear.com -- modified for direct console support DEC/1999 */
5 /* davidm@snapgear.com -- modified for init.conf SEP/2004 */
6 /* toby@snapgear.com -- allow the array of commands to grow as needed OCT/2004 */
7 /* davidm@snapgear.com -- use dynamically allocated tables APR/2005 */
9 #define _GNU_SOURCE /* For crypt() and termios defines */
11 #include <sys/types.h>
23 #include <sys/termios.h>
24 #include <sys/ioctl.h>
26 #include <linux/version.h>
35 #if __GNU_LIBRARY__ > 5
36 #include <sys/reboot.h>
39 #include "pathnames.h"
41 #define BUF_SIZ 100 /* max size of a line in inittab */
42 #define NUMCMD 30 /* step size when realloc more space for the commands */
43 #define NUMTOK 20 /* max number of tokens in inittab command */
45 /* Threshold time for detecting "fast" spawning processes */
46 static int testtime
= 90;
47 /* Number of rapid respawns that counts as too fast */
48 static int maxspawn
= 5;
49 /* Maximum delay between runs */
50 static int maxdelay
= 595;
51 /* Time between successive runs of a process */
52 static int delaytime
= 5;
54 #define MAXTRIES 3 /* number of tries allowed when giving the password */
56 #define RUN_RC /* Use a console if possible */
59 #define CTRL(X) ((X)&037)
62 #ifdef INCLUDE_TIMEZONE
65 /* #define DEBUGGING */
67 /* Define this if you want init to ignore the termcap field in inittab for
69 /* #define SPECIAL_CONSOLE_TERM */
84 struct initline
*inittab
;
85 /* How many struct initline's will fit in the memory pointed to by inittab */
88 int stopped
= 0; /* are we stopped */
89 int reload
= 0; /* are we stopped */
90 int run_sigint_processing
= 0;
92 extern void spawn(int);
93 extern void hup_handler();
94 extern void reload_inittab();
95 extern void read_inittab(void);
96 static int read_initfile(const char *);
97 extern void tstp_handler();
98 extern void int_handler();
99 extern void sigint_processing();
100 extern void cont_handler();
101 extern void set_tz(void);
102 extern void write_wtmp(void);
103 extern void make_ascii_tty(void);
104 extern void make_console(const char *);
105 extern int boot_single(int singlearg
, int argc
, char *argv
[]);
106 #ifdef CONFIG_USER_INIT_CONF
107 extern void load_init_conf(void);
110 /* Keep track of console device, if any... */
111 #if LINUX_VERSION_CODE < 0x020100
112 char *console_device
= NULL
;
113 int console_baud
= -1;
115 int have_console
= 0;
119 static void err(const char *s
)
121 struct iovec output
[2];
122 #if LINUX_VERSION_CODE < 0x020100
125 output
[0].iov_base
= "init: ";
126 output
[0].iov_len
= 6;
127 output
[1].iov_base
= (void *)s
;
128 output
[1].iov_len
= strlen(s
);
129 #if LINUX_VERSION_CODE < 0x020100
130 if (console_device
== NULL
) return;
131 if((fd
= open(console_device
, O_WRONLY
)) < 0) return;
132 writev(fd
, output
, 2);
136 writev(1, output
, 2);
141 add_tok(struct initline
*p
, char *tok
)
144 for (i
= 0; p
->toks
&& p
->toks
[i
]; i
++)
147 /* allocate space for new entry and terminating NULL */
148 p
->toks
= (char **) realloc(p
->toks
, (i
+ 2) * sizeof(char *));
150 err("malloc failed\n");
157 static void enter_single(void)
162 err("Booting to single user mode\n");
163 av
[0] = _PATH_BSHELL
;
165 if((pid
= vfork()) == 0) {
166 extern char **environ
;
168 execve(_PATH_BSHELL
, av
, environ
);
169 err("exec of single user shell failed\n");
173 while(wait(&i
) != pid
) /* nothing */;
175 err("fork of single user shell failed\n");
177 unlink(_PATH_SINGLE
);
181 #if LINUX_VERSION_CODE < 0x020100
183 set_console_baud(int baud
)
186 case 50: console_baud
= B50
; break;
187 case 75: console_baud
= B75
; break;
188 case 110: console_baud
= B110
; break;
189 case 134: console_baud
= B134
; break;
190 case 150: console_baud
= B150
; break;
191 case 200: console_baud
= B200
; break;
192 case 300: console_baud
= B300
; break;
193 case 600: console_baud
= B600
; break;
194 case 1200: console_baud
= B1200
; break;
195 case 1800: console_baud
= B1800
; break;
196 case 2400: console_baud
= B2400
; break;
197 case 4800: console_baud
= B4800
; break;
199 case 9600: console_baud
= B9600
; break;
200 case 19200: console_baud
= B19200
; break;
201 case 38400: console_baud
= B38400
; break;
202 case 57600: console_baud
= B57600
; break;
203 case 115200: console_baud
= B115200
; break;
204 case 230400: console_baud
= B230400
; break;
205 case 460800: console_baud
= B460800
; break;
210 static int do_command(const char *path
, const char *filename
, int dowait
)
215 if((pid
= vfork()) == 0) {
218 #ifdef INCLUDE_TIMEZONE
223 /* Use /dev/null for stdin */
225 open("/dev/null", O_RDONLY
);
227 argv
[0] = (char *)path
;
228 argv
[1] = (char *)filename
;
231 env
[0] = "PATH=/bin:/usr/bin:/etc:/sbin:/usr/sbin";
232 #ifdef INCLUDE_TIMEZONE
241 execve(path
, argv
, env
);
243 err("exec rc failed\n");
249 /* parent, wait till rc process dies before spawning */
250 while ((wpid
= wait(&stat
)) != pid
)
251 if (wpid
== -1 && errno
== ECHILD
) { /* see wait(2) manpage */
257 err("fork of rc shell failed\n");
260 st
= WEXITSTATUS(stat
);
265 * run /etc/rc. The environment is passed to the script, so the RC environment
266 * variable can be used to decide what to do. RC may be set from LILO.
268 static int do_rc(void)
272 rc
= do_command(_PATH_BSHELL
, _PATH_RC
, 1);
275 #ifdef CONFIG_USER_INIT_RUN_FIREWALL
276 rc
= do_command(_PATH_FIREWALL
, "-i", 1);
278 err(_PATH_FIREWALL
" failed!");
280 #ifdef CONFIG_USER_FLATFSD_FLATFSD
281 rc
= do_command(_PATH_BSHELL
, _PATH_CONFIGRC
, 1);
283 err(_PATH_CONFIGRC
" failed!");
285 #ifdef CONFIG_USER_INIT_RUN_FIREWALL
286 rc
= do_command(_PATH_FIREWALL
, NULL
, 0);
288 err(_PATH_FIREWALL
" failed!");
290 #ifdef INCLUDE_TIMEZONE
291 /* We read the timezone file here, because the flat file system
292 * has probably been created by now.
299 void respawn_children(int signo
) {
303 if ((now
= time(NULL
)) == 0) now
= 1;
304 for(i
= 0; i
< numcmd
; i
++) {
305 if(inittab
[i
].pid
< 0) { /* Start jobs */
309 ** Do not spawn child from signal handler !
310 ** SIGALRM would be blocked for the child
315 /* Check for naughty jobs */
316 if (inittab
[i
].nextrun
> now
) {
318 d
= inittab
[i
].nextrun
- now
;
319 if (delta
< 0 || d
< delta
)
328 int main(int argc
, char *argv
[])
334 * setup all the signal handlers here
337 memset(&sa
, 0, sizeof(sa
));
338 /* sa.sa_flags = SA_RESETHAND we want to keep the handlers armed */
340 sa
.sa_handler
= tstp_handler
;
341 sigaction(SIGTSTP
, &sa
, NULL
);
343 sa
.sa_handler
= cont_handler
;
344 sigaction(SIGCONT
, &sa
, NULL
);
346 sa
.sa_handler
= int_handler
;
347 sigaction(SIGINT
, &sa
, NULL
);
349 sa
.sa_handler
= respawn_children
;
350 sigaction(SIGALRM
, &sa
, NULL
);
352 sa
.sa_handler
= hup_handler
;
353 sigaction(SIGHUP
, &sa
, NULL
);
355 #if defined(CONSOLE_BAUD_RATE) && LINUX_VERSION_CODE < 0x020100
356 set_console_baud(CONSOLE_BAUD_RATE
);
360 * start up in single user mode if /etc/singleboot exists or if
361 * argv[1] is "single".
363 if(boot_single(0, argc
, argv
)) enter_single();
366 /* Register console if defined by boot */
367 #if LINUX_VERSION_CODE < 0x020100
368 if ((console_device
= getenv("CONSOLE"))) {
371 if ((sp
= strchr(console_device
, ','))) {
373 set_console_baud(atoi(sp
));
385 } else if (fstat(1, &st
) == -1 && errno
== EBADF
) {
386 /* No stdout, so send everything to /dev/null */
387 close(0); close(1); close(2);
388 open("/dev/null", O_RDWR
);
395 /*If we get a SIGTSTP before multi-user mode, do nothing*/
398 if(do_rc() != 0 && boot_single(1, argc
, argv
) && !stopped
)
400 while(stopped
) /*Also if /etc/rc fails & we get SIGTSTP*/
404 /* initialize the array of commands */
405 inittab
= (struct initline
*)malloc(NUMCMD
* sizeof(struct initline
));
406 inittab_size
= NUMCMD
;
409 /* failure case - what do you do if init fails? */
410 err("malloc failed");
414 write_wtmp(); /* write boottime record */
418 for(i
= 0; i
< numcmd
; i
++) {
419 char **p
= inittab
[i
].toks
;
420 printf("toks= %s %s %s %s\n",p
[0], p
[1], p
[2], p
[3]);
421 printf("tty= %s\n", inittab
[i
].tty
);
422 printf("termcap= %s\n", inittab
[i
].termcap
);
427 #if LINUX_VERSION_CODE < 0x020100
428 for(i
= 0; i
< getdtablesize(); i
++) close(i
);
430 /* Always leave 0, 1, and 2 connected (to /dev/null) for the child process */
431 for(i
= 3; i
< getdtablesize(); i
++) close(i
);
438 if (run_sigint_processing
) {
439 run_sigint_processing
= 0;
448 continue; /* process all reloads before waiting */
454 /* clear utmp entry, and append to wtmp if possible */
460 utmpname(_PATH_UTMP
);
462 while((ut
= getutent())) {
463 if(ut
->ut_pid
== pid
) {
465 bzero(&ut
->ut_user
, UT_NAMESIZE
);
466 bzero(&ut
->ut_host
, sizeof(ut
->ut_host
));
467 ut
->ut_type
= DEAD_PROCESS
;
472 if((ut_fd
= open(_PATH_WTMP
, O_APPEND
|O_WRONLY
)) >= 0) {
473 flock(ut_fd
, LOCK_EX
|LOCK_NB
);
474 write(ut_fd
, (const void *)ut
, sizeof(struct utmp
));
475 flock(ut_fd
, LOCK_UN
|LOCK_NB
);
485 for(i
= 0; i
< numcmd
; i
++) {
486 if(pid
== inittab
[i
].pid
) {
495 * return true if we should boot up in singleuser mode. If argv[i] is
496 * "single" or the file /etc/singleboot exists, then singleuser mode should
497 * be entered. If /etc/securesingle exists ask for root password first.
499 int boot_single(int singlearg
, int argc
, char *argv
[])
501 char *pass
, *rootpass
= NULL
;
505 for(i
= 1; i
< argc
; i
++) {
506 if(argv
[i
] && !strcmp(argv
[i
], "single")) singlearg
= 1;
522 /* Check for held process */
523 if ((unsigned long)(it
->nextrun
- t
- 1) < maxdelay
)
525 if (it
->lastrun
+ testtime
> t
) { /* Respawning quickly */
528 } else { /* Normal respawning */
531 it
->delay
= delaytime
;
533 if (it
->xcnt
>= maxspawn
) { /* Too many too quickly */
534 strcpy(buf
, it
->toks
[0]);
535 strcat(buf
, " respawning too fast\n");
538 if (it
->delay
>= maxdelay
)
539 it
->delay
= maxdelay
;
540 else if (it
->delay
< delaytime
)
541 it
->delay
= delaytime
;
542 else if((it
->delay
*= 2) > maxdelay
)
543 it
->delay
= maxdelay
;
544 it
->nextrun
= t
+ it
->delay
;
545 /* Fiddle with the tracking vars to ensure that only
546 * one attempt is made to run this next time around.
548 it
->lastrun
= it
->nextrun
;
552 it
->nextrun
= t
+ delaytime
;
554 if((pid
= vfork()) < 0) {
556 err("fork failed\n");
560 /* this is the parent */
564 /* this is the child */
567 #ifdef INCLUDE_TIMEZONE
574 /* Close everything other than 0, 1 and 2 */
575 for(j
= 3; j
< getdtablesize(); j
++) {
578 /* Now set up 0, 1 and 2 */
579 make_console(it
->tty
);
581 strcpy(term
, "TERM=");
582 strcat(term
, it
->termcap
);
584 env
[1] = "PATH=/bin:/usr/bin:/etc:/sbin:/usr/sbin";
585 #ifdef INCLUDE_TIMEZONE
595 if (*prog
== '-' && *(prog
+1))
597 execve(prog
, it
->toks
, env
);
598 strcpy(buf
, it
->toks
[0]);
599 strcat(buf
, " exec failed\n");
605 static void init_itab(struct initline
*p
) {
606 bzero(p
, sizeof(struct initline
));
608 p
->nextrun
= time(NULL
);
611 static void clear_itab(struct initline
*p
) {
621 void read_inittab(void)
626 * free any old data and start again
628 for (i
= 0; i
< numcmd
; i
++)
629 clear_itab(&inittab
[i
]);
632 /* Fake an inittab entry if boot console defined */
633 #ifdef CONFIG_USER_INIT_CONSOLE_SH
634 #if LINUX_VERSION_CODE < 0x020100
635 if (console_device
&& strcmp(console_device
, "/dev/null"))
641 p
= inittab
+ numcmd
++;
643 p
->fullline
= strdup("console");
644 strcpy(p
->tty
, "console");
645 strcpy(p
->termcap
, "linux");
646 add_tok(p
, "-/bin/sh");
651 if (read_initfile(_PATH_INITTAB
) == 0)
654 #ifdef CONFIG_USER_FLATFSD_FLATFSD
655 if (read_initfile(_PATH_CONFIGTAB
) == 0)
660 err("Failed to open " _PATH_INITTAB
661 #ifdef CONFIG_USER_FLATFSD_FLATFSD
662 " or " _PATH_CONFIGTAB
668 #ifdef CONFIG_USER_INIT_CONF
672 /* if needed, shrink the array using realloc -
673 * must be done here so that we include the results of all init files
674 * when calculating number of commands */
675 if ((numcmd
+ 2) < (inittab_size
- NUMCMD
)) {
676 /* round up from numcmd to the nearest multiple of NUMCMD */
677 inittab_size
= ((numcmd
+ 2) / NUMCMD
+ 1) * NUMCMD
;
678 inittab
= realloc(inittab
, inittab_size
* sizeof(struct initline
));
680 /* failure case - what do you do if init fails? */
681 err("malloc failed");
691 read_initfile(const char *initfile
)
699 #ifdef SPECIAL_CONSOLE_TERM
704 termenv
= getenv("TERM"); /* set by kernel */
709 if (!(f
= fopen(initfile
, "r")))
713 if (i
+2 == inittab_size
) {
714 /* need to realloc inittab */
715 inittab_size
+= NUMCMD
;
716 inittab
= realloc(inittab
, inittab_size
* sizeof(struct initline
));
718 /* failure case - what do you do if init fails? */
719 err("malloc failed");
723 if (getline(&buf
, &buf_len
, f
) == -1) break;
725 for(k
= 0; k
< buf_len
&& buf
[k
]; k
++) {
727 buf
[k
] = '\0'; break;
731 if(buf
[0] == '\0' || buf
[0] == '\n') continue;
735 p
->line
= strdup(buf
);
736 p
->fullline
= strdup(buf
);
737 if (!p
->line
|| !p
->fullline
) {
738 err("Not memory to allocate inittab entry");
742 ptr
= strtok(p
->line
, ":");
744 err("Missing TTY/ID field in inittab");
748 strncpy(p
->tty
, ptr
, 9);
750 ptr
= strtok(NULL
, ":");
752 err("Missing TERMTYPE field in inittab");
756 strncpy(p
->termcap
, ptr
, 29);
757 //p->termcap[29] = '\0';
759 getty
= strtok(NULL
, " \t\n");
761 err("Missing PROCESS field in inittab");
767 while((ptr
= strtok(NULL
, " \t\n")))
770 #ifdef SPECIAL_CONSOLE_TERM
771 /* special-case termcap for the console ttys */
772 strcpy(tty
, "/dev/");
774 if(!termenv
|| stat(tty
, &stb
) < 0) {
775 err("no TERM or cannot stat tty\n");
777 /* is it a console tty? */
778 if(major(stb
.st_rdev
) == 4 && minor(stb
.st_rdev
) < 64) {
779 strncpy(p
->termcap
, termenv
, 30);
802 void reload_inittab()
806 char ** saveline
= (char **) malloc(inittab_size
* sizeof(char *));
807 pid_t
* savepid
= (pid_t
*) malloc(inittab_size
* sizeof(pid_t
));
809 if (!saveline
|| !savepid
) {
810 /* another failure case - what DO you do if init fails */
811 err("malloc failed");
815 for (i
=0; i
<numcmd
; i
++) {
816 savepid
[i
] = inittab
[i
].pid
;
817 saveline
[i
] = strdup(inittab
[i
].fullline
);
819 err("malloc failed");
827 /* See which ones still exist */
828 for(i
= 0; i
< numcmd
; i
++) {
830 for(j
= 0; j
< oldnum
; j
++) {
831 if(strcmp(saveline
[j
], inittab
[i
].fullline
) == 0) {
832 inittab
[i
].pid
= savepid
[j
];
839 /* Kill off processes no longer needed and free memory */
840 for(i
= 0; i
< oldnum
; i
++) {
842 kill(savepid
[i
], SIGTERM
);
862 run_sigint_processing
= 1;
865 void sigint_processing()
868 * After Linux 0.96b PL1, we get a SIGINT when
869 * the user presses Ctrl-Alt-Del...
876 if((pid
= vfork()) == 0) {
878 extern char **environ
;
879 /* reboot properly... */
880 av
[0] = _PATH_REBOOT
;
883 execve(_PATH_REBOOT
, av
, environ
);
884 #if __GNU_LIBRARY__ > 5
887 reboot(0xfee1dead, 672274793, 0x1234567);
891 /* fork failed, try the hard way... */
892 #if __GNU_LIBRARY__ > 5
895 reboot(0xfee1dead, 672274793, 0x1234567);
900 #ifdef INCLUDE_TIMEZONE
906 if((f
= fopen("/etc/config/TZ", "r")) == NULL
&&
907 (f
= fopen("/etc/TZ", "r")) == NULL
)
909 fgets(tzone
, BUF_SIZ
-2, f
);
911 if((len
=strlen(tzone
)) < 2)
913 tzone
[len
-1] = 0; /* get rid of the '\n' */
914 setenv("TZ", tzone
, 0);
918 #ifdef CONFIG_USER_INIT_CONF
919 void load_init_conf(void)
924 if ((f
= fopen("/etc/config/init.conf", "r")) == NULL
&&
925 (f
= fopen("/etc/init.conf", "r")) == NULL
)
927 while (fgets(line
, sizeof(line
) - 2, f
)) {
928 if (strncasecmp(line
, "delaytime=", 10) == 0)
929 delaytime
= atoi(line
+ 10);
930 if (strncasecmp(line
, "maxdelay=", 9) == 0)
931 maxdelay
= atoi(line
+ 9);
932 if (strncasecmp(line
, "maxspawn=", 9) == 0)
933 maxspawn
= atoi(line
+ 9);
934 if (strncasecmp(line
, "testtime=", 9) == 0)
935 testtime
= atoi(line
+ 9);
941 void write_wtmp(void)
947 bzero((char *)&ut
, sizeof(ut
));
948 strcpy(ut
.ut_line
, "~");
949 bzero(ut
.ut_name
, sizeof(ut
.ut_name
));
951 ut
.ut_type
= BOOT_TIME
;
953 if((fd
= open(_PATH_WTMP
, O_WRONLY
|O_APPEND
)) >= 0) {
954 flock(fd
, LOCK_EX
|LOCK_NB
); /* make sure init won't hang */
955 write(fd
, (char *)&ut
, sizeof(ut
));
956 flock(fd
, LOCK_UN
|LOCK_NB
);
962 void make_ascii_tty(void)
967 if (tcgetattr(0, &tty
) < 0)
970 tty
.c_iflag
&= ~(INLCR
|IGNCR
|IUCLC
);
971 tty
.c_iflag
|= ICRNL
;
972 tty
.c_oflag
&= ~(OCRNL
|OLCUC
|ONOCR
|ONLRET
|OFILL
);
973 tty
.c_oflag
|= OPOST
|ONLCR
;
974 tty
.c_cflag
|= CLOCAL
;
975 tty
.c_lflag
= ISIG
|ICANON
|ECHO
|ECHOE
|ECHOK
|ECHOCTL
|ECHOKE
;
977 tty
.c_lflag
|= IEXTEN
;
980 #if LINUX_VERSION_CODE < 0x020100
981 if (console_baud
!= -1)
982 cfsetospeed(&tty
, console_baud
);
985 tty
.c_cc
[VINTR
] = CTRL('C');
986 tty
.c_cc
[VQUIT
] = CTRL('\\');
987 tty
.c_cc
[VERASE
] = CTRL('H'); /*127*/
988 tty
.c_cc
[VKILL
] = CTRL('U'); /*Changed from non-standard ^X*/
989 tty
.c_cc
[VEOF
] = CTRL('D');
992 tty
.c_cc
[VSTART
] = CTRL('Q');
993 tty
.c_cc
[VSTOP
] = CTRL('S');
994 tty
.c_cc
[VSUSP
] = CTRL('Z');
996 tty
.c_cc
[VWERASE
] = CTRL('W');
998 /* Pick up simple environment setting of VERASE.
999 * Useful for setting on kernel command line.
1002 pt
= getenv("TTYERASE");
1003 if (pt
&& pt
[0] == '^' && pt
[1]) {
1004 tty
.c_cc
[VERASE
] = (pt
[1] == '?') ? 127 : CTRL(pt
[1]);
1007 tcsetattr(0, TCSANOW
, &tty
);
1010 void make_console(const char *tty
)
1014 close(0); close(1); close(2);
1017 #if LINUX_VERSION_CODE < 0x020100
1019 * until we get proper console support under 2.0
1021 if (strcmp(tty
, "console") == 0) {
1022 strcpy(devname
, console_device
);
1027 strcpy(devname
, "/dev/");
1028 strcat(devname
, tty
);
1031 /* Try to open the specified console */
1032 if (open(devname
, O_RDWR
|O_NONBLOCK
) >= 0) {
1033 fcntl(0, F_SETFL
, 0);
1037 ioctl(0, TIOCSCTTY
, (char*)0);
1042 /* No go, so send to /dev/null */
1043 open("/dev/null", O_RDWR
|O_NONBLOCK
);