init: remove superfluous forks and messing up with argv[0]
authorDenis Vlasenko <vda.linux@googlemail.com>
Mon, 10 Dec 2007 07:06:04 +0000 (07:06 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Mon, 10 Dec 2007 07:06:04 +0000 (07:06 -0000)
cttyhack: add stealing of ctty

applets/Kbuild
docs/ctty.htm
init/Config.in
init/init.c
shell/cttyhack.c

index ef4b76fed260388f2014e078195518f9b147df31..2969e79228f9860a36f7e27dc905e61086e84f6d 100644 (file)
@@ -18,7 +18,8 @@ HOSTCFLAGS_usage.o = -I$(srctree)/include
 
 applets/applets.o: include/usage_compressed.h include/applet_tables.h
 
-applets/usage:     .config $(srctree)/applets/usage_compressed
+applets/usage:         .config $(srctree)/applets/usage_compressed
+applets/applet_tables: .config
 
 quiet_cmd_gen_usage_compressed = GEN     include/usage_compressed.h
       cmd_gen_usage_compressed = $(srctree)/applets/usage_compressed include/usage_compressed.h applets
index 26d2c79569f450092b6343035e4354245cd43554..b8bce003c0b49f5758f41076f3f10d4ee619b6ae 100644 (file)
@@ -365,7 +365,7 @@ this is a great mystery.
 becomes its controlling tty.
 </p><p>The BSD approach is that one has to explicitly call
 </p><blockquote>
-<pre>ioctl(fd, TIOCSCTTY, ...);
+<pre>ioctl(fd, TIOCSCTTY, 0/1);
 </pre>
 </blockquote>
 
@@ -378,6 +378,8 @@ and (ii) it does not yet have a controlling tty, and
 (iii) maybe the tty should not already control some other session;
 if it does it is an error if we aren't root, or we steal the tty
 if we are all-powerful.
+[vda: correction: third parameter controls this: if 1, we steal tty from
+any such session, if 0, we don't steal]
 </p><p>Opening some terminal will give us a controlling tty,
 provided that (i) the current process is a session leader, and
 (ii) it does not yet have a controlling tty, and
index 3987d8feeb6902d15fe9c74d6687e1ad0449e9d0..318f523a0dca436a4681fd8688fc3f583e689b05 100644 (file)
@@ -37,6 +37,7 @@ config FEATURE_INIT_SCTTY
          controlling tty (TIOCSCTTY).  This is not the traditional init
          behavour, but is often what you want in an embedded system where
          the console is only accessed during development or for maintenance.
+         NB: using cttyhack applet may work better.
 
 config FEATURE_INIT_SYSLOG
        bool "Enable init to write to syslog"
index fe0ec030a7ae45d0771be38c7929eaeb658e89c4..c0c8b17cbe4f9fbb83dae0c3ee08c8c4536151a5 100644 (file)
 #define SHUTDOWN    0x040
 #define RESTART     0x080
 
-/* A mapping between "inittab" action name strings and action type codes. */
-struct init_action_type {
-       const char *name;
-       int action;
-};
-
-static const struct init_action_type actions[] = {
-       {"sysinit", SYSINIT},
-       {"respawn", RESPAWN},
-       {"askfirst", ASKFIRST},
-       {"wait", WAIT},
-       {"once", ONCE},
-       {"ctrlaltdel", CTRLALTDEL},
-       {"shutdown", SHUTDOWN},
-       {"restart", RESTART},
-       {0, 0}
-};
+#define STR_SYSINIT     "\x01"
+#define STR_RESPAWN     "\x02"
+#define STR_ASKFIRST    "\x04"
+#define STR_WAIT        "\x08"
+#define STR_ONCE        "\x10"
+#define STR_CTRLALTDEL  "\x20"
+#define STR_SHUTDOWN    "\x40"
+#define STR_RESTART     "\x80"
 
 /* Set up a linked list of init_actions, to be read from inittab */
 struct init_action {
        struct init_action *next;
-       int action;
        pid_t pid;
-       char command[INIT_BUFFS_SIZE];
+       uint8_t action;
        char terminal[CONSOLE_NAME_SIZE];
+       char command[INIT_BUFFS_SIZE];
 };
 
 /* Static variables */
@@ -113,7 +104,7 @@ static const char *const environment[] = {
 
 /* Function prototypes */
 static void delete_init_action(struct init_action *a);
-static int waitfor(const struct init_action *a, pid_t pid);
+static int waitfor(pid_t pid);
 #if !ENABLE_DEBUG_INIT
 static void shutdown_signal(int sig);
 #endif
@@ -193,43 +184,6 @@ static void message(int device, const char *fmt, ...)
        }
 }
 
-/* Set terminal settings to reasonable defaults */
-static void set_sane_term(void)
-{
-       struct termios tty;
-
-       tcgetattr(STDIN_FILENO, &tty);
-
-       /* set control chars */
-       tty.c_cc[VINTR] = 3;    /* C-c */
-       tty.c_cc[VQUIT] = 28;   /* C-\ */
-       tty.c_cc[VERASE] = 127; /* C-? */
-       tty.c_cc[VKILL] = 21;   /* C-u */
-       tty.c_cc[VEOF] = 4;     /* C-d */
-       tty.c_cc[VSTART] = 17;  /* C-q */
-       tty.c_cc[VSTOP] = 19;   /* C-s */
-       tty.c_cc[VSUSP] = 26;   /* C-z */
-
-       /* use line dicipline 0 */
-       tty.c_line = 0;
-
-       /* Make it be sane */
-       tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD;
-       tty.c_cflag |= CREAD | HUPCL | CLOCAL;
-
-       /* input modes */
-       tty.c_iflag = ICRNL | IXON | IXOFF;
-
-       /* output modes */
-       tty.c_oflag = OPOST | ONLCR;
-
-       /* local modes */
-       tty.c_lflag =
-               ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN;
-
-       tcsetattr(STDIN_FILENO, TCSANOW, &tty);
-}
-
 /* From <linux/serial.h> */
 struct serial_struct {
        int     type;
@@ -277,7 +231,7 @@ static void console_init(void)
 
        s = getenv("TERM");
        if (ioctl(0, TIOCGSERIAL, &sr) == 0) {
-               /* Force the TERM setting to vt102 for serial console --
+               /* Force the TERM setting to vt102 for serial console
                 * if TERM is set to linux (the default) */
                if (!s || strcmp(s, "linux") == 0)
                        putenv((char*)"TERM=vt102");
@@ -288,14 +242,41 @@ static void console_init(void)
                putenv((char*)"TERM=linux");
 }
 
-static void fixup_argv(char **argv)
+/* Set terminal settings to reasonable defaults */
+static void set_sane_term(void)
 {
-       /* Fix up argv[0] to be certain we claim to be init */
-       strncpy(argv[0], "init", strlen(argv[0]));
+       struct termios tty;
 
-       /* Wipe argv[1]-argv[N] so they don't clutter the ps listing */
-       while (*++argv)
-               memset(*argv, 0, strlen(*argv));
+       tcgetattr(STDIN_FILENO, &tty);
+
+       /* set control chars */
+       tty.c_cc[VINTR] = 3;    /* C-c */
+       tty.c_cc[VQUIT] = 28;   /* C-\ */
+       tty.c_cc[VERASE] = 127; /* C-? */
+       tty.c_cc[VKILL] = 21;   /* C-u */
+       tty.c_cc[VEOF] = 4;     /* C-d */
+       tty.c_cc[VSTART] = 17;  /* C-q */
+       tty.c_cc[VSTOP] = 19;   /* C-s */
+       tty.c_cc[VSUSP] = 26;   /* C-z */
+
+       /* use line dicipline 0 */
+       tty.c_line = 0;
+
+       /* Make it be sane */
+       tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD;
+       tty.c_cflag |= CREAD | HUPCL | CLOCAL;
+
+       /* input modes */
+       tty.c_iflag = ICRNL | IXON | IXOFF;
+
+       /* output modes */
+       tty.c_oflag = OPOST | ONLCR;
+
+       /* local modes */
+       tty.c_lflag =
+               ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN;
+
+       tcsetattr(STDIN_FILENO, TCSANOW, &tty);
 }
 
 /* Open the new terminal device */
@@ -324,6 +305,7 @@ static void open_stdio_to_tty(const char* tty_name, int fail)
        set_sane_term();
 }
 
+/* Used only by run_actions */
 static pid_t run(const struct init_action *a)
 {
        int i;
@@ -333,16 +315,20 @@ static pid_t run(const struct init_action *a)
        char buf[INIT_BUFFS_SIZE + 6];  /* INIT_BUFFS_SIZE+strlen("exec ")+1 */
        sigset_t nmask, omask;
 
-       /* Block sigchild while forking */
+       /* Block sigchild while forking (why?) */
        sigemptyset(&nmask);
        sigaddset(&nmask, SIGCHLD);
        sigprocmask(SIG_BLOCK, &nmask, &omask);
        pid = fork();
        sigprocmask(SIG_SETMASK, &omask, NULL);
 
+       if (pid < 0)
+               message(L_LOG | L_CONSOLE, "Can't fork");
        if (pid)
                return pid;
 
+       /* Child */
+
        /* Reset signal handlers that were set by the parent process */
        signal(SIGUSR1, SIG_DFL);
        signal(SIGUSR2, SIG_DFL);
@@ -359,8 +345,9 @@ static pid_t run(const struct init_action *a)
        setsid();
 
        /* Open the new terminal device */
-       open_stdio_to_tty(a->terminal, 1);
+       open_stdio_to_tty(a->terminal, 1 /* - exit if open fails*/);
 
+#ifdef BUT_RUN_ACTIONS_ALREADY_DOES_WAITING
        /* If the init Action requires us to wait, then force the
         * supplied terminal to be the controlling tty. */
        if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) {
@@ -373,13 +360,13 @@ static pid_t run(const struct init_action *a)
                }
 
                if (pid > 0) {
-                       /* We are the parent -- wait till the child is done */
+                       /* Parent - wait till the child is done */
                        signal(SIGINT, SIG_IGN);
                        signal(SIGTSTP, SIG_IGN);
                        signal(SIGQUIT, SIG_IGN);
                        signal(SIGCHLD, SIG_DFL);
 
-                       waitfor(NULL, pid);
+                       waitfor(pid);
                        /* See if stealing the controlling tty back is necessary */
                        if (tcgetpgrp(0) != getpid())
                                _exit(0);
@@ -395,12 +382,13 @@ static pid_t run(const struct init_action *a)
                                ioctl(0, TIOCSCTTY, 1);
                                _exit(0);
                        }
-                       waitfor(NULL, pid);
+                       waitfor(pid);
                        _exit(0);
                }
 
