ash: significant overhaul of redirect saving logic
New code is similar to what hush is doing. Make CLOSED to -1: same as dash. popredir() loses "restore" parameter: same as dash. COPYFD_RESTORE bit is no longer necessary. This change fixes this interactive bug: $ ls -l /proc/$$/fd 10>&- ash: can't set tty process group: Bad file descriptor ash: can't set tty process group: Bad file descriptor [1]+ Done(2) ls -l /proc/${\$}/fd 10>&4294967295 function old new delta unwindredir 29 27 -2 tryexec 154 152 -2 evaltree 503 501 -2 evalcommand 1369 1367 -2 cmdloop 187 185 -2 redirect 1029 1018 -11 popredir 153 123 -30 need_to_remember 36 - -36 is_hidden_fd 68 - -68 ------------------------------------------------------------------------------ (add/remove: 0/2 grow/shrink: 0/7 up/down: 0/-155) Total: -155 bytes text data bss dec hex filename 914572 485 6848 921905 e1131 busybox_old 914553 485 6848 921886 e111e busybox_unstripped Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
657e9005a9
commit
035486c750
336
shell/ash.c
336
shell/ash.c
@ -5192,7 +5192,7 @@ stoppedjobs(void)
|
|||||||
#undef EMPTY
|
#undef EMPTY
|
||||||
#undef CLOSED
|
#undef CLOSED
|
||||||
#define EMPTY -2 /* marks an unused slot in redirtab */
|
#define EMPTY -2 /* marks an unused slot in redirtab */
|
||||||
#define CLOSED -3 /* marks a slot of previously-closed fd */
|
#define CLOSED -1 /* marks a slot of previously-closed fd */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle here documents. Normally we fork off a process to write the
|
* Handle here documents. Normally we fork off a process to write the
|
||||||
@ -5349,74 +5349,158 @@ dup2_or_raise(int from, int to)
|
|||||||
}
|
}
|
||||||
return newfd;
|
return newfd;
|
||||||
}
|
}
|
||||||
|
static int
|
||||||
|
fcntl_F_DUPFD(int fd, int avoid_fd)
|
||||||
|
{
|
||||||
|
int newfd;
|
||||||
|
repeat:
|
||||||
|
newfd = fcntl(fd, F_DUPFD, avoid_fd + 1);
|
||||||
|
if (newfd < 0) {
|
||||||
|
if (errno == EBUSY)
|
||||||
|
goto repeat;
|
||||||
|
if (errno == EINTR)
|
||||||
|
goto repeat;
|
||||||
|
}
|
||||||
|
return newfd;
|
||||||
|
}
|
||||||
|
static int
|
||||||
|
xdup_CLOEXEC_and_close(int fd, int avoid_fd)
|
||||||
|
{
|
||||||
|
int newfd;
|
||||||
|
repeat:
|
||||||
|
newfd = fcntl(fd, F_DUPFD, avoid_fd + 1);
|
||||||
|
if (newfd < 0) {
|
||||||
|
if (errno == EBUSY)
|
||||||
|
goto repeat;
|
||||||
|
if (errno == EINTR)
|
||||||
|
goto repeat;
|
||||||
|
/* fd was not open? */
|
||||||
|
if (errno == EBADF)
|
||||||
|
return fd;
|
||||||
|
ash_msg_and_raise_perror("%d", newfd);
|
||||||
|
}
|
||||||
|
fcntl(newfd, F_SETFD, FD_CLOEXEC);
|
||||||
|
close(fd);
|
||||||
|
return newfd;
|
||||||
|
}
|
||||||
|
|
||||||
/* Struct def and variable are moved down to the first usage site */
|
/* Struct def and variable are moved down to the first usage site */
|
||||||
struct two_fd_t {
|
struct squirrel {
|
||||||
int orig, copy;
|
int orig_fd;
|
||||||
|
int moved_to;
|
||||||
};
|
};
|
||||||
struct redirtab {
|
struct redirtab {
|
||||||
struct redirtab *next;
|
struct redirtab *next;
|
||||||
int pair_count;
|
int pair_count;
|
||||||
struct two_fd_t two_fd[];
|
struct squirrel two_fd[];
|
||||||
};
|
};
|
||||||
#define redirlist (G_var.redirlist)
|
#define redirlist (G_var.redirlist)
|
||||||
enum {
|
|
||||||
COPYFD_RESTORE = (int)~(INT_MAX),
|
|
||||||
};
|
|
||||||
|
|
||||||
static int
|
static void
|
||||||
need_to_remember(struct redirtab *rp, int fd)
|
add_squirrel_closed(struct redirtab *sq, int fd)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (!rp) /* remembering was not requested */
|
if (!sq)
|
||||||
return 0;
|
return;
|
||||||
|
|
||||||
for (i = 0; i < rp->pair_count; i++) {
|
for (i = 0; sq->two_fd[i].orig_fd != EMPTY; i++) {
|
||||||
if (rp->two_fd[i].orig == fd) {
|
/* If we collide with an already moved fd... */
|
||||||
/* already remembered */
|
if (fd == sq->two_fd[i].orig_fd) {
|
||||||
return 0;
|
/* Examples:
|
||||||
|
* "echo 3>FILE 3>&- 3>FILE"
|
||||||
|
* "echo 3>&- 3>FILE"
|
||||||
|
* No need for last redirect to insert
|
||||||
|
* another "need to close 3" indicator.
|
||||||
|
*/
|
||||||
|
TRACE(("redirect_fd %d: already moved or closed\n", fd));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 1;
|
TRACE(("redirect_fd %d: previous fd was closed\n", fd));
|
||||||
|
sq->two_fd[i].orig_fd = fd;
|
||||||
|
sq->two_fd[i].moved_to = CLOSED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* "hidden" fd is a fd used to read scripts, or a copy of such */
|
|
||||||
static int
|
static int
|
||||||
is_hidden_fd(struct redirtab *rp, int fd)
|
save_fd_on_redirect(int fd, int avoid_fd, struct redirtab *sq)
|
||||||
{
|
{
|
||||||
int i;
|
int i, new_fd;
|
||||||
struct parsefile *pf;
|
|
||||||
|
|
||||||
if (fd == -1)
|
if (avoid_fd < 9) /* the important case here is that it can be -1 */
|
||||||
return 0;
|
avoid_fd = 9;
|
||||||
/* Check open scripts' fds */
|
|
||||||
pf = g_parsefile;
|
#if JOBS
|
||||||
while (pf) {
|
if (fd == ttyfd) {
|
||||||
/* We skip pf_fd == 0 case because of the following case:
|
/* Testcase: "ls -l /proc/$$/fd 10>&-" should work */
|
||||||
* $ ash # running ash interactively
|
ttyfd = xdup_CLOEXEC_and_close(ttyfd, avoid_fd);
|
||||||
* $ . ./script.sh
|
TRACE(("redirect_fd %d: matches ttyfd, moving it to %d\n", fd, ttyfd));
|
||||||
* and in script.sh: "exec 9>&0".
|
return 1; /* "we closed fd" */
|
||||||
* Even though top-level pf_fd _is_ 0,
|
|
||||||
* it's still ok to use it: "read" builtin uses it,
|
|
||||||
* why should we cripple "exec" builtin?
|
|
||||||
*/
|
|
||||||
if (pf->pf_fd > 0 && fd == pf->pf_fd) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
pf = pf->prev;
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
if (!rp)
|
/* Are we called from redirect(0)? E.g. redirect
|
||||||
|
* in a forked child. No need to save fds,
|
||||||
|
* we aren't going to use them anymore, ok to trash.
|
||||||
|
*/
|
||||||
|
if (!sq)
|
||||||
return 0;
|
return 0;
|
||||||
/* Check saved fds of redirects */
|
|
||||||
fd |= COPYFD_RESTORE;
|
/* If this one of script's fds? */
|
||||||
for (i = 0; i < rp->pair_count; i++) {
|
if (fd != 0) {
|
||||||
if (rp->two_fd[i].copy == fd) {
|
struct parsefile *pf = g_parsefile;
|
||||||
return 1;
|
while (pf) {
|
||||||
|
/* We skip fd == 0 case because of the following:
|
||||||
|
* $ ash # running ash interactively
|
||||||
|
* $ . ./script.sh
|
||||||
|
* and in script.sh: "exec 9>&0".
|
||||||
|
* Even though top-level pf_fd _is_ 0,
|
||||||
|
* it's still ok to use it: "read" builtin uses it,
|
||||||
|
* why should we cripple "exec" builtin?
|
||||||
|
*/
|
||||||
|
if (fd == pf->pf_fd) {
|
||||||
|
pf->pf_fd = xdup_CLOEXEC_and_close(fd, avoid_fd);
|
||||||
|
return 1; /* "we closed fd" */
|
||||||
|
}
|
||||||
|
pf = pf->prev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
|
/* Check whether it collides with any open fds (e.g. stdio), save fds as needed */
|
||||||
|
|
||||||
|
/* First: do we collide with some already moved fds? */
|
||||||
|
for (i = 0; sq->two_fd[i].orig_fd != EMPTY; i++) {
|
||||||
|
/* If we collide with an already moved fd... */
|
||||||
|
if (fd == sq->two_fd[i].moved_to) {
|
||||||
|
new_fd = fcntl_F_DUPFD(fd, avoid_fd);
|
||||||
|
sq->two_fd[i].moved_to = new_fd;
|
||||||
|
TRACE(("redirect_fd %d: already busy, moving to %d\n", fd, new_fd));
|
||||||
|
if (new_fd < 0) /* what? */
|
||||||
|
xfunc_die();
|
||||||
|
return 0; /* "we did not close fd" */
|
||||||
|
}
|
||||||
|
if (fd == sq->two_fd[i].orig_fd) {
|
||||||
|
/* Example: echo Hello >/dev/null 1>&2 */
|
||||||
|
TRACE(("redirect_fd %d: already moved\n", fd));
|
||||||
|
return 0; /* "we did not close fd" */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If this fd is open, we move and remember it; if it's closed, new_fd = CLOSED (-1) */
|
||||||
|
new_fd = fcntl_F_DUPFD(fd, avoid_fd);
|
||||||
|
TRACE(("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, new_fd));
|
||||||
|
if (new_fd < 0) {
|
||||||
|
if (errno != EBADF)
|
||||||
|
xfunc_die();
|
||||||
|
/* new_fd = CLOSED; - already is -1 */
|
||||||
|
}
|
||||||
|
sq->two_fd[i].moved_to = new_fd;
|
||||||
|
sq->two_fd[i].orig_fd = fd;
|
||||||
|
|
||||||
|
/* if we move stderr, let "set -x" code know */
|
||||||
|
if (fd == preverrout_fd)
|
||||||
|
preverrout_fd = new_fd;
|
||||||
|
|
||||||
|
return 0; /* "we did not close fd" */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -5431,109 +5515,80 @@ redirect(union node *redir, int flags)
|
|||||||
{
|
{
|
||||||
struct redirtab *sv;
|
struct redirtab *sv;
|
||||||
int sv_pos;
|
int sv_pos;
|
||||||
int fd;
|
|
||||||
int newfd;
|
|
||||||
|
|
||||||
if (!redir)
|
if (!redir)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
sv_pos = 0;
|
||||||
sv = NULL;
|
sv = NULL;
|
||||||
INT_OFF;
|
INT_OFF;
|
||||||
if (flags & REDIR_PUSH)
|
if (flags & REDIR_PUSH)
|
||||||
sv = redirlist;
|
sv = redirlist;
|
||||||
sv_pos = 0;
|
|
||||||
do {
|
do {
|
||||||
int right_fd = -1;
|
int fd;
|
||||||
|
int newfd;
|
||||||
|
int close_fd;
|
||||||
|
int closed;
|
||||||
|
|
||||||
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) {
|
||||||
right_fd = redir->ndup.dupfd;
|
//bb_error_msg("doing %d > %d", fd, newfd);
|
||||||
//bb_error_msg("doing %d > %d", fd, right_fd);
|
newfd = redir->ndup.dupfd;
|
||||||
/* redirect from/to same file descriptor? */
|
close_fd = -1;
|
||||||
if (right_fd == fd)
|
|
||||||
continue;
|
|
||||||
/* "echo >&10" and 10 is a fd opened to a sh script? */
|
|
||||||
if (is_hidden_fd(sv, right_fd)) {
|
|
||||||
errno = EBADF; /* as if it is closed */
|
|
||||||
ash_msg_and_raise_perror("%d", right_fd);
|
|
||||||
}
|
|
||||||
newfd = -1;
|
|
||||||
} else {
|
} else {
|
||||||
newfd = openredirect(redir); /* always >= 0 */
|
newfd = openredirect(redir); /* always >= 0 */
|
||||||
if (fd == newfd) {
|
if (fd == newfd) {
|
||||||
/* Descriptor wasn't open before redirect.
|
/* open() gave us precisely the fd we wanted.
|
||||||
* Mark it for close in the future */
|
* This means that this fd was not busy
|
||||||
if (need_to_remember(sv, fd)) {
|
* (not opened to anywhere).
|
||||||
goto remember_to_close;
|
* Remember to close it on restore:
|
||||||
}
|
*/
|
||||||
|
add_squirrel_closed(sv, fd);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
close_fd = newfd;
|
||||||
}
|
}
|
||||||
#if BASH_REDIR_OUTPUT
|
|
||||||
redirect_more:
|
if (fd == newfd)
|
||||||
#endif
|
continue;
|
||||||
if (need_to_remember(sv, fd)) {
|
|
||||||
/* Copy old descriptor */
|
/* if "N>FILE": move newfd to fd */
|
||||||
/* Careful to not accidentally "save"
|
/* if "N>&M": dup newfd to fd */
|
||||||
* to the same fd as right side fd in N>&M */
|
/* if "N>&-": close fd (newfd is -1) */
|
||||||
int minfd = right_fd < 10 ? 10 : right_fd + 1;
|
|
||||||
#if defined(F_DUPFD_CLOEXEC)
|
IF_BASH_REDIR_OUTPUT(redirect_more:)
|
||||||
int copy = fcntl(fd, F_DUPFD_CLOEXEC, minfd);
|
|
||||||
#else
|
closed = save_fd_on_redirect(fd, /*avoid:*/ newfd, sv);
|
||||||
int copy = fcntl(fd, F_DUPFD, minfd);
|
if (newfd == -1) {
|
||||||
#endif
|
/* "N>&-" means "close me" */
|
||||||
if (copy == -1) {
|
if (!closed) {
|
||||||
int e = errno;
|
/* ^^^ optimization: saving may already
|
||||||
if (e != EBADF) {
|
* have closed it. If not... */
|
||||||
/* Strange error (e.g. "too many files" EMFILE?) */
|
close(fd);
|
||||||
if (newfd >= 0)
|
|
||||||
close(newfd);
|
|
||||||
errno = e;
|
|
||||||
ash_msg_and_raise_perror("%d", fd);
|
|
||||||
/* NOTREACHED */
|
|
||||||
}
|
|
||||||
/* EBADF: it is not open - good, remember to close it */
|
|
||||||
remember_to_close:
|
|
||||||
copy = CLOSED;
|
|
||||||
} else { /* fd is open, save its copy */
|
|
||||||
#if !defined(F_DUPFD_CLOEXEC)
|
|
||||||
fcntl(copy, F_SETFD, FD_CLOEXEC);
|
|
||||||
#endif
|
|
||||||
/* "exec fd>&-" should not close fds
|
|
||||||
* which point to script file(s).
|
|
||||||
* Force them to be restored afterwards */
|
|
||||||
if (is_hidden_fd(sv, fd))
|
|
||||||
copy |= COPYFD_RESTORE;
|
|
||||||
}
|
}
|
||||||
/* if we move stderr, let "set -x" code know */
|
} else {
|
||||||
if (fd == preverrout_fd)
|
///TODO: if _newfd_ is a script fd or saved fd, then simulate EBADF!
|
||||||
preverrout_fd = copy;
|
//if (newfd == ttyfd) {
|
||||||
sv->two_fd[sv_pos].orig = fd;
|
// errno = EBADF;
|
||||||
sv->two_fd[sv_pos].copy = copy;
|
// ash_msg_and_raise_perror("A %d", newfd);
|
||||||
sv_pos++;
|
//}
|
||||||
}
|
//if (newfd == g_parsefile->pf_fd) {
|
||||||
if (newfd < 0) {
|
// errno = EBADF;
|
||||||
/* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
|
// ash_msg_and_raise_perror("B %d", newfd);
|
||||||
if (redir->ndup.dupfd < 0) { /* "fd>&-" */
|
//}
|
||||||
/* Don't want to trigger debugging */
|
|
||||||
if (fd != -1)
|
|
||||||
close(fd);
|
|
||||||
} else {
|
|
||||||
dup2_or_raise(redir->ndup.dupfd, fd);
|
|
||||||
}
|
|
||||||
} else if (fd != newfd) { /* move newfd to fd */
|
|
||||||
dup2_or_raise(newfd, fd);
|
dup2_or_raise(newfd, fd);
|
||||||
|
if (close_fd >= 0) /* "N>FILE" or ">&FILE" or heredoc? */
|
||||||
|
close(close_fd);
|
||||||
#if BASH_REDIR_OUTPUT
|
#if BASH_REDIR_OUTPUT
|
||||||
if (!(redir->nfile.type == NTO2 && fd == 2))
|
if (redir->nfile.type == NTO2 && fd == 1) {
|
||||||
|
/* ">&FILE". we already redirected to 1, now copy 1 to 2 */
|
||||||
|
fd = 2;
|
||||||
|
newfd = 1;
|
||||||
|
close_fd = -1;
|
||||||
|
goto redirect_more;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
close(newfd);
|
|
||||||
}
|
}
|
||||||
#if BASH_REDIR_OUTPUT
|
|
||||||
if (redir->nfile.type == NTO2 && fd == 1) {
|
|
||||||
/* We already redirected it to fd 1, now copy it to 2 */
|
|
||||||
newfd = 1;
|
|
||||||
fd = 2;
|
|
||||||
goto redirect_more;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
} while ((redir = redir->nfile.next) != NULL);
|
} while ((redir = redir->nfile.next) != NULL);
|
||||||
INT_ON;
|
INT_ON;
|
||||||
|
|
||||||
@ -5542,7 +5597,7 @@ redirect(union node *redir, int flags)
|
|||||||
// dash has a bug: since REDIR_SAVEFD2=3 and REDIR_PUSH=1, this test
|
// dash has a bug: since REDIR_SAVEFD2=3 and REDIR_PUSH=1, this test
|
||||||
// triggers for pure REDIR_PUSH too. Thus, this is done almost always,
|
// triggers for pure REDIR_PUSH too. Thus, this is done almost always,
|
||||||
// not only for calls with flags containing REDIR_SAVEFD2.
|
// not only for calls with flags containing REDIR_SAVEFD2.
|
||||||
// We do this unconditionally (see code above).
|
// We do this unconditionally (see save_fd_on_redirect()).
|
||||||
//if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
|
//if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
|
||||||
// preverrout_fd = copied_fd2;
|
// preverrout_fd = copied_fd2;
|
||||||
}
|
}
|
||||||
@ -5557,7 +5612,7 @@ redirectsafe(union node *redir, int flags)
|
|||||||
|
|
||||||
SAVE_INT(saveint);
|
SAVE_INT(saveint);
|
||||||
/* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
|
/* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
|
||||||
err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
|
err = setjmp(jmploc.loc); /* was = setjmp(jmploc.loc) * 2; */
|
||||||
if (!err) {
|
if (!err) {
|
||||||
exception_handler = &jmploc;
|
exception_handler = &jmploc;
|
||||||
redirect(redir, flags);
|
redirect(redir, flags);
|
||||||
@ -5591,7 +5646,7 @@ pushredir(union node *redir)
|
|||||||
sv = ckzalloc(sizeof(*sv) + i * sizeof(sv->two_fd[0]));
|
sv = ckzalloc(sizeof(*sv) + i * sizeof(sv->two_fd[0]));
|
||||||
sv->pair_count = i;
|
sv->pair_count = i;
|
||||||
while (--i >= 0)
|
while (--i >= 0)
|
||||||
sv->two_fd[i].orig = sv->two_fd[i].copy = EMPTY;
|
sv->two_fd[i].orig_fd = sv->two_fd[i].moved_to = EMPTY;
|
||||||
sv->next = redirlist;
|
sv->next = redirlist;
|
||||||
redirlist = sv;
|
redirlist = sv;
|
||||||
return sv->next;
|
return sv->next;
|
||||||
@ -5601,7 +5656,7 @@ pushredir(union node *redir)
|
|||||||
* Undo the effects of the last redirection.
|
* Undo the effects of the last redirection.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
popredir(int drop, int restore)
|
popredir(int drop)
|
||||||
{
|
{
|
||||||
struct redirtab *rp;
|
struct redirtab *rp;
|
||||||
int i;
|
int i;
|
||||||
@ -5611,20 +5666,19 @@ popredir(int drop, int restore)
|
|||||||
INT_OFF;
|
INT_OFF;
|
||||||
rp = redirlist;
|
rp = redirlist;
|
||||||
for (i = 0; i < rp->pair_count; i++) {
|
for (i = 0; i < rp->pair_count; i++) {
|
||||||
int fd = rp->two_fd[i].orig;
|
int fd = rp->two_fd[i].orig_fd;
|
||||||
int copy = rp->two_fd[i].copy;
|
int copy = rp->two_fd[i].moved_to;
|
||||||
if (copy == CLOSED) {
|
if (copy == CLOSED) {
|
||||||
if (!drop)
|
if (!drop)
|
||||||
close(fd);
|
close(fd);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (copy != EMPTY) {
|
if (copy != EMPTY) {
|
||||||
if (!drop || (restore && (copy & COPYFD_RESTORE))) {
|
if (!drop) {
|
||||||
copy &= ~COPYFD_RESTORE;
|
|
||||||
/*close(fd);*/
|
/*close(fd);*/
|
||||||
dup2_or_raise(copy, fd);
|
dup2_or_raise(copy, fd);
|
||||||
}
|
}
|
||||||
close(copy & ~COPYFD_RESTORE);
|
close(copy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
redirlist = rp->next;
|
redirlist = rp->next;
|
||||||
@ -5636,7 +5690,7 @@ static void
|
|||||||
unwindredir(struct redirtab *stop)
|
unwindredir(struct redirtab *stop)
|
||||||
{
|
{
|
||||||
while (redirlist != stop)
|
while (redirlist != stop)
|
||||||
popredir(/*drop:*/ 0, /*restore:*/ 0);
|
popredir(/*drop:*/ 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -7715,7 +7769,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
|
|||||||
clearenv();
|
clearenv();
|
||||||
while (*envp)
|
while (*envp)
|
||||||
putenv(*envp++);
|
putenv(*envp++);
|
||||||
popredir(/*drop:*/ 1, /*restore:*/ 0);
|
popredir(/*drop:*/ 1);
|
||||||
run_applet_no_and_exit(applet_no, cmd, argv);
|
run_applet_no_and_exit(applet_no, cmd, argv);
|
||||||
}
|
}
|
||||||
/* re-exec ourselves with the new arguments */
|
/* re-exec ourselves with the new arguments */
|
||||||
@ -8754,7 +8808,7 @@ evaltree(union node *n, int flags)
|
|||||||
status = evaltree(n->nredir.n, flags & EV_TESTED);
|
status = evaltree(n->nredir.n, flags & EV_TESTED);
|
||||||
}
|
}
|
||||||
if (n->nredir.redirect)
|
if (n->nredir.redirect)
|
||||||
popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
|
popredir(/*drop:*/ 0);
|
||||||
goto setstatus;
|
goto setstatus;
|
||||||
case NCMD:
|
case NCMD:
|
||||||
evalfn = evalcommand;
|
evalfn = evalcommand;
|
||||||
@ -9902,7 +9956,7 @@ evalcommand(union node *cmd, int flags)
|
|||||||
|
|
||||||
out:
|
out:
|
||||||
if (cmd->ncmd.redirect)
|
if (cmd->ncmd.redirect)
|
||||||
popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
|
popredir(/*drop:*/ cmd_is_exec);
|
||||||
unwindredir(redir_stop);
|
unwindredir(redir_stop);
|
||||||
unwindlocalvars(localvar_stop);
|
unwindlocalvars(localvar_stop);
|
||||||
if (lastarg) {
|
if (lastarg) {
|
||||||
@ -13727,7 +13781,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
|
|||||||
* Testcase: ash -c 'exec 1>&0' must not complain. */
|
* Testcase: ash -c 'exec 1>&0' must not complain. */
|
||||||
// if (!sflag) g_parsefile->pf_fd = -1;
|
// if (!sflag) g_parsefile->pf_fd = -1;
|
||||||
// ^^ not necessary since now we special-case fd 0
|
// ^^ not necessary since now we special-case fd 0
|
||||||
// in is_hidden_fd() to not be considered "hidden fd"
|
// in save_fd_on_redirect()
|
||||||
evalstring(minusc, sflag ? 0 : EV_EXIT);
|
evalstring(minusc, sflag ? 0 : EV_EXIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,10 +20,15 @@ eval "find_fds $fds"
|
|||||||
|
|
||||||
# Shell should not lose that fd. Did it?
|
# Shell should not lose that fd. Did it?
|
||||||
find_fds
|
find_fds
|
||||||
test x"$fds1" = x"$fds" && { echo "Ok: script fd is not closed"; exit 0; }
|
test x"$fds1" = x"$fds" \
|
||||||
|
&& { echo "Ok: script fd is not closed"; exit 0; }
|
||||||
|
|
||||||
|
# One legit way to handle it is to move script fd. For example, if we see that fd 10 moved to fd 11:
|
||||||
|
test x"$fds1" = x" 10>&- 3>&-" && \
|
||||||
|
test x"$fds" = x" 11>&- 3>&-" \
|
||||||
|
&& { echo "Ok: script fd is not closed"; exit 0; }
|
||||||
|
|
||||||
echo "Bug: script fd is closed"
|
echo "Bug: script fd is closed"
|
||||||
echo "fds1:$fds1"
|
echo "fds1:$fds1"
|
||||||
echo "fds2:$fds"
|
echo "fds2:$fds"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
|
2
shell/ash_test/ash-redir/redir_to_bad_fd255.right
Normal file
2
shell/ash_test/ash-redir/redir_to_bad_fd255.right
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
./redir_to_bad_fd255.tests: line 2: 255: Bad file descriptor
|
||||||
|
OK
|
3
shell/ash_test/ash-redir/redir_to_bad_fd255.tests
Executable file
3
shell/ash_test/ash-redir/redir_to_bad_fd255.tests
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
# ash uses fd 10 (usually) for reading the script
|
||||||
|
echo LOST >&255
|
||||||
|
echo OK
|
2
shell/ash_test/ash-redir/redir_to_bad_fd3.right
Normal file
2
shell/ash_test/ash-redir/redir_to_bad_fd3.right
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
./redir_to_bad_fd3.tests: line 2: 3: Bad file descriptor
|
||||||
|
OK
|
3
shell/ash_test/ash-redir/redir_to_bad_fd3.tests
Executable file
3
shell/ash_test/ash-redir/redir_to_bad_fd3.tests
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
# ash uses fd 10 (usually) for reading the script
|
||||||
|
echo LOST >&3
|
||||||
|
echo OK
|
@ -20,10 +20,15 @@ eval "find_fds $fds"
|
|||||||
|
|
||||||
# Shell should not lose that fd. Did it?
|
# Shell should not lose that fd. Did it?
|
||||||
find_fds
|
find_fds
|
||||||
test x"$fds1" = x"$fds" && { echo "Ok: script fd is not closed"; exit 0; }
|
test x"$fds1" = x"$fds" \
|
||||||
|
&& { echo "Ok: script fd is not closed"; exit 0; }
|
||||||
|
|
||||||
|
# One legit way to handle it is to move script fd. For example, if we see that fd 10 moved to fd 11:
|
||||||
|
test x"$fds1" = x" 10>&- 3>&-" && \
|
||||||
|
test x"$fds" = x" 11>&- 3>&-" \
|
||||||
|
&& { echo "Ok: script fd is not closed"; exit 0; }
|
||||||
|
|
||||||
echo "Bug: script fd is closed"
|
echo "Bug: script fd is closed"
|
||||||
echo "fds1:$fds1"
|
echo "fds1:$fds1"
|
||||||
echo "fds2:$fds"
|
echo "fds2:$fds"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
|
1
shell/hush_test/hush-redir/redir_to_bad_fd255.right
Normal file
1
shell/hush_test/hush-redir/redir_to_bad_fd255.right
Normal file
@ -0,0 +1 @@
|
|||||||
|
hush: can't duplicate file descriptor: Bad file descriptor
|
3
shell/hush_test/hush-redir/redir_to_bad_fd255.tests
Executable file
3
shell/hush_test/hush-redir/redir_to_bad_fd255.tests
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
# ash uses fd 10 (usually) for reading the script
|
||||||
|
echo LOST >&255
|
||||||
|
echo OK
|
1
shell/hush_test/hush-redir/redir_to_bad_fd3.right
Normal file
1
shell/hush_test/hush-redir/redir_to_bad_fd3.right
Normal file
@ -0,0 +1 @@
|
|||||||
|
hush: can't duplicate file descriptor: Bad file descriptor
|
3
shell/hush_test/hush-redir/redir_to_bad_fd3.tests
Executable file
3
shell/hush_test/hush-redir/redir_to_bad_fd3.tests
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
# ash uses fd 10 (usually) for reading the script
|
||||||
|
echo LOST >&3
|
||||||
|
echo OK
|
Loading…
x
Reference in New Issue
Block a user