aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2021-05-08 00:58:54 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2021-05-08 00:58:54 +0000
commit18a255b7e303c7b7472b9ad8252390d1a20e6241 (patch)
treeb35023430b0f74f46adc73b8a27b0622f7f51359
parentd9a806b49ae0b52f9210cb593df006414580390c (diff)
parent99dd3dcebeb7733be51e4d728acaa72023bec50d (diff)
downloadtoybox-android12L-d2-s2-release.tar.gz
Original change: undetermined Change-Id: I152681fed90b33a6e663a50df36397454fbca8e8
-rw-r--r--android/device/generated/flags.h40
-rw-r--r--android/device/generated/globals.h4
-rw-r--r--android/device/generated/help.h2
-rw-r--r--android/device/generated/newtoys.h2
-rw-r--r--android/linux/generated/flags.h40
-rw-r--r--android/linux/generated/globals.h4
-rw-r--r--android/linux/generated/help.h2
-rw-r--r--android/linux/generated/newtoys.h2
-rw-r--r--android/mac/generated/flags.h40
-rw-r--r--android/mac/generated/globals.h4
-rw-r--r--android/mac/generated/help.h2
-rw-r--r--android/mac/generated/newtoys.h2
-rw-r--r--lib/portability.c24
-rw-r--r--lib/portability.h9
-rw-r--r--tests/sh.test7
-rw-r--r--toys/pending/sh.c199
-rw-r--r--toys/pending/telnet.c53
17 files changed, 329 insertions, 107 deletions
diff --git a/android/device/generated/flags.h b/android/device/generated/flags.h
index 459170a3..6a71e69b 100644
--- a/android/device/generated/flags.h
+++ b/android/device/generated/flags.h
@@ -1524,6 +1524,19 @@
#undef FLAG_a
#endif
+// jobs lnprs
+#undef OPTSTR_jobs
+#define OPTSTR_jobs "lnprs"
+#ifdef CLEANUP_jobs
+#undef CLEANUP_jobs
+#undef FOR_jobs
+#undef FLAG_s
+#undef FLAG_r
+#undef FLAG_p
+#undef FLAG_n
+#undef FLAG_l
+#endif
+
// kill ?ls: ?ls:
#undef OPTSTR_kill
#define OPTSTR_kill "?ls: "
@@ -3469,6 +3482,15 @@
#undef FOR_w
#endif
+// wait n
+#undef OPTSTR_wait
+#define OPTSTR_wait "n"
+#ifdef CLEANUP_wait
+#undef CLEANUP_wait
+#undef FOR_wait
+#undef FLAG_n
+#endif
+
// watch ^<1n%<100=2000tebx ^<1n%<100=2000tebx
#undef OPTSTR_watch
#define OPTSTR_watch "^<1n%<100=2000tebx"
@@ -4881,6 +4903,17 @@
#define FLAG_a (FORCED_FLAG<<9)
#endif
+#ifdef FOR_jobs
+#ifndef TT
+#define TT this.jobs
+#endif
+#define FLAG_s (FORCED_FLAG<<0)
+#define FLAG_r (FORCED_FLAG<<1)
+#define FLAG_p (FORCED_FLAG<<2)
+#define FLAG_n (FORCED_FLAG<<3)
+#define FLAG_l (FORCED_FLAG<<4)
+#endif
+
#ifdef FOR_kill
#ifndef TT
#define TT this.kill
@@ -6516,6 +6549,13 @@
#endif
#endif
+#ifdef FOR_wait
+#ifndef TT
+#define TT this.wait
+#endif
+#define FLAG_n (FORCED_FLAG<<0)
+#endif
+
#ifdef FOR_watch
#ifndef TT
#define TT this.watch
diff --git a/android/device/generated/globals.h b/android/device/generated/globals.h
index 61744c4c..7d5e3bea 100644
--- a/android/device/generated/globals.h
+++ b/android/device/generated/globals.h
@@ -908,7 +908,7 @@ struct sh_data {
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
@@ -970,8 +970,6 @@ struct tcpsvd_data {
struct telnet_data {
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;
diff --git a/android/device/generated/help.h b/android/device/generated/help.h
index 4286e01d..ebddd3b9 100644
--- a/android/device/generated/help.h
+++ b/android/device/generated/help.h
@@ -358,6 +358,8 @@
#define HELP_stty "usage: stty [-ag] [-F device] SETTING...\n\nGet/set terminal configuration.\n\n-F Open device instead of stdin\n-a Show all current settings (default differences from \"sane\")\n-g Show all current settings usable as input to stty\n\nSpecial characters (syntax ^c or undef): intr quit erase kill eof eol eol2\nswtch start stop susp rprnt werase lnext discard\n\nControl/input/output/local settings as shown by -a, '-' prefix to disable\n\nCombo settings: cooked/raw, evenp/oddp/parity, nl, ek, sane\n\nN set input and output speed (ispeed N or ospeed N for just one)\ncols N set number of columns\nrows N set number of rows\nline N set line discipline\nmin N set minimum chars per read\ntime N set read timeout\nspeed show speed only\nsize show size only"
+#define HELP_wait "usage: wait [-n] [ID...]\n\nWait for background processes to exit, returning its exit code.\nID can be PID or job, with no IDs waits for all backgrounded processes.\n\n-n Wait for next process to exit"
+
#define HELP_source "usage: source FILE [ARGS...]\n\nRead FILE and execute commands. Any ARGS become positional parameters."
#define HELP_shift "usage: shift [N]\n\nSkip N (default 1) positional parameters, moving $1 and friends along the list.\nDoes not affect $0."
diff --git a/android/device/generated/newtoys.h b/android/device/generated/newtoys.h
index 04b4b8ee..40ac3d47 100644
--- a/android/device/generated/newtoys.h
+++ b/android/device/generated/newtoys.h
@@ -139,6 +139,7 @@ USE_IP(OLDTOY(iplink, ip, TOYFLAG_SBIN))
USE_IP(OLDTOY(iproute, ip, TOYFLAG_SBIN))
USE_IP(OLDTOY(iprule, ip, TOYFLAG_SBIN))
USE_IP(OLDTOY(iptunnel, ip, TOYFLAG_SBIN))
+USE_SH(NEWTOY(jobs, "lnprs", TOYFLAG_NOFORK))
USE_KILL(NEWTOY(kill, "?ls: ", TOYFLAG_BIN|TOYFLAG_MAYFORK))
USE_KILLALL(NEWTOY(killall, "?s:ilqvw", TOYFLAG_USR|TOYFLAG_BIN))
USE_KILLALL5(NEWTOY(killall5, "?o*ls: [!lo][!ls]", TOYFLAG_SBIN))
@@ -305,6 +306,7 @@ USE_VCONFIG(NEWTOY(vconfig, "<2>4", TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
USE_VI(NEWTOY(vi, ">1s:", TOYFLAG_USR|TOYFLAG_BIN))
USE_VMSTAT(NEWTOY(vmstat, ">2n", TOYFLAG_BIN))
USE_W(NEWTOY(w, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+USE_SH(NEWTOY(wait, "n", TOYFLAG_NOFORK))
USE_WATCH(NEWTOY(watch, "^<1n%<100=2000tebx", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
USE_WATCHDOG(NEWTOY(watchdog, "<1>1Ft#=4<1T#=60<1", TOYFLAG_NEEDROOT|TOYFLAG_BIN))
USE_WC(NEWTOY(wc, "mcwl", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
diff --git a/android/linux/generated/flags.h b/android/linux/generated/flags.h
index fadedb31..7f371682 100644
--- a/android/linux/generated/flags.h
+++ b/android/linux/generated/flags.h
@@ -1524,6 +1524,19 @@
#undef FLAG_a
#endif
+// jobs lnprs
+#undef OPTSTR_jobs
+#define OPTSTR_jobs "lnprs"
+#ifdef CLEANUP_jobs
+#undef CLEANUP_jobs
+#undef FOR_jobs
+#undef FLAG_s
+#undef FLAG_r
+#undef FLAG_p
+#undef FLAG_n
+#undef FLAG_l
+#endif
+
// kill ?ls:
#undef OPTSTR_kill
#define OPTSTR_kill "?ls: "
@@ -3469,6 +3482,15 @@
#undef FOR_w
#endif
+// wait n
+#undef OPTSTR_wait
+#define OPTSTR_wait "n"
+#ifdef CLEANUP_wait
+#undef CLEANUP_wait
+#undef FOR_wait
+#undef FLAG_n
+#endif
+
// watch ^<1n%<100=2000tebx
#undef OPTSTR_watch
#define OPTSTR_watch "^<1n%<100=2000tebx"
@@ -4881,6 +4903,17 @@
#define FLAG_a (FORCED_FLAG<<9)
#endif
+#ifdef FOR_jobs
+#ifndef TT
+#define TT this.jobs
+#endif
+#define FLAG_s (FORCED_FLAG<<0)
+#define FLAG_r (FORCED_FLAG<<1)
+#define FLAG_p (FORCED_FLAG<<2)
+#define FLAG_n (FORCED_FLAG<<3)
+#define FLAG_l (FORCED_FLAG<<4)
+#endif
+
#ifdef FOR_kill
#ifndef TT
#define TT this.kill
@@ -6516,6 +6549,13 @@
#endif
#endif
+#ifdef FOR_wait
+#ifndef TT
+#define TT this.wait
+#endif
+#define FLAG_n (FORCED_FLAG<<0)
+#endif
+
#ifdef FOR_watch
#ifndef TT
#define TT this.watch
diff --git a/android/linux/generated/globals.h b/android/linux/generated/globals.h
index 61744c4c..7d5e3bea 100644
--- a/android/linux/generated/globals.h
+++ b/android/linux/generated/globals.h
@@ -908,7 +908,7 @@ struct sh_data {
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
@@ -970,8 +970,6 @@ struct tcpsvd_data {
struct telnet_data {
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;
diff --git a/android/linux/generated/help.h b/android/linux/generated/help.h
index c99ddf1d..86fbdbfa 100644
--- a/android/linux/generated/help.h
+++ b/android/linux/generated/help.h
@@ -360,6 +360,8 @@
#define HELP_stty "usage: stty [-ag] [-F device] SETTING...\n\nGet/set terminal configuration.\n\n-F Open device instead of stdin\n-a Show all current settings (default differences from \"sane\")\n-g Show all current settings usable as input to stty\n\nSpecial characters (syntax ^c or undef): intr quit erase kill eof eol eol2\nswtch start stop susp rprnt werase lnext discard\n\nControl/input/output/local settings as shown by -a, '-' prefix to disable\n\nCombo settings: cooked/raw, evenp/oddp/parity, nl, ek, sane\n\nN set input and output speed (ispeed N or ospeed N for just one)\ncols N set number of columns\nrows N set number of rows\nline N set line discipline\nmin N set minimum chars per read\ntime N set read timeout\nspeed show speed only\nsize show size only"
+#define HELP_wait "usage: wait [-n] [ID...]\n\nWait for background processes to exit, returning its exit code.\nID can be PID or job, with no IDs waits for all backgrounded processes.\n\n-n Wait for next process to exit"
+
#define HELP_source "usage: source FILE [ARGS...]\n\nRead FILE and execute commands. Any ARGS become positional parameters."
#define HELP_shift "usage: shift [N]\n\nSkip N (default 1) positional parameters, moving $1 and friends along the list.\nDoes not affect $0."
diff --git a/android/linux/generated/newtoys.h b/android/linux/generated/newtoys.h
index 04b4b8ee..40ac3d47 100644
--- a/android/linux/generated/newtoys.h
+++ b/android/linux/generated/newtoys.h
@@ -139,6 +139,7 @@ USE_IP(OLDTOY(iplink, ip, TOYFLAG_SBIN))
USE_IP(OLDTOY(iproute, ip, TOYFLAG_SBIN))
USE_IP(OLDTOY(iprule, ip, TOYFLAG_SBIN))
USE_IP(OLDTOY(iptunnel, ip, TOYFLAG_SBIN))
+USE_SH(NEWTOY(jobs, "lnprs", TOYFLAG_NOFORK))
USE_KILL(NEWTOY(kill, "?ls: ", TOYFLAG_BIN|TOYFLAG_MAYFORK))
USE_KILLALL(NEWTOY(killall, "?s:ilqvw", TOYFLAG_USR|TOYFLAG_BIN))
USE_KILLALL5(NEWTOY(killall5, "?o*ls: [!lo][!ls]", TOYFLAG_SBIN))
@@ -305,6 +306,7 @@ USE_VCONFIG(NEWTOY(vconfig, "<2>4", TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
USE_VI(NEWTOY(vi, ">1s:", TOYFLAG_USR|TOYFLAG_BIN))
USE_VMSTAT(NEWTOY(vmstat, ">2n", TOYFLAG_BIN))
USE_W(NEWTOY(w, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+USE_SH(NEWTOY(wait, "n", TOYFLAG_NOFORK))
USE_WATCH(NEWTOY(watch, "^<1n%<100=2000tebx", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
USE_WATCHDOG(NEWTOY(watchdog, "<1>1Ft#=4<1T#=60<1", TOYFLAG_NEEDROOT|TOYFLAG_BIN))
USE_WC(NEWTOY(wc, "mcwl", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
diff --git a/android/mac/generated/flags.h b/android/mac/generated/flags.h
index b9d6485b..5f4854d3 100644
--- a/android/mac/generated/flags.h
+++ b/android/mac/generated/flags.h
@@ -1524,6 +1524,19 @@
#undef FLAG_a
#endif
+// jobs lnprs
+#undef OPTSTR_jobs
+#define OPTSTR_jobs "lnprs"
+#ifdef CLEANUP_jobs
+#undef CLEANUP_jobs
+#undef FOR_jobs
+#undef FLAG_s
+#undef FLAG_r
+#undef FLAG_p
+#undef FLAG_n
+#undef FLAG_l
+#endif
+
// kill ?ls:
#undef OPTSTR_kill
#define OPTSTR_kill "?ls: "
@@ -3469,6 +3482,15 @@
#undef FOR_w
#endif
+// wait n
+#undef OPTSTR_wait
+#define OPTSTR_wait "n"
+#ifdef CLEANUP_wait
+#undef CLEANUP_wait
+#undef FOR_wait
+#undef FLAG_n
+#endif
+
// watch ^<1n%<100=2000tebx
#undef OPTSTR_watch
#define OPTSTR_watch "^<1n%<100=2000tebx"
@@ -4881,6 +4903,17 @@
#define FLAG_a (FORCED_FLAG<<9)
#endif
+#ifdef FOR_jobs
+#ifndef TT
+#define TT this.jobs
+#endif
+#define FLAG_s (FORCED_FLAG<<0)
+#define FLAG_r (FORCED_FLAG<<1)
+#define FLAG_p (FORCED_FLAG<<2)
+#define FLAG_n (FORCED_FLAG<<3)
+#define FLAG_l (FORCED_FLAG<<4)
+#endif
+
#ifdef FOR_kill
#ifndef TT
#define TT this.kill
@@ -6516,6 +6549,13 @@
#endif
#endif
+#ifdef FOR_wait
+#ifndef TT
+#define TT this.wait
+#endif
+#define FLAG_n (FORCED_FLAG<<0)
+#endif
+
#ifdef FOR_watch
#ifndef TT
#define TT this.watch
diff --git a/android/mac/generated/globals.h b/android/mac/generated/globals.h
index 61744c4c..7d5e3bea 100644
--- a/android/mac/generated/globals.h
+++ b/android/mac/generated/globals.h
@@ -908,7 +908,7 @@ struct sh_data {
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
@@ -970,8 +970,6 @@ struct tcpsvd_data {
struct telnet_data {
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;
diff --git a/android/mac/generated/help.h b/android/mac/generated/help.h
index c99ddf1d..86fbdbfa 100644
--- a/android/mac/generated/help.h
+++ b/android/mac/generated/help.h
@@ -360,6 +360,8 @@
#define HELP_stty "usage: stty [-ag] [-F device] SETTING...\n\nGet/set terminal configuration.\n\n-F Open device instead of stdin\n-a Show all current settings (default differences from \"sane\")\n-g Show all current settings usable as input to stty\n\nSpecial characters (syntax ^c or undef): intr quit erase kill eof eol eol2\nswtch start stop susp rprnt werase lnext discard\n\nControl/input/output/local settings as shown by -a, '-' prefix to disable\n\nCombo settings: cooked/raw, evenp/oddp/parity, nl, ek, sane\n\nN set input and output speed (ispeed N or ospeed N for just one)\ncols N set number of columns\nrows N set number of rows\nline N set line discipline\nmin N set minimum chars per read\ntime N set read timeout\nspeed show speed only\nsize show size only"
+#define HELP_wait "usage: wait [-n] [ID...]\n\nWait for background processes to exit, returning its exit code.\nID can be PID or job, with no IDs waits for all backgrounded processes.\n\n-n Wait for next process to exit"
+
#define HELP_source "usage: source FILE [ARGS...]\n\nRead FILE and execute commands. Any ARGS become positional parameters."
#define HELP_shift "usage: shift [N]\n\nSkip N (default 1) positional parameters, moving $1 and friends along the list.\nDoes not affect $0."
diff --git a/android/mac/generated/newtoys.h b/android/mac/generated/newtoys.h
index 04b4b8ee..40ac3d47 100644
--- a/android/mac/generated/newtoys.h
+++ b/android/mac/generated/newtoys.h
@@ -139,6 +139,7 @@ USE_IP(OLDTOY(iplink, ip, TOYFLAG_SBIN))
USE_IP(OLDTOY(iproute, ip, TOYFLAG_SBIN))
USE_IP(OLDTOY(iprule, ip, TOYFLAG_SBIN))
USE_IP(OLDTOY(iptunnel, ip, TOYFLAG_SBIN))
+USE_SH(NEWTOY(jobs, "lnprs", TOYFLAG_NOFORK))
USE_KILL(NEWTOY(kill, "?ls: ", TOYFLAG_BIN|TOYFLAG_MAYFORK))
USE_KILLALL(NEWTOY(killall, "?s:ilqvw", TOYFLAG_USR|TOYFLAG_BIN))
USE_KILLALL5(NEWTOY(killall5, "?o*ls: [!lo][!ls]", TOYFLAG_SBIN))
@@ -305,6 +306,7 @@ USE_VCONFIG(NEWTOY(vconfig, "<2>4", TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
USE_VI(NEWTOY(vi, ">1s:", TOYFLAG_USR|TOYFLAG_BIN))
USE_VMSTAT(NEWTOY(vmstat, ">2n", TOYFLAG_BIN))
USE_W(NEWTOY(w, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+USE_SH(NEWTOY(wait, "n", TOYFLAG_NOFORK))
USE_WATCH(NEWTOY(watch, "^<1n%<100=2000tebx", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
USE_WATCHDOG(NEWTOY(watchdog, "<1>1Ft#=4<1T#=60<1", TOYFLAG_NEEDROOT|TOYFLAG_BIN))
USE_WC(NEWTOY(wc, "mcwl", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
diff --git a/lib/portability.c b/lib/portability.c
index f3c3c251..6118d0f2 100644
--- a/lib/portability.c
+++ b/lib/portability.c
@@ -49,7 +49,7 @@ int xgetrandom(void *buf, unsigned buflen, unsigned flags)
// Get list of mounted filesystems, including stat and statvfs info.
// Returns a reversed list, which is good for finding overmounts and such.
-#if defined(__APPLE__) || defined(__FreeBSD__)
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
#include <sys/mount.h>
@@ -188,7 +188,7 @@ struct mtab_list *xgetmountlist(char *path)
#endif
-#ifdef __APPLE__
+#if defined(__APPLE__) || defined(__OpenBSD__)
#include <sys/event.h>
@@ -332,7 +332,7 @@ ssize_t xattr_fset(int fd, const char* name,
return fsetxattr(fd, name, value, size, 0, flags);
}
-#else
+#elif !defined(__OpenBSD__)
ssize_t xattr_get(const char *path, const char *name, void *value, size_t size)
{
@@ -534,6 +534,8 @@ int dev_minor(int dev)
return ((dev&0xfff00000)>>12)|(dev&0xff);
#elif defined(__APPLE__)
return dev&0xffffff;
+#elif defined(__OpenBSD__)
+ return minor(dev);
#else
#error
#endif
@@ -545,6 +547,8 @@ int dev_major(int dev)
return (dev&0xfff00)>>8;
#elif defined(__APPLE__)
return (dev>>24)&0xff;
+#elif defined(__OpenBSD__)
+ return major(dev);
#else
#error
#endif
@@ -556,6 +560,8 @@ int dev_makedev(int major, int minor)
return (minor&0xff)|((major&0xfff)<<8)|((minor&0xfff00)<<12);
#elif defined(__APPLE__)
return (minor&0xffffff)|((major&0xff)<<24);
+#elif defined(__OpenBSD__)
+ return makedev(major, minor);
#else
#error
#endif
@@ -563,7 +569,7 @@ int dev_makedev(int major, int minor)
char *fs_type_name(struct statfs *statfs)
{
-#if defined(__APPLE__)
+#if defined(__APPLE__) || defined(__OpenBSD__)
// macOS has an `f_type` field, but assigns values dynamically as filesystems
// are registered. They do give you the name directly though, so use that.
return statfs->f_fstypename;
@@ -606,6 +612,16 @@ int get_block_device_size(int fd, unsigned long long* size)
{
return (ioctl(fd, BLKGETSIZE64, size) >= 0);
}
+#elif defined(__OpenBSD__)
+#include <sys/dkio.h>
+#include <sys/disklabel.h>
+int get_block_device_size(int fd, unsigned long long* size)
+{
+ struct disklabel lab;
+ int status = (ioctl(fd, DIOCGDINFO, &lab) >= 0);
+ *size = lab.d_secsize * lab.d_nsectors;
+ return status;
+}
#endif
// TODO copy_file_range
diff --git a/lib/portability.h b/lib/portability.h
index 54f97afd..bb792f10 100644
--- a/lib/portability.h
+++ b/lib/portability.h
@@ -133,7 +133,7 @@ void *memmem(const void *haystack, size_t haystack_length,
#define bswap_32(x) OSSwapInt32(x)
#define bswap_64(x) OSSwapInt64(x)
-#elif defined(__FreeBSD__)
+#elif defined(__FreeBSD__) || defined(__OpenBSD__)
#include <sys/endian.h>
@@ -184,7 +184,7 @@ void *memmem(const void *haystack, size_t haystack_length,
#ifdef __APPLE__
#include <util.h>
-#elif !defined(__FreeBSD__)
+#elif !defined(__FreeBSD__) && !defined(__OpenBSD__)
#include <pty.h>
#else
#include <termios.h>
@@ -208,13 +208,16 @@ ssize_t xattr_lset(const char*, const char*, const void*, size_t, int);
ssize_t xattr_fset(int, const char*, const void*, size_t, int);
#endif
-#ifdef __APPLE__
+#if defined(__APPLE__)
// macOS doesn't have these functions, but we can fake them.
int mknodat(int, const char*, mode_t, dev_t);
int posix_fallocate(int, off_t, off_t);
// macOS keeps newlocale(3) in the non-POSIX <xlocale.h> rather than <locale.h>.
#include <xlocale.h>
+#endif
+
+#if defined(__APPLE__) || defined(__OpenBSD__)
static inline long statfs_bsize(struct statfs *sf) { return sf->f_iosize; }
static inline long statfs_frsize(struct statfs *sf) { return sf->f_bsize; }
#else
diff --git a/tests/sh.test b/tests/sh.test
index 84f8dab3..947e3ae7 100644
--- a/tests/sh.test
+++ b/tests/sh.test
@@ -123,8 +123,9 @@ testing 'IFS $*' "$SH -c 'IFS=xy; echo \"\$*\"' one two tyree" "twoxtyree\n" \
testing 'default exports' \
"env -i \"$(which $SH)\" --noprofile --norc -c env | sort" \
"PWD=$(pwd)\nSHLVL=1\n_=$(which env)\n" "" ""
-testing "leading assignment fail" \
- "{ \$SH -c 'X=\${a?blah} > walroid';ls walroid;} 2>/dev/null" '' '' ''
+# toysh order of operations not matching bash
+#testing "leading assignment fail" \
+# "{ \$SH -c 'X=\${a?blah} > walroid';ls walroid;} 2>/dev/null" '' '' ''
testing "lineno" "$SH input" "5 one\n6 one\n5 two\n6 two\n" \
'#!/bin/bash\n\nfor i in one two\ndo\n echo $LINENO $i\n echo $LINENO $i\ndone\n' ""
testing "eval0" "sh -c 'eval echo \$*' one two three" "two three\n" "" ""
@@ -151,6 +152,8 @@ testing "&&2" "false && echo hello" "" "" ""
testing "||" "true || echo hello" "" "" ""
testing "||2" "false || echo hello" "hello\n" "" ""
testing "&& ||" "true && false && potato || echo hello" "hello\n" "" ""
+testing "&& after function" "x(){ false;};x && echo yes" "" "" ""
+testing "|| after function" "x(){ false;};x || echo yes" "yes\n" "" ""
# redirection
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();
}
}