hush: fix trashing of environment by local env vars:

a=a; a=b cmd; - a was unset!
  +57 bytes

function                                             old     new   delta
add_string_to_strings                                  -     110    +110
putenv_all                                             -      27     +27
run_list                                            2086    2111     +25
free_strings                                           -       7      +7
free_pipe                                            210     208      -2
add_malloced_string_to_strings                       110       -    -110
------------------------------------------------------------------------------
(add/remove: 3/1 grow/shrink: 1/1 up/down: 169/-112)           Total: 57 bytes
This commit is contained in:
Denis Vlasenko 2008-10-13 08:53:43 +00:00
parent 1e19afd38a
commit 22d10a0292
2 changed files with 106 additions and 62 deletions

View File

@ -614,7 +614,7 @@ static char *unbackslash(char *src)
return dst; return dst;
} }
static char **add_malloced_strings_to_strings(char **strings, char **add) static char **add_strings_to_strings(char **strings, char **add)
{ {
int i; int i;
unsigned count1; unsigned count1;
@ -643,14 +643,37 @@ static char **add_malloced_strings_to_strings(char **strings, char **add)
return v; return v;
} }
static char **add_malloced_string_to_strings(char **strings, char *add) static char **add_string_to_strings(char **strings, char *add)
{ {
char *v[2]; char *v[2];
v[0] = add; v[0] = add;
v[1] = NULL; v[1] = NULL;
return add_strings_to_strings(strings, v);
}
return add_malloced_strings_to_strings(strings, v); static void putenv_all(char **strings)
{
if (!strings)
return;
while (*strings)
putenv(*strings++);
}
static char **putenv_all_and_save_old(char **strings)
{
char **old = NULL;
char **s = strings;
if (!strings)
return old;
while (*strings) {
char *v = getenv(*strings++);
if (!v)
continue;
old = add_string_to_strings(old, v);
}
putenv_all(s);
return old;
} }
static void free_strings_and_unsetenv(char **strings, int unset) static void free_strings_and_unsetenv(char **strings, int unset)
@ -1432,7 +1455,7 @@ static void pseudo_exec_argv(char ***ptr_ptrs2free, char **argv, int assignment_
p = expand_string_to_string(*argv); p = expand_string_to_string(*argv);
putenv(p); putenv(p);
#if !BB_MMU #if !BB_MMU
*ptr_ptrs2free = add_malloced_string_to_strings(*ptr_ptrs2free, p); *ptr_ptrs2free = add_string_to_strings(*ptr_ptrs2free, p);
#endif #endif
argv++; argv++;
} }
@ -1442,8 +1465,8 @@ static void pseudo_exec_argv(char ***ptr_ptrs2free, char **argv, int assignment_
argv = expand_strvec_to_strvec(argv); argv = expand_strvec_to_strvec(argv);
#if !BB_MMU #if !BB_MMU
/* Inserting magic guard pointer to not unsetenv junk later */ /* Inserting magic guard pointer to not unsetenv junk later */
*ptr_ptrs2free = add_malloced_string_to_strings(*ptr_ptrs2free, (char*)hush_version_str); *ptr_ptrs2free = add_string_to_strings(*ptr_ptrs2free, (char*)hush_version_str);
*ptr_ptrs2free = add_malloced_string_to_strings(*ptr_ptrs2free, (char*)argv); *ptr_ptrs2free = add_string_to_strings(*ptr_ptrs2free, (char*)argv);
#endif #endif
} }
@ -1762,14 +1785,24 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe)
* Returns -1 only if started some children. IOW: we have to * Returns -1 only if started some children. IOW: we have to
* mask out retvals of builtins etc with 0xff! * mask out retvals of builtins etc with 0xff!
*/ */
/* A little helper first */
static char **expand_assignments(char **argv, int count)
{
int i;
char **p = NULL;
/* Expand assignments into one string each */
for (i = 0; i < count; i++) {
p = add_string_to_strings(p, expand_string_to_string(argv[i]));
}
return p;
}
static int run_pipe(struct pipe *pi) static int run_pipe(struct pipe *pi)
{ {
int i; int i;
int nextin; int nextin;
int pipefds[2]; /* pipefds[0] is for reading */ int pipefds[2]; /* pipefds[0] is for reading */
struct command *command; struct command *command;
char **ptrs2free = NULL; char **argv_expanded;
char **argv_expanded = NULL;
char **argv; char **argv;
const struct built_in_command *x; const struct built_in_command *x;
char *p; char *p;
@ -1791,6 +1824,7 @@ static int run_pipe(struct pipe *pi)
* pseudo_exec. "echo foo | read bar" doesn't work on bash, either. * pseudo_exec. "echo foo | read bar" doesn't work on bash, either.
*/ */
command = &(pi->cmds[0]); command = &(pi->cmds[0]);
if (single_and_fg && command->group && command->subshell == 0) { if (single_and_fg && command->group && command->subshell == 0) {
debug_printf("non-subshell grouping\n"); debug_printf("non-subshell grouping\n");
setup_redirects(command, squirrel); setup_redirects(command, squirrel);
@ -1803,8 +1837,12 @@ static int run_pipe(struct pipe *pi)
} }
argv = command->argv; argv = command->argv;
argv_expanded = NULL;
if (single_and_fg && argv != NULL) { if (single_and_fg && argv != NULL) {
char **new_env = NULL;
char **old_env = NULL;
i = command->assignment_cnt; i = command->assignment_cnt;
if (i != 0 && argv[i] == NULL) { if (i != 0 && argv[i] == NULL) {
/* assignments, but no command: set local environment */ /* assignments, but no command: set local environment */
@ -1816,52 +1854,49 @@ static int run_pipe(struct pipe *pi)
return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */ return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */
} }
/* Expand assignments into one string each */
for (i = 0; i < command->assignment_cnt; i++) {
p = expand_string_to_string(argv[i]);
putenv(p);
ptrs2free = add_malloced_string_to_strings(ptrs2free, p);
}
/* Expand the rest into (possibly) many strings each */ /* Expand the rest into (possibly) many strings each */
argv_expanded = expand_strvec_to_strvec(argv + i); argv_expanded = expand_strvec_to_strvec(argv + i);
for (x = bltins; x != &bltins[ARRAY_SIZE(bltins)]; x++) { for (x = bltins; x != &bltins[ARRAY_SIZE(bltins)]; x++) {
if (strcmp(argv_expanded[0], x->cmd) == 0) { if (strcmp(argv_expanded[0], x->cmd) != 0)
if (x->function == builtin_exec && argv_expanded[1] == NULL) { continue;
debug_printf("exec with redirects only\n"); if (x->function == builtin_exec && argv_expanded[1] == NULL) {
setup_redirects(command, NULL); debug_printf("exec with redirects only\n");
rcode = EXIT_SUCCESS; setup_redirects(command, NULL);
goto clean_up_and_ret1; rcode = EXIT_SUCCESS;
} goto clean_up_and_ret1;
debug_printf("builtin inline %s\n", argv_expanded[0]);
/* XXX setup_redirects acts on file descriptors, not FILEs.
* This is perfect for work that comes after exec().
* Is it really safe for inline use? Experimentally,
* things seem to work with glibc. */
setup_redirects(command, squirrel);
debug_printf_exec(": builtin '%s' '%s'...\n", x->cmd, argv_expanded[1]);
rcode = x->function(argv_expanded) & 0xff;
USE_FEATURE_SH_STANDALONE(clean_up_and_ret:)
restore_redirects(squirrel);
clean_up_and_ret1:
free_strings_and_unsetenv(ptrs2free, 1);
free(argv_expanded);
IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
debug_printf_exec("run_pipe return %d\n", rcode);
return rcode;
} }
debug_printf("builtin inline %s\n", argv_expanded[0]);
/* XXX setup_redirects acts on file descriptors, not FILEs.
* This is perfect for work that comes after exec().
* Is it really safe for inline use? Experimentally,
* things seem to work with glibc. */
setup_redirects(command, squirrel);
new_env = expand_assignments(argv, command->assignment_cnt);
old_env = putenv_all_and_save_old(new_env);
debug_printf_exec(": builtin '%s' '%s'...\n", x->cmd, argv_expanded[1]);
rcode = x->function(argv_expanded) & 0xff;
USE_FEATURE_SH_STANDALONE(clean_up_and_ret:)
restore_redirects(squirrel);
free_strings_and_unsetenv(new_env, 1);
putenv_all(old_env);
free_strings(old_env);
clean_up_and_ret1:
free(argv_expanded);
IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
debug_printf_exec("run_pipe return %d\n", rcode);
return rcode;
} }
#if ENABLE_FEATURE_SH_STANDALONE #if ENABLE_FEATURE_SH_STANDALONE
{ i = find_applet_by_name(argv_expanded[0]);
int a = find_applet_by_name(argv_expanded[0]); if (i >= 0 && APPLET_IS_NOFORK(i)) {
if (a >= 0 && APPLET_IS_NOFORK(a)) { setup_redirects(command, squirrel);
setup_redirects(command, squirrel); save_nofork_data(&G.nofork_save);
save_nofork_data(&G.nofork_save); new_env = expand_assignments(argv, command->assignment_cnt);
debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", argv_expanded[0], argv_expanded[1]); old_env = putenv_all_and_save_old(new_env);
rcode = run_nofork_applet_prime(&G.nofork_save, a, argv_expanded); debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", argv_expanded[0], argv_expanded[1]);
goto clean_up_and_ret; rcode = run_nofork_applet_prime(&G.nofork_save, i, argv_expanded);
} goto clean_up_and_ret;
} }
#endif #endif
} }
@ -1883,7 +1918,7 @@ static int run_pipe(struct pipe *pi)
/* Avoid confusion WHAT is volatile. Pointer is volatile, /* Avoid confusion WHAT is volatile. Pointer is volatile,
* not the stuff it points to. */ * not the stuff it points to. */
typedef char **ppchar_t; typedef char **ppchar_t;
volatile ppchar_t shared_across_vfork; volatile ppchar_t shared_across_vfork = NULL;
#endif #endif
command = &(pi->cmds[i]); command = &(pi->cmds[i]);
@ -1898,9 +1933,6 @@ static int run_pipe(struct pipe *pi)
if ((i + 1) < pi->num_cmds) if ((i + 1) < pi->num_cmds)
xpipe(pipefds); xpipe(pipefds);
#if !BB_MMU
shared_across_vfork = ptrs2free;
#endif
command->pid = BB_MMU ? fork() : vfork(); command->pid = BB_MMU ? fork() : vfork();
if (!command->pid) { /* child */ if (!command->pid) { /* child */
if (ENABLE_HUSH_JOB) if (ENABLE_HUSH_JOB)
@ -1941,12 +1973,11 @@ for single_and_fg, it's already set yes? */
} }
/* parent */ /* parent */
#if !BB_MMU #if !BB_MMU
ptrs2free = shared_across_vfork; //BUG: does not restore OLD env var contents
free_strings_and_unsetenv((char **)shared_across_vfork, 1);
#endif #endif
free(argv_expanded); free(argv_expanded);
argv_expanded = NULL; argv_expanded = NULL;
free_strings_and_unsetenv(ptrs2free, 1);
ptrs2free = NULL;
if (command->pid < 0) { /* [v]fork failed */ if (command->pid < 0) { /* [v]fork failed */
/* Clearly indicate, was it fork or vfork */ /* Clearly indicate, was it fork or vfork */
bb_perror_msg(BB_MMU ? "fork" : "vfork"); bb_perror_msg(BB_MMU ? "fork" : "vfork");
@ -3145,7 +3176,7 @@ static int done_word(o_string *word, struct parse_context *ctx)
o_addchr(word, SPECIAL_VAR_SYMBOL); o_addchr(word, SPECIAL_VAR_SYMBOL);
} }
} }
command->argv = add_malloced_string_to_strings(command->argv, xstrdup(word->data)); command->argv = add_string_to_strings(command->argv, xstrdup(word->data));
debug_print_strings("word appended to argv", command->argv); debug_print_strings("word appended to argv", command->argv);
} }
@ -3436,6 +3467,11 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
endch = ")"; endch = ")";
command->subshell = 1; command->subshell = 1;
} }
#if 0 /* TODO function support */
if (ch == 'F') { /* function definition */
command->subshell = 2;
}
#endif
rcode = parse_stream(dest, &sub, input, endch); rcode = parse_stream(dest, &sub, input, endch);
if (rcode == 0) { if (rcode == 0) {
done_word(dest, &sub); /* finish off the final word in the subcontext */ done_word(dest, &sub); /* finish off the final word in the subcontext */
@ -3929,10 +3965,10 @@ static int parse_stream(o_string *dest, struct parse_context *ctx,
case '(': case '(':
#if ENABLE_HUSH_CASE #if ENABLE_HUSH_CASE
/* "case... in [(]word)..." - skip '(' */ /* "case... in [(]word)..." - skip '(' */
if (dest->length == 0 /* not word(... */ if (ctx->ctx_res_w == RES_MATCH
&& dest->nonnull == 0 /* not ""(... */
&& ctx->ctx_res_w == RES_MATCH
&& ctx->command->argv == NULL /* not (word|(... */ && ctx->command->argv == NULL /* not (word|(... */
&& dest->length == 0 /* not word(... */
&& dest->nonnull == 0 /* not ""(... */
) { ) {
continue; continue;
} }
@ -3942,10 +3978,18 @@ static int parse_stream(o_string *dest, struct parse_context *ctx,
&& dest->nonnull == 0 /* not a"b"c() */ && dest->nonnull == 0 /* not a"b"c() */
&& ctx->command->argv == NULL /* it's the first word */ && ctx->command->argv == NULL /* it's the first word */
&& i_peek(input) == ')' && i_peek(input) == ')'
&& !match_reserved_word(dest)
) { ) {
bb_error_msg("seems like a function definition"); bb_error_msg("seems like a function definition");
if (match_reserved_word(dest)) i_getch(input);
bb_error_msg("but '%s' is a reserved word!", dest->data); do {
ch = i_getch(input);
} while (ch == ' ' || ch == '\n');
if (ch != '{') {
syntax("was expecting {");
debug_printf_parse("parse_stream return 1\n");
return 1;
}
} }
#endif #endif
case '{': case '{':

View File

@ -165,7 +165,7 @@ config FINDFS
default n default n
select VOLUMEID select VOLUMEID
help help
Prints the name of a filesystem with given laver or UUID. Prints the name of a filesystem with given label or UUID.
WARNING: WARNING:
With all submodules selected, it will add ~8k to busybox. With all submodules selected, it will add ~8k to busybox.