aboutsummaryrefslogtreecommitdiff
path: root/toys/pending
diff options
context:
space:
mode:
Diffstat (limited to 'toys/pending')
-rw-r--r--toys/pending/sh.c199
-rw-r--r--toys/pending/telnet.c53
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();
}
}