diff options
author | Rob Landley <rob@landley.net> | 2020-01-26 11:12:51 -0600 |
---|---|---|
committer | Rob Landley <rob@landley.net> | 2020-01-26 11:12:51 -0600 |
commit | aefc278c02875d6f47e651d0df79734b7aaef74d (patch) | |
tree | beb046f58af3c9acd0c0bdc4fc66178404829ddc /toys/pending | |
parent | a459a19c9c78433a4ec8546dd8dcf14c68d087c7 (diff) | |
download | toybox-aefc278c02875d6f47e651d0df79734b7aaef74d.tar.gz |
Implement nommu subshell plumbing, <(command), and fix 2>&1
Diffstat (limited to 'toys/pending')
-rw-r--r-- | toys/pending/sh.c | 184 |
1 files changed, 108 insertions, 76 deletions
diff --git a/toys/pending/sh.c b/toys/pending/sh.c index 29c030e7..98f5a17e 100644 --- a/toys/pending/sh.c +++ b/toys/pending/sh.c @@ -97,7 +97,7 @@ GLOBALS( char *command; long lineno; - char **locals; + char **locals, *subshell_env; struct double_list functions; unsigned options, jobcnt; int hfd; // next high filehandle (>= 10) @@ -243,6 +243,18 @@ int skip_quote(char *s, int dquot, int *depth) return i; } +// add argument to an arg_list +void add_arg(struct arg_list **list, char *arg) +{ + struct arg_list *al; + + if (!list) return; + al = xmalloc(sizeof(struct arg_list)); + al->next = *list; + al->arg = arg; + *list = al; +} + // quote removal, brace, tilde, parameter/variable, $(command), // $((arithmetic)), split, path #define NO_PATH (1<<0) @@ -337,13 +349,7 @@ TODO this recurses // Record result. if (old==new && (flags&FORCE_COPY)) new = xstrdup(new); - if (old!=new && delete) { - struct arg_list *al = xmalloc(sizeof(struct arg_list)); - - al->next = *delete; - al->arg = new; - *delete = al; - } + if (old!=new) add_arg(delete, new); array_add(&arg->v, arg->c++, new); } @@ -611,9 +617,34 @@ struct sh_function { static int parse_line(char *line, struct sh_function *sp); void free_function(struct sh_function *sp); -void run_subshell(struct sh_pipeline *sp) +// TODO: waitpid(WNOHANG) to clean up zombies and catch background& ending + +static void subshell_callback(void) { - dprintf(2, "TODO: run_subshell\n"); + TT.subshell_env = xmprintf("@%d,%d=", getpid(), getppid()); + xsetenv(TT.subshell_env, 0); + TT.subshell_env[strlen(TT.subshell_env)-1] = 0; +} + +// Pass environment and command string to child shell +static int run_subshell(char *str, int len) +{ + int pipes[2], pid, i; + + if (pipe(pipes) || 254 != dup2(pipes[0], 254)) return 1; + close(pipes[0]); + + fcntl(pipes[1], F_SETFD, FD_CLOEXEC); + + pid = xpopen_setup(0, 0, subshell_callback); + close(254); + + if (TT.locals) + for (i = 0; TT.locals[i]; i++) dprintf(pipes[1], "%s\n", TT.locals[i]); + dprintf(pipes[1], "%.*s\n", len, str); + close(pipes[1]); + + return pid; } // Expand arguments and perform redirections. Return new process object with @@ -639,32 +670,27 @@ static struct sh_process *expand_redir(struct sh_arg *arg, int envlen, int *urd) // Handle <() >() redirectionss if ((*s == '<' || *s == '>') && s[1] == '(') { - struct sh_function sf; int pipes[2], *uu = 0, dd; // Grab subshell data - memset(&sf, 0, sizeof(struct sh_function)); - errno = 0; - if (parse_line(s+1, &sf) || pipe(pipes)) { + if (pipe(pipes)) { perror_msg_raw(s); - free_function(&sf); pp->exit = 1; return pp; } -// pipe[1] > pipe[0]->0 -// pipe[0] < pipe[1]->1 // Perform input or output redirect and launch process dd = *s == '<'; save_redirect(&uu, pipes[dd], dd); - fcntl(pipes[!dd], F_SETFD, FD_CLOEXEC); - run_subshell(sf.pipeline); // ignore errors, don't track + close(pipes[dd]); + run_subshell(s+2, strlen(s+2)-1); // ignore errors, don't track unredirect(uu); save_redirect(&urd, -1, pipes[!dd]); - // Argument is /dev/fd/%d with pipe filehandle - dlist_add((void *)&pp->delete, ss = xmprintf("/dev/fd/%d", pipes[dd])); + // bash uses /dev/fd/%d which requires /dev/fd to be a symlink to + // /proc/self/fd so we just produce that directly. + add_arg(&pp->delete, ss = xmprintf("/proc/self/fd/%d", pipes[!dd])); array_add(&pp->arg.v, pp->arg.c++, ss); continue; @@ -755,7 +781,7 @@ static struct sh_process *expand_redir(struct sh_arg *arg, int envlen, int *urd) free(tmp); if (bad) break; - // from>=0 means it's fd<<2 (new fd to dup2() after vfork()) plus + // from is fd<<2 (new fd to dup2() after vfork()) plus // 2 if we should close(from>>2) after dup2(from>>2, to), // 1 if we should close but dup for nofork recovery (ala <&2-) @@ -772,7 +798,7 @@ static struct sh_process *expand_redir(struct sh_arg *arg, int envlen, int *urd) } from = (ss==sss) ? to : atoi(sss); - if (*ss == '-') saveclose++; + saveclose = 2-(*ss == '-'); } else { // Permissions to open external file with: < > >> <& >& <> >| &>> &> @@ -804,8 +830,8 @@ static struct sh_process *expand_redir(struct sh_arg *arg, int envlen, int *urd) setvar(cv, TAKE_MEM); cv = 0; } - if (saveclose && save_redirect(&pp->urd, -1, from)) bad++; - close(from); + if ((saveclose&1) && save_redirect(&pp->urd, -1, from)) bad++; + if (!(saveclose&2)) close(from); if (bad) break; } @@ -1382,6 +1408,24 @@ static void dump_state(struct sh_function *sp) } } +void dump_filehandles(char *when) +{ + int fd = open("/proc/self/fd", O_RDONLY); + DIR *dir = fdopendir(fd); + char buf[256]; + + if (dir) { + struct dirent *dd; + + while ((dd = readdir(dir))) { + if (atoi(dd->d_name)!=fd && 0<readlinkat(fd, dd->d_name, buf,sizeof(buf))) + dprintf(2, "OPEN %s %d: %s = %s\n", when, getpid(), dd->d_name, buf); + } + closedir(dir); + } + close(fd); +} + /* Flow control statements: if/then/elif/else/fi, for select while until/do/done, case/esac, @@ -1686,8 +1730,6 @@ static int sh_run(char *new) struct sh_function scratch; int rc; -// TODO: parse with len? (End early?) - memset(&scratch, 0, sizeof(struct sh_function)); if (!parse_line(new, &scratch)) run_function(&scratch); free_function(&scratch); @@ -1757,57 +1799,48 @@ static void do_prompt(char *prompt) writeall(2, toybuf, len); } +// sanitize environment and handle nommu subshell handoff void subshell_imports(void) { -/* - // TODO cull local variables because 'env "()=42" env | grep 42' works. - - // vfork() means subshells have to export and then re-import locals/functions - sprintf(toybuf, "(%d#%d)", getpid(), getppid()); - if ((s = getenv(toybuf))) { - char *from, *to, *ss; - - unsetenv(toybuf); - ss = s; - - // Loop through packing \\ until \0 - for (from = to = s; *from; from++, to++) { - *to = *from; - if (*from != '\\') continue; - if (from[1] == '\\' || from[1] == '0') from++; - if (from[1] != '0') continue; - *to = 0; - - // save chunk - for (ss = s; ss<to; ss++) { - if (*ss == '=') { - // first char of name is variable type ala declare - if (s+1<ss && strchr("aAilnru", *s)) { - setvar(ss, *s); - - break; - } - } else if (!strncmp(ss, "(){", 3)) { - FILE *ff = fmemopen(s, to-s, "r"); + int to, from, pid = 0, ppid = 0, len; + struct stat st; + FILE *fp; + char *s; - while ((new = xgetline(ff, 0))) { - if ((prompt = parse_line(new, &scratch))<0) break; - free(new); - } - if (!prompt) { - add_function(s, scratch.pipeline); - free_function(&scratch); - break; - } - fclose(ff); - } else if (!isspace(*s) && !ispunct(*s)) continue; + // Ensure environ copied and toys.envc set, and clean out illegal entries + xunsetenv(""); + for (to = from = 0; (s = environ[from]); from++) { - error_exit("bad locals"); - } - s = from+1; + // If nommu subshell gets handoff + if (!CFG_TOYBOX_FORK && !toys.stacktop) { + len = 0; + sscanf(s, "@%d,%d%n", &pid, &ppid, &len); + if (len && s[len]) pid = ppid = 0; } + + // Filter out non-shell variable names + for (len = 0; s[len] && ((s[len] == '_') || !ispunct(s[len])); len++); + if (s[len] == '=') environ[to++] = environ[from]; } -*/ + environ[toys.optc = to] = 0; + +//TODO indexed array,associative array,integer,local,nameref,readonly,uppercase +// if (s+1<ss && strchr("aAilnru", *s)) { + + // sanity check: magic env variable, pipe status + if (CFG_TOYBOX_FORK || toys.stacktop || pid != getpid() || ppid != getppid()) + return; + if (fstat(254, &st) || !S_ISFIFO(st.st_mode)) error_exit(0); + fcntl(254, F_SETFD, FD_CLOEXEC); + fp = fdopen(254, "r"); + + // This is not efficient, could array_add the local vars. +// TODO implicit exec when possible + while ((s = xgetline(fp, 0))) to = sh_run(s); + fclose(fp); + + toys.exitval = to; + xexit(); } void sh_main(void) @@ -1936,14 +1969,15 @@ void cd_main(void) // cancel out . and .. in the string for (from = to = dd; *from;) { - while (*from=='/' && from[1]=='/') from++; - if (*from!='/' || from[1]!='.') *to++ = *from++; + if (*from=='/' && from[1]=='/') from++; + else if (*from!='/' || from[1]!='.') *to++ = *from++; else if (!from[2] || from[2]=='/') from += 2; else if (from[2]=='.' && (!from[3] || from[3]=='/')) { from += 3; while (to>dd && *--to != '/'); } else *to++ = *from++; } + if (to == dd) to++; if (to-dd>1 && to[-1]=='/') to--; *to = 0; } @@ -1960,5 +1994,3 @@ void exit_main(void) { exit(*toys.optargs ? atoi(*toys.optargs) : 0); } - - |