ash: fix nofork bug where environment is not properly passed to a command

function                                             old     new   delta
listvars                                             144     252    +108
evalcommand                                         1500    1546     +46
showvars                                             142     147      +5
shellexec                                            242     245      +3
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 4/0 up/down: 162/0)             Total: 162 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2017-11-03 14:16:25 +01:00
parent f5e8b42788
commit a5060b8364
5 changed files with 88 additions and 5 deletions

View File

@ -2380,8 +2380,11 @@ listsetvar(struct strlist *list_set_var, int flags)
/*
* Generate a list of variables satisfying the given conditions.
*/
#if !ENABLE_FEATURE_SH_NOFORK
# define listvars(on, off, lp, end) listvars(on, off, end)
#endif
static char **
listvars(int on, int off, char ***end)
listvars(int on, int off, struct strlist *lp, char ***end)
{
struct var **vpp;
struct var *vp;
@ -2394,12 +2397,40 @@ listvars(int on, int off, char ***end)
do {
for (vp = *vpp; vp; vp = vp->next) {
if ((vp->flags & mask) == on) {
#if ENABLE_FEATURE_SH_NOFORK
/* If variable with the same name is both
* exported and temporarily set for a command:
* export ZVAR=5
* ZVAR=6 printenv
* then "ZVAR=6" will be both in vartab and
* lp lists. Do not pass it twice to printenv.
*/
struct strlist *lp1 = lp;
while (lp1) {
if (strcmp(lp1->text, vp->var_text) == 0)
goto skip;
lp1 = lp1->next;
}
#endif
if (ep == stackstrend())
ep = growstackstr();
*ep++ = (char*)vp->var_text;
#if ENABLE_FEATURE_SH_NOFORK
skip: ;
#endif
}
}
} while (++vpp < vartab + VTABSIZE);
#if ENABLE_FEATURE_SH_NOFORK
while (lp) {
if (ep == stackstrend())
ep = growstackstr();
*ep++ = lp->text;
lp = lp->next;
}
#endif
if (ep == stackstrend())
ep = growstackstr();
if (end)
@ -7860,7 +7891,7 @@ static void shellexec(char *prog, char **argv, const char *path, int idx)
int exerrno;
int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */
envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL);
envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL);
if (strchr(prog, '/') != NULL
#if ENABLE_FEATURE_SH_STANDALONE
|| (applet_no = find_applet_by_name(prog)) >= 0
@ -9930,7 +9961,11 @@ evalcommand(union node *cmd, int flags)
/* find_command() encodes applet_no as (-2 - applet_no) */
int applet_no = (- cmdentry.u.index - 2);
if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
listsetvar(varlist.list, VEXPORT|VSTACK);
char **sv_environ;
INT_OFF;
sv_environ = environ;
environ = listvars(VEXPORT, VUNSET, varlist.list, /*end:*/ NULL);
/*
* Run <applet>_main().
* Signals (^C) can't interrupt here.
@ -9939,8 +9974,8 @@ evalcommand(union node *cmd, int flags)
* and/or wait for user input ineligible for NOFORK:
* for example, "yes" or "rm" (rm -i waits for input).
*/
INT_OFF;
status = run_nofork_applet(applet_no, argv);
environ = sv_environ;
/*
* Try enabling NOFORK for "yes" applet.
* ^C _will_ stop it (write returns EINTR),
@ -10854,7 +10889,7 @@ showvars(const char *sep_prefix, int on, int off)
const char *sep;
char **ep, **epend;
ep = listvars(on, off, &epend);
ep = listvars(on, off, /*strlist:*/ NULL, &epend);
qsort(ep, epend - ep, sizeof(char *), vpcmp);
sep = *sep_prefix ? " " : sep_prefix;

View File

@ -0,0 +1,9 @@
ZVAR=1
ZVAR=2
ZVAR=3
ZVAR=4
ZVAR=5
ZVAR=6
ZVAR=7
ZVAR=8
Ok:0

View File

@ -0,0 +1,15 @@
# ash had a bug where NOFORKed applet (env/printenv) was not seeing new exported variables
(export ZVAR=1; printenv) | grep ^ZVAR=
(ZVAR=2 printenv) | grep ^ZVAR=
(export ZVAR=3; env) | grep ^ZVAR=
(ZVAR=4 env) | grep ^ZVAR=
export ZVAR=5; printenv | grep ^ZVAR=
ZVAR=6 printenv | grep ^ZVAR=
export ZVAR=7; env | grep ^ZVAR=
ZVAR=8 env | grep ^ZVAR=
echo Ok:$?

View File

@ -0,0 +1,9 @@
ZVAR=1
ZVAR=2
ZVAR=3
ZVAR=4
ZVAR=5
ZVAR=6
ZVAR=7
ZVAR=8
Ok:0

View File

@ -0,0 +1,15 @@
# ash had a bug where NOFORKed applet (env/printenv) was not seeing new exported variables
(export ZVAR=1; printenv) | grep ^ZVAR=
(ZVAR=2 printenv) | grep ^ZVAR=
(export ZVAR=3; env) | grep ^ZVAR=
(ZVAR=4 env) | grep ^ZVAR=
export ZVAR=5; printenv | grep ^ZVAR=
ZVAR=6 printenv | grep ^ZVAR=
export ZVAR=7; env | grep ^ZVAR=
ZVAR=8 env | grep ^ZVAR=
echo Ok:$?