diff options
Diffstat (limited to 'src/sysvinit.c')
-rw-r--r-- | src/sysvinit.c | 296 |
1 files changed, 265 insertions, 31 deletions
diff --git a/src/sysvinit.c b/src/sysvinit.c index 940125f..23cad9a 100644 --- a/src/sysvinit.c +++ b/src/sysvinit.c @@ -16,13 +16,13 @@ #include "pies.h" #include "prog.h" +#include <termios.h> enum boot_state { sysinit, boot, - single0, - single1, + single, normal, max_boot_state }; @@ -30,12 +30,11 @@ enum boot_state static char *boot_state_name[] = { "sysinit", "boot", - "single0", - "single1", + "single", "normal" }; -static char boot_state_str[] = "#*sS "; +static char boot_state_str[] = "#*s "; static const char valid_runlevels[] = "0123456789S"; @@ -43,22 +42,154 @@ static int boot_trans_tab[max_boot_state][sizeof(valid_runlevels)-1] = { /* 0, 1, 2, 3, 4, */ /* 5, 6, 7, 8, 9, S */ /* sysinit */ { boot, boot, boot, boot, boot, - boot, boot, boot, boot, boot, single0 }, + boot, boot, boot, boot, boot, single }, /* boot */ { normal, normal, normal, normal, normal, - normal, normal, normal, normal, normal, single1 }, - /* single0 */ { normal, normal, normal, normal, normal, - normal, normal, normal, normal, normal, single0 }, - /* single1 */ { normal, normal, normal, normal, normal, - normal, normal, normal, normal, normal, single1 }, + normal, normal, normal, normal, normal, normal }, + /* single */ { boot, boot, boot, boot, boot, + boot, boot, boot, boot, boot, single }, /* normal */ { normal, normal, normal, normal, normal, - normal, normal, normal, normal, normal, single1 }, + normal, normal, normal, normal, normal, normal }, }; enum boot_state boot_state; int runlevel = 0; +int prevlevel = 'N'; int initdefault; /* Default runlevel */ int dfl_level; + +int +console_open (int mode) +{ + int i, fd; + + for (i = 0; i < 5; i++) + { + fd = open (console_device, mode | O_NONBLOCK); + if (fd >= 0) + { + fcntl (fd, F_SETFL, mode); + return fd; + } + } + return -1; +} + +void +console_stty () +{ + struct termios tty; + int fd; + + if ((fd = console_open (O_RDWR|O_NOCTTY)) < 0) + { + logmsg (LOG_CRIT, "can't open %s", console_device); + return; + } + + tcgetattr (fd, &tty); + + tty.c_cflag &= CBAUD|CBAUDEX|CSIZE|CSTOPB|PARENB|PARODD; + tty.c_cflag |= HUPCL|CLOCAL|CREAD; + + tty.c_cc[VINTR] = 3; /* ctrl('c') */ + tty.c_cc[VQUIT] = 28; /* ctrl('\\') */ + tty.c_cc[VERASE] = 127; + tty.c_cc[VKILL] = 24; /* ctrl('x') */ + tty.c_cc[VEOF] = 4; /* ctrl('d') */ + tty.c_cc[VTIME] = 0; + tty.c_cc[VMIN] = 1; + tty.c_cc[VSTART] = 17; /* ctrl('q') */ + tty.c_cc[VSTOP] = 19; /* ctrl('s') */ + tty.c_cc[VSUSP] = 26; /* ctrl('z') */ + + /* + * Set pre and post processing + */ + tty.c_iflag = IGNPAR|ICRNL|IXON|IXANY; + tty.c_oflag = OPOST|ONLCR; + tty.c_lflag = ISIG|ICANON|ECHO|ECHOCTL|ECHOPRT|ECHOKE; + + /* + * Now set the terminal line. + * We don't care about non-transmitted output data + * and non-read input data. + */ + tcsetattr (fd, TCSANOW, &tty); + tcflush(fd, TCIOFLUSH); + close (fd); +} + +int +askrunlevel () +{ + int fd; + char c, lvl = -1; + enum { srl_init, srl_char, srl_r, srl_n, srl_skip } srl = srl_init; + static const char prompt[] = "\nEnter runlevel: "; + + console_stty (); + fd = console_open (O_RDWR|O_NOCTTY); + if (fd == -1) + return 'S'; + + while (1) + { + if (srl == srl_init) + { + write (fd, prompt, sizeof (prompt) - 1); + if (read (fd, &lvl, 1) != 1) + return 'S'; + srl = srl_char; + } + else if (srl == srl_n) + { + if (is_valid_runlevel (lvl)) + break; + srl = srl_init; + } + else + { + if (read (fd, &c, 1) != 1) + { + if (!is_valid_runlevel (lvl)) + lvl = 'S'; + break; + } + + switch (srl) + { + case srl_char: + if (c == '\r') + srl = srl_r; + else if (c == '\n') + srl = srl_n; + else + srl = srl_skip; + break; + + case srl_r: + if (c == '\n') + srl = srl_n; + else + srl = srl_skip; + break; + + case srl_skip: + if (c == '\n') + srl = srl_init; + } + } + } + close (fd); + return toupper (lvl); +} + +static int +getinitdefault () +{ + return initdefault ? initdefault : askrunlevel (); +} static int runlevel_index (int n) @@ -75,10 +206,11 @@ runlevel_index (int n) } static int -enablecomp (struct component *comp, int finished, void *data) +enablecomp (struct prog *prog, void *data) { int *wait = data; int rc; + struct component *comp = prog->v.p.comp; switch (boot_state) { @@ -88,8 +220,7 @@ enablecomp (struct component *comp, int finished, void *data) case boot: return comp->mode == pies_comp_boot || comp->mode == pies_comp_bootwait; - case single0: - case single1: + case single: case normal: switch (comp->mode) { @@ -111,7 +242,7 @@ enablecomp (struct component *comp, int finished, void *data) rc = !!strchr (comp->runlevels, runlevel); if (!rc) return rc; - if (finished) + if (prog->v.p.status == status_finished) return -1; if (wait) { @@ -125,6 +256,31 @@ enablecomp (struct component *comp, int finished, void *data) return rc; } +static int +runlevel_setup_prog (struct prog *prog, void *data) +{ + if (is_sysvinit (prog->v.p.comp)) + { + int rc = enablecomp (prog, data); + if (rc < 0) + return 0; + if (rc) + prog->v.p.status = status_enabled; + else + prog->v.p.status = status_disabled; + debug (1, ("%s: %s", prog->tag, + prog->v.p.status == status_enabled ? + "enabled" : "disabled")); + } + return 0; +} + +static void +sysvinit_runlevel_setup (int *wait) +{ + progman_foreach (runlevel_setup_prog, wait); +} + static const char valid_runlevel_arg[] = "0123456789SsQqAaBbCcUu"; int @@ -133,6 +289,57 @@ is_valid_runlevel (int c) return !!strchr (valid_runlevel_arg, c); } +#define ENVAR_CONSOLE "CONSOLE=" +#define ENVTMPL_CONSOLE "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + +static char env_prevlevel[] = "PREVLEVEL=x"; +static char env_runlevel[] = "RUNLEVEL=x"; +static char env_console[] = ENVAR_CONSOLE ENVTMPL_CONSOLE; + +char *sysvinit_environ_hint[] = { + env_prevlevel, + env_runlevel, + env_console, + "INIT_VERSION=" PACKAGE_STRING, + "PATH=/bin:/usr/bin:/sbin:/usr/sbin", + NULL +}; + +#define ENVI_PREVLEVEL 0 +#define ENVI_RUNLEVEL 1 +#define ENVI_CONSOLE 2 +#define ENVI_VERSION 3 +#define ENVI_PATH 4 + +static void +envsetup () +{ + int i; + + for (i = 0; sysvinit_environ_hint[i]; i++) + { + char *str = sysvinit_environ_hint[i]; + switch (i) + { + case ENVI_PREVLEVEL: + str[strlen (str) - 1] = prevlevel; + break; + + case ENVI_RUNLEVEL: + str[strlen (str) - 1] = + boot_state_str[boot_state] == ' ' ? + (runlevel ? runlevel : '#') : boot_state_str[boot_state]; + break; + + case ENVI_CONSOLE: + if (strlen (console_device) >= sizeof (ENVTMPL_CONSOLE)) + logmsg (LOG_ERR, "console device name too long"); + else + strcpy (str + sizeof (ENVAR_CONSOLE) - 1, console_device); + } + } +} + static void create_fifo (void); static int @@ -265,10 +472,42 @@ create_fifo () register_socket (fd, sysvinit_fifo_handler, NULL); } +static char *try_console[] = { NULL, "/dev/console", "/dev/tty0" }; +char *console_device; + +static void +set_console_dev () +{ + int i; + for (i = 0; i < ARRAY_SIZE (try_console); i++) + { + if (try_console[i]) + { + int fd = open (try_console[i], O_RDONLY|O_NONBLOCK); + + if (fd >= 0) + { + close (fd); + console_device = try_console[i]; + return; + } + } + } + /* provide default */ + console_device = "/dev/null"; +} + void sysvinit_begin () { - progman_sysvinit_enable (enablecomp, NULL); + close (0); + close (1); + close (2); + set_console_dev (); + console_stty (); + setsid (); + envsetup (); + sysvinit_runlevel_setup (NULL); } int @@ -281,23 +520,17 @@ inittrans () static int wait = 0; if (progman_running_p ()) - { - debug (1, ("%s exiting: some components still running",__FUNCTION__)); - /* Noting to do if there are processes left in the previous runlevel */ - return 0; - } + /* Noting to do if there are processes left in the previous runlevel */ + return 0; if (runlevel == 0) - n = runlevel_index (dfl_level ? dfl_level : initdefault); + n = runlevel_index (dfl_level ? dfl_level : getinitdefault ()); else n = runlevel_index (runlevel); if (n == -1) n = runlevel_index ('S'); newstate = boot_trans_tab[boot_state][n]; - debug (1, ("STATE TRANS: %s(%c,%d) -> %s", boot_state_name[boot_state], - runlevel,n, - boot_state_name[newstate])); if (newstate != boot_state) { debug (1, ("STATE TRANS: %s -> %s", boot_state_name[boot_state], @@ -314,13 +547,12 @@ inittrans () case boot: sysvinit_acct (SYSV_ACCT_BOOT, "reboot", "~~", 0, "~"); break; - case single0: + case single: newlevel = 'S'; break; - case single1: case normal: /* boot -> normal */ - newlevel = dfl_level ? dfl_level : initdefault; + newlevel = dfl_level ? dfl_level : getinitdefault (); create_fifo (); } if (newlevel && newlevel != runlevel) @@ -329,6 +561,7 @@ inittrans () sysvinit_acct (SYSV_ACCT_RUNLEVEL, "runlevel", "~~", newlevel + 256 * runlevel, "~"); mf_proctitle_format ("init [%c]", newlevel); + prevlevel = runlevel ? runlevel : 'N'; runlevel = newlevel; trans = 1; wait = 0; @@ -337,13 +570,14 @@ inittrans () trans = 1; if (trans) { + envsetup (); if (wait == 0) { - progman_sysvinit_enable (enablecomp, &wait); + sysvinit_runlevel_setup (&wait); if (wait) return 1; } - progman_sysvinit_enable (enablecomp, NULL); + sysvinit_runlevel_setup (NULL); wait = 0; } return trans; |