aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilly Tarreau <w@1wt.eu>2007-09-24 23:34:00 +0200
committerWilly Tarreau <w@1wt.eu>2007-09-24 23:34:00 +0200
commitd9f873a18453558d0d4027a8ec8e12b6e2f322c1 (patch)
treec03a0cb61cab55ddfd3e3030c88cb48e23e1377d
parentinit: set controlling TTY when executing programs (diff)
downloadflxutils-d9f873a18453558d0d4027a8ec8e12b6e2f322c1.tar.xz
init: provide support for environment variables in the scripts
It is now possible to use environment variables within scripts. Two forms are supported : ${variable} is replaced with the variables's value or an empty string if the variable is not set ${variable-default} is replaced with the variable's value, or the "default" string if the variable is not set. The variables are not evaluated within quotes, but quotes can be temporarily closed to reach the variable. For instance : ec "my TERM is ${TERM}" will output "my TERM is ${TERM}" ec "my TERM is "${TERM} will output "my TERM is linux"
Diffstat (limited to '')
-rwxr-xr-xinit/initbin7388 -> 7868 bytes
-rw-r--r--init/init.c146
2 files changed, 137 insertions, 9 deletions
diff --git a/init/init b/init/init
index 2f6aa3a..156614e 100755
--- a/init/init
+++ b/init/init
Binary files differ
diff --git a/init/init.c b/init/init.c
index 58f57d8..e8a0662 100644
--- a/init/init.c
+++ b/init/init.c
@@ -93,10 +93,19 @@ lo l </dev/loopX> <file>
The commands may be prefixed with a '|' or '&', in which case, they will be
executed only if the previous command failed (|) or succeeded (&).
+ Each command argument may reference an environment variable and an optional default
+ value. Syntax is similar to the Bourne shell with braces : ${var} and ${var-default}.
+ The default value will be used if the variable is not set, not if it's empty. An unset
+ variable with no default value will return an empty string.
+
+ The variables are not evaluated within quotes, but quotes can stop before the '$' sign
+ and start again after the brace.
+
Example :
ln hda3 /dev/disk => symlinks /dev/disk to hda3
md /var/tmp 1777 => creates a directory /var/tmp with mode 1777
mt /dev/hda1 /boot ext2 => attempts to mount /dev/hda1 read-only under /boot.
+ mt /dev/${flash-boot} /boot ${fs-ext2} => attempts to mount /dev/$flash read-only under /boot.
|mt /dev/hda1(3:1) /boot ext2 => only if the previous command failed, creates /dev/hda1 with major 3, minor 1 and mounts it under /boot
in /sbin/init-std => the following init will be this /sbin/init-std (31 chars max)
ex /sbin/initramdisk /dev/ram6 1200 => executes /sbin/initramdisk with these args and waits for its completion
@@ -321,6 +330,15 @@ enum {
/* counts from TOK_LN to TOK_DOT */
#define NB_TOKENS 25
+/* possible states for variable parsing */
+enum {
+ VAR_NONE = 0,
+ VAR_DOLLAR,
+ VAR_OBRACE,
+ VAR_DEFAULT,
+ VAR_CBRACE,
+};
+
/* this contains all two-chars command, 1-char commands, followed by a token
* number.
*/
@@ -520,7 +538,9 @@ char *find_arg(char *arg) {
/*
* looks for the last assignment of variable 'var=' in 'envp'.
- * the value found is returned (or NULL if not found).
+ * the value found is returned (or NULL if not found). Note
+ * that if the variable name does not end with an '=', then it
+ * will be implicitly checked.
* if 'remove' is not zero, the assignment is removed afterwards.
* eg: init=my_getenv(envp, "init=", 1);
*/
@@ -530,9 +550,13 @@ char *my_getenv(char **envp, char *var, const int remove) {
last = NULL;
namelen = strlen(var);
+ if (!namelen || !envp)
+ return NULL;
+
while (*envp != NULL) {
//printf("comparing <%s> against <%s> for %d chars\n", *envp, var, namelen);
- if (!strncmp(*envp, var, namelen))
+ if (strncmp(*envp, var, namelen) == 0 &&
+ ((var[namelen-1] == '=') || envp[0][namelen] == '='))
last = envp;
envp++;
}
@@ -542,6 +566,8 @@ char *my_getenv(char **envp, char *var, const int remove) {
return NULL;
} else {
char *ret = (*last) + namelen;
+ if (var[namelen-1] != '=')
+ ret++;
if (remove)
while (*last != NULL) {
//printf("moving <%s> over <%s>\n", *(last+1),*last);
@@ -576,8 +602,11 @@ static inline int read_cfg(char *cfg_file) {
* returned as the result of this function, or TOK_UNK if none matches, or
* TOK_EOF if nothing left. All args are copied into <cfg_args> as an array
* of pointers. Maximum line length is 256 chars and maximum args number is 15.
+ * <bufend> must point to the first non-addressable byte of the buffer so that
+ * the function can insert data if required due to variables resolving. If
+ * <envp> is not NULL, variables may be looked up there.
*/
-static int parse_cfg(char **cfg_data) {
+static int parse_cfg(char **cfg_data, char *bufend, char **envp) {
int nbargs;
int token;
int cond;
@@ -639,7 +668,9 @@ static int parse_cfg(char **cfg_data) {
cfg_args[nbargs++] = p;
for (; *p && (nbargs < MAX_CFG_ARGS - 1); nbargs++) {
- int backslash = 0, quote = 0;
+ int backslash = 0, quote = 0, var_state = VAR_NONE;
+ char *dollar_ptr, *name_beg, *name_end;
+ char *brace_end, *def_beg, *def_end, *repl;
cfg_args[nbargs] = p;
@@ -655,14 +686,111 @@ static int parse_cfg(char **cfg_data) {
memmove(p - 1, p, cfg_line - p);
} else {
backslash = (*p == '\\');
+
if (*p == '"') {
memmove(p, p + 1, cfg_line - p - 1);
quote = !quote;
+ continue;
+ }
+
+ if (quote) {
+ p++;
+ continue;
+ }
+
+ /* variables are not evaluated within quotes for now */
+ switch (var_state) {
+ case VAR_NONE:
+ if (*p != '$')
+ break;
+ dollar_ptr = p;
+ var_state = VAR_DOLLAR;
+ name_beg = name_end = def_beg = def_end = brace_end = NULL;
+ break;
+ case VAR_DOLLAR:
+ if (*p != '{') {
+ var_state = VAR_NONE;
+ dollar_ptr = NULL;
+ }
+ name_beg = p + 1;
+ var_state = VAR_OBRACE;
+ break;
+ case VAR_OBRACE:
+ if (*p == '-') {
+ var_state = VAR_DEFAULT;
+ name_end = p;
+ def_beg = p + 1;
+ }
+ else if (*p == '}') {
+ var_state = VAR_CBRACE;
+ name_end = p;
+ brace_end = p + 1;
+ }
+ break;
+ case VAR_DEFAULT:
+ if (*p == '}') {
+ var_state = VAR_CBRACE;
+ def_end = p;
+ brace_end = p + 1;
+ }
+ break;
+ }
+
+ if (var_state == VAR_CBRACE) {
+ /* here we have to do everything */
+ var_state = VAR_NONE;
+
+ *name_end = 0;
+ repl = my_getenv(envp, name_beg, 0); // supports envp==NULL and name==NULL
+
+ if (!repl) {
+ /* easy part: variable not found, we use the
+ * default value. It will always be shorter
+ * than the expression because it's contained
+ * in it.
+ */
+ int ofs;
+
+ if (def_end == def_beg) {
+ memmove(dollar_ptr, brace_end, bufend - brace_end);
+ ofs = brace_end - dollar_ptr;
+ p -= ofs;
+ cfg_line -= ofs;
+ *cfg_data = cfg_line;
+ } else {
+ memmove(dollar_ptr, def_beg, def_end - def_beg);
+ memmove(dollar_ptr + (def_end - def_beg), brace_end, bufend - brace_end);
+ ofs = (def_beg - dollar_ptr) + (brace_end - def_end);
+ p -= ofs;
+ cfg_line -= ofs;
+ *cfg_data = cfg_line;
+ }
+ } else {
+ /* complicated part, we have to move the tail
+ * left or right, the right size for what we
+ * have to replace.
+ */
+ int l = strlen(repl);
+ int ofs = l - (brace_end - dollar_ptr); /* <0: reduce, >0: increase */
+
+ if (ofs + brace_end > bufend) { // too large, truncate the value.
+ l -= (ofs + brace_end) - bufend;
+ ofs = bufend - brace_end;
+ }
+
+ memmove(dollar_ptr + l, brace_end, (bufend - brace_end) - ((ofs > 0)?ofs:0));
+ memcpy(dollar_ptr, repl, l);
+
+ p += ofs;
+ cfg_line += ofs;
+ *cfg_data = cfg_line;
+ //printf(" OK: -->%s<--, l=%d, ofs=%d\n", dollar_ptr, l, ofs);
+ }
}
- else
- p++;
+
+ p++;
}
- } while (*p && (backslash || quote || (*p != ' ' && *p != '\t')));
+ } while (*p && (backslash || quote || var_state || (*p != ' ' && *p != '\t')));
if (*p) {
*p = 0;
do p++; while (*p == ' ' || *p == '\t');
@@ -1243,7 +1371,7 @@ int main(int argc, char **argv, char **envp) {
len = read(0, cmd_line, sizeof(cmd_line)-1);
if (len > 0) {
cmd_line[len] = 0;
- token = parse_cfg(&cmd_ptr);
+ token = parse_cfg(&cmd_ptr, cmd_line + sizeof(cmd_line), envp);
if (token == TOK_EOF) /* empty line */
continue;
} else {
@@ -1261,7 +1389,7 @@ int main(int argc, char **argv, char **envp) {
} /* end of kbd */
if (cmd_input == INPUT_FILE) {
- token = parse_cfg(&cfg_line);
+ token = parse_cfg(&cfg_line, cfg_data + sizeof(cfg_data), envp);
if (token == TOK_EOF || token == TOK_DOT)
break;
}