diff options
Diffstat (limited to 'toys/pending')
-rw-r--r-- | toys/pending/sh.c | 199 | ||||
-rw-r--r-- | toys/pending/telnet.c | 53 |
2 files changed, 163 insertions, 89 deletions
diff --git a/toys/pending/sh.c b/toys/pending/sh.c index 3ae66f62..ab2408a0 100644 --- a/toys/pending/sh.c +++ b/toys/pending/sh.c @@ -45,11 +45,13 @@ USE_SH(NEWTOY(eval, 0, TOYFLAG_NOFORK)) USE_SH(NEWTOY(exec, "^cla:", TOYFLAG_NOFORK)) USE_SH(NEWTOY(exit, 0, TOYFLAG_NOFORK)) USE_SH(NEWTOY(export, "np", TOYFLAG_NOFORK)) +USE_SH(NEWTOY(jobs, "lnprs", TOYFLAG_NOFORK)) USE_SH(NEWTOY(set, 0, TOYFLAG_NOFORK)) USE_SH(NEWTOY(shift, ">1", TOYFLAG_NOFORK)) USE_SH(NEWTOY(source, "<1", TOYFLAG_NOFORK)) USE_SH(OLDTOY(., source, TOYFLAG_NOFORK)) USE_SH(NEWTOY(unset, "fvn[!fv]", TOYFLAG_NOFORK)) +USE_SH(NEWTOY(wait, "n", TOYFLAG_NOFORK)) USE_SH(NEWTOY(sh, "0(noediting)(noprofile)(norc)sc:i", TOYFLAG_BIN)) USE_SH(OLDTOY(toysh, sh, TOYFLAG_BIN)) @@ -201,6 +203,18 @@ config SOURCE usage: source FILE [ARGS...] Read FILE and execute commands. Any ARGS become positional parameters. + +config WAIT + bool + default n + depends on SH + help + usage: wait [-n] [ID...] + + Wait for background processes to exit, returning its exit code. + ID can be PID or job, with no IDs waits for all backgrounded processes. + + -n Wait for next process to exit */ #define FOR_sh @@ -271,7 +285,7 @@ GLOBALS( struct sh_process *next, *prev; // | && || struct arg_list *delete; // expanded strings // undo redirects, a=b at start, child PID, exit status, has !, job # - int *urd, envlen, pid, exit, not, job; + int *urd, envlen, pid, exit, not, job, dash; long long when; // when job backgrounded/suspended struct sh_arg *raw, arg; } *pp; // currently running process @@ -752,6 +766,7 @@ static void subshell_callback(char **argv) // TODO: test $$ in (nommu) } +// TODO what happens when you background a function? // turn a parsed pipeline back into a string. static char *pl2str(struct sh_pipeline *pl, int one) { @@ -2327,6 +2342,7 @@ static struct sh_process *run_command(void) // Create new function context to hold local vars? if (funk != TT.funcslen || (envlen && pp->arg.c) || TT.ff->blk->pipe) { call_function(); +// TODO function needs to run asynchronously in pipeline if (funk != TT.funcslen) { TT.ff->delete = pp->delete; pp->delete = 0; @@ -2370,7 +2386,6 @@ static struct sh_process *run_command(void) else if (funk != TT.funcslen) { (TT.ff->func = TT.functions[funk])->refcount++; TT.ff->pl = TT.ff->func->pipeline; - TT.ff->next->pl = TT.ff->next->pl->next; TT.ff->arg = pp->arg; } else { struct toy_list *tl = toy_find(*pp->arg.v); @@ -2420,11 +2435,16 @@ static struct sh_process *run_command(void) return pp; } -static void free_process(void *ppp) +static int free_process(struct sh_process *pp) { - struct sh_process *pp = ppp; + int rc; + + if (!pp) return 127; + rc = pp->exit; llist_traverse(pp->delete, llist_free_arg); free(pp); + + return rc; } // if then fi for while until select done done case esac break continue return @@ -2563,6 +2583,7 @@ static int parse_line(char *line, struct sh_pipeline **ppl, pl->next = *ppl; (*ppl)->prev = pl; dlist_terminate(funky->pipeline = add_pl(&funky->pipeline, 0)); + funky->pipeline->type = 'f'; // Immature function has matured (meaning cleanup is different) pl->type = 'F'; @@ -2903,6 +2924,80 @@ flush: return 0-!!s; } +// Find + and - jobs. Returns index of plus, writes minus to *minus +int find_plus_minus(int *minus) +{ + long long when, then; + int i, plus; + + if (minus) *minus = 0; + for (then = i = plus = 0; i<TT.jobs.c; i++) { + if ((when = ((struct sh_process *)TT.jobs.v[i])->when) > then) { + then = when; + if (minus) *minus = plus; + plus = i; + } + } + + return plus; +} + +char is_plus_minus(int i, int plus, int minus) +{ + return (i == plus) ? '+' : (i == minus) ? '-' : ' '; +} + + +// We pass in dash to avoid looping over every job each time +char *show_job(struct sh_process *pp, char dash) +{ + char *s = "Run", *buf = 0; + int i, j, len, len2; + +// TODO Terminated (Exited) + if (pp->exit<0) s = "Stop"; + else if (pp->exit>126) s = "Kill"; + else if (pp->exit>0) s = "Done"; + for (i = len = len2 = 0;; i++) { + len += snprintf(buf, len2, "[%d]%c %-6s", pp->job, dash, s); + for (j = 0; j<pp->raw->c; j++) + len += snprintf(buf, len2, " %s"+!j, pp->raw->v[j]); + if (!i) buf = xmalloc(len2 = len+1); + else break; + } + + return buf; +} + +// Wait for pid to exit and remove from jobs table, returning process or 0. +struct sh_process *wait_job(int pid, int nohang) +{ + struct sh_process *pp; + int ii, status, minus, plus; + + if (TT.jobs.c<1) return 0; + for (;;) { + errno = 0; + if (1>(pid = waitpid(pid, &status, nohang ? WNOHANG : 0))) { + if (!nohang && errno==EINTR && !toys.signal) continue; + return 0; + } + for (ii = 0; ii<TT.jobs.c; ii++) { + pp = (void *)TT.jobs.v[ii]; + if (pp->pid == pid) break; + } + if (ii == TT.jobs.c) continue; + if (pid<1) return 0; + if (!WIFSTOPPED(status) && !WIFCONTINUED(status)) break; + } + plus = find_plus_minus(&minus); + memmove(TT.jobs.v+ii, TT.jobs.v+ii+1, (TT.jobs.c--)-ii); + pp->exit = WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status)+128; + pp->dash = is_plus_minus(ii, plus, minus); + + return pp; +} + // wait for every process in a pipeline to end static int wait_pipeline(struct sh_process *pp) { @@ -2918,6 +3013,13 @@ static int wait_pipeline(struct sh_process *pp) rc = pp->not ? !pp->exit : pp->exit; } + while ((pp = wait_job(-1, 1)) && (TT.options&FLAG_i)) { + char *s = show_job(pp, pp->dash); + + dprintf(2, "%s\n", s); + free(s); + } + return rc; } @@ -3049,14 +3151,13 @@ static void run_lines(void) for (;;) { if (!TT.ff->pl) { if (!end_function(1)) break; - - continue; + goto advance; } ctl = TT.ff->pl->end->arg->v[TT.ff->pl->end->arg->c]; s = *TT.ff->pl->arg->v; ss = TT.ff->pl->arg->v[1]; -//dprintf(2, "%d s=%s ss=%s ctl=%s type=%d\n", getpid(), (TT.ff->pl->type == 'F') ? ((struct sh_function *)s)->name : s, ss, ctl, TT.ff->pl->type); +//dprintf(2, "%d s=%s ss=%s ctl=%s type=%d pl=%p ff=%p\n", getpid(), (TT.ff->pl->type == 'F') ? ((struct sh_function *)s)->name : s, ss, ctl, TT.ff->pl->type, TT.ff->pl, TT.ff); if (!pplist) TT.hfd = 10; // Skip disabled blocks, handle pipes and backgrounding @@ -3303,7 +3404,6 @@ dprintf(2, "TODO skipped running for((;;)), need math parser\n"); // end of block } else if (TT.ff->pl->type == 3) { - // If we end a block we're not in, exit subshell if (!TT.ff->blk->next) xexit(); @@ -3340,20 +3440,23 @@ dprintf(2, "TODO skipped running for((;;)), need math parser\n"); // If we ran a process and didn't pipe output, background or wait for exit if (pplist && TT.ff->blk->pout == -1) { if (ctl && !strcmp(ctl, "&")) { + if (!TT.jobs.c) TT.jobcnt = 0; pplist->job = ++TT.jobcnt; arg_add(&TT.jobs, (void *)pplist); + if (TT.options&FLAG_i) dprintf(2, "[%u] %u\n", pplist->job,pplist->pid); } else { toys.exitval = wait_pipeline(pplist); - llist_traverse(pplist, free_process); + llist_traverse(pplist, (void *)free_process); } pplist = 0; } - +advance: // for && and || skip pipeline segment(s) based on return code if (!TT.ff->pl->type || TT.ff->pl->type == 3) { - while (ctl && !strcmp(ctl, toys.exitval ? "&&" : "||")) { - if ((TT.ff->pl = TT.ff->pl->next)->type) TT.ff->pl = TT.ff->pl->end; + for (;;) { ctl = TT.ff->pl->arg->v[TT.ff->pl->arg->c]; + if (!ctl || strcmp(ctl, toys.exitval ? "&&" : "||")) break; + if ((TT.ff->pl = TT.ff->pl->next)->type) TT.ff->pl = TT.ff->pl->end; } } TT.ff->pl = TT.ff->pl->next; @@ -3362,7 +3465,7 @@ dprintf(2, "TODO skipped running for((;;)), need math parser\n"); // clean up any unfinished stuff if (pplist) { toys.exitval = wait_pipeline(pplist); - llist_traverse(pplist, free_process); + llist_traverse(pplist, (void *)free_process); } // exit source context (and function calls on syntax err) @@ -3914,24 +4017,6 @@ void exec_main(void) environ = old; } -// Find + and - jobs. Returns index of plus, writes minus to *minus -int find_plus_minus(int *minus) -{ - long long when, then; - int i, plus; - - if (minus) *minus = 0; - for (then = i = plus = 0; i<TT.jobs.c; i++) { - if ((when = ((struct sh_process *)TT.jobs.v[i])->when) > then) { - then = when; - if (minus) *minus = plus; - plus = i; - } - } - - return plus; -} - // Return T.jobs index or -1 from identifier // Note, we don't return "ambiguous job spec", we return the first hit or -1. // TODO %% %+ %- %?ab @@ -3965,22 +4050,6 @@ int find_job(char *s) return -1; } -// We pass in dash to avoid looping over every job each time -void print_job(int i, char dash) -{ - struct sh_process *pp = (void *)TT.jobs.v[i]; - char *s = "Run"; - int j; - -// TODO Terminated (Exited) - if (pp->exit<0) s = "Stop"; - else if (pp->exit>126) s = "Kill"; - else if (pp->exit>0) s = "Done"; - printf("[%d]%c %-6s", pp->job, dash, s); - for (j = 0; j<pp->raw->c; j++) printf(" %s"+!j, pp->raw->v[j]); - printf("\n"); -} - void jobs_main(void) { int i, j, minus, plus = find_plus_minus(&minus); @@ -3998,7 +4067,9 @@ void jobs_main(void) } } else if ((j = i) >= TT.jobs.c) break; - print_job(i, (i == plus) ? '+' : (i == minus) ? '-' : ' '); + s = show_job((void *)TT.jobs.v[i], is_plus_minus(i, plus, minus)); + printf("%s\n", s); + free(s); } } @@ -4077,3 +4148,35 @@ void source_main(void) free(dlist_pop(&TT.ff)); --TT.srclvl; } + +#define CLEANUP_local +#define FOR_wait +#include "generated/flags.h" + +void wait_main(void) +{ + struct sh_process *pp; + int ii, jj; + long long ll; + char *s; + + // TODO does -o pipefail affect return code here + if (FLAG(n)) toys.exitval = free_process(wait_job(-1, 0)); + else if (!toys.optc) while (TT.jobs.c) { + if (!(pp = wait_job(-1, 0))) break; + } else for (ii = 0; ii<toys.optc; ii++) { + ll = estrtol(toys.optargs[ii], &s, 10); + if (errno || *s) { + if (-1 == (jj = find_job(toys.optargs[ii]))) { + error_msg("%s: bad pid/job", toys.optargs[ii]); + continue; + } + ll = ((struct sh_process *)TT.jobs.v[jj])->pid; + } + if (!(pp = wait_job(ll, 0))) { + if (toys.signal) toys.exitval = 128+toys.signal; + break; + } + toys.exitval = free_process(pp); + } +} diff --git a/toys/pending/telnet.c b/toys/pending/telnet.c index a7ea91e2..6b2c2732 100644 --- a/toys/pending/telnet.c +++ b/toys/pending/telnet.c @@ -24,8 +24,6 @@ config TELNET GLOBALS( int sock; char buf[2048]; // Half sizeof(toybuf) allows a buffer full of IACs. - char iac[128]; - int iac_len; struct termios old_term; struct termios raw_term; uint8_t mode; @@ -51,29 +49,6 @@ static void raw(int raw) tcsetattr(0, TCSADRAIN, raw ? &TT.raw_term : &TT.old_term); } -static void flush_iac(void) -{ - xwrite(TT.sock, TT.iac, TT.iac_len); - TT.iac_len = 0; -} - -static void iac(int n, ...) -{ - va_list va; - - if (TT.iac_len + n >= sizeof(TT.iac)) flush_iac(); - va_start(va, n); - while (n--) TT.iac[TT.iac_len++] = va_arg(va, int); - va_end(va); -} - -static void iacstr(char *str) -{ - if (TT.iac_len) flush_iac(); - xwrite(TT.sock, str, strlen(str)); - TT.iac_len = 0; -} - static void slc(int line) { TT.mode = line ? CM_OFF : CM_ON; @@ -116,8 +91,7 @@ static void handle_esc(void) TT.mode = CM_TRY; TT.echo = TT.sga = 0; set_mode(); - iac(6, IAC, DONT, TELOPT_ECHO, IAC, DONT, TELOPT_SGA); - flush_iac(); + dprintf(TT.sock,"%c%c%c%c%c%c",IAC,DONT,TELOPT_ECHO,IAC,DONT,TELOPT_SGA); goto ret; } } else if (input == 'c') { @@ -125,8 +99,7 @@ static void handle_esc(void) TT.mode = CM_TRY; TT.echo = TT.sga = 1; set_mode(); - iac(6, IAC, DO, TELOPT_ECHO, IAC, DO, TELOPT_SGA); - flush_iac(); + dprintf(TT.sock,"%c%c%c%c%c%c",IAC,DO,TELOPT_ECHO,IAC,DO,TELOPT_SGA); goto ret; } } else if (input == 'z') { @@ -146,32 +119,32 @@ ret: static void handle_wwdd(char opt) { if (opt == TELOPT_ECHO) { - if (TT.request == DO) iac(3, IAC, WONT, TELOPT_ECHO); + if (TT.request == DO) dprintf(TT.sock, "%c%c%c", IAC, WONT, TELOPT_ECHO); if (TT.request == DONT) return; if (TT.echo) { if (TT.request == WILL) return; } else if (TT.request == WONT) return; if (TT.mode != CM_OFF) TT.echo ^= 1; - iac(3, IAC, TT.echo ? DO : DONT, TELOPT_ECHO); + dprintf(TT.sock, "%c%c%c", IAC, TT.echo ? DO : DONT, TELOPT_ECHO); set_mode(); } else if (opt == TELOPT_SGA) { // Suppress Go Ahead if (TT.sga) { if (TT.request == WILL) return; } else if (TT.request == WONT) return; TT.sga ^= 1; - iac(3, IAC, TT.sga ? DO : DONT, TELOPT_SGA); + dprintf(TT.sock, "%c%c%c", IAC, TT.sga ? DO : DONT, TELOPT_SGA); } else if (opt == TELOPT_TTYPE) { // Terminal TYPE - iac(3, IAC, WILL, TELOPT_TTYPE); + dprintf(TT.sock, "%c%c%c", IAC, WILL, TELOPT_TTYPE); } else if (opt == TELOPT_NAWS) { // Negotiate About Window Size unsigned cols = 80, rows = 24; terminal_size(&cols, &rows); - iac(3, IAC, WILL, TELOPT_NAWS); - iac(7, IAC, SB, TELOPT_NAWS, cols>>8, cols, rows>>8, rows); - iac(2, IAC, SE); + dprintf(TT.sock, "%c%c%c%c%c%c%c%c%c%c%c%c", IAC, WILL, TELOPT_NAWS, + IAC, SB, TELOPT_NAWS, cols>>8, cols, rows>>8, rows, + IAC, SE); } else { // Say "no" to anything we don't understand. - iac(3, IAC, (TT.request == WILL) ? DONT : WONT, opt); + dprintf(TT.sock, "%c%c%c", IAC, (TT.request == WILL) ? DONT : WONT, opt); } } @@ -210,9 +183,8 @@ static void handle_server_output(int n) else TT.state = WANT_IAC; } else if (TT.state == SAW_SB_TTYPE) { if (ch == TELQUAL_SEND) { - iac(4, IAC, SB, TELOPT_TTYPE, TELQUAL_IS); - iacstr(getenv("TERM") ?: "NVT"); - iac(2, IAC, SE); + dprintf(TT.sock, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE, TELQUAL_IS, + getenv("TERM") ?: "NVT", IAC, SE); } TT.state = WANT_IAC; } else if (TT.state == WANT_IAC) { @@ -295,6 +267,5 @@ void telnet_main(void) error_exit("Connection closed by foreign host\r"); handle_server_output(n); } - if (TT.iac_len) flush_iac(); } } |