diff options
author | Elliott Hughes <enh@google.com> | 2019-01-22 17:28:52 -0800 |
---|---|---|
committer | Elliott Hughes <enh@google.com> | 2019-01-22 17:31:32 -0800 |
commit | a065dbe6c5d6eb6b723cf04c08a7beeccf1026c9 (patch) | |
tree | 541a3e6c299a1b2de7dae90d24d4e8d25ec626fb | |
parent | b7a38dd7a5700d889fc4c97237276e1c78400157 (diff) | |
parent | bbadc5e14136a4a2011080c08e064108d71e1429 (diff) | |
download | toybox-a065dbe6c5d6eb6b723cf04c08a7beeccf1026c9.tar.gz |
Merge remote-tracking branch 'toybox/master' into HEAD
Change-Id: Ib387b8d25ff1d36bff7a017fd59ef47fbc4e1adf
-rw-r--r-- | generated/flags.h | 24 | ||||
-rw-r--r-- | generated/globals.h | 4 | ||||
-rw-r--r-- | generated/help.h | 6 | ||||
-rw-r--r-- | generated/newtoys.h | 4 | ||||
-rw-r--r-- | lib/lib.h | 6 | ||||
-rw-r--r-- | scripts/config2help.c | 4 | ||||
-rwxr-xr-x | scripts/genconfig.sh | 7 | ||||
-rwxr-xr-x | scripts/make.sh | 13 | ||||
-rw-r--r-- | scripts/mkflags.c | 2 | ||||
-rw-r--r-- | scripts/mktags.c | 2 | ||||
-rw-r--r-- | scripts/portability.sh | 6 | ||||
-rwxr-xr-x | scripts/test.sh | 5 | ||||
-rw-r--r-- | tests/xargs.test | 11 | ||||
-rw-r--r-- | toys.h | 2 | ||||
-rw-r--r-- | toys/lsb/hostname.c | 17 | ||||
-rw-r--r-- | toys/pending/sh.c | 4 | ||||
-rw-r--r-- | toys/pending/sntp.c | 131 | ||||
-rw-r--r-- | toys/posix/ps.c | 62 | ||||
-rw-r--r-- | toys/posix/xargs.c | 50 | ||||
-rw-r--r-- | www/code.html | 2 |
20 files changed, 257 insertions, 105 deletions
diff --git a/generated/flags.h b/generated/flags.h index 09b34213..9c3145bd 100644 --- a/generated/flags.h +++ b/generated/flags.h @@ -2486,12 +2486,14 @@ #undef FOR_sleep #endif -// sntp <1 +// sntp sp: #undef OPTSTR_sntp -#define OPTSTR_sntp "<1" +#define OPTSTR_sntp "sp:" #ifdef CLEANUP_sntp #undef CLEANUP_sntp #undef FOR_sntp +#undef FLAG_p +#undef FLAG_s #endif // sort gS:T:mo:k*t:xVbMcszdfirun gS:T:mo:k*t:xVbMcszdfirun @@ -3232,9 +3234,9 @@ #undef FLAG_a #endif -// xargs ^I:E:L#ptxrn#<1s#0[!0E] ^I:E:L#ptxrn#<1s#0[!0E] +// xargs ^I:E:ptrn#<1s#0[!0E] ^I:E:ptrn#<1s#0[!0E] #undef OPTSTR_xargs -#define OPTSTR_xargs "^I:E:L#ptxrn#<1s#0[!0E]" +#define OPTSTR_xargs "^I:E:ptrn#<1s#0[!0E]" #ifdef CLEANUP_xargs #undef CLEANUP_xargs #undef FOR_xargs @@ -3242,10 +3244,8 @@ #undef FLAG_s #undef FLAG_n #undef FLAG_r -#undef FLAG_x #undef FLAG_t #undef FLAG_p -#undef FLAG_L #undef FLAG_E #undef FLAG_I #endif @@ -5385,6 +5385,8 @@ #ifndef TT #define TT this.sntp #endif +#define FLAG_p (FORCED_FLAG<<0) +#define FLAG_s (FORCED_FLAG<<1) #endif #ifdef FOR_sort @@ -6013,12 +6015,10 @@ #define FLAG_s (1<<1) #define FLAG_n (1<<2) #define FLAG_r (1<<3) -#define FLAG_x (1<<4) -#define FLAG_t (1<<5) -#define FLAG_p (1<<6) -#define FLAG_L (1<<7) -#define FLAG_E (1<<8) -#define FLAG_I (1<<9) +#define FLAG_t (1<<4) +#define FLAG_p (1<<5) +#define FLAG_E (1<<6) +#define FLAG_I (1<<7) #endif #ifdef FOR_xxd diff --git a/generated/globals.h b/generated/globals.h index 1ac1a5a4..a66ca10b 100644 --- a/generated/globals.h +++ b/generated/globals.h @@ -764,7 +764,7 @@ struct sh_data { // toys/pending/sntp.c struct sntp_data { - int unused; + char *p, *m; }; // toys/pending/stty.c @@ -1326,7 +1326,7 @@ struct wc_data { // toys/posix/xargs.c struct xargs_data { - long s, n, L; + long s, n; char *E, *I; long entries, bytes; diff --git a/generated/help.h b/generated/help.h index 472ee2ec..b1ba4a1f 100644 --- a/generated/help.h +++ b/generated/help.h @@ -108,7 +108,7 @@ #define HELP_killall "usage: killall [-l] [-iqv] [-SIGNAL|-s SIGNAL] PROCESS_NAME...\n\nSend a signal (default: TERM) to all processes with the given names.\n\n-i Ask for confirmation before killing\n-l Print list of all available signals\n-q Don't print any warnings or error messages\n-s Send SIGNAL instead of SIGTERM\n-v Report if the signal was successfully sent\n\n" -#define HELP_hostname "usage: hostname [-bsf] [-F FILENAME] [newname]\n\nGet/set the current hostname.\n\n-b Set hostname to 'localhost' if otherwise unset\n-d Show DNS domain name (no host)\n-f Show fully-qualified name (host+domain, FQDN)\n-F Set hostname to contents of FILENAME\n-s Show short host name (no domain)\n\n" +#define HELP_hostname "usage: hostname [-bdsf] [-F FILENAME] [newname]\n\nGet/set the current hostname.\n\n-b Set hostname to 'localhost' if otherwise unset\n-d Show DNS domain name (no host)\n-f Show fully-qualified name (host+domain, FQDN)\n-F Set hostname to contents of FILENAME\n-s Show short host name (no domain)\n\n" #define HELP_dmesg "usage: dmesg [-Cc] [-r|-t|-T] [-n LEVEL] [-s SIZE] [-w]\n\nPrint or control the kernel ring buffer.\n\n-C Clear ring buffer without printing\n-c Clear ring buffer after printing\n-n Set kernel logging LEVEL (1-9)\n-r Raw output (with <level markers>)\n-S Use syslog(2) rather than /dev/kmsg\n-s Show the last SIZE many bytes\n-T Human readable timestamps\n-t Don't print timestamps\n-w Keep waiting for more output (aka --follow)\n\n" @@ -340,7 +340,7 @@ #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\n\n" -#define HELP_sntp "usage: sntp SERVER...\n\nSimple Network Time Protocol client, set system clock from a server.\n\n" +#define HELP_sntp "usage: sntp [-sm] [-p PORT] SERVER...\n\nSimple Network Time Protocol client, set system clock from a server.\n\n-p Use PORT (default 123)\n-s Serer\n\n" #define HELP_exit "usage: exit [status]\n\nExit shell. If no return value supplied on command line, use value\nof most recent command, or 0 if none.\n\n" @@ -438,7 +438,7 @@ #define HELP_xargs_pedantic "This version supports insane posix whitespace handling rendered obsolete\nby -0 mode.\n\n\n" -#define HELP_xargs "usage: xargs [-ptxr0] [-s NUM] [-n NUM] [-L NUM] [-E STR] COMMAND...\n\nRun command line one or more times, appending arguments from stdin.\n\nIf command exits with 255, don't launch another even if arguments remain.\n\n-s Size in bytes per command line\n-n Max number of arguments per command\n-0 Each argument is NULL terminated, no whitespace or quote processing\n#-p Prompt for y/n from tty before running each command\n#-t Trace, print command line to stderr\n#-x Exit if can't fit everything in one command\n#-r Don't run command with empty input\n#-L Max number of lines of input per command\n-E stop at line matching string\n\n" +#define HELP_xargs "usage: xargs [-0prt] [-s NUM] [-n NUM] [-E STR] COMMAND...\n\nRun command line one or more times, appending arguments from stdin.\n\nIf command exits with 255, don't launch another even if arguments remain.\n\n-0 Each argument is NULL terminated, no whitespace or quote processing\n-E Stop at line matching string\n-n Max number of arguments per command\n-p Prompt for y/n from tty before running each command\n-r Don't run command with empty input\n-s Size in bytes per command line\n-t Trace, print command line to stderr\n\n" #define HELP_who "usage: who\n\nPrint logged user information on system\n\n" diff --git a/generated/newtoys.h b/generated/newtoys.h index 772b8c1f..faeefff7 100644 --- a/generated/newtoys.h +++ b/generated/newtoys.h @@ -225,7 +225,7 @@ USE_SHRED(NEWTOY(shred, "<1zxus#<1n#<1o#<0f", TOYFLAG_USR|TOYFLAG_BIN)) USE_SKELETON(NEWTOY(skeleton, "(walrus)(blubber):;(also):e@d*c#b:a", TOYFLAG_USR|TOYFLAG_BIN)) USE_SKELETON_ALIAS(NEWTOY(skeleton_alias, "b#dq", TOYFLAG_USR|TOYFLAG_BIN)) USE_SLEEP(NEWTOY(sleep, "<1", TOYFLAG_BIN)) -USE_SNTP(NEWTOY(sntp, "<1", TOYFLAG_USR|TOYFLAG_BIN)) +USE_SNTP(NEWTOY(sntp, "sp:", TOYFLAG_USR|TOYFLAG_BIN)) USE_SORT(NEWTOY(sort, USE_SORT_FLOAT("g")"S:T:m" "o:k*t:" "xVbMcszdfirun", TOYFLAG_USR|TOYFLAG_BIN)) USE_SPLIT(NEWTOY(split, ">2a#<1=2>9b#<1l#<1[!bl]", TOYFLAG_USR|TOYFLAG_BIN)) USE_START(NEWTOY(start, "", TOYFLAG_USR|TOYFLAG_SBIN)) @@ -289,7 +289,7 @@ USE_WGET(NEWTOY(wget, "f:", TOYFLAG_USR|TOYFLAG_BIN)) USE_WHICH(NEWTOY(which, "<1a", TOYFLAG_USR|TOYFLAG_BIN)) USE_WHO(NEWTOY(who, "a", TOYFLAG_USR|TOYFLAG_BIN)) USE_WHOAMI(OLDTOY(whoami, logname, TOYFLAG_USR|TOYFLAG_BIN)) -USE_XARGS(NEWTOY(xargs, "^I:E:L#ptxrn#<1s#0[!0E]", TOYFLAG_USR|TOYFLAG_BIN)) +USE_XARGS(NEWTOY(xargs, "^I:E:ptrn#<1s#0[!0E]", TOYFLAG_USR|TOYFLAG_BIN)) USE_XXD(NEWTOY(xxd, ">1c#l#o#g#<1=2iprs#[!rs]", TOYFLAG_USR|TOYFLAG_BIN)) USE_XZCAT(NEWTOY(xzcat, NULL, TOYFLAG_USR|TOYFLAG_BIN)) USE_YES(NEWTOY(yes, NULL, TOYFLAG_USR|TOYFLAG_BIN)) @@ -360,12 +360,12 @@ void names_to_pid(char **names, int (*callback)(pid_t pid, char *name)); pid_t __attribute__((returns_twice)) xvforkwrap(pid_t pid); #define XVFORK() xvforkwrap(vfork()) -// Wrapper to make xfuncs() return (via longjmp) instead of exiting. +// Wrapper to make xfuncs() return (via siglongjmp) instead of exiting. // Assigns true/false "did it exit" value to first argument. -#define WOULD_EXIT(y, x) do { jmp_buf _noexit; \ +#define WOULD_EXIT(y, x) do { sigjmp_buf _noexit; \ int _noexit_res; \ toys.rebound = &_noexit; \ - _noexit_res = setjmp(_noexit); \ + _noexit_res = sigsetjmp(_noexit, 1); \ if (!_noexit_res) do {x;} while(0); \ toys.rebound = 0; \ y = _noexit_res; \ diff --git a/scripts/config2help.c b/scripts/config2help.c index be53547d..edb1caea 100644 --- a/scripts/config2help.c +++ b/scripts/config2help.c @@ -19,7 +19,7 @@ #include <poll.h> #include <sys/socket.h> -/****************** functions copied from lib/*.c ********************/ +//****************** functions copied from lib/*.c ******************** struct double_list { struct double_list *next, *prev; @@ -107,7 +107,7 @@ struct double_list *dlist_add(struct double_list **list, char *data) return new; } -/****************** end copies of lib/*.c *************/ +//****************** end copies of lib/*.c ************* // Parse config files into data structures. diff --git a/scripts/genconfig.sh b/scripts/genconfig.sh index 3887b077..c4940dfd 100755 --- a/scripts/genconfig.sh +++ b/scripts/genconfig.sh @@ -6,6 +6,7 @@ mkdir -p generated source configure +source scripts/portability.sh probecc() { @@ -129,7 +130,7 @@ genconfig() do # Grab the config block for Config.in echo "# $i" - sed -n '/^\*\//q;/^config [A-Z]/,$p' $i || return 1 + $SED -n '/^\*\//q;/^config [A-Z]/,$p' $i || return 1 echo done @@ -144,7 +145,7 @@ genconfig > generated/Config.in || rm generated/Config.in toys() { grep 'TOY(.*)' "$@" | grep -v TOYFLAG_NOFORK | grep -v "0))" | \ - sed -En 's/([^:]*):.*(OLD|NEW)TOY\( *([a-zA-Z][^,]*) *,.*/\1:\3/p' + $SED -En 's/([^:]*):.*(OLD|NEW)TOY\( *([a-zA-Z][^,]*) *,.*/\1:\3/p' } WORKING= @@ -163,5 +164,5 @@ done && echo -e "clean::\n\trm -f $WORKING $PENDING" && echo -e "list:\n\t@echo $(echo $WORKING | tr ' ' '\n' | sort | xargs)" && echo -e "list_pending:\n\t@echo $(echo $PENDING | tr ' ' '\n' | sort | xargs)" && -echo -e ".PHONY: $WORKING $PENDING" | sed 's/ \([^ ]\)/ test_\1/g' +echo -e ".PHONY: $WORKING $PENDING" | $SED 's/ \([^ ]\)/ test_\1/g' ) > .singlemake diff --git a/scripts/make.sh b/scripts/make.sh index 45d6f1ca..388af651 100755 --- a/scripts/make.sh +++ b/scripts/make.sh @@ -6,6 +6,7 @@ export LANG=c export LC_ALL=C set -o pipefail source ./configure +source scripts/portability.sh [ ! -z "$CROSS_COMPILE" ] && [ ! -e "$CROSS_COMPILE"cc ] && echo "missing ${CROSS_COMPILE}cc" && exit 1 @@ -15,12 +16,7 @@ source ./configure UNSTRIPPED="generated/unstripped/$(basename "$OUTNAME")" # Try to keep one more cc invocation going than we have processors -[ -z "$CPUS" ] && CPUS=$(($(nproc)+1)) - -if [ -z "$SED" ] -then - [ ! -z "$(which gsed 2>/dev/null)" ] && SED=gsed || SED=sed -fi +[ -z "$CPUS" ] && CPUS=$(($(nproc 2>/dev/null)+1)) # Respond to V= by echoing command lines as well as running them DOTPROG= @@ -102,7 +98,7 @@ genbuildsh() } if ! cmp -s <(genbuildsh 2>/dev/null | head -n 6 ; echo LINK="'"$LDOPTIMIZE $LDFLAGS) \ - <(head -n 7 generated/build.sh 2>/dev/null | sed '7s/ -o .*//') + <(head -n 7 generated/build.sh 2>/dev/null | $SED '7s/ -o .*//') then echo -n "Library probe" @@ -135,7 +131,8 @@ then # This long and roundabout sed invocation is to make old versions of sed # happy. New ones have '\n' so can replace one line with two without all - # the branches and tedious mucking about with hold space. + # the branches and tedious mucking about with hyperspace. + # TODO: clean this up to use modern stuff. $SED -n \ -e 's/^# CONFIG_\(.*\) is not set.*/\1/' \ diff --git a/scripts/mkflags.c b/scripts/mkflags.c index b28ae1e1..93294884 100644 --- a/scripts/mkflags.c +++ b/scripts/mkflags.c @@ -64,7 +64,7 @@ char *mark_gaps(char *flags, char *all) if (bare) bare = chrtype(c); if (*flags == c) { *(new++) = c; - *flags++; + flags++; continue; } diff --git a/scripts/mktags.c b/scripts/mktags.c index 9c23cf17..05494b2a 100644 --- a/scripts/mktags.c +++ b/scripts/mktags.c @@ -14,7 +14,7 @@ int main(int argc, char *argv[]) char *line = 0, *s; ssize_t len; - len = getline(&line, &len, stdin); + len = getline(&line, (void *)&len, stdin); if (len<0) break; while (len && isspace(line[len-1])) line[--len]=0; diff --git a/scripts/portability.sh b/scripts/portability.sh new file mode 100644 index 00000000..fddd84ec --- /dev/null +++ b/scripts/portability.sh @@ -0,0 +1,6 @@ +# sourced to find alternate names for things + +if [ -z "$SED" ] +then + [ ! -z "$(which gsed 2>/dev/null)" ] && SED=gsed || SED=sed +fi diff --git a/scripts/test.sh b/scripts/test.sh index 7e327a5d..a31021c6 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -1,6 +1,7 @@ #!/bin/bash -. scripts/runtest.sh +source scripts/runtest.sh +source scripts/portability.sh TOPDIR="$PWD" FILES="$PWD"/tests/files @@ -26,7 +27,7 @@ cd testdir export LC_COLLATE=C [ -f "$TOPDIR/generated/config.h" ] && - export OPTIONFLAGS=:$(echo $(sed -nr 's/^#define CFG_(.*) 1/\1/p' "$TOPDIR/generated/config.h") | sed 's/ /:/g') + export OPTIONFLAGS=:$(echo $($SED -nr 's/^#define CFG_(.*) 1/\1/p' "$TOPDIR/generated/config.h") | $SED 's/ /:/g') do_test() { diff --git a/tests/xargs.test b/tests/xargs.test index 407817cc..dce93ed3 100644 --- a/tests/xargs.test +++ b/tests/xargs.test @@ -27,6 +27,17 @@ rm one two three testing "-0 -n1" "printf 'a\0b\0c\0d\0e\0f' | xargs -0 -n1 echo _" "_ a\n_ b\n_ c\n_ d\n_ e\n_ f\n" "" "" testing "-0 -n2" "printf 'a\0b\0c\0d\0e\0f' | xargs -0 -n2 echo _" "_ a b\n_ c d\n_ e f\n" "" "" +testing "-t" "xargs -t 2>stderr ; cat stderr ; rm stderr" "one two\necho one two \n" "" "one\ntwo\n" + +testing "-E END" "xargs -E END" "a b\n" "" "a\nb\nEND\nc\nd\n" + +testing "-r" "xargs -r echo x" "" "" "" + +# TODO: what exactly is -x supposed to do? why does coreutils output "one"? +#testing "-x" "xargs -x -s 9 || echo expected" "one\nexpected\n" "" "one\ntwo\nthree" + +# TODO: test for -L? what exactly is the difference between -n and -L? + #testing "-n exact match" #testing "-s exact match" #testing "-s 0" @@ -108,7 +108,7 @@ extern struct toy_context { char wasroot; // dropped setuid // This is at the end so toy_init() doesn't zero it. - jmp_buf *rebound; // longjmp here instead of exit when do_rebound set + sigjmp_buf *rebound; // siglongjmp here instead of exit when do_rebound struct arg_list *xexit; // atexit() functions for xexit(), set by sigatexit() void *stacktop; // nested toy_exec() call count, or 0 if vforked } toys; diff --git a/toys/lsb/hostname.c b/toys/lsb/hostname.c index c9fafa54..ac7f9163 100644 --- a/toys/lsb/hostname.c +++ b/toys/lsb/hostname.c @@ -10,7 +10,7 @@ config HOSTNAME bool "hostname" default y help - usage: hostname [-bsf] [-F FILENAME] [newname] + usage: hostname [-bdsf] [-F FILENAME] [newname] Get/set the current hostname. @@ -36,13 +36,13 @@ void hostname_main(void) if (TT.F && (hostname = xreadfile(TT.F, 0, 0))) { if (!*chomp(hostname)) { if (CFG_TOYBOX_FREE) free(hostname); - if (!(toys.optflags&FLAG_b)) error_exit("empty '%s'", TT.F); + if (!FLAG(b)) error_exit("empty '%s'", TT.F); hostname = 0; } } // Implement -b. - if (!hostname && (toys.optflags&FLAG_b)) + if (!hostname && FLAG(b)) if (gethostname(toybuf, sizeof(toybuf)-1) || !*toybuf) hostname = "localhost"; @@ -55,9 +55,12 @@ void hostname_main(void) // Get the hostname. if (gethostname(toybuf, sizeof(toybuf)-1)) perror_exit("gethostname"); - if (!(h = gethostbyname(toybuf))) perror_exit("gethostbyname"); - snprintf(toybuf, sizeof(toybuf), "%s", h->h_name); + // We only do the DNS lookup for -d and -f. + if (FLAG(d) || FLAG(f)) { + if (!(h = gethostbyname(toybuf))) perror_exit("gethostbyname"); + snprintf(toybuf, sizeof(toybuf), "%s", h->h_name); + } dot = strchr(toybuf, '.'); - if (toys.optflags&FLAG_s) *dot = '\0'; - xputs(toys.optflags&FLAG_d ? dot+1 : toybuf); + if (FLAG(s) && dot) *dot = '\0'; + xputs(FLAG(d) ? dot+1 : toybuf); } diff --git a/toys/pending/sh.c b/toys/pending/sh.c index 0bfd32c5..8a9e93b6 100644 --- a/toys/pending/sh.c +++ b/toys/pending/sh.c @@ -205,13 +205,13 @@ static void run_pipeline(struct pipeline *line) // Is this command a builtin that should run in this process? if (tl && (tl->flags & TOYFLAG_NOFORK)) { struct toy_context temp; - jmp_buf rebound; + sigjmp_buf rebound; // This fakes lots of what toybox_main() does. memcpy(&temp, &toys, sizeof(struct toy_context)); memset(&toys, 0, sizeof(struct toy_context)); - if (!setjmp(rebound)) { + if (!sigsetjmp(rebound, 1)) { toys.rebound = &rebound; toy_init(tl, cmd->argv); tl->toy_main(); diff --git a/toys/pending/sntp.c b/toys/pending/sntp.c index f34c3da2..446e3f75 100644 --- a/toys/pending/sntp.c +++ b/toys/pending/sntp.c @@ -4,24 +4,30 @@ * * See https://www.ietf.org/rfc/rfc4330.txt -USE_SNTP(NEWTOY(sntp, "<1", TOYFLAG_USR|TOYFLAG_BIN)) +USE_SNTP(NEWTOY(sntp, "sp:", TOYFLAG_USR|TOYFLAG_BIN)) config SNTP bool "sntp" default n help - usage: sntp SERVER... + usage: sntp [-sm] [-p PORT] SERVER... Simple Network Time Protocol client, set system clock from a server. + + -p Use PORT (default 123) + -s Serer */ #define FOR_sntp #include "toys.h" GLOBALS( - int unused; + char *p, *m; ) +// Seconds from 1900 to 1970, including appropriate leap days +#define SEVENTIES 2208988800L + union socksaddr { struct sockaddr_in in; struct sockaddr_in6 in6; @@ -46,20 +52,123 @@ int xrecvwait(int fd, char *buf, int len, union socksaddr *sa, int timeout) return len; } +/// hardwired getaddrinfo() variant for what we want here. +static struct addrinfo *gai(char *server) +{ + return xgetaddrinfo(server, TT.p, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, 0); +} + +// Get time and return ntptime (saving timespec in pointer if not null) +// NTP time is high 32 bits = seconds since 1970 (blame RFC 868), low 32 bits +// fraction of a second. +static unsigned long long lunchtime(struct timespec *television) +{ + struct timespec tv; + + clock_gettime(CLOCK_REALTIME, &tv); + + if (television) *television = tv; + + // Unix time is 1970 but RFCs 868 and 958 said 1900, so add seconds 1900->1970 + // If they'd done a 34/30 bit split the Y2036 problem would be centuries + // from now and still give us nanosecond accuracy, but no... + return ((tv.tv_sec+SEVENTIES)<<32)+(((long long)tv.tv_nsec)<<32)/1000000000; +} + +// convert ntptime back to struct timespec. +static void doublyso(unsigned long long now, struct timespec *tv) +{ + // Y2036 fixup: if time wrapped, it's in the future + tv->tv_sec = (now>>32) + (1L<<32)*!(now&(1L<<63)); + tv->tv_sec -= SEVENTIES; // Force signed math for Y2038 fixup + tv->tv_nsec = ((now&((1L<<32)-1))*1000000000)>>32; +} + +// return difference between two timespecs in nanosecs +static long long nanodiff(struct timespec *old, struct timespec *new) +{ + return (new->tv_sec - old->tv_sec)*1000000000LL+(new->tv_nsec - old->tv_nsec); +} + +// Adjust timespec by nanosecond offset +static void nanomove(struct timespec *ts, long long offset) +{ + long long nano = ts->tv_nsec + offset, secs = nano/1000000000; + + ts->tv_sec += secs; + nano %= 1000000000; + if (nano<0) { + ts->tv_sec--; + nano += 1000000000; + } + ts->tv_nsec = nano; +} + +int multicast = 0; + void sntp_main(void) { + struct timespec tv, tv2; + unsigned long long *pktime = (void *)toybuf, now, then, before; + long long diff; struct addrinfo *ai; union socksaddr sa; - int fd, len; + int fd, attempts; + + if (!FLAG(s) && !*toys.optargs) error_exit("Need -s or SERVER"); - ai = xgetaddrinfo(*toys.optargs, 0, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, 0); + // Lookup address and open server or client UDP socket + if (!TT.p || !*TT.p) TT.p = "123"; + ai = gai(*toys.optargs); // When root, bind to local server address - if (!getuid()) - fd = xbind(xgetaddrinfo("", "123", AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, 0)); + if (!getuid()) fd = xbind(gai("")); else fd = xsocket(ai->ai_family, SOCK_DGRAM, IPPROTO_UDP); - xsendto(fd, toybuf, 48, ai->ai_addr); - len = xrecvwait(fd, toybuf, sizeof(toybuf), &sa, 5000); - printf("%d\n", len); - if (len>0) write(1, toybuf, len); + // Try 3 times + for (attempts = 0; attempts < 3; attempts++) { + // Prepare outgoing NTP packet + memset(toybuf, 0, 48); + *toybuf = 0xe3; // li = 3 (unsynchronized), version = 4, mode = 3 (client) + toybuf[2] = 8; // poll frequency 1<<8 = 256 seconds + pktime[5] = SWAP_BE64(before = lunchtime(&tv)); + + // Send and ye shall receive + xsendto(fd, toybuf, 48, ai->ai_addr); + then = (now = millitime())+4000; + while (now<then) { + // TODO: confirm sa matches + if (48 == xrecvwait(fd, toybuf, sizeof(toybuf), &sa, then-now)) { + if (multicast || pktime[3] == SWAP_BE64(before)) break; + } + now = millitime(); + } + if (now < then) break; + } + lunchtime(&tv2); + +//printf("before) %ld %ld\n", (long)tv.tv_sec, (long)tv.tv_nsec); +//printf("after) %ld %ld\n", (long)tv2.tv_sec, (long)tv2.tv_nsec); + + // determine midpoint of packet transit time according to local clock + // (simple calculation: assume each direction took same time so midpoint + // is time reported by other clock) + diff = nanodiff(&tv, &tv2)/2; +//printf("halfoff = %lld %llx\n", diff, diff); + nanomove(&tv, diff); +//printf("midpoint) %ld %ld\n", (long)tv.tv_sec, (long)tv.tv_nsec); + + doublyso(SWAP_BE64(pktime[5]), &tv2); + diff = nanodiff(&tv2, &tv); +//printf("server) %ld %ld\n", (long)tv2.tv_sec, (long)tv2.tv_nsec); +//printf("offby = %lld\n", diff); + +//int i; +//for (i=0; i<48; ) { +// printf("%02x", toybuf[i]); +// if (!(++i&15)) printf("\n"); +//} + + format_iso_time(toybuf, sizeof(toybuf)-1, &tv); + printf("%s offset %c%d.%09d secs\n", toybuf, (diff<0) ? '-' : '+', + abs(diff/1000000000), abs(diff%1000000000)); } diff --git a/toys/posix/ps.c b/toys/posix/ps.c index f5e080c7..fef9c908 100644 --- a/toys/posix/ps.c +++ b/toys/posix/ps.c @@ -234,7 +234,7 @@ struct ofields { * struct procpid contains a slot[] array of 64 bit values, with the following * data at each position in the array. Most is read from /proc/$PID/stat (see * https://kernel.org/doc/Documentation/filesystems/proc.txt table 1-4) but - * we we replace several fields with don't use with other data. */ + * we replace several fields with don't use with other data. */ enum { SLOT_pid, /*process id*/ SLOT_ppid, // parent process id @@ -297,7 +297,7 @@ struct procpid { * get the overflow back). * * slot: which slot[] out of procpid. Negative means it's a string field. - * Setting bit |64 requests extra display/sort processing. + * value|XX requests extra display/sort processing. * * The TAGGED_ARRAY plumbing produces an enum of indexes, the "tag" is the * first string argument and the prefix is the first argument to TAGGED_ARRAY @@ -308,8 +308,9 @@ struct procpid { * if (TT.bits & _PS_NAME) printf("-o included PS_NAME"); */ +#define XX 64 // force string representation for sorting, etc + // TODO: Android uses -30 for LABEL, but ideally it would auto-size. -// 64|slot means compare as string when sorting struct typography { char *name, *help; signed char width, slot; @@ -349,13 +350,13 @@ struct typography { // user/group (may call getpwuid() or similar) {"UID", "User id", 5, SLOT_uid}, - {"USER", "User name", -12, 64|SLOT_uid}, + {"USER", "User name", -12, XX|SLOT_uid}, {"RUID", "Real (before suid) user ID", 4, SLOT_ruid}, - {"RUSER", "Real (before suid) user name", -8, 64|SLOT_ruid}, + {"RUSER", "Real (before suid) user name", -8, XX|SLOT_ruid}, {"GID", "Group ID", 8, SLOT_gid}, - {"GROUP", "Group name", -8, 64|SLOT_gid}, + {"GROUP", "Group name", -8, XX|SLOT_gid}, {"RGID", "Real (before sgid) Group ID", 4, SLOT_rgid}, - {"RGROUP", "Real (before sgid) group name", -8, 64|SLOT_rgid}, + {"RGROUP", "Real (before sgid) group name", -8, XX|SLOT_rgid}, // clock displays (00:00:00) {"TIME", "CPU time consumed", 8, SLOT_utime}, @@ -382,20 +383,19 @@ struct typography { // Misc (special cases) {"STIME", "Start time (ISO 8601)", 5, SLOT_starttime}, - {"F", "Flags 1=FORKNOEXEC 4=SUPERPRIV", 1, 64|SLOT_flags}, + {"F", "Flags 1=FORKNOEXEC 4=SUPERPRIV", 1, XX|SLOT_flags}, {"S", "Process state:\n" "\t R (running) S (sleeping) D (device I/O) T (stopped) t (traced)\n" "\t Z (zombie) X (deader) x (dead) K (wakekill) W (waking)", - -1, 64}, + -1, XX}, {"STAT", "Process state (S) plus:\n" "\t < high priority N low priority L locked memory\n" "\t s session leader + foreground l multithreaded", - -5, 64}, - {"PCY", "Android scheduling policy", 3, 64|SLOT_pcy}, + -5, XX}, + {"PCY", "Android scheduling policy", 3, XX|SLOT_pcy}, ); // Show sorted "-o help" text for fields listed in toybuf[len] - static void help_fields(int len, int multi) { int i, j, k, left = 0; @@ -429,7 +429,7 @@ static void help_fields(int len, int multi) if (!multi && left) xputc('\n'); } -// Print help text for all -o field, with categories. +// Print help text for each -o field, with categories. static void help_help(void) { int i, jump = PS_CMD+1-PS_COMM; @@ -454,7 +454,7 @@ static void help_help(void) xexit(); } -// Return 0 to discard, nonzero to keep +// process match filter for top/ps/pgrep: Return 0 to discard, nonzero to keep static int shared_match_process(long long *slot) { struct ptr_len match[] = { @@ -478,8 +478,7 @@ static int shared_match_process(long long *slot) return ll ? 0 : -1; } - -// Return 0 to discard, nonzero to keep +// process match filter for ps: Return 0 to discard, nonzero to keep static int ps_match_process(long long *slot) { int i = shared_match_process(slot); @@ -497,12 +496,12 @@ static int ps_match_process(long long *slot) return 1; } -// Convert field to string representation +// Generate display string (260 bytes at end of toybuf) from struct ofield static char *string_field(struct procpid *tb, struct ofields *field) { char *buf = toybuf+sizeof(toybuf)-260, *out = buf, *s; int which = field->which, sl = typos[which].slot; - long long *slot = tb->slot, ll = (sl >= 0) ? slot[sl&63] : 0; + long long *slot = tb->slot, ll = (sl >= 0) ? slot[sl&(XX-1)] : 0; // numbers, mostly from /proc/$PID/stat if (which <= PS_BIT) { @@ -536,7 +535,7 @@ static char *string_field(struct procpid *tb, struct ofields *field) // user/group } else if (which <= PS_RGROUP) { sprintf(out, "%lld", ll); - if (sl&64) { + if (sl&XX) { if (which > PS_RUSER) { struct group *gr = bufgetgrgid(ll); @@ -576,7 +575,7 @@ static char *string_field(struct procpid *tb, struct ofields *field) // Percentage displays } else if (which <= PS__CPU) { - ll = slot[sl&63]*1000; + ll = slot[sl&(XX-1)]*1000; if (which==PS__VSZ || which==PS__MEM) ll /= slot[SLOT_totalram]/((which==PS__VSZ) ? 1024 : 4096); else if (slot[SLOT_upticks]) ll /= slot[SLOT_upticks]; @@ -624,7 +623,7 @@ static char *string_field(struct procpid *tb, struct ofields *field) return out; } -// Display process data that get_ps() read from /proc, formatting with TT.fields +// Display process data that get_ps() read from /proc, formatting via TT.fields static void show_ps(void *p) { struct procpid *tb = p; @@ -687,7 +686,7 @@ static void show_ps(void *p) putchar(TT.time ? '\r' : '\n'); } -// dirtree callback: read data about process, then display or store it. +// dirtree callback: read data about a process, then display or store it. // Fills toybuf with struct procpid and either DIRTREE_SAVEs a copy to ->extra // (in -k mode) or calls show_ps directly on toybuf (for low memory systems). static int get_ps(struct dirtree *new) @@ -982,6 +981,7 @@ static int get_ps(struct dirtree *new) return DIRTREE_SAVE; } +// wrapper for get_ps() that also collects threads under each processes static int get_threads(struct dirtree *new) { struct dirtree *dt; @@ -1033,6 +1033,7 @@ static int get_threads(struct dirtree *new) return 0; } +// Parse one FIELD argument (with optional =name :width) into struct ofields static char *parse_ko(void *data, char *type, int length) { struct ofields *field; @@ -1100,6 +1101,8 @@ static char *parse_ko(void *data, char *type, int length) return 0; } +// Write FIELD list into display header string (truncating at blen), +// and return bitfield of which FIELDs are used. static long long get_headers(struct ofields *field, char *buf, int blen) { long long bits = 0; @@ -1114,7 +1117,7 @@ static long long get_headers(struct ofields *field, char *buf, int blen) return bits; } -// Parse -p -s -t -u -U -g -G +// Parse command line options -p -s -t -u -U -g -G static char *parse_rest(void *data, char *str, int len) { struct ptr_len *pl = (struct ptr_len *)data; @@ -1192,7 +1195,7 @@ static char *parse_rest(void *data, char *str, int len) return str; } -// sort for -k +// sort processes by FIELD(s) listed in option -k static int ksort(void *aa, void *bb) { struct ofields *field; @@ -1203,7 +1206,7 @@ static int ksort(void *aa, void *bb) slot = typos[field->which].slot; // Can we do numeric sort? - if (!(slot&64)) { + if (!(slot&XX)) { if (ta->slot[slot]<tb->slot[slot]) ret = -1; if (ta->slot[slot]>tb->slot[slot]) ret = 1; } @@ -1220,6 +1223,8 @@ static int ksort(void *aa, void *bb) return ret; } +// Collect ->extra field from leaf nodes DIRTREE_SAVEd by get_ps() into array +// (recursion because tree from get_thread() isn't flat list of siblings) static struct procpid **collate_leaves(struct procpid **tb, struct dirtree *dt) { while (dt) { @@ -1234,6 +1239,8 @@ static struct procpid **collate_leaves(struct procpid **tb, struct dirtree *dt) return tb; } +// Allocate struct procpid array of length count and populate it with ->extra +// fields from dirtree leaf nodes. (top diffs old & new array to show changes) static struct procpid **collate(int count, struct dirtree *dt) { struct procpid **tbsort = xmalloc(count*sizeof(struct procpid *)); @@ -1243,6 +1250,7 @@ static struct procpid **collate(int count, struct dirtree *dt) return tbsort; } +// parse command line arguments (ala -k -o) with a comma separated FIELD list static void default_ko(char *s, void *fields, char *err, struct arg_list *arg) { struct arg_list def; @@ -1320,7 +1328,7 @@ void ps_main(void) for (field = TT.fields; field; field = field->next) { if (FLAG(n) && field->which>=PS_UID - && field->which<=PS_RGROUP && (typos[field->which].slot&64)) + && field->which<=PS_RGROUP && (typos[field->which].slot&XX)) field->which--; } } @@ -1585,7 +1593,7 @@ static void top_common( pos = stpcpy(toybuf, "Totals:"); for (field = TT.fields; field; field = field->next) { long long ll, bits = 0; - int slot = typos[field->which].slot&63; + int slot = typos[field->which].slot&(XX-1); if (field->which<PS_C || field->which>PS_DIO) continue; ll = 1LL<<field->which; diff --git a/toys/posix/xargs.c b/toys/posix/xargs.c index d25a2e3f..e23d14c4 100644 --- a/toys/posix/xargs.c +++ b/toys/posix/xargs.c @@ -5,28 +5,28 @@ * See http://opengroup.org/onlinepubs/9699919799/utilities/xargs.html * * TODO: Rich's whitespace objection, env size isn't fixed anymore. + * TODO: -x Exit if can't fit everything in one command + * TODO: -L Max number of lines of input per command -USE_XARGS(NEWTOY(xargs, "^I:E:L#ptxrn#<1s#0[!0E]", TOYFLAG_USR|TOYFLAG_BIN)) +USE_XARGS(NEWTOY(xargs, "^I:E:ptrn#<1s#0[!0E]", TOYFLAG_USR|TOYFLAG_BIN)) config XARGS bool "xargs" default y help - usage: xargs [-ptxr0] [-s NUM] [-n NUM] [-L NUM] [-E STR] COMMAND... + usage: xargs [-0prt] [-s NUM] [-n NUM] [-E STR] COMMAND... Run command line one or more times, appending arguments from stdin. If command exits with 255, don't launch another even if arguments remain. - -s Size in bytes per command line - -n Max number of arguments per command -0 Each argument is NULL terminated, no whitespace or quote processing - #-p Prompt for y/n from tty before running each command - #-t Trace, print command line to stderr - #-x Exit if can't fit everything in one command - #-r Don't run command with empty input - #-L Max number of lines of input per command - -E stop at line matching string + -E Stop at line matching string + -n Max number of arguments per command + -p Prompt for y/n from tty before running each command + -r Don't run command with empty input + -s Size in bytes per command line + -t Trace, print command line to stderr config XARGS_PEDANTIC bool "TODO xargs pedantic posix compatability" @@ -41,7 +41,7 @@ config XARGS_PEDANTIC #include "toys.h" GLOBALS( - long s, n, L; + long s, n; char *E, *I; long entries, bytes; @@ -134,6 +134,8 @@ void xargs_main(void) // Loop through exec chunks. while (data || !done) { + int doit = 1; + TT.entries = 0; TT.bytes = bytes; @@ -163,6 +165,8 @@ void xargs_main(void) break; } + if (TT.entries == 0 && FLAG(r)) continue; + // Accumulate cally thing if (data && !TT.entries) error_exit("argument too long"); @@ -176,13 +180,25 @@ void xargs_main(void) for (dtemp = dlist; dtemp; dtemp = dtemp->next) handle_entries(dtemp->data, out+entries); - if (!(pid = XVFORK())) { - xclose(0); - open("/dev/null", O_RDONLY); - xexec(out); + if (FLAG(p) || FLAG(t)) { + int i; + + for (i = 0; out[i]; ++i) fprintf(stderr, "%s ", out[i]); + if (FLAG(p)) { + fprintf(stderr, "?"); + doit = yesno(0); + } else fprintf(stderr, "\n"); + } + + if (doit) { + if (!(pid = XVFORK())) { + xclose(0); + open("/dev/null", O_RDONLY); + xexec(out); + } + waitpid(pid, &status, 0); + status = WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status)+127; } - waitpid(pid, &status, 0); - status = WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status)+127; // Abritrary number of execs, can't just leak memory each time... while (dlist) { diff --git a/www/code.html b/www/code.html index b1b17ef5..953c53bb 100644 --- a/www/code.html +++ b/www/code.html @@ -653,7 +653,7 @@ and change the exit code to indicate error, lets our toys.exit function change happen for signal exit paths and lets us remove the functions after we've called them.</p> -<p>You can intercept our exit by assigning a setjmp/longjmp buffer to +<p>You can intercept our exit by assigning a sigsetjmp/siglongjmp buffer to toys.rebound (set it back to zero to restore the default behavior). If you do this, cleaning up resource leaks is your problem.</p> |