[U-Boot] [PATCH] Handle environment variables like commands

The command interpreter checks always if an environment variable for that name exists and in this case the content of the variable is executed. It becomes possible to redefine all U-Boot commands. A new "builtin" command is added to be able to run builtin U-boot commands even if they are redefined. For this reason, builtin is a reserved word an no environment variable can be set with this name.
Signed-off-by : Stefano Babic sbabic@denx.de --- common/cmd_nvedit.c | 8 ++++++++ common/hush.c | 9 ++++++++- common/main.c | 37 +++++++++++++++++++++++++++++++++++++ tools/env/fw_env.c | 8 ++++++++ 4 files changed, 61 insertions(+), 1 deletions(-)
diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c index 637d6c9..c6e4ec7 100644 --- a/common/cmd_nvedit.c +++ b/common/cmd_nvedit.c @@ -161,6 +161,14 @@ int _do_setenv (int flag, int argc, char *argv[]) }
/* + * Check for reserved words + */ + if (strcmp(name, "builtin") == 0) { + printf ("## Error: builtin is a reserved word. You cannot set a variable with this name\n"); + return 1; + } + + /* * search if variable with this name already exists */ oldval = -1; diff --git a/common/hush.c b/common/hush.c index 093c428..bd7b223 100644 --- a/common/hush.c +++ b/common/hush.c @@ -1677,9 +1677,16 @@ static int run_pipe_real(struct pipe *pi) child->argv[i]); return -1; } - /* Look up command in command table */
+ /* Check if exists a variable with that name, builtin is forbidden */ + if ((strcmp (child->argv[i], "builtin") != 0) && (p = getenv (child->argv[i])) != NULL ) { + int rcode; + rcode = (parse_string_outer(p, + FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP) != 0); + return rcode; + }
+ /* Look up command in command table */ if ((cmdtp = find_cmd(child->argv[i])) == NULL) { printf ("Unknown command '%s' - try 'help'\n", child->argv[i]); return -1; /* give up after bad command */ diff --git a/common/main.c b/common/main.c index c06ea07..d2be2ac 100644 --- a/common/main.c +++ b/common/main.c @@ -1289,6 +1289,7 @@ int run_command (const char *cmd, int flag) int argc, inquotes; int repeatable = 1; int rc = 0; + char *runenv;
#ifdef DEBUG_PARSER printf ("[RUN_COMMAND] cmd[%p]="", cmd); @@ -1357,6 +1358,13 @@ int run_command (const char *cmd, int flag) continue; }
+ if ((strcmp (argv[0], "builtin") != 0) && ((runenv = getenv (argv[0])) != NULL )) { + if (run_command(runenv,flag) == -1) { + rc = -1; + } + continue; + } + /* Look up command in command table */ if ((cmdtp = find_cmd(argv[0])) == NULL) { printf ("Unknown command '%s' - try 'help'\n", argv[0]); @@ -1433,3 +1441,32 @@ int do_run (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) return 0; } #endif + +int do_builtin (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + int i; + + if (argc < 2) { + printf ("Usage:\n%s\n", cmdtp->usage); + return 1; + } + + /* Look up command in command table */ + if ((cmdtp = find_cmd(argv[1])) == NULL) { + printf ("Unknown builtin command '%s' - try 'help'\n", argv[1]); + return 1; + } + + /* Remove builtin from arg list */ + argc--; + + /* OK - call function to do the command */ + if ((cmdtp->cmd) (cmdtp, flag, argc, &argv[1]) != 0) { + return 1; + } +} + +U_BOOT_CMD( + builtin, CFG_MAXARGS, 1, do_builtin, + "builtin - run builtin commands\n", +); diff --git a/tools/env/fw_env.c b/tools/env/fw_env.c index 6e9c34f..cc31b79 100644 --- a/tools/env/fw_env.c +++ b/tools/env/fw_env.c @@ -353,6 +353,14 @@ int fw_setenv (int argc, char *argv[]) name = argv[1];
/* + * Check for reserved words + */ + if (strcmp(name, "builtin") == 0) { + errno = EINVAL; + return -1; + } + + /* * search if variable with this name already exists */ for (nxt = env = environment.data; *env; env = nxt + 1) {

Dear Stefano Babic,
In message 1224078992-16173-1-git-send-email-sbabic@denx.de you wrote:
The command interpreter checks always if an environment variable for that name exists and in this case the content of the variable is executed. It becomes possible to redefine all U-Boot commands. A new "builtin" command is added to be able to run builtin U-boot commands even if they are redefined. For this reason, builtin is a reserved word an no environment
----------------------------------------------^^ and
variable can be set with this name.
...
--- a/common/hush.c +++ b/common/hush.c @@ -1677,9 +1677,16 @@ static int run_pipe_real(struct pipe *pi) child->argv[i]); return -1; }
/* Look up command in command table */
/* Check if exists a variable with that name, builtin is forbidden */
if ((strcmp (child->argv[i], "builtin") != 0) && (p = getenv (child->argv[i])) != NULL ) {
int rcode;
rcode = (parse_string_outer(p,
FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP) != 0);
return rcode;
}
We have a problem here.
Assume a simple test case like this:
=> setenv bootm 'echo Run bootm command; builtin bootm'
The idea of the extension is that something like
=> bootm ${kernel} ${fdt} ${ramdisk}
will still work.
Unfortunately, it doesn't, as we have no way to pass the arguments to the (substituted) command to the commands run in the executed script.
One idea to solve this problem would be to introduce positional parameters, similar to how it's being done in the shell. In the script, we could then refer to the original command args using "$1", "$2", ... so the example above would look like that:
=> setenv bootm 'echo Run bootm command; builtin bootm $1 $2 $3"
I have to admit that I didn't spend much thought on this yet, so all feedback or sugggestions of alternative / better ideas is welcome.
Best regards,
Wolfgang Denk

Wolfgang Denk wrote:
The idea of the extension is that something like
=> bootm ${kernel} ${fdt} ${ramdisk}
will still work.
Unfortunately, it doesn't, as we have no way to pass the arguments to the (substituted) command to the commands run in the executed script.
yes, that is true ;(. Arguments are not passed to the underlying script.
One idea to solve this problem would be to introduce positional parameters, similar to how it's being done in the shell. In the script, we could then refer to the original command args using "$1", "$2", ... so the example above would look like that:
=> setenv bootm 'echo Run bootm command; builtin bootm $1 $2 $3"
Ok, I understand. We need the same behavior as in the real shell to perform this.
I have to admit that I didn't spend much thought on this yet, so all feedback or sugggestions of alternative / better ideas is welcome.
I admit I have no better ideas...with positional parameters we can start even some complex scripts. I take a look how to implement them, if there are no other suggestions.
Best regards, Stefano Babic
participants (3)
-
Stefano Babic
-
stefano babic
-
Wolfgang Denk