From 4785b7611de7eae714941b2e648ffa85c336108b Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Tue, 27 Apr 2021 01:51:43 -0500 Subject: Don't send reverse DNS lookups out into the world for something that's mostly only safe to use behind a firewall or through a VPN these days. --- toys/pending/telnetd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toys/pending/telnetd.c b/toys/pending/telnetd.c index 032104f2..235bd174 100644 --- a/toys/pending/telnetd.c +++ b/toys/pending/telnetd.c @@ -162,7 +162,7 @@ static int new_session(int sockfd) if (TT.fork_pid < 0) perror_exit("fork"); if (getpeername(sockfd, (void *)&sa, &sl)) perror_exit("getpeername"); - if (getnameinfo((void *)&sa, sl, toybuf, sizeof(toybuf), NULL, 0, 0)) + if (getnameinfo((void *)&sa, sl, toybuf, sizeof(toybuf), NULL, 0, NI_NUMERICHOST)) perror_exit("getnameinfo"); write_issue(tty_name); -- cgit v1.2.3 From 2c30d4f7a6a6ee16f9149519f6e2634b58c281cb Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Mon, 26 Apr 2021 15:13:24 -0700 Subject: More line buffering. This patch does two things: 1. Enable line buffering for echo and yes. I found this through test flakiness from the toybox xargs tests running in CI on devices where "echo" is provided by toybox. For `echo y`, GNU echo does one write of "y\n" but toybox echo was doing two writes, which makes it more likely (4% on the heavily-loaded CI machines) for writes from the two processes to be interleaved. 2. Fix line buffering on glibc if you're calling `toybox foo` rather than `foo`. Otherwise we come through once and switch to unbuffered mode, then again and switch to line buffered mode --- which doesn't seem to actually work in glibc unless you specify a buffer (so passing toybuf and sizeof(toybuf) works, but NULL and 0 doesn't). I hit the second issue trying to reproduce the first issue on the desktop rather than on Android. (If you're scratching your head wondering "why yes(1) too, not just echo(1)?", that represents a blind alley I went down when I mistook which tool was in use. It seemed like the same principle should apply, and it matches what other implementations do.) --- main.c | 2 +- toys/other/yes.c | 2 +- toys/posix/echo.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/main.c b/main.c index deea9897..6e3da111 100644 --- a/main.c +++ b/main.c @@ -95,7 +95,7 @@ void toy_singleinit(struct toy_list *which, char *argv[]) for (toys.optc = 0; toys.optargs[toys.optc]; toys.optc++); } - if (!(which->flags & TOYFLAG_NOFORK)) { + if (strcmp(which->name, "toybox") && !(which->flags & TOYFLAG_NOFORK)) { toys.old_umask = umask(0); if (!(which->flags & TOYFLAG_UMASK)) umask(toys.old_umask); diff --git a/toys/other/yes.c b/toys/other/yes.c index 773a5a88..8edba0ae 100644 --- a/toys/other/yes.c +++ b/toys/other/yes.c @@ -2,7 +2,7 @@ * * Copyright 2007 Rob Landley -USE_YES(NEWTOY(yes, NULL, TOYFLAG_USR|TOYFLAG_BIN)) +USE_YES(NEWTOY(yes, NULL, TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LINEBUF)) config YES bool "yes" diff --git a/toys/posix/echo.c b/toys/posix/echo.c index b546e94b..9b7b9222 100644 --- a/toys/posix/echo.c +++ b/toys/posix/echo.c @@ -9,7 +9,7 @@ * We also honor -- to _stop_ option parsing (bash doesn't, we go with * consistency over compatibility here). -USE_ECHO(NEWTOY(echo, "^?Een[-eE]", TOYFLAG_BIN|TOYFLAG_MAYFORK)) +USE_ECHO(NEWTOY(echo, "^?Een[-eE]", TOYFLAG_BIN|TOYFLAG_MAYFORK|TOYFLAG_LINEBUF)) config ECHO bool "echo" -- cgit v1.2.3 From a28ad6de961169acf50d96427259f9eba6039b5d Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Tue, 27 Apr 2021 02:40:54 -0500 Subject: Use cheaper test that works with "toybox" name as a prefix. --- main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.c b/main.c index 6e3da111..d5abe6e6 100644 --- a/main.c +++ b/main.c @@ -95,7 +95,7 @@ void toy_singleinit(struct toy_list *which, char *argv[]) for (toys.optc = 0; toys.optargs[toys.optc]; toys.optc++); } - if (strcmp(which->name, "toybox") && !(which->flags & TOYFLAG_NOFORK)) { + if (!(CFG_TOYBOX && which == toy_list) && !(which->flags & TOYFLAG_NOFORK)) { toys.old_umask = umask(0); if (!(which->flags & TOYFLAG_UMASK)) umask(toys.old_umask); -- cgit v1.2.3 From de04ee7f8dede68073518fc8ed3889051a527157 Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Tue, 27 Apr 2021 06:51:05 -0500 Subject: Make toysh actually run a shell function. --- toys/pending/sh.c | 84 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 44 insertions(+), 40 deletions(-) diff --git a/toys/pending/sh.c b/toys/pending/sh.c index fd72037a..70d7280c 100644 --- a/toys/pending/sh.c +++ b/toys/pending/sh.c @@ -2532,42 +2532,41 @@ static int parse_line(char *line, struct sh_pipeline **ppl, arg[pl->count].c = 0; if (s[2] == '<') pl->here++; // <<< doesn't load more data } - pl = 0; - } - if (done) break; - s = 0; - // Did we just end a function? - if (ex == (void *)1) { - struct sh_function *funky; + // Did we just end a function? + if (ex == (void *)1) { + struct sh_function *funky; - // function must be followed by a compound statement for some reason - if ((*ppl)->prev->type != 3) { - s = *(*ppl)->prev->arg->v; - goto flush; - } + // function must be followed by a compound statement for some reason + if ((*ppl)->prev->type != 3) { + s = *(*ppl)->prev->arg->v; + goto flush; + } - // Back up to saved function() statement and create sh_function - free(dlist_lpop(expect)); - pl = (void *)(*expect)->data; - funky = xmalloc(sizeof(struct sh_function)); - funky->refcount = 1; - funky->name = *pl->arg->v; - *pl->arg->v = (void *)funky; - - // Chop out pipeline segments added since saved function - funky->pipeline = pl->next; - pl->next->prev = (*ppl)->prev; - (*ppl)->prev->next = pl->next; - pl->next = *ppl; - (*ppl)->prev = pl; - - // Immature function has matured (meaning cleanup is different) - pl->type = 'F'; + // Back up to saved function() statement and create sh_function + free(dlist_lpop(expect)); + pl = (void *)(*expect)->data; + funky = xmalloc(sizeof(struct sh_function)); + funky->refcount = 1; + funky->name = *pl->arg->v; + *pl->arg->v = (void *)funky; + + // Chop out pipeline segments added since saved function + funky->pipeline = pl->next; + pl->next->prev = (*ppl)->prev; + (*ppl)->prev->next = pl->next; + pl->next = *ppl; + (*ppl)->prev = pl; + + // Immature function has matured (meaning cleanup is different) + pl->type = 'F'; + free(dlist_lpop(expect)); + ex = *expect ? (*expect)->prev->data : 0; + } pl = 0; - free(dlist_lpop(expect)); - ex = *expect ? (*expect)->prev->data : 0; } + if (done) break; + s = 0; // skip leading whitespace/comment here to know where next word starts while (isspace(*start)) ++start; @@ -2575,7 +2574,7 @@ static int parse_line(char *line, struct sh_pipeline **ppl, // Parse next word and detect overflow (too many nested quotes). if ((end = parse_word(start, 0, 0)) == (void *)1) goto flush; -//dprintf(2, "%d %p %s word=%.*s\n", getpid(), pl, ex, (int)(end-start), end ? start : ""); +//dprintf(2, "%d %p %s word=%.*s\n", getpid(), pl, (ex != (void *)1) ? ex : "function", (int)(end-start), end ? start : ""); if (pl && pl->type == 'f' && arg->c == 1 && (end-start!=1 || *start!='(')) { funky: @@ -2604,7 +2603,7 @@ funky: // Ok, we have a word. What does it _mean_? // case/esac parsing is weird (unbalanced parentheses!), handle first - i = ex && !strcmp(ex, "esac") && + i = (unsigned long)ex>1 && !strcmp(ex, "esac") && ((pl->type && pl->type != 3) || (*start==';' && end-start>1)); if (i) { @@ -2644,7 +2643,7 @@ funky: } // "for" on its own line is an error. - if (arg->c == 1 && ex && !memcmp(ex, "do\0A", 4)) { + if (arg->c == 1 && (unsigned long)ex>1 && !memcmp(ex, "do\0A", 4)) { s = "newline"; goto flush; } @@ -2735,7 +2734,7 @@ funky: free(s); s = 0; // TODO can't have ; between "for i" and in or do. (Newline yes, ; no. Why?) - if (!arg->c && ex && !memcmp(ex, "do\0C", 4)) continue; + if (!arg->c && (unsigned long)ex>1 && !memcmp(ex, "do\0C", 4)) continue; // ;; and friends only allowed in case statements } else if (*s == ';') goto flush; @@ -2747,7 +2746,7 @@ funky: continue; // a for/select must have at least one additional argument on same line - } else if (ex && !memcmp(ex, "do\0A", 4)) { + } else if ((unsigned long)ex>1 && !memcmp(ex, "do\0A", 4)) { // Sanity check and break the segment if (strncmp(s, "((", 2) && *varend(s)) goto flush; @@ -2760,7 +2759,7 @@ funky: } else if (arg->c>1) continue; // Do we expect something that _must_ come next? (no multiple statements) - if (ex) { + if ((unsigned long)ex>1) { // The "test" part of for/select loops can have (at most) one "in" line, // for {((;;))|name [in...]} do if (!memcmp(ex, "do\0C", 4)) { @@ -2805,7 +2804,7 @@ funky: if (*expect && !(*expect)->prev->data) free(dlist_lpop(expect)); // if can't end a statement here skip next few tests - } else if (!ex); + } else if ((unsigned long)ex<2); // If we got here we expect a specific word to end this block: is this it? else if (!strcmp(s, ex)) { @@ -2887,7 +2886,12 @@ flush: if (s) syntax_err(s); llist_traverse(*ppl, free_pipeline); *ppl = 0; - llist_traverse(*expect, free); + while (*expect) { + struct double_list *del = dlist_pop(expect); + + if (del->data != (void *)1) free(del->data); + free(del); + } *expect = 0; return 0-!!s; @@ -3046,7 +3050,7 @@ static void run_lines(void) 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(), s, ss, ctl, TT.ff->pl->type); +//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); if (!pplist) TT.hfd = 10; // Skip disabled blocks, handle pipes and backgrounding -- cgit v1.2.3 From da735a2052f64cd53a8b79596946ee187cc58a81 Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Tue, 27 Apr 2021 07:10:11 -0500 Subject: Make toysh function return properly and run next statement. --- toys/pending/sh.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/toys/pending/sh.c b/toys/pending/sh.c index 70d7280c..d0e73dc8 100644 --- a/toys/pending/sh.c +++ b/toys/pending/sh.c @@ -2366,6 +2366,7 @@ 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); @@ -2450,7 +2451,7 @@ static struct sh_pipeline *add_pl(struct sh_pipeline **ppl, struct sh_arg **arg) { struct sh_pipeline *pl = xzalloc(sizeof(struct sh_pipeline)); - *arg = pl->arg; + if (arg) *arg = pl->arg; pl->lineno = TT.LINENO; dlist_add_nomalloc((void *)ppl, (void *)pl); @@ -2557,6 +2558,7 @@ static int parse_line(char *line, struct sh_pipeline **ppl, (*ppl)->prev->next = pl->next; pl->next = *ppl; (*ppl)->prev = pl; + dlist_terminate(funky->pipeline = add_pl(&funky->pipeline, 0)); // Immature function has matured (meaning cleanup is different) pl->type = 'F'; -- cgit v1.2.3 From 137ab99aa35a94ad1519b294906522ce108e3451 Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Wed, 28 Apr 2021 03:07:49 -0500 Subject: Toysh don't free function arguments before function returns. --- toys/pending/sh.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/toys/pending/sh.c b/toys/pending/sh.c index d0e73dc8..3ae66f62 100644 --- a/toys/pending/sh.c +++ b/toys/pending/sh.c @@ -2327,6 +2327,10 @@ 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(); + if (funk != TT.funcslen) { + TT.ff->delete = pp->delete; + pp->delete = 0; + } addvar(0, TT.ff); // function context (not source) so end_function deletes locals = 1; } -- cgit v1.2.3