diff options
author | Elliott Hughes <enh@google.com> | 2021-05-07 23:59:22 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2021-05-07 23:59:22 +0000 |
commit | 9ca15f063b4329fd6d9fb5028e31a792878c842c (patch) | |
tree | b35023430b0f74f46adc73b8a27b0622f7f51359 | |
parent | 1ee3b5cac00d353bd2975803c84e6ed87e2fe4f6 (diff) | |
parent | fd67c867d25b1d2515c8863dbed9a43aaf608b29 (diff) | |
download | toybox-9ca15f063b4329fd6d9fb5028e31a792878c842c.tar.gz |
Merge remote-tracking branch 'toybox/master' into update am: fd67c867d2
Original change: undetermined
Change-Id: Ib6a5e5e665dea1d1dacdbb81e5d902ed6e4a4418
-rw-r--r-- | android/device/generated/flags.h | 40 | ||||
-rw-r--r-- | android/device/generated/globals.h | 4 | ||||
-rw-r--r-- | android/device/generated/help.h | 2 | ||||
-rw-r--r-- | android/device/generated/newtoys.h | 2 | ||||
-rw-r--r-- | android/linux/generated/flags.h | 40 | ||||
-rw-r--r-- | android/linux/generated/globals.h | 4 | ||||
-rw-r--r-- | android/linux/generated/help.h | 2 | ||||
-rw-r--r-- | android/linux/generated/newtoys.h | 2 | ||||
-rw-r--r-- | android/mac/generated/flags.h | 40 | ||||
-rw-r--r-- | android/mac/generated/globals.h | 4 | ||||
-rw-r--r-- | android/mac/generated/help.h | 2 | ||||
-rw-r--r-- | android/mac/generated/newtoys.h | 2 | ||||
-rw-r--r-- | lib/portability.c | 24 | ||||
-rw-r--r-- | lib/portability.h | 9 | ||||
-rw-r--r-- | tests/sh.test | 7 | ||||
-rw-r--r-- | toys/pending/sh.c | 199 | ||||
-rw-r--r-- | toys/pending/telnet.c | 53 |
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(); } } |