aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2019-01-22 17:28:52 -0800
committerElliott Hughes <enh@google.com>2019-01-22 17:31:32 -0800
commita065dbe6c5d6eb6b723cf04c08a7beeccf1026c9 (patch)
tree541a3e6c299a1b2de7dae90d24d4e8d25ec626fb
parentb7a38dd7a5700d889fc4c97237276e1c78400157 (diff)
parentbbadc5e14136a4a2011080c08e064108d71e1429 (diff)
downloadtoybox-a065dbe6c5d6eb6b723cf04c08a7beeccf1026c9.tar.gz
Merge remote-tracking branch 'toybox/master' into HEAD
Change-Id: Ib387b8d25ff1d36bff7a017fd59ef47fbc4e1adf
-rw-r--r--generated/flags.h24
-rw-r--r--generated/globals.h4
-rw-r--r--generated/help.h6
-rw-r--r--generated/newtoys.h4
-rw-r--r--lib/lib.h6
-rw-r--r--scripts/config2help.c4
-rwxr-xr-xscripts/genconfig.sh7
-rwxr-xr-xscripts/make.sh13
-rw-r--r--scripts/mkflags.c2
-rw-r--r--scripts/mktags.c2
-rw-r--r--scripts/portability.sh6
-rwxr-xr-xscripts/test.sh5
-rw-r--r--tests/xargs.test11
-rw-r--r--toys.h2
-rw-r--r--toys/lsb/hostname.c17
-rw-r--r--toys/pending/sh.c4
-rw-r--r--toys/pending/sntp.c131
-rw-r--r--toys/posix/ps.c62
-rw-r--r--toys/posix/xargs.c50
-rw-r--r--www/code.html2
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))
diff --git a/lib/lib.h b/lib/lib.h
index 546b32b3..ed31ffa9 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -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"
diff --git a/toys.h b/toys.h
index 4083725a..9e49900b 100644
--- a/toys.h
+++ b/toys.h
@@ -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>