Merge remote-tracking branch 'toybox/master' into HEAD
Change-Id: I6ba143f54cdea32f90c0c4ccc262058104a13180
diff --git a/generated/flags.h b/generated/flags.h
index 774078a..bef3806 100644
--- a/generated/flags.h
+++ b/generated/flags.h
@@ -1892,12 +1892,13 @@
#undef FLAG_n
#endif
-// netcat ^tlLw#<1W#<1p#<1>65535q#<1s:f:46u[!tlL][!Lw][!46] ^tlLw#<1W#<1p#<1>65535q#<1s:f:46u[!tlL][!Lw][!46]
+// netcat ^tlLw#<1W#<1p#<1>65535q#<1s:f:46uU[!tlL][!Lw][!46U] ^tlLw#<1W#<1p#<1>65535q#<1s:f:46uU[!tlL][!Lw][!46U]
#undef OPTSTR_netcat
-#define OPTSTR_netcat "^tlLw#<1W#<1p#<1>65535q#<1s:f:46u[!tlL][!Lw][!46]"
+#define OPTSTR_netcat "^tlLw#<1W#<1p#<1>65535q#<1s:f:46uU[!tlL][!Lw][!46U]"
#ifdef CLEANUP_netcat
#undef CLEANUP_netcat
#undef FOR_netcat
+#undef FLAG_U
#undef FLAG_u
#undef FLAG_6
#undef FLAG_4
@@ -4922,18 +4923,19 @@
#ifndef TT
#define TT this.netcat
#endif
-#define FLAG_u (1<<0)
-#define FLAG_6 (1<<1)
-#define FLAG_4 (1<<2)
-#define FLAG_f (1<<3)
-#define FLAG_s (1<<4)
-#define FLAG_q (1<<5)
-#define FLAG_p (1<<6)
-#define FLAG_W (1<<7)
-#define FLAG_w (1<<8)
-#define FLAG_L (1<<9)
-#define FLAG_l (1<<10)
-#define FLAG_t (1<<11)
+#define FLAG_U (1<<0)
+#define FLAG_u (1<<1)
+#define FLAG_6 (1<<2)
+#define FLAG_4 (1<<3)
+#define FLAG_f (1<<4)
+#define FLAG_s (1<<5)
+#define FLAG_q (1<<6)
+#define FLAG_p (1<<7)
+#define FLAG_W (1<<8)
+#define FLAG_w (1<<9)
+#define FLAG_L (1<<10)
+#define FLAG_l (1<<11)
+#define FLAG_t (1<<12)
#endif
#ifdef FOR_netstat
diff --git a/generated/help.h b/generated/help.h
index 7466a97..65c49d5 100644
--- a/generated/help.h
+++ b/generated/help.h
@@ -122,7 +122,7 @@
#define HELP_netstat "usage: netstat [-pWrxwutneal]\n\nDisplay networking information. Default is netstat -tuwx\n\n-r Routing table\n-a All sockets (not just connected)\n-l Listening server sockets\n-t TCP sockets\n-u UDP sockets\n-w Raw sockets\n-x Unix sockets\n-e Extended info\n-n Don't resolve names\n-W Wide display\n-p Show PID/program name of sockets"
-#define HELP_netcat "usage: netcat [-46t] [-lL COMMAND...] [-u] [-wpq #] [-s addr] {IPADDR PORTNUM|-f FILENAME}\n\nForward stdin/stdout to a file or network connection.\n\n-4 Force IPv4\n-6 Force IPv6\n-L Listen for multiple incoming connections (server mode)\n-W SECONDS timeout for more data on an idle connection\n-f Use FILENAME (ala /dev/ttyS0) instead of network\n-l Listen for one incoming connection\n-p Local port number\n-q Quit SECONDS after EOF on stdin, even if stdout hasn't closed yet\n-s Local source address\n-t Allocate tty (must come before -l or -L)\n-u Use UDP\n-w SECONDS timeout to establish connection\n\nUse \"stty 115200 -F /dev/ttyS0 && stty raw -echo -ctlecho\" with\nnetcat -f to connect to a serial port.\n\nThe command line after -l or -L is executed (as a child process) to handle\neach incoming connection. If blank -l waits for a connection and forwards\nit to stdin/stdout. If no -p specified, -l prints port it bound to and\nbackgrounds itself (returning immediately).\n\nFor a quick-and-dirty server, try something like:\nnetcat -s 127.0.0.1 -p 1234 -tL /bin/bash -l"
+#define HELP_netcat "usage: netcat [-46Ut] [-lL COMMAND...] [-u] [-wpq #] [-s addr] {IPADDR PORTNUM|-f FILENAME}\n\nForward stdin/stdout to a file or network connection.\n\n-4 Force IPv4\n-6 Force IPv6\n-L Listen for multiple incoming connections (server mode)\n-U Use a UNIX domain socket\n-W SECONDS timeout for more data on an idle connection\n-f Use FILENAME (ala /dev/ttyS0) instead of network\n-l Listen for one incoming connection\n-p Local port number\n-q Quit SECONDS after EOF on stdin, even if stdout hasn't closed yet\n-s Local source address\n-t Allocate tty (must come before -l or -L)\n-u Use UDP\n-w SECONDS timeout to establish connection\n\nUse \"stty 115200 -F /dev/ttyS0 && stty raw -echo -ctlecho\" with\nnetcat -f to connect to a serial port.\n\nThe command line after -l or -L is executed (as a child process) to handle\neach incoming connection. If blank -l waits for a connection and forwards\nit to stdin/stdout. If no -p specified, -l prints port it bound to and\nbackgrounds itself (returning immediately).\n\nFor a quick-and-dirty server, try something like:\nnetcat -s 127.0.0.1 -p 1234 -tL /bin/bash -l"
#define HELP_microcom "usage: microcom [-s SPEED] [-X] DEVICE\n\nSimple serial console.\n\n-s Set baud rate to SPEED\n-X Ignore ^@ (send break) and ^] (exit)"
diff --git a/generated/newtoys.h b/generated/newtoys.h
index 0662f39..0b93d2f 100644
--- a/generated/newtoys.h
+++ b/generated/newtoys.h
@@ -169,7 +169,7 @@
USE_NBD_CLIENT(OLDTOY(nbd-client, nbd_client, TOYFLAG_USR|TOYFLAG_BIN))
USE_NBD_CLIENT(NEWTOY(nbd_client, "<3>3ns", 0))
USE_NETCAT(OLDTOY(nc, netcat, TOYFLAG_USR|TOYFLAG_BIN))
-USE_NETCAT(NEWTOY(netcat, USE_NETCAT_LISTEN("^tlL")"w#<1W#<1p#<1>65535q#<1s:f:46u"USE_NETCAT_LISTEN("[!tlL][!Lw]")"[!46]", TOYFLAG_BIN))
+USE_NETCAT(NEWTOY(netcat, USE_NETCAT_LISTEN("^tlL")"w#<1W#<1p#<1>65535q#<1s:f:46uU"USE_NETCAT_LISTEN("[!tlL][!Lw]")"[!46U]", TOYFLAG_BIN))
USE_NETSTAT(NEWTOY(netstat, "pWrxwutneal", TOYFLAG_BIN))
USE_NICE(NEWTOY(nice, "^<1n#", TOYFLAG_BIN))
USE_NL(NEWTOY(nl, "v#<1=1l#w#<0=6Eb:n:s:", TOYFLAG_USR|TOYFLAG_BIN))
diff --git a/toys/net/netcat.c b/toys/net/netcat.c
index 833d32a..65c41ac 100644
--- a/toys/net/netcat.c
+++ b/toys/net/netcat.c
@@ -7,13 +7,13 @@
* netcat -L zombies
USE_NETCAT(OLDTOY(nc, netcat, TOYFLAG_USR|TOYFLAG_BIN))
-USE_NETCAT(NEWTOY(netcat, USE_NETCAT_LISTEN("^tlL")"w#<1W#<1p#<1>65535q#<1s:f:46u"USE_NETCAT_LISTEN("[!tlL][!Lw]")"[!46]", TOYFLAG_BIN))
+USE_NETCAT(NEWTOY(netcat, USE_NETCAT_LISTEN("^tlL")"w#<1W#<1p#<1>65535q#<1s:f:46uU"USE_NETCAT_LISTEN("[!tlL][!Lw]")"[!46U]", TOYFLAG_BIN))
config NETCAT
bool "netcat"
default y
help
- usage: netcat [-46] [-u] [-wpq #] [-s addr] {IPADDR PORTNUM|-f FILENAME}
+ usage: netcat [-46U] [-u] [-wpq #] [-s addr] {IPADDR PORTNUM|-f FILENAME}
Forward stdin/stdout to a file or network connection.
@@ -24,6 +24,7 @@
-q Quit SECONDS after EOF on stdin, even if stdout hasn't closed yet
-s Local source address
-u Use UDP
+ -U Use a UNIX domain socket
-w SECONDS timeout to establish connection
-W SECONDS timeout for more data on an idle connection
@@ -85,11 +86,13 @@
// The argument parsing logic can't make "<2" conditional on other
// arguments like -f and -l, so do it by hand here.
if ((toys.optflags&FLAG_f) ? toys.optc :
- (!(toys.optflags&(FLAG_l|FLAG_L)) && toys.optc!=2))
+ (!(toys.optflags&(FLAG_l|FLAG_L)) &&
+ toys.optc!=(toys.optflags&FLAG_U?1:2)))
help_exit("bad argument count");
if (toys.optflags&FLAG_4) family = AF_INET;
else if (toys.optflags&FLAG_6) family = AF_INET6;
+ else if (toys.optflags&FLAG_U) family = AF_UNIX;
if (toys.optflags&FLAG_u) type = SOCK_DGRAM;
@@ -97,9 +100,28 @@
else {
// Setup socket
if (!(toys.optflags&(FLAG_L|FLAG_l))) {
- struct addrinfo *addr = xgetaddrinfo(toys.optargs[0], toys.optargs[1],
- family, type, 0, 0);
- sockfd = xconnect(addr);
+ if (toys.optflags&FLAG_U) {
+ struct sockaddr_un sockaddr;
+
+ memset(&sockaddr, 0, sizeof(struct sockaddr_un));
+
+ if (strlen(toys.optargs[0]) + 1 > sizeof(sockaddr.sun_path))
+ error_exit("socket path too long %s", toys.optargs[0]);
+ strcpy(sockaddr.sun_path, toys.optargs[0]);
+ sockaddr.sun_family = AF_UNIX;
+
+ sockfd = xsocket(AF_UNIX, type | SOCK_CLOEXEC, 0);
+ if (connect(sockfd, (struct sockaddr*)&sockaddr,
+ sizeof(sockaddr)) != 0) {
+ perror_exit("could not bind to unix domain socket");
+ }
+
+ } else {
+ struct addrinfo *addr = xgetaddrinfo(toys.optargs[0], toys.optargs[1],
+ family, type, 0, 0);
+
+ sockfd = xconnect(addr);
+ }
// We have a connection. Disarm timeout.
set_alarm(0);
@@ -109,14 +131,33 @@
pollinate(in1, in2, out1, out2, TT.W, TT.q);
} else {
// Listen for incoming connections
- struct sockaddr* address = (void*)toybuf;
- socklen_t len = sizeof(struct sockaddr_storage);
+ if (toys.optflags&FLAG_U) {
+ struct sockaddr_un sockaddr;
- sprintf(toybuf, "%ld", TT.p);
- sockfd = xbind(xgetaddrinfo(TT.s, toybuf, family, type, 0, 0));
+ memset(&sockaddr, 0, sizeof(struct sockaddr_un));
+
+ if (!(toys.optflags&FLAG_s))
+ error_exit("-s must be provided if using -U with -L/-l");
+
+ if (strlen(TT.s) + 1 > sizeof(sockaddr.sun_path))
+ error_exit("socket path too long %s", TT.s);
+ strcpy(sockaddr.sun_path, TT.s);
+ sockaddr.sun_family = AF_UNIX;
+
+ sockfd = xsocket(AF_UNIX, type | SOCK_CLOEXEC, 0);
+ if (bind(sockfd, (struct sockaddr*)&sockaddr,
+ sizeof(struct sockaddr_un)) != 0) {
+ perror_exit("unable to bind to UNIX domain socket");
+ }
+ } else {
+ sprintf(toybuf, "%ld", TT.p);
+ sockfd = xbind(xgetaddrinfo(TT.s, toybuf, family, type, 0, 0));
+ }
if (listen(sockfd, 5)) error_exit("listen");
- if (!TT.p) {
+ if (!TT.p && !(toys.optflags&FLAG_U)) {
+ struct sockaddr* address = (void*)toybuf;
+ socklen_t len = sizeof(struct sockaddr_storage);
short port_be;
getsockname(sockfd, address, &len);
@@ -136,7 +177,7 @@
do {
child = 0;
- in1 = out2 = accept(sockfd, (struct sockaddr *)address, &len);
+ in1 = out2 = accept(sockfd, NULL, NULL);
if (in1<0) perror_exit("accept");
// We have a connection. Disarm timeout.
diff --git a/toys/pending/sh.c b/toys/pending/sh.c
index 8c898cd..e8a11ce 100644
--- a/toys/pending/sh.c
+++ b/toys/pending/sh.c
@@ -184,7 +184,7 @@
pipe[0] = 0;
pipe[1] = 1;
if (-1 == (pp->pid = xpopen_both(pp->arg.v, pipe)))
- perror_msg("%s: not found", *pp->arg.v);
+ perror_msg("%s: vfork", *pp->arg.v);
else pp->exit = xpclose_both(pp->pid, 0);
}
@@ -290,7 +290,7 @@
// pointer to start of unused part of line if it needs another line of input.
static char *parse_line(char *line, struct double_list **pipeline)
{
- char *start = line, *end, *s;
+ char *start = line, *end, *s, *ex, *add;
struct sh_arg *arg = 0;
struct double_list *pl, *expect = 0;
unsigned i, paren = 0;
@@ -350,40 +350,81 @@
for (pl = *pipeline; pl ; pl = (pl->next == *pipeline) ? 0 : pl->next) {
arg = (void *)pl->data;
if (!arg->c) continue;
+ add = 0;
// parse flow control statements in this command line
- for (i = 0; i<arg->c; i++) {
- char *ex = expect ? expect->prev->data : 0;
-
+ for (i = 0; ; i++) {
+ ex = expect ? expect->prev->data : 0;
s = arg->v[i];
- if (!strcmp(s, "if")) ex = "then";
- else if (!strcmp(s, "for") || !strcmp(s, "select")
- || !strcmp(s, "while") || !strcmp(s, "until")) ex = "do";
- else if (!strcmp(s, "case")) ex = "esac";
- else if (!strcmp(s, "{")) ex = "}";
- else if (!strcmp(s, "[[")) ex = "]]";
- // If we expect a non-flow-control command, eat rest of line
- else if (expect && !ex) {
+ // push word to expect to end this block, and expect a command first
+ if (add) {
+ dlist_add(&expect, add);
+ dlist_add(&expect, add = 0);
+ }
+
+ // end of statement?
+ if (i == arg->c) break;
+
+ // When waiting for { it must be next symbol, but can be on a new line.
+ if (ex && !strcmp(ex, "{") && (strcmp(s, "{") || (!i && end))) {
+ syntax_err("need {");
+ goto flush;
+ }
+
+ if (!strcmp(s, "if")) add = "then";
+ else if (!strcmp(s, "for") || !strcmp(s, "select")
+ || !strcmp(s, "while") || !strcmp(s, "until")) add = "do";
+ else if (!strcmp(s, "case")) add = "esac";
+ else if (!strcmp(s, "{")) add = "}";
+ else if (!strcmp(s, "[[")) add = "]]";
+
+ // function NAME () [nl] { [nl] body ; }
+ // Why can you to declare functions inside other functions?
+ else if (arg->c>i+1 && !strcmp(arg->v[i+1], "(")) goto funky;
+ else if (!strcmp(s, "function")) {
+ i++;
+funky:
+ // At this point we can only have a function: barf if it's invalid
+ if (arg->c<i+3 || !strcmp(arg->v[i+1], "(")
+ || !strcmp(arg->v[i+2], ")"))
+ {
+ syntax_err("bad function ()");
+ goto flush;
+ }
+ dlist_add(&expect, "}");
+ dlist_add(&expect, 0);
+ dlist_add(&expect, "{");
+
+ // Expecting NULL will take any otherwise unrecognized word
+ } else if (expect && !ex) {
free(dlist_pop(&expect));
continue;
- // Did we find a specific word we were waiting for?
- } else if (ex && !strcmp(arg->v[i], ex)) {
+ // If we expect nothing and didn't just start a new flow control block,
+ // rest of statement is a command and arguments, so stop now
+ } else if (!ex) break;
+
+ if (add) continue;
+
+ // If we got here we expect a word to end this block: is this it?
+ if (!strcmp(arg->v[i], ex)) {
free(dlist_pop(&expect));
+
+ // can't "if | then" or "while && do", only ; or newline works
if (end && !strcmp(end, ";")) {
- // can't if | then or while && do, only ; or newline counts
syntax_err("bad %s", end);
goto flush;
}
- if (!strcmp(s, "do")) dlist_add(&expect, "done");
- else if (!strcmp(s, "then")) dlist_add(&expect, "fi\0A");
- break;
+
+ // if it's a multipart block, what comes next?
+ if (!strcmp(s, "do")) ex = "done";
+ else if (!strcmp(s, "then")) add = "fi\0A";
// fi could have elif, which queues a then.
- } else if (ex && !strcmp(ex, "fi")) {
+ } else if (!strcmp(ex, "fi")) {
if (!strcmp(s, "elif")) {
free(dlist_pop(&expect));
- dlist_add(&expect, "then");
+ add = "then";
// catch duplicate else while we're here
} else if (!strcmp(s, "else")) {
if (ex[3] != 'A') {
@@ -391,23 +432,21 @@
goto flush;
}
free(dlist_pop(&expect));
- dlist_add(&expect, "fi\0B");
+ add = "fi\0B";
}
- } else break;
-
- dlist_add(&expect, ex);
+ }
}
- // Record how the previous stanza ended
+ // Record how the previous stanza ended: ; | & ;; || && ;& ;;& |& NULL
end = arg->v[arg->c];
}
- // If we need more lines to finish flow control...
- // TODO: functions
- if (expect) {
+ // Do we need more lines to finish a flow control statement?
+ if (expect || paren) {
llist_traverse(expect, free);
return start;
}
+ // iterate through the commands running each one
for (pl = *pipeline; pl ; pl = (pl->next == *pipeline) ? 0 : pl->next) {
struct sh_process *pp = xzalloc(sizeof(struct sh_process));
@@ -445,7 +484,12 @@
size_t linelen = 0;
// Prompt and read line
- if (!f) do_prompt(getenv(command ? "PS2" : "PS1"));
+ if (!f) {
+ char *s = getenv(command ? "PS2" : "PS1");
+
+ if (!s) s = command ? "> " : (getpid() ? "\\$ " : "# ");
+ do_prompt(s);
+ }
if (1 > getline(&new, &linelen, f ? f : stdin)) break;
if (f) TT.lineno++;