aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilly Tarreau <w@1wt.eu>2006-07-16 14:53:21 +0200
committerWilly Tarreau <willy@wtap.(none)>2006-07-26 11:50:55 +0200
commitf02c30a017a7902f07d0c639bd9a16c146353c50 (patch)
tree532da549c61f3826555f28e75d2d73b4e1b5aaae
parent[RELEASE] flxutils-0.1.11.1 (diff)
downloadflxutils-f02c30a017a7902f07d0c639bd9a16c146353c50.tar.gz
[RELEASE] flxutils-0.1.11.2v0.1.11.2
-rw-r--r--init/README.21
-rw-r--r--init/init.c193
2 files changed, 166 insertions, 28 deletions
diff --git a/init/README.2 b/init/README.2
index f306601..03ce196 100644
--- a/init/README.2
+++ b/init/README.2
@@ -15,6 +15,7 @@ Résumé rapide des commandes les plus courantes :
- mt : mount ; um : umount
- mv : mount --move ; bi : mount --bind
- ex : exécute dans un sous-process (system())
+ - /path/prog args : idem.
- rx xxx yyy : execute yyy dans chroot xxx;
- md : mkdir ; ln : lien
- bl,ch,fi : devices block, char, fifo
diff --git a/init/init.c b/init/init.c
index 9204f62..b73234e 100644
--- a/init/init.c
+++ b/init/init.c
@@ -106,6 +106,44 @@ lo l </dev/loopX> <file>
For executable reduction reasons, the .preinit file is limited to 4 kB.
+ The choice of the init program is quite complex, because we want to avoid
+ stupid loops and buggy behaviours while conservating quite a parametrable
+ init. Basically, we'll use environment variables that we will destroy once
+ read, to avoid getting them again if we loop on ourselves (imagine an
+ 'exec /sbin/init" from a shell cmd line, which will blindly re-exec a shell).
+ So there are two distinct cases (init and linuxrc) :
+
+ 1) linuxrc
+ We want to be able to either return or transfer execution to the real init.
+
+ - if we find "init2=xxxx" in the env, we memorize it and move it away from the env.
+ - if "in xxxx" has been specified on a keyboard input from an "rd"
+ statement, we unconditionnaly do an execve("xxx", "xxx", ##no args##, ##envp##).
+ - if the env contained "init2=xxxx", we do an execve("xxxx", "xxxx", ##no args##, ##envp##).
+ we don't pass the args because it is a rescue init, such as a shell, and
+ we don't want it to fail on "bash: auto: command not found" or similar.
+ - if the conf contained "in xxxx", we do an execve("xxxx", "xxxx", argv[], ##envp##)
+ because we want it to know about the original args. Eg: init=/linuxrc single
+ - if the conf didn't contain "in xxxx", we unmount all what we can and
+ return. The kernel will be able to switch over to the next init stage.
+
+ 2) init, or anything else (telinit, ...)
+ We want to transfer execution to the real init.
+
+ - if we find "INIT=xxxx" in the env, we memorize it and move it away from the env.
+ - if "in xxxx" has been specified on a keyboard input from an "rd"
+ statement, we unconditionnaly do an execve("xxx", "xxx", ##no args##, ##envp##).
+ - if the env contained "INIT=xxxx", we do an execve("xxxx", "xxxx", ##no args##, ##envp##).
+ we don't pass the args because it is a rescue init, such as a shell, and
+ we don't want it to fail on "bash: auto: command not found" or similar.
+ - if the conf contained "in xxxx", we do an execve("xxxx", "xxxx", argv[], ##envp##)
+ because we want it to know about the original args. Eg: init=/.preinit single
+ - if the conf didn't contain "in xxxx", we transfer execution to "/sbin/init-sysv".
+
+ Note: basically, each time an environment variable is read, it must be killed afterwards.
+ Eg: init2=, INIT=, ...
+
+
The root directory should contain the following dirs :
- /var (directory) -> can be a ramfs or a real dir on another device
- /var/tmp (directory) -> idem
@@ -201,12 +239,12 @@ static const char __attribute__ ((__section__(STR_SECT),__aligned__(1))) str_reb
static const char __attribute__ ((__section__(STR_SECT),__aligned__(1))) var_tmp[] = "/var/tmp";
static const char __attribute__ ((__section__(STR_SECT),__aligned__(1))) var_run[] = "/var/run";
static const char __attribute__ ((__section__(STR_SECT),__aligned__(1))) proc_self_fd[] = "/proc/self/fd";
-static const char __attribute__ ((__section__(STR_SECT),__aligned__(1))) proc_cmdline[] = "/proc/cmdline";
-static const char __attribute__ ((__section__(STR_SECT),__aligned__(1))) sbin_init[] = "sbin/init";
+//static const char __attribute__ ((__section__(STR_SECT),__aligned__(1))) proc_cmdline[] = "/proc/cmdline";
+//static const char __attribute__ ((__section__(STR_SECT),__aligned__(1))) sbin_init[] = "sbin/init";
static const char __attribute__ ((__section__(STR_SECT),__aligned__(1))) sbin_init_sysv[] = "sbin/init-sysv";
static const char __attribute__ ((__section__(STR_SECT),__aligned__(1))) cfg_linuxrc[] = "/.linuxrc";
-static const char __attribute__ ((__section__(STR_SECT),__aligned__(1))) str__linuxrc[] = "/linuxrc";
static const char __attribute__ ((__section__(STR_SECT),__aligned__(1))) dev_options[] = "size=0,nr_inodes=4096,mode=755";
+static const char __attribute__ ((__section__(STR_SECT),__aligned__(1))) str__linuxrc[] = "/linuxrc";
static const char __attribute__ ((__section__(STR_SECT),__aligned__(1))) proc_dir[] = "/proc";
static const char __attribute__ ((__section__(STR_SECT),__aligned__(1))) tmpfs_fs[] = "tmpfs";
static const char __attribute__ ((__section__(STR_SECT),__aligned__(1))) dev_root[] = "dev/root";
@@ -225,7 +263,7 @@ static const char __attribute__ ((__section__(STR_SECT),__aligned__(1))) dev_roo
#define MAX_DEVNAME_LEN 64
#define MAX_CFG_SIZE 4096
#define MAX_CFG_ARGS 16
-#define MAX_CMDLINE_LEN 512
+//#define MAX_CMDLINE_LEN 512
#define MAX_BRACE_LEVEL 10
struct dev_varstr {
@@ -340,7 +378,7 @@ static const __attribute__ ((__section__(STR_SECT),__aligned__(1))) struct {
static char cfg_data[MAX_CFG_SIZE];
static char *cfg_args[MAX_CFG_ARGS];
static char *cfg_line;
-static char cmdline[MAX_CMDLINE_LEN];
+//static char cmdline[MAX_CMDLINE_LEN];
static char *cst_str[MAX_FIELDS];
static char *var_str[MAX_FIELDS];
static struct dev_varstr var[MAX_FIELDS];
@@ -411,6 +449,7 @@ static void reopen_console() {
print("init/info : reopened /dev/console\n");
}
+#if 0
/* reads the kernel command line </proc/cmdline> into memory and searches
* for the first assignment of the required variable. Return its value
* (which may be empty) or NULL if not found.
@@ -469,6 +508,42 @@ char *find_arg(char *arg) {
else
return NULL; /* not found */
}
+#endif
+
+/*
+ * looks for the last assignment of variable 'var=' in 'envp'.
+ * the value found is returned (or NULL if not found).
+ * if 'remove' is not zero, the assignment is removed afterwards.
+ * eg: init=my_getenv(envp, "init=", 1);
+ */
+char *my_getenv(char **envp, char *var, const int remove) {
+ int namelen;
+ char **last;
+
+ last = NULL;
+ namelen = strlen(var);
+ while (*envp != NULL) {
+ //printf("comparing <%s> against <%s> for %d chars\n", *envp, var, namelen);
+ if (!strncmp(*envp, var, namelen))
+ last = envp;
+ envp++;
+ }
+ //printf("found <%s>\n", (last?*last:"null"));
+
+ if (last == NULL) {
+ return NULL;
+ } else {
+ char *ret = (*last) + namelen;
+ if (remove)
+ while (*last != NULL) {
+ //printf("moving <%s> over <%s>\n", *(last+1),*last);
+ *last = *(last+1);
+ last++;
+ }
+ //printf("returning <%s>\n", ret);
+ return ret;
+ }
+}
/* reads the configuration file <cfg_file> into memory.
* returns 0 if OK, -1 if error.
@@ -544,10 +619,22 @@ static int parse_cfg(char **cfg_data) {
/* fills the cfg_args[] array with the command itself, followed by all
* args. cfg_args[last+1]=NULL.
+ *
+ * We have a particular case here :
+ * if the line begins with '/', then we return TOK_EX so that it executes
+ * the command just as it would have done with 'ex' prefixed to the line.
+ * For this, we insert a fake cfg_args[0], also pointing to the '/', which
+ * will be matched later.
*/
- for (nbargs = 0; *p && (nbargs < MAX_CFG_ARGS - 1); nbargs++) {
+ nbargs = 0;
+ if (*p == '/')
+ cfg_args[nbargs++] = p;
+
+ for (; *p && (nbargs < MAX_CFG_ARGS - 1); nbargs++) {
int backslash = 0, quote = 0;
+
cfg_args[nbargs] = p;
+
do {
if (backslash) {
backslash = 0;
@@ -575,8 +662,15 @@ static int parse_cfg(char **cfg_data) {
}
/* search a matching token for the command : it can either be a single
- * char (old language) or a double char (new language)
+ * char (old language) or a double char (new language).
+ *
+ * We have a particular case :
+ * if the line begins with '/', then we return TOK_EX so that it executes
+ * the command just as it would have done with 'ex' prefixed to the line.
*/
+ if (**cfg_args == '/')
+ return TOK_EX | cond;
+
for (token = 0; token < NB_TOKENS; token++)
if ((!cfg_args[0][1] && tokens[token].scmd == cfg_args[0][0]) ||
(cfg_args[0][1] &&
@@ -936,7 +1030,7 @@ int main(int argc, char **argv, char **envp) {
int cmd_input = INPUT_FILE;
static char cmd_line[256]; /* one line of config from the prompt */
struct stat statf;
- char *cmdline_arg;
+ // char *cmdline_arg;
char *cfg_file;
int brace_level, kbd_level, run_level;
struct {
@@ -944,6 +1038,8 @@ int main(int argc, char **argv, char **envp) {
int cond;
} context[MAX_BRACE_LEVEL];
+ char *conf_init, *force_init;
+
/* first, we'll check if we have been called as 'linuxrc', used only in
initrd. In this particular case, we ignore all arguments and use
/.linuxrc as a configuration file.
@@ -986,7 +1082,10 @@ int main(int argc, char **argv, char **envp) {
* from userspace, the config file is not read again so only the hardcoded
* name will be used for the executable name.
*/
- *argv = (char *)&sbin_init_sysv; /*"sbin/init-sysv"*/;
+ //*argv = (char *)&sbin_init_sysv; /*"sbin/init-sysv"*/;
+ conf_init = (char *)&sbin_init_sysv; /*"sbin/init-sysv"*/;
+ force_init = my_getenv(envp, CONST_STR("INIT="), 1);
+ //printf("force_init=<%s>, INIT_new=%p\n", my_getenv(envp, "INIT=", 0));
/* if "rebuild" is passed as the only argument, then we'll try to rebuild a
* full /dev even if not pid==1, but only if it was not already populated
*/
@@ -994,7 +1093,10 @@ int main(int argc, char **argv, char **envp) {
pid1 = (getpid() == 1);
}
else {
- *argv = (char *)&sbin_init; /* "sbin/init" */
+ conf_init = NULL;
+ force_init = my_getenv(envp, CONST_STR("init2="), 1);
+ //*argv = (char *)&sbin_init; /* "sbin/init" */
+ //printf("force_init=<%s>, init2_new=%s\n", my_getenv(envp, "init2=", 0));
}
if (pid1 || linuxrc) {
@@ -1213,17 +1315,17 @@ int main(int argc, char **argv, char **envp) {
* So we flush them all.
*/
print("<I>nit : used config name for init\n");
- argv[0] = cfg_args[1];
- argv[1] = NULL;
/* in keyboard mode, specifying init stops any further parsing,
* so that the rest of the config file can be skipped.
*/
- if (cmd_input == INPUT_KBD)
+ if (cmd_input == INPUT_KBD) {
+ force_init = cfg_args[1];
break;
- else {
+ } else {
/* non sense to switch error status when assigning init */
error = context[brace_level].error;
+ conf_init = cfg_args[1];
continue;
}
//} else if (token == TOK_EC) {
@@ -1345,10 +1447,12 @@ int main(int argc, char **argv, char **envp) {
* in /proc/cmdline
*/
mntdev = cfg_args[1];
- if ((*mntdev != '/') && ((cmdline_arg = find_arg(mntdev)) != NULL)) {
- mntdev = cmdline_arg;
- print("<M>ount : using command line device\n");
- }
+ // the following code handles "var=/dev/sda2" or "home=/dev/hda1(3:1)" on
+ // the kernel command line.
+ //if ((*mntdev != '/') && ((cmdline_arg = find_arg(mntdev)) != NULL)) {
+ // mntdev = cmdline_arg;
+ // print("<M>ount : using command line device\n");
+ //}
maj = mntdev; /* handles /dev/xxx(maj:min) */
while (*maj && *maj != '(')
@@ -1577,6 +1681,31 @@ int main(int argc, char **argv, char **envp) {
* we don't close 0/1/2 nor dismount /dev, else we wouldn't be able to do
* anything.
*/
+
+ if (force_init != NULL) {
+ /* we keep the cmdline args if we take it from the kernel cmdline,
+ * but we flush any args if it comes from the keyboard
+ */
+ argv[0] = force_init;
+ if (cmd_input == INPUT_KBD)
+ argv[1] = NULL;
+ } else {
+ /* standard conf or new linuxrc : !NULL = chained init */
+ /* old linuxrc : NULL = no chained init */
+ argv[0] = conf_init;
+ }
+
+ if (linuxrc && force_init == NULL) {
+ /* normal linuxrc : we close and unmount all what we have done, but not
+ * in case of forced init, because if we force, it should mean that we
+ * want a prompt or something special.
+ */
+ close(2); close(1); close(0);
+ umount2(dev_name, MNT_DETACH);
+ }
+
+#if 0
+
if (cmd_input != INPUT_KBD) {
if (linuxrc) {
close(2); close(1); close(0);
@@ -1586,10 +1715,12 @@ int main(int argc, char **argv, char **envp) {
sleep(10);
#endif
/* handle the lilo command line "init2=prog" */
- cmdline_arg = find_arg(CONST_STR("init2"));
+ //cmdline_arg = find_arg(CONST_STR("init2"));
+ //cmdline_arg = my_getenv(envp, CONST_STR("init2="), 1);
} else {
/* handle the lilo command line "INIT=prog" */
- cmdline_arg = find_arg(CONST_STR("INIT"));
+ //cmdline_arg = find_arg(CONST_STR("INIT"));
+ cmdline_arg = my_getenv(envp, CONST_STR("INIT="), 1);
}
if (cmdline_arg != NULL) {
@@ -1597,17 +1728,23 @@ int main(int argc, char **argv, char **envp) {
argv[1] = NULL;
}
}
+
print("init/debug: *argv = "); print (*argv); print("\n");
+#endif
+
umask(old_umask);
#ifdef SLOW_DEBUG
sleep(10);
#endif
- err = execve(*argv, argv, envp);
- print("init/error : last execve() failed\n");
-
- /* we'll get a panic there, so let some time for the user to read messages */
- if (pid1)
- sleep(60);
-
- return err;
+ /* the old linuxrc behaviour doesn't exec on exit. */
+ if (*argv != NULL) {
+ err = execve(*argv, argv, envp);
+ print("init/error : last execve() failed\n");
+
+ /* we'll get a panic there, so let some time for the user to read messages */
+ if (pid1)
+ sleep(60);
+ return err;
+ }
+ return 0;
}