-               /* Now fall though to actually execute things */
+               /* Child - fall though to actually execute things */
        }
+#endif
 
        /* See if any special /bin/sh requiring characters are present */
        if (strpbrk(a->command, "~`!$^&*()=|\\{}[];\"'<>?") != NULL) {
@@ -433,9 +421,9 @@ static pid_t run(const struct init_action *a)
                /* skip over the dash */
                ++cmdpath;
 
+#ifdef WHY_WE_DO_THIS_SHELL_MUST_HANDLE_THIS_ITSELF
                /* find the last component in the command pathname */
                s = bb_get_last_path_component_nostrip(cmdpath);
-
                /* make a new argv[0] */
                cmd[0] = malloc(strlen(s) + 2);
                if (cmd[0] == NULL) {
@@ -445,16 +433,16 @@ static pid_t run(const struct init_action *a)
                        cmd[0][0] = '-';
                        strcpy(cmd[0] + 1, s);
                }
+#endif
+
 #if ENABLE_FEATURE_INIT_SCTTY
                /* Establish this process as session leader and
-                * (attempt) to make the tty (if any) a controlling tty.
+                * _attempt_ to make stdin a controlling tty.
                 */
-               setsid();
-               ioctl(0, TIOCSCTTY, 0 /*don't steal it*/);
+               ioctl(0, TIOCSCTTY, 0 /*only try, don't steal*/);
 #endif
        }
 
