ash: fix redirection of fd 0 in scripts are sourced from interactive ash
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
3e47cfec90
commit
08d8b3cee1
35
shell/ash.c
35
shell/ash.c
@ -1070,7 +1070,7 @@ ash_vmsg(const char *msg, va_list ap)
|
|||||||
if (commandname) {
|
if (commandname) {
|
||||||
if (strcmp(arg0, commandname))
|
if (strcmp(arg0, commandname))
|
||||||
fprintf(stderr, "%s: ", commandname);
|
fprintf(stderr, "%s: ", commandname);
|
||||||
if (!iflag || g_parsefile->fd)
|
if (!iflag || g_parsefile->fd > 0)
|
||||||
fprintf(stderr, "line %d: ", startlinno);
|
fprintf(stderr, "line %d: ", startlinno);
|
||||||
}
|
}
|
||||||
vfprintf(stderr, msg, ap);
|
vfprintf(stderr, msg, ap);
|
||||||
@ -5063,15 +5063,26 @@ static int is_hidden_fd(struct redirtab *rp, int fd)
|
|||||||
|
|
||||||
if (fd == -1)
|
if (fd == -1)
|
||||||
return 0;
|
return 0;
|
||||||
|
/* Check open scripts' fds */
|
||||||
pf = g_parsefile;
|
pf = g_parsefile;
|
||||||
while (pf) {
|
while (pf) {
|
||||||
if (fd == pf->fd) {
|
/* We skip fd == 0 case because of the following case:
|
||||||
|
* $ ash # running ash interactively
|
||||||
|
* $ . ./script.sh
|
||||||
|
* and in script.sh: "exec 9>&0".
|
||||||
|
* Even though top-level fd _is_ 0,
|
||||||
|
* it's still ok to use it: "read" builtin uses it,
|
||||||
|
* why should we cripple "exec" builtin?
|
||||||
|
*/
|
||||||
|
if (pf->fd > 0 && fd == pf->fd) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
pf = pf->prev;
|
pf = pf->prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rp)
|
if (!rp)
|
||||||
return 0;
|
return 0;
|
||||||
|
/* Check saved fds of redirects */
|
||||||
fd |= COPYFD_RESTORE;
|
fd |= COPYFD_RESTORE;
|
||||||
for (i = 0; i < rp->pair_count; i++) {
|
for (i = 0; i < rp->pair_count; i++) {
|
||||||
if (rp->two_fd[i].copy == fd) {
|
if (rp->two_fd[i].copy == fd) {
|
||||||
@ -5084,9 +5095,7 @@ static int is_hidden_fd(struct redirtab *rp, int fd)
|
|||||||
/*
|
/*
|
||||||
* Process a list of redirection commands. If the REDIR_PUSH flag is set,
|
* Process a list of redirection commands. If the REDIR_PUSH flag is set,
|
||||||
* old file descriptors are stashed away so that the redirection can be
|
* old file descriptors are stashed away so that the redirection can be
|
||||||
* undone by calling popredir. If the REDIR_BACKQ flag is set, then the
|
* undone by calling popredir.
|
||||||
* standard output, and the standard error if it becomes a duplicate of
|
|
||||||
* stdout, is saved in memory.
|
|
||||||
*/
|
*/
|
||||||
/* flags passed to redirect */
|
/* flags passed to redirect */
|
||||||
#define REDIR_PUSH 01 /* save previous values of file descriptors */
|
#define REDIR_PUSH 01 /* save previous values of file descriptors */
|
||||||
@ -5132,13 +5141,15 @@ redirect(union node *redir, int flags)
|
|||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
int right_fd = -1;
|
||||||
fd = redir->nfile.fd;
|
fd = redir->nfile.fd;
|
||||||
if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
|
if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
|
||||||
int right_fd = redir->ndup.dupfd;
|
right_fd = redir->ndup.dupfd;
|
||||||
|
//bb_error_msg("doing %d > %d", fd, right_fd);
|
||||||
/* redirect from/to same file descriptor? */
|
/* redirect from/to same file descriptor? */
|
||||||
if (right_fd == fd)
|
if (right_fd == fd)
|
||||||
continue;
|
continue;
|
||||||
/* echo >&10 and 10 is a fd opened to the sh script? */
|
/* "echo >&10" and 10 is a fd opened to a sh script? */
|
||||||
if (is_hidden_fd(sv, right_fd)) {
|
if (is_hidden_fd(sv, right_fd)) {
|
||||||
errno = EBADF; /* as if it is closed */
|
errno = EBADF; /* as if it is closed */
|
||||||
ash_msg_and_raise_error("%d: %m", right_fd);
|
ash_msg_and_raise_error("%d: %m", right_fd);
|
||||||
@ -5160,7 +5171,10 @@ redirect(union node *redir, int flags)
|
|||||||
#endif
|
#endif
|
||||||
if (need_to_remember(sv, fd)) {
|
if (need_to_remember(sv, fd)) {
|
||||||
/* Copy old descriptor */
|
/* Copy old descriptor */
|
||||||
i = fcntl(fd, F_DUPFD, 10);
|
/* Careful to not accidentally "save"
|
||||||
|
* to the same fd as right side fd in N>&M */
|
||||||
|
int minfd = right_fd < 10 ? 10 : right_fd + 1;
|
||||||
|
i = fcntl(fd, F_DUPFD, minfd);
|
||||||
/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
|
/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
|
||||||
* are closed in popredir() in the child, preventing them from leaking
|
* are closed in popredir() in the child, preventing them from leaking
|
||||||
* into child. (popredir() also cleans up the mess in case of failures)
|
* into child. (popredir() also cleans up the mess in case of failures)
|
||||||
@ -12994,8 +13008,9 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
|
|||||||
* Ensure we don't falsely claim that 0 (stdin)
|
* Ensure we don't falsely claim that 0 (stdin)
|
||||||
* is one of stacked source fds.
|
* is one of stacked source fds.
|
||||||
* Testcase: ash -c 'exec 1>&0' must not complain. */
|
* Testcase: ash -c 'exec 1>&0' must not complain. */
|
||||||
if (!sflag)
|
// if (!sflag) g_parsefile->fd = -1;
|
||||||
g_parsefile->fd = -1;
|
// ^^ not necessary since now we special-case fd 0
|
||||||
|
// in is_hidden_fd() to not be considered "hidden fd"
|
||||||
evalstring(minusc, 0);
|
evalstring(minusc, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
sleeping for 3 sec
|
Sleeping
|
||||||
sleeping for 2 sec
|
Sleeping
|
||||||
Waiting
|
Waiting
|
||||||
2 sec passed, sending USR1 to parent
|
2 sec passed, sending USR1 to parent
|
||||||
USR1 received
|
USR1 received
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
trap "echo USR1 received" USR1
|
trap "echo USR1 received" USR1
|
||||||
stub() {
|
stub() {
|
||||||
echo "sleeping for $1 sec"
|
echo "Sleeping"
|
||||||
sleep $1
|
sleep $1
|
||||||
echo "$1 sec passed, sending USR1 to parent"
|
echo "$1 sec passed, sending USR1 to parent"
|
||||||
kill -USR1 $$
|
kill -USR1 $$
|
||||||
|
Loading…
Reference in New Issue
Block a user