aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--shell/ash.c142
1 files changed, 97 insertions, 45 deletions
diff --git a/shell/ash.c b/shell/ash.c
index 6de71f6d7..22575ffbc 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -62,6 +62,10 @@
#include <termios.h>
#endif
+#ifndef PIPE_BUF
+#define PIPE_BUF 4096 /* amount of buffering in a pipe */
+#endif
+
#if defined(__uClinux__)
#error "Do not even bother, ash will not run on uClinux"
#endif
@@ -4285,7 +4289,6 @@ cmdtxt(union node *n)
union node *np;
struct nodelist *lp;
const char *p;
- char s[2];
if (!n)
return;
@@ -4417,14 +4420,11 @@ cmdtxt(union node *n)
case NFROMTO:
p = "<>";
redir:
- s[0] = n->nfile.fd + '0';
- s[1] = '\0';
- cmdputs(s);
+ cmdputs(utoa(n->nfile.fd));
cmdputs(p);
if (n->type == NTOFD || n->type == NFROMFD) {
- s[0] = n->ndup.dupfd + '0';
- p = s;
- goto dotail2;
+ cmdputs(utoa(n->ndup.dupfd));
+ break;
}
n = n->nfile.fname;
goto donode;
@@ -4675,11 +4675,6 @@ stoppedjobs(void)
#define EMPTY -2 /* marks an unused slot in redirtab */
#define CLOSED -3 /* marks a slot of previously-closed fd */
-#ifndef PIPE_BUF
-# define PIPESIZE 4096 /* amount of buffering in a pipe */
-#else
-# define PIPESIZE PIPE_BUF
-#endif
/*
* Open a file in noclobber mode.
@@ -4756,7 +4751,7 @@ openhere(union node *redir)
ash_msg_and_raise_error("pipe call failed");
if (redir->type == NHERE) {
len = strlen(redir->nhere.doc->narg.text);
- if (len <= PIPESIZE) {
+ if (len <= PIPE_BUF) {
full_write(pip[1], redir->nhere.doc->narg.text, len);
goto out;
}
@@ -4860,19 +4855,40 @@ copyfd(int from, int to)
if (newfd < 0) {
if (errno == EMFILE)
return EMPTY;
+ /* Happens when source fd is not open: try "echo >&99" */
ash_msg_and_raise_error("%d: %m", from);
}
return newfd;
}
/* Struct def and variable are moved down to the first usage site */
+struct two_fd_t {
+ int orig, copy;
+};
struct redirtab {
struct redirtab *next;
- int renamed[10];
int nullredirs;
+ int pair_count;
+ struct two_fd_t two_fd[0];
};
#define redirlist (G_var.redirlist)
+static int need_to_remember(struct redirtab *rp, int fd)
+{
+ int i;
+
+ if (!rp) /* remebering was not requested */
+ return 0;
+
+ for (i = 0; i < rp->pair_count; i++) {
+ if (rp->two_fd[i].orig == fd) {
+ /* already remembered */
+ return 0;
+ }
+ }
+ return 1;
+}
+
/*
* 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
@@ -4887,24 +4903,36 @@ static void
redirect(union node *redir, int flags)
{
struct redirtab *sv;
+ int sv_pos;
int i;
int fd;
int newfd;
+ int copied_fd2 = -1;
g_nullredirs++;
if (!redir) {
return;
}
+
sv = NULL;
+ sv_pos = 0;
INT_OFF;
if (flags & REDIR_PUSH) {
- sv = ckmalloc(sizeof(*sv));
+ union node *tmp = redir;
+ do {
+ sv_pos++;
+ tmp = tmp->nfile.next;
+ } while (tmp);
+ sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
sv->next = redirlist;
+ sv->pair_count = sv_pos;
redirlist = sv;
sv->nullredirs = g_nullredirs - 1;
g_nullredirs = 0;
- for (i = 0; i < 10; i++)
- sv->renamed[i] = EMPTY;
+ while (sv_pos > 0) {
+ sv_pos--;
+ sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
+ }
}
do {
@@ -4918,19 +4946,25 @@ redirect(union node *redir, int flags)
if (fd == newfd) {
/* Descriptor wasn't open before redirect.
* Mark it for close in the future */
- if (sv && sv->renamed[fd] == EMPTY)
- sv->renamed[fd] = CLOSED;
+ if (need_to_remember(sv, fd)) {
+ if (fd == 2)
+ copied_fd2 = CLOSED; /// do we need this?
+ sv->two_fd[sv_pos].orig = fd;
+ sv->two_fd[sv_pos].copy = CLOSED;
+ sv_pos++;
+ }
continue;
}
}
- if (sv && sv->renamed[fd] == EMPTY) {
+ if (need_to_remember(sv, fd)) {
/* Copy old descriptor */
i = fcntl(fd, F_DUPFD, 10);
if (i == -1) {
i = errno;
if (i != EBADF) {
/* Strange error (e.g. "too many files" EMFILE?) */
- /*if (newfd >= 0)*/ close(newfd);
+ if (newfd >= 0)
+ close(newfd);
errno = i;
ash_msg_and_raise_error("%d: %m", fd);
/* NOTREACHED */
@@ -4938,10 +4972,15 @@ redirect(union node *redir, int flags)
/* EBADF: it is not open - ok */
} else {
/* fd is open, save its copy */
-//TODO: CLOEXEC the copy? currently these extra "saved" fds are closed
-// in popredir() in the child, preventing them from leaking into child.
-// (popredir() also cleans up the mess in case of failures)
- sv->renamed[fd] = i;
+/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
+ * are closed in popredir() in the child, preventing them from leaking
+ * into child. (popredir() also cleans up the mess in case of failures)
+ */
+ if (fd == 2)
+ copied_fd2 = i;
+ sv->two_fd[sv_pos].orig = fd;
+ sv->two_fd[sv_pos].copy = i;
+ sv_pos++;
close(fd);
}
} else {
@@ -4960,8 +4999,8 @@ redirect(union node *redir, int flags)
} while ((redir = redir->nfile.next) != NULL);
INT_ON;
- if ((flags & REDIR_SAVEFD2) && sv && sv->renamed[2] >= 0)
- preverrout_fd = sv->renamed[2];
+ if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
+ preverrout_fd = copied_fd2;
}
/*
@@ -4977,18 +5016,19 @@ popredir(int drop)
return;
INT_OFF;
rp = redirlist;
- for (i = 0; i < 10; i++) {
- if (rp->renamed[i] == CLOSED) {
+ for (i = 0; i < rp->pair_count; i++) {
+ int fd = rp->two_fd[i].orig;
+ if (rp->two_fd[i].copy == CLOSED) {
if (!drop)
- close(i);
+ close(fd);
continue;
}
- if (rp->renamed[i] != EMPTY) {
+ if (rp->two_fd[i].copy != EMPTY) {
if (!drop) {
- close(i);
- copyfd(rp->renamed[i], i);
+ close(fd);
+ copyfd(rp->two_fd[i].copy, fd);
}
- close(rp->renamed[i]);
+ close(rp->two_fd[i].copy);
}
}
redirlist = rp->next;
@@ -10003,12 +10043,15 @@ makename(void)
static void
fixredir(union node *n, const char *text, int err)
{
+ int fd;
+
TRACE(("Fix redir %s %d\n", text, err));
if (!err)
n->ndup.vname = NULL;
- if (isdigit(text[0]) && text[1] == '\0')
- n->ndup.dupfd = text[0] - '0';
+ fd = bb_strtou(text, NULL, 10);
+ if (!errno && fd >= 0)
+ n->ndup.dupfd = fd;
else if (LONE_DASH(text))
n->ndup.dupfd = -1;
else {
@@ -10630,7 +10673,7 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
}
c = pgetc_macro();
- } /* for(;;) */
+ } /* for (;;) */
}
endword:
#if ENABLE_ASH_MATH_SUPPORT
@@ -10650,12 +10693,20 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
if (eofmark == NULL) {
if ((c == '>' || c == '<')
&& quotef == 0
- && len <= 2 // THIS LIMITS fd to 1 char: N>file, but no NN>file!
- && (*out == '\0' || isdigit(*out))
+ // && len <= 2 // THIS LIMITS fd to 1 char: N>file, but no NN>file!
+ // && (*out == '\0' || isdigit(*out))
) {
- PARSEREDIR();
- lasttoken = TREDIR;
- return lasttoken;
+ int maxlen = 9 + 1; /* max 9 digit fd#: 999999999 */
+ char *np = out;
+ while (--maxlen && isdigit(*np))
+ np++;
+ if (*np == '\0') {
+ PARSEREDIR(); /* passed as params: out, c */
+ lasttoken = TREDIR;
+ return lasttoken;
+ }
+ /* else: non-number X seen, interpret it
+ * as "NNNX>file" = "NNNX >file" */
}
pungetc();
}
@@ -10710,7 +10761,8 @@ checkend: {
* first character of the redirection operator.
*/
parseredir: {
- char fd = *out;
+ /* out is already checked to be a valid number or "" */
+ int fd = (*out == '\0' ? -1 : atoi(out));
union node *np;
np = stzalloc(sizeof(struct nfile));
@@ -10762,8 +10814,8 @@ parseredir: {
break;
}
}
- if (fd != '\0')
- np->nfile.fd = fd - '0';
+ if (fd >= 0)
+ np->nfile.fd = fd;
redirnode = np;
goto parseredir_return;
}