-#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__)
        if (a->action & ASKFIRST) {
                static const char press_enter[] ALIGN1 =
 #ifdef CUSTOMIZED_BANNER
@@ -474,10 +462,10 @@ static pid_t run(const struct init_action *a)
                                        "(pid %d, tty '%s')\n",
                                  cmdpath, getpid(), a->terminal);
                full_write(1, press_enter, sizeof(press_enter) - 1);
-               while (read(0, &c, 1) == 1 && c != '\n')
-                       ;
+               while (safe_read(0, &c, 1) == 1 && c != '\n')
+                       continue;
        }
-#endif
+
        /* Log the process name and args */
        message(L_LOG, "starting pid %d, tty '%s': '%s'",
                          getpid(), a->terminal, cmdpath);
@@ -504,22 +492,15 @@ static pid_t run(const struct init_action *a)
        _exit(-1);
 }
 
-static int waitfor(const struct init_action *a, pid_t pid)
+static int waitfor(pid_t runpid)
 {
-       int runpid;
        int status, wpid;
 
-       runpid = (NULL == a)? pid : run(a);
        while (1) {
                wpid = waitpid(runpid, &status, 0);
-               if (wpid == runpid)
-                       break;
-               if (wpid == -1 && errno == ECHILD) {
-                       /* we missed its termination */
-                       break;
-               }
-               /* FIXME other errors should maybe trigger an error, but allow
-                * the program to continue */
+               if (wpid == -1 && errno == EINTR)
+                       continue;
+               break;
        }
        return wpid;
 }
