|
@@ -0,0 +1,1046 @@
|
|
|
+/* simpleinit.c - poe@daimi.aau.dk */
|
|
|
+/* Version 1.21 */
|
|
|
+
|
|
|
+/* gerg@snapgear.com -- modified for direct console support DEC/1999 */
|
|
|
+/* davidm@snapgear.com -- modified for init.conf SEP/2004 */
|
|
|
+/* toby@snapgear.com -- allow the array of commands to grow as needed OCT/2004 */
|
|
|
+/* davidm@snapgear.com -- use dynamically allocated tables APR/2005 */
|
|
|
+
|
|
|
+#define _GNU_SOURCE /* For crypt() and termios defines */
|
|
|
+
|
|
|
+#include <sys/types.h>
|
|
|
+#include <stdlib.h>
|
|
|
+#include <unistd.h>
|
|
|
+#include <limits.h>
|
|
|
+#include <stdio.h>
|
|
|
+#include <fcntl.h>
|
|
|
+#include <string.h>
|
|
|
+#include <signal.h>
|
|
|
+#include <pwd.h>
|
|
|
+#include <sys/file.h>
|
|
|
+#include <sys/wait.h>
|
|
|
+#include <sys/stat.h>
|
|
|
+#include <sys/termios.h>
|
|
|
+#include <sys/ioctl.h>
|
|
|
+#include <sys/uio.h>
|
|
|
+#include <linux/version.h>
|
|
|
+#include <utmp.h>
|
|
|
+#include <errno.h>
|
|
|
+#include <time.h>
|
|
|
+#include <termios.h>
|
|
|
+#ifdef SHADOW_PWD
|
|
|
+#include <shadow.h>
|
|
|
+#endif
|
|
|
+
|
|
|
+#if __GNU_LIBRARY__ > 5
|
|
|
+#include <sys/reboot.h>
|
|
|
+#endif
|
|
|
+
|
|
|
+#include "pathnames.h"
|
|
|
+
|
|
|
+#define BUF_SIZ 100 /* max size of a line in inittab */
|
|
|
+#define NUMCMD 30 /* step size when realloc more space for the commands */
|
|
|
+#define NUMTOK 20 /* max number of tokens in inittab command */
|
|
|
+
|
|
|
+/* Threshold time for detecting "fast" spawning processes */
|
|
|
+static int testtime = 90;
|
|
|
+/* Number of rapid respawns that counts as too fast */
|
|
|
+static int maxspawn = 5;
|
|
|
+/* Maximum delay between runs */
|
|
|
+static int maxdelay = 595;
|
|
|
+/* Time between successive runs of a process */
|
|
|
+static int delaytime = 5;
|
|
|
+
|
|
|
+#define MAXTRIES 3 /* number of tries allowed when giving the password */
|
|
|
+
|
|
|
+#define RUN_RC /* Use a console if possible */
|
|
|
+
|
|
|
+#ifndef CTRL
|
|
|
+#define CTRL(X) ((X)&037)
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef INCLUDE_TIMEZONE
|
|
|
+char tzone[BUF_SIZ];
|
|
|
+#endif
|
|
|
+/* #define DEBUGGING */
|
|
|
+
|
|
|
+/* Define this if you want init to ignore the termcap field in inittab for
|
|
|
+ console ttys. */
|
|
|
+/* #define SPECIAL_CONSOLE_TERM */
|
|
|
+
|
|
|
+struct initline {
|
|
|
+ pid_t pid;
|
|
|
+ time_t lastrun;
|
|
|
+ time_t nextrun;
|
|
|
+ char **toks;
|
|
|
+ short delay;
|
|
|
+ char tty[10];
|
|
|
+ char termcap[30];
|
|
|
+ char *line;
|
|
|
+ char *fullline;
|
|
|
+ unsigned char xcnt;
|
|
|
+};
|
|
|
+
|
|
|
+struct initline *inittab;
|
|
|
+/* How many struct initline's will fit in the memory pointed to by inittab */
|
|
|
+int inittab_size = 0;
|
|
|
+int numcmd;
|
|
|
+int stopped = 0; /* are we stopped */
|
|
|
+int reload = 0; /* are we stopped */
|
|
|
+int run_sigint_processing = 0;
|
|
|
+
|
|
|
+extern void spawn(int);
|
|
|
+extern void hup_handler();
|
|
|
+extern void reload_inittab();
|
|
|
+extern void read_inittab(void);
|
|
|
+static int read_initfile(const char *);
|
|
|
+extern void tstp_handler();
|
|
|
+extern void int_handler();
|
|
|
+extern void sigint_processing();
|
|
|
+extern void cont_handler();
|
|
|
+extern void set_tz(void);
|
|
|
+extern void write_wtmp(void);
|
|
|
+extern void make_ascii_tty(void);
|
|
|
+extern void make_console(const char *);
|
|
|
+extern int boot_single(int singlearg, int argc, char *argv[]);
|
|
|
+#ifdef CONFIG_USER_INIT_CONF
|
|
|
+extern void load_init_conf(void);
|
|
|
+#endif
|
|
|
+
|
|
|
+/* Keep track of console device, if any... */
|
|
|
+#if LINUX_VERSION_CODE < 0x020100
|
|
|
+char *console_device = NULL;
|
|
|
+int console_baud = -1;
|
|
|
+#else
|
|
|
+int have_console = 0;
|
|
|
+#endif
|
|
|
+
|
|
|
+
|
|
|
+static void err(const char *s)
|
|
|
+{
|
|
|
+ struct iovec output[2];
|
|
|
+#if LINUX_VERSION_CODE < 0x020100
|
|
|
+ int fd;
|
|
|
+#endif
|
|
|
+ output[0].iov_base = "init: ";
|
|
|
+ output[0].iov_len = 6;
|
|
|
+ output[1].iov_base = (void *)s;
|
|
|
+ output[1].iov_len = strlen(s);
|
|
|
+#if LINUX_VERSION_CODE < 0x020100
|
|
|
+ if (console_device == NULL) return;
|
|
|
+ if((fd = open(console_device, O_WRONLY)) < 0) return;
|
|
|
+ writev(fd, output, 2);
|
|
|
+ close(fd);
|
|
|
+#else
|
|
|
+ if (have_console)
|
|
|
+ writev(1, output, 2);
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+add_tok(struct initline *p, char *tok)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ for (i = 0; p->toks && p->toks[i]; i++)
|
|
|
+ ;
|
|
|
+
|
|
|
+ /* allocate space for new entry and terminating NULL */
|
|
|
+ p->toks = (char **) realloc(p->toks, (i + 2) * sizeof(char *));
|
|
|
+ if (!p->toks) {
|
|
|
+ err("malloc failed\n");
|
|
|
+ _exit(1);
|
|
|
+ }
|
|
|
+ p->toks[i++] = tok;
|
|
|
+ p->toks[i] = NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static void enter_single(void)
|
|
|
+{
|
|
|
+ pid_t pid;
|
|
|
+ char *av[2];
|
|
|
+
|
|
|
+ err("Booting to single user mode\n");
|
|
|
+ av[0] = _PATH_BSHELL;
|
|
|
+ av[1] = NULL;
|
|
|
+ if((pid = vfork()) == 0) {
|
|
|
+ extern char **environ;
|
|
|
+ /* the child */
|
|
|
+ execve(_PATH_BSHELL, av, environ);
|
|
|
+ err("exec of single user shell failed\n");
|
|
|
+ _exit(0);
|
|
|
+ } else if(pid > 0) {
|
|
|
+ int i;
|
|
|
+ while(wait(&i) != pid) /* nothing */;
|
|
|
+ } else if(pid < 0) {
|
|
|
+ err("fork of single user shell failed\n");
|
|
|
+ }
|
|
|
+ unlink(_PATH_SINGLE);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#if LINUX_VERSION_CODE < 0x020100
|
|
|
+static void
|
|
|
+set_console_baud(int baud)
|
|
|
+{
|
|
|
+ switch (baud) {
|
|
|
+ case 50: console_baud = B50; break;
|
|
|
+ case 75: console_baud = B75; break;
|
|
|
+ case 110: console_baud = B110; break;
|
|
|
+ case 134: console_baud = B134; break;
|
|
|
+ case 150: console_baud = B150; break;
|
|
|
+ case 200: console_baud = B200; break;
|
|
|
+ case 300: console_baud = B300; break;
|
|
|
+ case 600: console_baud = B600; break;
|
|
|
+ case 1200: console_baud = B1200; break;
|
|
|
+ case 1800: console_baud = B1800; break;
|
|
|
+ case 2400: console_baud = B2400; break;
|
|
|
+ case 4800: console_baud = B4800; break;
|
|
|
+ default:
|
|
|
+ case 9600: console_baud = B9600; break;
|
|
|
+ case 19200: console_baud = B19200; break;
|
|
|
+ case 38400: console_baud = B38400; break;
|
|
|
+ case 57600: console_baud = B57600; break;
|
|
|
+ case 115200: console_baud = B115200; break;
|
|
|
+ case 230400: console_baud = B230400; break;
|
|
|
+ case 460800: console_baud = B460800; break;
|
|
|
+ }
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+static int do_command(const char *path, const char *filename, int dowait)
|
|
|
+{
|
|
|
+ pid_t pid, wpid;
|
|
|
+ int stat, st;
|
|
|
+
|
|
|
+ if((pid = vfork()) == 0) {
|
|
|
+ /* the child */
|
|
|
+ char *argv[3];
|
|
|
+#ifdef INCLUDE_TIMEZONE
|
|
|
+ char tz[BUF_SIZ];
|
|
|
+#endif
|
|
|
+ char *env[3];
|
|
|
+
|
|
|
+ /* Use /dev/null for stdin */
|
|
|
+ close(0);
|
|
|
+ open("/dev/null", O_RDONLY);
|
|
|
+
|
|
|
+ argv[0] = (char *)path;
|
|
|
+ argv[1] = (char *)filename;
|
|
|
+ argv[2] = NULL;
|
|
|
+
|
|
|
+ env[0] = "PATH=/bin:/usr/bin:/etc:/sbin:/usr/sbin";
|
|
|
+#ifdef INCLUDE_TIMEZONE
|
|
|
+ strcpy(tz, "TZ=");
|
|
|
+ strcat(tz, tzone);
|
|
|
+ env[1] = tz;
|
|
|
+ env[2] = NULL;
|
|
|
+#else
|
|
|
+ env[1] = NULL;
|
|
|
+#endif
|
|
|
+
|
|
|
+ execve(path, argv, env);
|
|
|
+
|
|
|
+ err("exec rc failed\n");
|
|
|
+ _exit(2);
|
|
|
+ } else if(pid > 0) {
|
|
|
+ if (!dowait)
|
|
|
+ stat = 0;
|
|
|
+ else {
|
|
|
+ /* parent, wait till rc process dies before spawning */
|
|
|
+ while ((wpid = wait(&stat)) != pid)
|
|
|
+ if (wpid == -1 && errno == ECHILD) { /* see wait(2) manpage */
|
|
|
+ stat = 0;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if(pid < 0) {
|
|
|
+ err("fork of rc shell failed\n");
|
|
|
+ stat = -1;
|
|
|
+ }
|
|
|
+ st = WEXITSTATUS(stat);
|
|
|
+ return st;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * run /etc/rc. The environment is passed to the script, so the RC environment
|
|
|
+ * variable can be used to decide what to do. RC may be set from LILO.
|
|
|
+ */
|
|
|
+static int do_rc(void)
|
|
|
+{
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ rc = do_command(_PATH_BSHELL, _PATH_RC, 1);
|
|
|
+ if (rc)
|
|
|
+ return(rc);
|
|
|
+#ifdef CONFIG_USER_INIT_RUN_FIREWALL
|
|
|
+ rc = do_command(_PATH_FIREWALL, "-i", 1);
|
|
|
+ if (rc)
|
|
|
+ err(_PATH_FIREWALL " failed!");
|
|
|
+#endif
|
|
|
+#ifdef CONFIG_USER_FLATFSD_FLATFSD
|
|
|
+ rc = do_command(_PATH_BSHELL, _PATH_CONFIGRC, 1);
|
|
|
+ if (rc)
|
|
|
+ err(_PATH_CONFIGRC " failed!");
|
|
|
+#endif
|
|
|
+#ifdef CONFIG_USER_INIT_RUN_FIREWALL
|
|
|
+ rc = do_command(_PATH_FIREWALL, NULL, 0);
|
|
|
+ if (rc)
|
|
|
+ err(_PATH_FIREWALL " failed!");
|
|
|
+#endif
|
|
|
+#ifdef INCLUDE_TIMEZONE
|
|
|
+ /* We read the timezone file here, because the flat file system
|
|
|
+ * has probably been created by now.
|
|
|
+ */
|
|
|
+ set_tz();
|
|
|
+#endif
|
|
|
+ return(0);
|
|
|
+}
|
|
|
+
|
|
|
+void respawn_children(int signo) {
|
|
|
+ int i, delta = -1;
|
|
|
+ time_t now;
|
|
|
+ alarm(0);
|
|
|
+ if ((now = time(NULL)) == 0) now = 1;
|
|
|
+ for(i = 0; i < numcmd; i++) {
|
|
|
+ if(inittab[i].pid < 0) { /* Start jobs */
|
|
|
+ if(stopped)
|
|
|
+ inittab[i].pid = -1;
|
|
|
+ /*
|
|
|
+ ** Do not spawn child from signal handler !
|
|
|
+ ** SIGALRM would be blocked for the child
|
|
|
+ */
|
|
|
+ else if (signo == 0)
|
|
|
+ spawn(i);
|
|
|
+ }
|
|
|
+ /* Check for naughty jobs */
|
|
|
+ if (inittab[i].nextrun > now) {
|
|
|
+ int d;
|
|
|
+ d = inittab[i].nextrun - now;
|
|
|
+ if (delta < 0 || d < delta)
|
|
|
+ delta = d;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (delta > 0) {
|
|
|
+ alarm(delta);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+int main(int argc, char *argv[])
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ struct sigaction sa;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * setup all the signal handlers here
|
|
|
+ */
|
|
|
+
|
|
|
+ memset(&sa, 0, sizeof(sa));
|
|
|
+ /* sa.sa_flags = SA_RESETHAND we want to keep the handlers armed */
|
|
|
+
|
|
|
+ sa.sa_handler = tstp_handler;
|
|
|
+ sigaction(SIGTSTP, &sa, NULL);
|
|
|
+
|
|
|
+ sa.sa_handler = cont_handler;
|
|
|
+ sigaction(SIGCONT, &sa, NULL);
|
|
|
+
|
|
|
+ sa.sa_handler = int_handler;
|
|
|
+ sigaction(SIGINT, &sa, NULL);
|
|
|
+
|
|
|
+ sa.sa_handler = respawn_children;
|
|
|
+ sigaction(SIGALRM, &sa, NULL);
|
|
|
+
|
|
|
+ sa.sa_handler = hup_handler;
|
|
|
+ sigaction(SIGHUP, &sa, NULL);
|
|
|
+
|
|
|
+#if defined(CONSOLE_BAUD_RATE) && LINUX_VERSION_CODE < 0x020100
|
|
|
+ set_console_baud(CONSOLE_BAUD_RATE);
|
|
|
+#endif
|
|
|
+
|
|
|
+ /*
|
|
|
+ * start up in single user mode if /etc/singleboot exists or if
|
|
|
+ * argv[1] is "single".
|
|
|
+ */
|
|
|
+ if(boot_single(0, argc, argv)) enter_single();
|
|
|
+
|
|
|
+#ifdef RUN_RC
|
|
|
+ /* Register console if defined by boot */
|
|
|
+#if LINUX_VERSION_CODE < 0x020100
|
|
|
+ if ((console_device = getenv("CONSOLE"))) {
|
|
|
+ char *sp;
|
|
|
+ unsetenv("CONSOLE");
|
|
|
+ if ((sp = strchr(console_device, ','))) {
|
|
|
+ *sp++ = 0;
|
|
|
+ set_console_baud(atoi(sp));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ make_ascii_tty();
|
|
|
+#else
|
|
|
+{
|
|
|
+ struct stat st;
|
|
|
+
|
|
|
+ if (isatty(1)) {
|
|
|
+ have_console = 1;
|
|
|
+ make_ascii_tty();
|
|
|
+ } else if (fstat(1, &st) == -1 && errno == EBADF) {
|
|
|
+ /* No stdout, so send everything to /dev/null */
|
|
|
+ close(0); close(1); close(2);
|
|
|
+ open("/dev/null", O_RDWR);
|
|
|
+ dup(0);
|
|
|
+ dup(0);
|
|
|
+ }
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+ /*If we get a SIGTSTP before multi-user mode, do nothing*/
|
|
|
+ while(stopped)
|
|
|
+ pause();
|
|
|
+ if(do_rc() != 0 && boot_single(1, argc, argv) && !stopped)
|
|
|
+ enter_single();
|
|
|
+ while(stopped) /*Also if /etc/rc fails & we get SIGTSTP*/
|
|
|
+ pause();
|
|
|
+#endif
|
|
|
+
|
|
|
+ /* initialize the array of commands */
|
|
|
+ inittab = (struct initline *)malloc(NUMCMD * sizeof(struct initline));
|
|
|
+ inittab_size = NUMCMD;
|
|
|
+
|
|
|
+ if (!inittab) {
|
|
|
+ /* failure case - what do you do if init fails? */
|
|
|
+ err("malloc failed");
|
|
|
+ _exit(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ write_wtmp(); /* write boottime record */
|
|
|
+ read_inittab();
|
|
|
+
|
|
|
+#ifdef DEBUGGING
|
|
|
+ for(i = 0; i < numcmd; i++) {
|
|
|
+ char **p = inittab[i].toks;
|
|
|
+ printf("toks= %s %s %s %s\n",p[0], p[1], p[2], p[3]);
|
|
|
+ printf("tty= %s\n", inittab[i].tty);
|
|
|
+ printf("termcap= %s\n", inittab[i].termcap);
|
|
|
+ }
|
|
|
+ /*exit(0);*/
|
|
|
+#endif
|
|
|
+
|
|
|
+#if LINUX_VERSION_CODE < 0x020100
|
|
|
+ for(i = 0; i < getdtablesize(); i++) close(i);
|
|
|
+#else
|
|
|
+ /* Always leave 0, 1, and 2 connected (to /dev/null) for the child process */
|
|
|
+ for(i = 3; i < getdtablesize(); i++) close(i);
|
|
|
+#endif
|
|
|
+
|
|
|
+ for (;;) {
|
|
|
+ pid_t pid;
|
|
|
+ int vec;
|
|
|
+
|
|
|
+ if (run_sigint_processing) {
|
|
|
+ run_sigint_processing = 0;
|
|
|
+ sigint_processing();
|
|
|
+ }
|
|
|
+
|
|
|
+ respawn_children(0);
|
|
|
+
|
|
|
+ if (reload) {
|
|
|
+ reload = 0;
|
|
|
+ reload_inittab();
|
|
|
+ continue; /* process all reloads before waiting */
|
|
|
+ }
|
|
|
+
|
|
|
+ pid = wait(&vec);
|
|
|
+ alarm(0);
|
|
|
+
|
|
|
+ /* clear utmp entry, and append to wtmp if possible */
|
|
|
+#if 0 /* DAVIDM */
|
|
|
+ {
|
|
|
+ struct utmp *ut;
|
|
|
+ int ut_fd;
|
|
|
+
|
|
|
+ utmpname(_PATH_UTMP);
|
|
|
+ setutent();
|
|
|
+ while((ut = getutent())) {
|
|
|
+ if(ut->ut_pid == pid) {
|
|
|
+ time(&ut->ut_time);
|
|
|
+ bzero(&ut->ut_user, UT_NAMESIZE);
|
|
|
+ bzero(&ut->ut_host, sizeof(ut->ut_host));
|
|
|
+ ut->ut_type = DEAD_PROCESS;
|
|
|
+ ut->ut_pid = 0;
|
|
|
+ ut->ut_addr = 0;
|
|
|
+ endutent();
|
|
|
+ pututline(ut);
|
|
|
+ if((ut_fd = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
|
|
|
+ flock(ut_fd, LOCK_EX|LOCK_NB);
|
|
|
+ write(ut_fd, (const void *)ut, sizeof(struct utmp));
|
|
|
+ flock(ut_fd, LOCK_UN|LOCK_NB);
|
|
|
+ close(ut_fd);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ endutent();
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
+ for(i = 0; i < numcmd; i++) {
|
|
|
+ if(pid == inittab[i].pid) {
|
|
|
+ inittab[i].pid = -1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/*
|
|
|
+ * return true if we should boot up in singleuser mode. If argv[i] is
|
|
|
+ * "single" or the file /etc/singleboot exists, then singleuser mode should
|
|
|
+ * be entered. If /etc/securesingle exists ask for root password first.
|
|
|
+ */
|
|
|
+int boot_single(int singlearg, int argc, char *argv[])
|
|
|
+{
|
|
|
+ char *pass, *rootpass = NULL;
|
|
|
+ struct passwd *pwd;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for(i = 1; i < argc; i++) {
|
|
|
+ if(argv[i] && !strcmp(argv[i], "single")) singlearg = 1;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+void spawn(int i)
|
|
|
+{
|
|
|
+ pid_t pid;
|
|
|
+ int j;
|
|
|
+ time_t t;
|
|
|
+ struct initline *it;
|
|
|
+ char buf[150];
|
|
|
+
|
|
|
+ it = inittab + i;
|
|
|
+
|
|
|
+ t = time(NULL);
|
|
|
+ /* Check for held process */
|
|
|
+ if ((unsigned long)(it->nextrun - t - 1) < maxdelay)
|
|
|
+ return;
|
|
|
+ if (it->lastrun + testtime > t) { /* Respawning quickly */
|
|
|
+ if (it->xcnt < 0xff)
|
|
|
+ it->xcnt++;
|
|
|
+ } else { /* Normal respawning */
|
|
|
+ it->xcnt = 0;
|
|
|
+ it->lastrun = t;
|
|
|
+ it->delay = delaytime;
|
|
|
+ }
|
|
|
+ if (it->xcnt >= maxspawn) { /* Too many too quickly */
|
|
|
+ strcpy(buf, it->toks[0]);
|
|
|
+ strcat(buf, " respawning too fast\n");
|
|
|
+ err(buf);
|
|
|
+ it->pid = -1;
|
|
|
+ if (it->delay >= maxdelay)
|
|
|
+ it->delay = maxdelay;
|
|
|
+ else if (it->delay < delaytime)
|
|
|
+ it->delay = delaytime;
|
|
|
+ else if((it->delay *= 2) > maxdelay)
|
|
|
+ it->delay = maxdelay;
|
|
|
+ it->nextrun = t + it->delay;
|
|
|
+ /* Fiddle with the tracking vars to ensure that only
|
|
|
+ * one attempt is made to run this next time around.
|
|
|
+ */
|
|
|
+ it->lastrun = it->nextrun;
|
|
|
+ it->xcnt -= 2;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ it->nextrun = t + delaytime;
|
|
|
+
|
|
|
+ if((pid = vfork()) < 0) {
|
|
|
+ it->pid = -1;
|
|
|
+ err("fork failed\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if(pid) {
|
|
|
+ /* this is the parent */
|
|
|
+ it->pid = pid;
|
|
|
+ return;
|
|
|
+ } else {
|
|
|
+ /* this is the child */
|
|
|
+ char term[40];
|
|
|
+ char *prog;
|
|
|
+#ifdef INCLUDE_TIMEZONE
|
|
|
+ char tz[BUF_SIZ];
|
|
|
+#endif
|
|
|
+ char *env[4];
|
|
|
+
|
|
|
+ setsid();
|
|
|
+
|
|
|
+ /* Close everything other than 0, 1 and 2 */
|
|
|
+ for(j = 3; j < getdtablesize(); j++) {
|
|
|
+ close(j);
|
|
|
+ }
|
|
|
+ /* Now set up 0, 1 and 2 */
|
|
|
+ make_console(it->tty);
|
|
|
+
|
|
|
+ strcpy(term, "TERM=");
|
|
|
+ strcat(term, it->termcap);
|
|
|
+ env[0] = term;
|
|
|
+ env[1] = "PATH=/bin:/usr/bin:/etc:/sbin:/usr/sbin";
|
|
|
+#ifdef INCLUDE_TIMEZONE
|
|
|
+ strcpy(tz, "TZ=");
|
|
|
+ strcat(tz, tzone);
|
|
|
+ env[2] = tz;
|
|
|
+ env[3] = NULL;
|
|
|
+#else
|
|
|
+ env[2] = NULL;
|
|
|
+#endif
|
|
|
+
|
|
|
+ prog = it->toks[0];
|
|
|
+ if (*prog == '-' && *(prog+1))
|
|
|
+ prog++;
|
|
|
+ execve(prog, it->toks, env);
|
|
|
+ strcpy(buf, it->toks[0]);
|
|
|
+ strcat(buf, " exec failed\n");
|
|
|
+ err(buf);
|
|
|
+ _exit(1);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void init_itab(struct initline *p) {
|
|
|
+ bzero(p, sizeof(struct initline));
|
|
|
+ p->pid = -1;
|
|
|
+ p->nextrun = time(NULL);
|
|
|
+}
|
|
|
+
|
|
|
+static void clear_itab(struct initline *p) {
|
|
|
+ if (p->line)
|
|
|
+ free(p->line);
|
|
|
+ if (p->fullline)
|
|
|
+ free(p->fullline);
|
|
|
+ if (p->toks)
|
|
|
+ free(p->toks);
|
|
|
+ init_itab(p);
|
|
|
+}
|
|
|
+
|
|
|
+void read_inittab(void)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * free any old data and start again
|
|
|
+ */
|
|
|
+ for (i = 0; i < numcmd; i++)
|
|
|
+ clear_itab(&inittab[i]);
|
|
|
+ numcmd = 0;
|
|
|
+
|
|
|
+ /* Fake an inittab entry if boot console defined */
|
|
|
+#ifdef CONFIG_USER_INIT_CONSOLE_SH
|
|
|
+#if LINUX_VERSION_CODE < 0x020100
|
|
|
+ if (console_device && strcmp(console_device, "/dev/null"))
|
|
|
+#else
|
|
|
+ if (have_console)
|
|
|
+#endif
|
|
|
+ {
|
|
|
+ struct initline *p;
|
|
|
+ p = inittab + numcmd++;
|
|
|
+ init_itab(p);
|
|
|
+ p->fullline = strdup("console");
|
|
|
+ strcpy(p->tty, "console");
|
|
|
+ strcpy(p->termcap, "linux");
|
|
|
+ add_tok(p, "-/bin/sh");
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
+ i = 0;
|
|
|
+ if (read_initfile(_PATH_INITTAB) == 0)
|
|
|
+ i++;
|
|
|
+
|
|
|
+#ifdef CONFIG_USER_FLATFSD_FLATFSD
|
|
|
+ if (read_initfile(_PATH_CONFIGTAB) == 0)
|
|
|
+ i++;
|
|
|
+#endif
|
|
|
+
|
|
|
+ if (i == 0) {
|
|
|
+ err("Failed to open " _PATH_INITTAB
|
|
|
+#ifdef CONFIG_USER_FLATFSD_FLATFSD
|
|
|
+ " or " _PATH_CONFIGTAB
|
|
|
+#endif
|
|
|
+ "."
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+#ifdef CONFIG_USER_INIT_CONF
|
|
|
+ load_init_conf();
|
|
|
+#endif
|
|
|
+
|
|
|
+ /* if needed, shrink the array using realloc -
|
|
|
+ * must be done here so that we include the results of all init files
|
|
|
+ * when calculating number of commands */
|
|
|
+ if ((numcmd + 2) < (inittab_size - NUMCMD)) {
|
|
|
+ /* round up from numcmd to the nearest multiple of NUMCMD */
|
|
|
+ inittab_size = ((numcmd + 2) / NUMCMD + 1) * NUMCMD;
|
|
|
+ inittab = realloc(inittab, inittab_size * sizeof(struct initline));
|
|
|
+ if (!inittab) {
|
|
|
+ /* failure case - what do you do if init fails? */
|
|
|
+ err("malloc failed");
|
|
|
+ _exit(1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (numcmd == 0)
|
|
|
+ _exit(1);
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+read_initfile(const char *initfile)
|
|
|
+{
|
|
|
+ struct initline *p;
|
|
|
+ FILE *f;
|
|
|
+ char *buf = NULL;
|
|
|
+ size_t buf_len = 0;
|
|
|
+ int i,j,k;
|
|
|
+ char *ptr, *getty;
|
|
|
+#ifdef SPECIAL_CONSOLE_TERM
|
|
|
+ char tty[50];
|
|
|
+ struct stat stb;
|
|
|
+ char *termenv;
|
|
|
+
|
|
|
+ termenv = getenv("TERM"); /* set by kernel */
|
|
|
+#endif
|
|
|
+
|
|
|
+ i = numcmd;
|
|
|
+
|
|
|
+ if (!(f = fopen(initfile, "r")))
|
|
|
+ return 1;
|
|
|
+
|
|
|
+ while(!feof(f)) {
|
|
|
+ if (i+2 == inittab_size) {
|
|
|
+ /* need to realloc inittab */
|
|
|
+ inittab_size += NUMCMD;
|
|
|
+ inittab = realloc(inittab, inittab_size * sizeof(struct initline));
|
|
|
+ if (!inittab) {
|
|
|
+ /* failure case - what do you do if init fails? */
|
|
|
+ err("malloc failed");
|
|
|
+ _exit(1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (getline(&buf, &buf_len, f) == -1) break;
|
|
|
+
|
|
|
+ for(k = 0; k < buf_len && buf[k]; k++) {
|
|
|
+ if(buf[k] == '#') {
|
|
|
+ buf[k] = '\0'; break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if(buf[0] == '\0' || buf[0] == '\n') continue;
|
|
|
+
|
|
|
+ p = inittab + i;
|
|
|
+ init_itab(p);
|
|
|
+ p->line = strdup(buf);
|
|
|
+ p->fullline = strdup(buf);
|
|
|
+ if (!p->line || !p->fullline) {
|
|
|
+ err("Not memory to allocate inittab entry");
|
|
|
+ clear_itab(p);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ ptr = strtok(p->line, ":");
|
|
|
+ if (!ptr) {
|
|
|
+ err("Missing TTY/ID field in inittab");
|
|
|
+ clear_itab(p);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ strncpy(p->tty, ptr, 9);
|
|
|
+ //p->tty[9] = '\0';
|
|
|
+ ptr = strtok(NULL, ":");
|
|
|
+ if (!ptr) {
|
|
|
+ err("Missing TERMTYPE field in inittab");
|
|
|
+ clear_itab(p);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ strncpy(p->termcap, ptr, 29);
|
|
|
+ //p->termcap[29] = '\0';
|
|
|
+
|
|
|
+ getty = strtok(NULL, " \t\n");
|
|
|
+ if (!getty) {
|
|
|
+ err("Missing PROCESS field in inittab");
|
|
|
+ clear_itab(p);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ add_tok(p, getty);
|
|
|
+ j = 1;
|
|
|
+ while((ptr = strtok(NULL, " \t\n")))
|
|
|
+ add_tok(p, ptr);
|
|
|
+
|
|
|
+#ifdef SPECIAL_CONSOLE_TERM
|
|
|
+ /* special-case termcap for the console ttys */
|
|
|
+ strcpy(tty, "/dev/");
|
|
|
+ strcat(tty, p->tty);
|
|
|
+ if(!termenv || stat(tty, &stb) < 0) {
|
|
|
+ err("no TERM or cannot stat tty\n");
|
|
|
+ } else {
|
|
|
+ /* is it a console tty? */
|
|
|
+ if(major(stb.st_rdev) == 4 && minor(stb.st_rdev) < 64) {
|
|
|
+ strncpy(p->termcap, termenv, 30);
|
|
|
+ p->termcap[29] = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
+ i++;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (buf)
|
|
|
+ free(buf);
|
|
|
+
|
|
|
+ fclose(f);
|
|
|
+
|
|
|
+ numcmd = i;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+void hup_handler()
|
|
|
+{
|
|
|
+ reload = 1;
|
|
|
+}
|
|
|
+
|
|
|
+void reload_inittab()
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ int oldnum;
|
|
|
+ char ** saveline = (char **) malloc(inittab_size * sizeof(char *));
|
|
|
+ pid_t * savepid = (pid_t*) malloc(inittab_size * sizeof(pid_t));
|
|
|
+
|
|
|
+ if (!saveline || !savepid) {
|
|
|
+ /* another failure case - what DO you do if init fails */
|
|
|
+ err("malloc failed");
|
|
|
+ _exit(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i=0; i<numcmd; i++) {
|
|
|
+ savepid[i] = inittab[i].pid;
|
|
|
+ saveline[i] = strdup(inittab[i].fullline);
|
|
|
+ if (!saveline[i]) {
|
|
|
+ err("malloc failed");
|
|
|
+ _exit(1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ oldnum = numcmd;
|
|
|
+ read_inittab();
|
|
|
+
|
|
|
+ /* See which ones still exist */
|
|
|
+ for(i = 0; i < numcmd; i++) {
|
|
|
+ int j;
|
|
|
+ for(j = 0; j < oldnum; j++) {
|
|
|
+ if(strcmp(saveline[j], inittab[i].fullline) == 0) {
|
|
|
+ inittab[i].pid = savepid[j];
|
|
|
+ savepid[j] = -1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Kill off processes no longer needed and free memory */
|
|
|
+ for(i = 0; i < oldnum; i++) {
|
|
|
+ if (savepid[i] > 1)
|
|
|
+ kill(savepid[i], SIGTERM);
|
|
|
+ free(saveline[i]);
|
|
|
+ }
|
|
|
+
|
|
|
+ free(saveline);
|
|
|
+ free(savepid);
|
|
|
+}
|
|
|
+
|
|
|
+void tstp_handler()
|
|
|
+{
|
|
|
+ stopped++;
|
|
|
+}
|
|
|
+
|
|
|
+void cont_handler()
|
|
|
+{
|
|
|
+ stopped = 0;
|
|
|
+}
|
|
|
+
|
|
|
+void int_handler()
|
|
|
+{
|
|
|
+ run_sigint_processing = 1;
|
|
|
+}
|
|
|
+
|
|
|
+void sigint_processing()
|
|
|
+{
|
|
|
+ /*
|
|
|
+ * After Linux 0.96b PL1, we get a SIGINT when
|
|
|
+ * the user presses Ctrl-Alt-Del...
|
|
|
+ */
|
|
|
+
|
|
|
+ int pid;
|
|
|
+
|
|
|
+ sync();
|
|
|
+ sync();
|
|
|
+ if((pid = vfork()) == 0) {
|
|
|
+ char *av[2];
|
|
|
+ extern char **environ;
|
|
|
+ /* reboot properly... */
|
|
|
+ av[0] = _PATH_REBOOT;
|
|
|
+ av[1] = NULL;
|
|
|
+
|
|
|
+ execve(_PATH_REBOOT, av, environ);
|
|
|
+#if __GNU_LIBRARY__ > 5
|
|
|
+ reboot(0x1234567);
|
|
|
+#else
|
|
|
+ reboot(0xfee1dead, 672274793, 0x1234567);
|
|
|
+#endif
|
|
|
+ _exit(2);
|
|
|
+ } else if(pid < 0) {
|
|
|
+ /* fork failed, try the hard way... */
|
|
|
+#if __GNU_LIBRARY__ > 5
|
|
|
+ reboot(0x1234567);
|
|
|
+#else
|
|
|
+ reboot(0xfee1dead, 672274793, 0x1234567);
|
|
|
+#endif
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#ifdef INCLUDE_TIMEZONE
|
|
|
+void set_tz(void)
|
|
|
+{
|
|
|
+ FILE *f;
|
|
|
+ int len;
|
|
|
+
|
|
|
+ if((f = fopen("/etc/config/TZ", "r")) == NULL &&
|
|
|
+ (f = fopen("/etc/TZ", "r")) == NULL)
|
|
|
+ return;
|
|
|
+ fgets(tzone, BUF_SIZ-2, f);
|
|
|
+ fclose(f);
|
|
|
+ if((len=strlen(tzone)) < 2)
|
|
|
+ return;
|
|
|
+ tzone[len-1] = 0; /* get rid of the '\n' */
|
|
|
+ setenv("TZ", tzone, 0);
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef CONFIG_USER_INIT_CONF
|
|
|
+void load_init_conf(void)
|
|
|
+{
|
|
|
+ char line[BUF_SIZ];
|
|
|
+ FILE *f;
|
|
|
+
|
|
|
+ if ((f = fopen("/etc/config/init.conf", "r")) == NULL &&
|
|
|
+ (f = fopen("/etc/init.conf", "r")) == NULL)
|
|
|
+ return;
|
|
|
+ while (fgets(line, sizeof(line) - 2, f)) {
|
|
|
+ if (strncasecmp(line, "delaytime=", 10) == 0)
|
|
|
+ delaytime = atoi(line + 10);
|
|
|
+ if (strncasecmp(line, "maxdelay=", 9) == 0)
|
|
|
+ maxdelay = atoi(line + 9);
|
|
|
+ if (strncasecmp(line, "maxspawn=", 9) == 0)
|
|
|
+ maxspawn = atoi(line + 9);
|
|
|
+ if (strncasecmp(line, "testtime=", 9) == 0)
|
|
|
+ testtime = atoi(line + 9);
|
|
|
+ }
|
|
|
+ fclose(f);
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+void write_wtmp(void)
|
|
|
+{
|
|
|
+#if 0
|
|
|
+ int fd;
|
|
|
+ struct utmp ut;
|
|
|
+
|
|
|
+ bzero((char *)&ut, sizeof(ut));
|
|
|
+ strcpy(ut.ut_line, "~");
|
|
|
+ bzero(ut.ut_name, sizeof(ut.ut_name));
|
|
|
+ time(&ut.ut_time);
|
|
|
+ ut.ut_type = BOOT_TIME;
|
|
|
+
|
|
|
+ if((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND)) >= 0) {
|
|
|
+ flock(fd, LOCK_EX|LOCK_NB); /* make sure init won't hang */
|
|
|
+ write(fd, (char *)&ut, sizeof(ut));
|
|
|
+ flock(fd, LOCK_UN|LOCK_NB);
|
|
|
+ close(fd);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+void make_ascii_tty(void)
|
|
|
+{
|
|
|
+ struct termios tty;
|
|
|
+ const char *pt;
|
|
|
+
|
|
|
+ if (tcgetattr(0, &tty) < 0)
|
|
|
+ return;
|
|
|
+
|
|
|
+ tty.c_iflag &= ~(INLCR|IGNCR|IUCLC);
|
|
|
+ tty.c_iflag |= ICRNL;
|
|
|
+ tty.c_oflag &= ~(OCRNL|OLCUC|ONOCR|ONLRET|OFILL);
|
|
|
+ tty.c_oflag |= OPOST|ONLCR;
|
|
|
+ tty.c_cflag |= CLOCAL;
|
|
|
+ tty.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE;
|
|
|
+#ifdef IEXTEN
|
|
|
+ tty.c_lflag |= IEXTEN;
|
|
|
+#endif
|
|
|
+
|
|
|
+#if LINUX_VERSION_CODE < 0x020100
|
|
|
+ if (console_baud != -1)
|
|
|
+ cfsetospeed(&tty, console_baud);
|
|
|
+#endif
|
|
|
+
|
|
|
+ tty.c_cc[VINTR] = CTRL('C');
|
|
|
+ tty.c_cc[VQUIT] = CTRL('\\');
|
|
|
+ tty.c_cc[VERASE] = CTRL('H'); /*127*/
|
|
|
+ tty.c_cc[VKILL] = CTRL('U'); /*Changed from non-standard ^X*/
|
|
|
+ tty.c_cc[VEOF] = CTRL('D');
|
|
|
+ tty.c_cc[VTIME] = 0;
|
|
|
+ tty.c_cc[VMIN] = 1;
|
|
|
+ tty.c_cc[VSTART] = CTRL('Q');
|
|
|
+ tty.c_cc[VSTOP] = CTRL('S');
|
|
|
+ tty.c_cc[VSUSP] = CTRL('Z');
|
|
|
+#ifdef VWERASE
|
|
|
+ tty.c_cc[VWERASE] = CTRL('W');
|
|
|
+#endif
|
|
|
+ /* Pick up simple environment setting of VERASE.
|
|
|
+ * Useful for setting on kernel command line.
|
|
|
+ * e.g. TTYERASE=^?
|
|
|
+ */
|
|
|
+ pt = getenv("TTYERASE");
|
|
|
+ if (pt && pt[0] == '^' && pt[1]) {
|
|
|
+ tty.c_cc[VERASE] = (pt[1] == '?') ? 127 : CTRL(pt[1]);
|
|
|
+ }
|
|
|
+
|
|
|
+ tcsetattr(0, TCSANOW, &tty);
|
|
|
+}
|
|
|
+
|
|
|
+void make_console(const char *tty)
|
|
|
+{
|
|
|
+ char devname[32];
|
|
|
+
|
|
|
+ close(0); close(1); close(2);
|
|
|
+
|
|
|
+ if (tty && *tty) {
|
|
|
+#if LINUX_VERSION_CODE < 0x020100
|
|
|
+ /*
|
|
|
+ * until we get proper console support under 2.0
|
|
|
+ */
|
|
|
+ if (strcmp(tty, "console") == 0) {
|
|
|
+ strcpy(devname, console_device);
|
|
|
+ }
|
|
|
+ else
|
|
|
+#endif
|
|
|
+ {
|
|
|
+ strcpy(devname, "/dev/");
|
|
|
+ strcat(devname, tty);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Try to open the specified console */
|
|
|
+ if (open(devname, O_RDWR|O_NONBLOCK) >= 0) {
|
|
|
+ fcntl(0, F_SETFL, 0);
|
|
|
+ dup(0);
|
|
|
+ dup(0);
|
|
|
+ make_ascii_tty();
|
|
|
+ ioctl(0, TIOCSCTTY, (char*)0);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* No go, so send to /dev/null */
|
|
|
+ open("/dev/null", O_RDWR|O_NONBLOCK);
|
|
|
+ dup(0);
|
|
|
+ dup(0);
|
|
|
+}
|