@@ -534,9 +515,10 @@ static void run_actions(int action)
                if (a->action == action) {
                        /* a->terminal of "" means "init's console" */
                        if (a->terminal[0] && access(a->terminal, R_OK | W_OK)) {
+                               //message(L_LOG | L_CONSOLE, "Device %s cannot be opened in RW mode", a->terminal /*, strerror(errno)*/);
                                delete_init_action(a);
                        } else if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) {
-                               waitfor(a, 0);
+                               waitfor(run(a));
                                delete_init_action(a);
                        } else if (a->action & ONCE) {
                                run(a);
@@ -607,6 +589,29 @@ static void shutdown_system(void)
        sleep(1);
 }
 
+static void shutdown_signal(int sig)
+{
+       const char *m;
+       int rb;
+
+       shutdown_system();
+
+       m = "halt";
+       rb = RB_HALT_SYSTEM;
+       if (sig == SIGTERM) {
+               m = "reboot";
+               rb = RB_AUTOBOOT;
+       } else if (sig == SIGUSR2) {
+               m = "poweroff";
+               rb = RB_POWER_OFF;
+       }
+       message(L_CONSOLE | L_LOG, "Requesting system %s", m);
+       /* allow time for last message to reach serial console */
+       sleep(2);
+       init_reboot(rb);
+       loop_forever();
+}
+
 static void exec_signal(int sig ATTRIBUTE_UNUSED)
 {
        struct init_action *a, *tmp;
@@ -632,7 +637,7 @@ static void exec_signal(int sig ATTRIBUTE_UNUSED)
                        sigprocmask(SIG_UNBLOCK, &unblock_signals, NULL);
 
                        /* Open the new terminal device */
-                       open_stdio_to_tty(a->terminal, 0);
+                       open_stdio_to_tty(a->terminal, 0 /* - shutdown_signal(SIGUSR1) [halt] if open fails */);
 
                        messageD(L_CONSOLE | L_LOG, "Trying to re-exec %s", a->command);
                        BB_EXECLP(a->command, a->command, NULL);
@@ -646,29 +651,6 @@ static void exec_signal(int sig ATTRIBUTE_UNUSED)
        }
 }
 
-static void shutdown_signal(int sig)
-{
-       const char *m;
-       int rb;
-
-       shutdown_system();
-
-       m = "halt";
-       rb = RB_HALT_SYSTEM;
-       if (sig == SIGTERM) {
-               m = "reboot";
-               rb = RB_AUTOBOOT;
-       } else if (sig == SIGUSR2) {
-               m = "poweroff";
-               rb = RB_POWER_OFF;
-       }
-       message(L_CONSOLE | L_LOG, "Requesting system %s", m);
-       /* allow time for last message to reach serial console */
-       sleep(2);
-       init_reboot(rb);
-       loop_forever();
-}
-
 static void ctrlaltdel_signal(int sig ATTRIBUTE_UNUSED)
 {
        run_actions(CTRLALTDEL);
@@ -682,7 +664,7 @@ static void stop_handler(int sig ATTRIBUTE_UNUSED)
        got_cont = 0;
        while (!got_cont)
                pause();
-       got_cont = 0;
+
        errno = saved_errno;
 }
 
@@ -694,7 +676,7 @@ static void cont_handler(int sig ATTRIBUTE_UNUSED)
 
 #endif /* !ENABLE_DEBUG_INIT */
 
-static void new_init_action(int action, const char *command, const char *cons)
+static void new_init_action(uint8_t action, const char *command, const char *cons)
 {
        struct init_action *new_action, *a, *last;
 
@@ -754,11 +736,21 @@ static void delete_init_action(struct init_action *action)
 static void parse_inittab(void)
 {
 #if ENABLE_FEATURE_USE_INITTAB
+       static const char actions[] =
+               STR_SYSINIT    "sysinit\0"
+               STR_RESPAWN    "respawn\0"
+               STR_ASKFIRST   "askfirst\0"
+               STR_WAIT       "wait\0"
+               STR_ONCE       "once\0"
+               STR_CTRLALTDEL "ctrlaltdel\0"
+               STR_SHUTDOWN   "shutdown\0"
+               STR_RESTART    "restart\0"
+       ;
+
        FILE *file;
        char buf[INIT_BUFFS_SIZE], lineAsRead[INIT_BUFFS_SIZE];
        char tmpConsole[CONSOLE_NAME_SIZE];
        char *id, *runlev, *action, *command, *eol;
-       const struct init_action_type *a = actions;
 
        file = fopen(INITTAB, "r");
        if (file == NULL) {
@@ -769,7 +761,8 @@ static void parse_inittab(void)
                /* Umount all filesystems on halt/reboot */
                new_init_action(SHUTDOWN, "umount -a -r", "");
                /* Swapoff on halt/reboot */
-               if (ENABLE_SWAPONOFF) new_init_action(SHUTDOWN, "swapoff -a", "");
+               if (ENABLE_SWAPONOFF)
+                       new_init_action(SHUTDOWN, "swapoff -a", "");
                /* Prepare to restart init when a HUP is received */
                new_init_action(RESTART, "init", "");
                /* Askfirst shell on tty1-4 */
@@ -785,6 +778,8 @@ static void parse_inittab(void)
        }
 
        while (fgets(buf, INIT_BUFFS_SIZE, file) != NULL) {
+               const char *a;
+
                /* Skip leading spaces */
                for (id = buf; *id == ' ' || *id == '\t'; id++);
 
@@ -832,8 +827,8 @@ static void parse_inittab(void)
                }
 
                /* Ok, now process it */
-               for (a = actions; a->name != 0; a++) {
-                       if (strcmp(a->name, action) == 0) {
+               for (a = actions; a[0]; a += strlen(a) + 1) {
+                       if (strcmp(a + 1, action) == 0) {
                                if (*id != '\0') {
                                        if (strncmp(id, "/dev/", 5) == 0)
                                                id += 5;
@@ -842,11 +837,11 @@ static void parse_inittab(void)
                                                sizeof(tmpConsole) - 5);
                                        id = tmpConsole;
                                }
-                               new_init_action(a->action, command, id);
+                               new_init_action((uint8_t)a[0], command, id);
                                break;
                        }
                }
-               if (a->name == 0) {
+               if (!a[0]) {
                        /* Choke on an unknown action */
                        message(L_LOG | L_CONSOLE, "Bad inittab entry: %s", lineAsRead);
                }
@@ -981,8 +976,11 @@ int init_main(int argc, char **argv)
        }
 #endif /* CONFIG_SELINUX */
 
-       /* Make the command line just say "init"  -- thats all, nothing else */
-       fixup_argv(argv);
+       /* Make the command line just say "init"  - thats all, nothing else */
+       strncpy(argv[0], "init", strlen(argv[0]));
+       /* Wipe argv[1]-argv[N] so they don't clutter the ps listing */
+       while (*++argv)
+               memset(*argv, 0, strlen(*argv));
 
        /* Now run everything that needs to be run */
 
index cdd0ed1d6dd39fc9605344af957da0fb52c43776..915ab5142fe9c4733916b7b57c0428d76204b304 100644 (file)
@@ -1,16 +1,18 @@
-/* This code is adapted from busybox project
- *
+/* vi: set sw=4 ts=4: */
+/*
  * Licensed under GPLv2
+ *
+ * Copyright (c) 2007 Denys Vlasenko <vda.linux@googlemail.com>
  */
 #include "libbb.h"
 
 /* From <linux/vt.h> */
 struct vt_stat {
-       unsigned short v_active;        /* active vt */
-       unsigned short v_signal;        /* signal to send */
-       unsigned short v_state; /* vt bitmask */
+       unsigned short v_active;        /* active vt */
+       unsigned short v_signal;        /* signal to send */
+       unsigned short v_state;         /* vt bitmask */
 };
-enum { VT_GETSTATE = 0x5603 }; /* get global vt state info */
+enum { VT_GETSTATE = 0x5603 }; /* get global vt state info */
 
 /* From <linux/serial.h> */
 struct serial_struct {
@@ -26,8 +28,8 @@ struct serial_struct {
        char    io_type;
        char    reserved_char[1];
        int     hub6;
-       unsigned short  closing_wait; /* time to wait before closing */
-       unsigned short  closing_wait2; /* no longer used... */
+       unsigned short  closing_wait;   /* time to wait before closing */
+       unsigned short  closing_wait2;  /* no longer used... */
        unsigned char   *iomem_base;
        unsigned short  iomem_reg_shift;
        unsigned int    port_high;
@@ -66,8 +68,10 @@ int cttyhack_main(int argc, char **argv)
                dup2(fd, 1);
                dup2(fd, 2);
                while (fd > 2) close(fd--);
+               /* Some other session may have it as ctty. Steal it from them */
+               ioctl(0, TIOCSCTTY, 1)
        }
 
-       execvp(argv[0], argv);
+       BB_EXECVP(argv[0], argv);
        bb_perror_msg_and_die("cannot exec '%s'", argv[0]);
 }