Merge remote-tracking branch 'toybox/master' into HEAD
diff --git a/lib/getmountlist.c b/lib/getmountlist.c
index 7a43101..332852a 100644
--- a/lib/getmountlist.c
+++ b/lib/getmountlist.c
@@ -19,7 +19,7 @@
arg = al->arg;
while ((next = comma_iterate(&arg, &len)))
if ((next = callback(data, next, len)))
- perror_exit("%s '%s'\n%*c", err, al->arg,
+ error_exit("%s '%s'\n%*c", err, al->arg,
(int)(5+strlen(toys.which->name)+strlen(err)+next-al->arg), '^');
al = al->next;
}
diff --git a/lib/interestingtimes.c b/lib/interestingtimes.c
index 93e2ec5..c4ea2c2 100644
--- a/lib/interestingtimes.c
+++ b/lib/interestingtimes.c
@@ -67,10 +67,14 @@
{
int key;
- while (512&(key = scan_key(scratch, miliwait))) {
- if (key<0) break;
- if (xx) *xx = (key>>10)&1023;
- if (yy) *yy = (key>>20)&1023;
+ if (512&(key = scan_key(scratch, miliwait))) {
+ if (key>0) {
+ if (xx) *xx = (key>>10)&1023;
+ if (yy) *yy = (key>>20)&1023;
+ toys.signal = SIGWINCH;
+
+ return -3;
+ }
}
return key;
@@ -195,7 +199,8 @@
// Read 1 byte so we don't overshoot sequence match. (We can deviate
// and fail to match, but match consumes entire buffer.)
- if (1 != read(0, scratch+1+*scratch, 1)) return -1;
+ if (toys.signal || 1 != read(0, scratch+1+*scratch, 1))
+ return toys.signal ? -3 : -1;
++*scratch;
}
diff --git a/lib/lib.c b/lib/lib.c
index b0ac471..6559030 100644
--- a/lib/lib.c
+++ b/lib/lib.c
@@ -1015,3 +1015,21 @@
return libbuf;
}
+
+// Returns pointer to letter at end, 0 if none. *start = initial %
+char *next_printf(char *s, char **start)
+{
+ for (; *s; s++) {
+ if (*s != '%') continue;
+ if (*++s == '%') continue;
+ if (start) *start = s-1;
+ while (0 <= stridx("0'#-+ ", *s)) s++;
+ while (isdigit(*s)) s++;
+ if (*s == '.') s++;
+ while (isdigit(*s)) s++;
+
+ return s;
+ }
+
+ return 0;
+}
diff --git a/lib/lib.h b/lib/lib.h
index f1cea22..5733103 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -101,6 +101,7 @@
void *xrealloc(void *ptr, size_t size);
char *xstrndup(char *s, size_t n);
char *xstrdup(char *s);
+void *xmemdup(void *s, long len);
char *xmprintf(char *format, ...) printf_format;
void xprintf(char *format, ...) printf_format;
void xputs(char *s);
@@ -196,6 +197,7 @@
int qstrcmp(const void *a, const void *b);
void create_uuid(char *uuid);
char *show_uuid(char *uuid);
+char *next_printf(char *s, char **start);
#define HR_SPACE 1 // Space between number and units
#define HR_B 2 // Use "B" for single byte units
diff --git a/lib/net.c b/lib/net.c
index facb096..2e72b26 100644
--- a/lib/net.c
+++ b/lib/net.c
@@ -49,6 +49,7 @@
for (;;) {
if (0>(i = poll(fds, nfds, timeout))) {
+ if (toys.signal) return i;
if (errno != EINTR && errno != ENOMEM) perror_exit("xpoll");
else if (timeout>0) timeout--;
} else return i;
diff --git a/lib/xwrap.c b/lib/xwrap.c
index a20d4b6..65e3018 100644
--- a/lib/xwrap.c
+++ b/lib/xwrap.c
@@ -21,9 +21,9 @@
void xstrncat(char *dest, char *src, size_t size)
{
- long len = strlen(src);
+ long len = strlen(dest);
- if (len+strlen(dest)+1 > size)
+ if (len+strlen(src)+1 > size)
error_exit("'%s%s' > %ld bytes", dest, src, (long)size);
strcpy(dest+len, src);
}
@@ -80,6 +80,14 @@
return xstrndup(s, strlen(s));
}
+void *xmemdup(void *s, long len)
+{
+ void *ret = xmalloc(len);
+ memcpy(ret, s, len);
+
+ return ret;
+}
+
// Die unless we can allocate enough space to sprintf() into.
char *xmprintf(char *format, ...)
{
diff --git a/main.c b/main.c
index 3949c84..e95c641 100644
--- a/main.c
+++ b/main.c
@@ -96,15 +96,20 @@
// Full init needed by multiplexer or reentrant calls, calls singleinit at end
void toy_init(struct toy_list *which, char *argv[])
{
+ void *oldwhich = toys.which;
+
// Drop permissions for non-suid commands.
if (CFG_TOYBOX_SUID) {
+ if (!toys.which) toys.which = toy_list;
+
uid_t uid = getuid(), euid = geteuid();
if (!(which->flags & TOYFLAG_STAYROOT)) {
if (uid != euid) {
if (!setuid(uid)) perror_exit("setuid %d->%d", euid, uid); // drop root
- else euid = uid;
+ euid = uid;
+ toys.wasroot++;
}
} else if (CFG_TOYBOX_DEBUG && uid && which != toy_list)
error_msg("Not installed suid root");
@@ -116,7 +121,7 @@
// don't blank old optargs if our new argc lives in the old optargs.
if (argv<toys.optargs || argv>toys.optargs+toys.optc) free(toys.optargs);
memset(&toys, 0, offsetof(struct toy_context, rebound));
- if (toys.which) memset(&this, 0, sizeof(this));
+ if (oldwhich) memset(&this, 0, sizeof(this));
// Continue to portion of init needed by standalone commands
toy_singleinit(which, argv);
@@ -136,7 +141,7 @@
return;
// Return if we need to re-exec to acquire root via suid bit.
- if (toys.which && (which->flags&TOYFLAG_ROOTONLY) && getuid()) return;
+ if (toys.which && (which->flags&TOYFLAG_ROOTONLY) && toys.wasroot) return;
// Run command
toy_init(which, argv);
diff --git a/tests/blkid.test b/tests/blkid.test
index 25ba9d1..b80cf4f 100755
--- a/tests/blkid.test
+++ b/tests/blkid.test
@@ -29,7 +29,7 @@
testing "blkid ntfs" 'bzcat "$BDIR"/ntfs.bz2 | blkid -' \
'-: UUID="8585600838bfe16e" TYPE="ntfs"\n' "" ""
testing "blkid reiserfs" 'bzcat "$BDIR"/reiser3.bz2 | blkid -' \
- '-: LABEL="" UUID="a5b99bec-45cc-41d7-986e-32f4b6fc28f2" TYPE="reiserfs"\n' \
+ '-: LABEL="myreiser" UUID="a5b99bec-45cc-41d7-986e-32f4b6fc28f2" TYPE="reiserfs"\n' \
"" ""
testing "blkid squashfs" 'bzcat "$BDIR"/squashfs.bz2 | blkid -' \
'-: TYPE="squashfs"\n' "" ""
diff --git a/tests/cat.test b/tests/cat.test
index 3d5842a..7be515d 100755
--- a/tests/cat.test
+++ b/tests/cat.test
@@ -16,8 +16,9 @@
"cat file1 notfound file2 2>stderr && echo ok ; cat stderr; rm stderr" \
"one\ntwo\ncat: notfound: No such file or directory\n" "" ""
+FILE="$(readlink -f /proc/self/exe)"
testing "cat file1" \
- "cat /proc/self/exe > file1 && cmp /proc/self/exe file1 && echo yes" \
+ 'cat "$FILE" > file1 && cmp "$FILE" file1 && echo yes' \
"yes\n" "" ""
testing "cat - file1" \
@@ -28,4 +29,4 @@
"cat - > /dev/full 2>stderr && echo ok; cat stderr; rm stderr" \
"cat: xwrite: No space left on device\n" "" "zero\n"
-rm file1 file2
\ No newline at end of file
+rm file1 file2
diff --git a/tests/chgrp.test b/tests/chgrp.test
index 2af9385..5a741d9 100755
--- a/tests/chgrp.test
+++ b/tests/chgrp.test
@@ -4,7 +4,7 @@
if [ "$(id -u)" -ne 0 ]
then
- echo "SKIPPED: chgrp (not root)"
+ echo "$SHOWSKIP: chgrp (not root)"
continue 2>/dev/null
exit
fi
diff --git a/tests/chmod.test b/tests/chmod.test
index 77106a6..887f738 100755
--- a/tests/chmod.test
+++ b/tests/chmod.test
@@ -5,6 +5,8 @@
#testing "name" "command" "result" "infile" "stdin"
+umask 022
+
PERM="---""--x""-w-""-wx""r--""r-x""rw-""rwx"
num2perm()
@@ -21,7 +23,7 @@
mkdir dir
touch file
-# We don't need to test all 511 permissions
+# We don't need to test all 512 permissions
for u in 0 1 2 3 4 5 6 7
do
for g in 0 3 6
diff --git a/tests/chown.test b/tests/chown.test
index 40ed7b5..408d5f3 100755
--- a/tests/chown.test
+++ b/tests/chown.test
@@ -4,7 +4,7 @@
if [ "$(id -u)" -ne 0 ]
then
- echo "SKIPPED: chown (not root)"
+ echo "$SHOWSKIP: chown (not root)"
continue 2>/dev/null
exit
fi
diff --git a/tests/cpio.test b/tests/cpio.test
index d0528e5..2d91b06 100755
--- a/tests/cpio.test
+++ b/tests/cpio.test
@@ -25,10 +25,10 @@
# the relevant bit should be here:
# 110*5 + 4*3 + 2 + 6*3 = 550 + 12 + 20 = 582
# files are padded to n*4, names are padded to 2 + n*4 due to the header length
-testing "cpio archive length" "cpio -o -H newc|dd ibs=2 skip=291 count=5" "TRAILER!!!" "" "a\nbb\nccc\ndddd\n"
-testing "cpio archive magic" "cpio -o -H newc|dd ibs=2 count=3" "070701" "" "a\n"
+testing "cpio archive length" "cpio -o -H newc|dd ibs=2 skip=291 count=5 2>/dev/null" "TRAILER!!!" "" "a\nbb\nccc\ndddd\n"
+testing "cpio archive magic" "cpio -o -H newc|dd ibs=2 count=3 2>/dev/null" "070701" "" "a\n"
# check name length (8 bytes before the empty "crc")
-testing "cpio name length" "cpio -o -H newc|dd ibs=2 skip=47 count=4" "00000002" "" "a\n"
+testing "cpio name length" "cpio -o -H newc|dd ibs=2 skip=47 count=4 2>/dev/null" "00000002" "" "a\n"
rm a bb ccc dddd
# archive dangling symlinks and empty files even if we cannot open them
diff --git a/tests/find.test b/tests/find.test
index 71a3506..4e987b6 100755
--- a/tests/find.test
+++ b/tests/find.test
@@ -70,7 +70,7 @@
# Still fails
testing "find unterminated -exec {}" \
- "find dir -type f -exec ls {}" "" "" ""
+ "find dir -type f -exec ls {} 2>/dev/null || echo bad" "bad\n" "" ""
testing "find -exec {} +" \
"find dir -type f -exec ls {} +" "dir/file\n" "" ""
diff --git a/tests/grep.test b/tests/grep.test
index f3bc9a3..aa11c70 100755
--- a/tests/grep.test
+++ b/tests/grep.test
@@ -97,3 +97,11 @@
testing "grep -o ''" "grep -o '' input" "" "one one one\n" ""
testing "grep backref" 'grep -e "a\(b\)" -e "b\(c\)\1"' "bcc\nab\n" \
"" "bcc\nbcb\nab\n"
+
+testing "grep -A" "grep -A 2 yes" "yes\nno\nno\n--\nyes\nno\nno\nyes\nno\n" \
+ "" "yes\nno\nno\nno\nyes\nno\nno\nyes\nno"
+testing "grep -B" "grep -B 1 yes" "no\nyes\n--\nno\nyes\nno\nyes\n" \
+ "" "no\nno\nno\nyes\nno\nno\nyes\nno\nyes"
+testing "grep -C" "grep -C 1 yes" \
+ "yes\nno\n--\nno\nyes\nno\nno\nyes\nno\nyes\nno\n" \
+ "" "yes\nno\nno\nno\nyes\nno\nno\nyes\nno\nyes\nno\nno"
diff --git a/tests/ifconfig.test b/tests/ifconfig.test
index 0c0af46..c30c74c 100755
--- a/tests/ifconfig.test
+++ b/tests/ifconfig.test
@@ -23,7 +23,7 @@
if [ "$(id -u)" -ne 0 ]
then
- echo "SKIPPED: ifconfig (not root)"
+ echo "$SHOWSKIP: ifconfig (not root)"
continue 2>/dev/null
exit
fi
diff --git a/tests/losetup.test b/tests/losetup.test
index fe1e520..84ac4b1 100755
--- a/tests/losetup.test
+++ b/tests/losetup.test
@@ -4,7 +4,7 @@
if [ "$(id -u)" -ne 0 ]
then
- echo "SKIPPED: losetup (not root)"
+ echo "$SHOWSKIP: losetup (not root)"
continue 2>/dev/null
exit
fi
diff --git a/tests/seq.test b/tests/seq.test
index 4d23f8e..5996617 100755
--- a/tests/seq.test
+++ b/tests/seq.test
@@ -28,14 +28,14 @@
# Test -f format filtering
for i in %f %e %g "boo %f yah" "% f" %-1.2f %+-f "%+ - f" %.2f %3.f "%'.2f" \
- %%%f%% %%%f%%%
+ %%%f%%
do
testing "seq filter -f \"$i\"" "seq -f \"$i\" 1 3 > /dev/null && echo yes" \
"yes\n" "" ""
done
# Test -f format filtering failures
for i in %d %s "" "boo %f %f yah" "%*f" %-1.2.3f '%2$f' %1-f "%1 f" \
- %2..2f
+ %2..2f %%%f%%%
do
testing "seq filter reject -f '$i'" \
"seq -f '$i' 1 3 2>/dev/null || echo no" "no\n" "" ""
diff --git a/tests/useradd.test b/tests/useradd.test
index a954e72..6ffe707 100755
--- a/tests/useradd.test
+++ b/tests/useradd.test
@@ -7,7 +7,7 @@
if [ "$(id -u)" -ne 0 ]
then
- echo "SKIPPED: useradd (not root)"
+ echo "$SHOWSKIP: useradd (not root)"
continue 2>/dev/null
exit
fi
diff --git a/toys.h b/toys.h
index 8bd3be2..4bfccb1 100644
--- a/toys.h
+++ b/toys.h
@@ -127,13 +127,14 @@
int exitval; // Value error_exit feeds to exit()
int optc; // Count of optargs
int old_umask; // Old umask preserved by TOYFLAG_UMASK
- int toycount; // Total number of commands in this build
- int signal; // generic_signal() records what signal it saw here
+ short toycount; // Total number of commands in this build
+ short signal; // generic_signal() records what signal it saw here
int signalfd; // and writes signal to this fd, if set
+ int 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
- void *stacktop; // nested toy_exec() call count, or -1 if vforked
+ void *stacktop; // nested toy_exec() call count, or 0 if vforked
} toys;
// Two big temporary buffers: one for use by commands, one for library functions
diff --git a/toys/lsb/seq.c b/toys/lsb/seq.c
index c620474..3ff9ca4 100644
--- a/toys/lsb/seq.c
+++ b/toys/lsb/seq.c
@@ -32,22 +32,13 @@
// Ensure there's one %f escape with correct attributes
static void insanitize(char *f)
{
- char *s;
- int found = 0;
+ char *s = next_printf(f, 0);
- for (s = f; *s; s++) {
- if (*s != '%') continue;
- if (*++s == '%') continue;
- if (found++) break;
- while (0 <= stridx("0'#-+ ", *s)) s++;
- while (isdigit(*s)) s++;
- if (*s == '.') s++;
- while (isdigit(*s)) s++;
- if (-1 == stridx("aAeEfFgG", *s)) break;
+ if (!s) error_exit("bad -f no %%f");
+ if (-1 == stridx("aAeEfFgG", *s) || (s = next_printf(s, 0))) {
+ // The @ is a byte offset, not utf8 chars. Waiting for somebody to complain.
+ error_exit("bad -f '%s'@%ld", f, s-f+1);
}
-
- // The @ is a byte offset, not utf8 chars. Waiting for somebody to complain...
- if (*s || !found) error_exit("bad -f '%s'@%ld", f, s-f+1);
}
void seq_main(void)
diff --git a/toys/other/blkid.c b/toys/other/blkid.c
index 8aab8f7..2880812 100644
--- a/toys/other/blkid.c
+++ b/toys/other/blkid.c
@@ -48,7 +48,7 @@
{"jfs", 0x3153464a, 4, 32768, 32920, 16, 32904},
{"nilfs", 0x3434, 2, 1030, 1176, 80, 1192},
{"reiserfs", 0x724573496552ULL, 6, 8244, 8276, 16, 8292},
- {"reiserfs", 0x724573496552ULL, 6, 65588, 65620, 16, 65536},
+ {"reiserfs", 0x724573496552ULL, 6, 65588, 65620, 16, 65636},
{"romfs", 0x2d6d6f72, 4, 0, 0,0,0},
{"squashfs", 0x73717368, 4, 0, 0,0,0},
{"xiafs", 0x012fd16d, 4, 572, 0,0,0},
diff --git a/toys/other/nsenter.c b/toys/other/nsenter.c
index bda77ac..1375728 100644
--- a/toys/other/nsenter.c
+++ b/toys/other/nsenter.c
@@ -13,7 +13,7 @@
// Note: flags go in same order (right to left) for shared subset
USE_NSENTER(NEWTOY(nsenter, "<1F(no-fork)t#<1(target)i:(ipc);m:(mount);n:(net);p:(pid);u:(uts);U:(user);", TOYFLAG_USR|TOYFLAG_BIN))
-USE_UNSHARE(NEWTOY(unshare, "<1^rimnpuU", TOYFLAG_USR|TOYFLAG_BIN))
+USE_UNSHARE(NEWTOY(unshare, "<1^f(fork);r(map-root-user);i:(ipc);m:(mount);n:(net);p:(pid);u:(uts);U:(user);", TOYFLAG_USR|TOYFLAG_BIN))
config UNSHARE
bool "unshare"
@@ -25,13 +25,14 @@
Create new container namespace(s) for this process and its children, so
some attribute is not shared with the parent process.
- -i SysV IPC (message queues, semaphores, shared memory)
- -m Mount/unmount tree
- -n Network address, sockets, routing, iptables
- -p Process IDs and init
- -r Become root (map current euid/egid to 0/0, implies -U)
- -u Host and domain names
- -U UIDs, GIDs, capabilities
+ -f Fork command in the background (--fork)
+ -i SysV IPC (message queues, semaphores, shared memory) (--ipc)
+ -m Mount/unmount tree (--mount)
+ -n Network address, sockets, routing, iptables (--net)
+ -p Process IDs and init (--pid)
+ -r Become root (map current euid/egid to 0/0, implies -U) (--map-root-user)
+ -u Host and domain names (--uts)
+ -U UIDs, GIDs, capabilities (--user)
A namespace allows a set of processes to have a different view of the
system than other sets of processes.
@@ -50,7 +51,7 @@
The namespaces to switch are:
-i SysV IPC: message queues, semaphores, shared memory (--ipc)
- -m Mount/unmount tree (--mnt)
+ -m Mount/unmount tree (--mount)
-n Network address, sockets, routing, iptables (--net)
-p Process IDs and init, will fork unless -F is used (--pid)
-u Host and domain names (--uts)
@@ -102,6 +103,11 @@
return toys.optflags & FLAG_r;
}
+static int test_f()
+{
+ return toys.optflags & FLAG_f;
+}
+
// Shift back to the context GLOBALS lives in (I.E. matching the filename).
#define CLEANUP_unshare
#define FOR_nsenter
@@ -127,6 +133,11 @@
if (unshare(f)) perror_exit(0);
if (test_r()) handle_r(euid, egid);
+ if (test_f()) {
+ toys.exitval = xrun(toys.optargs);
+
+ return;
+ }
// Bind to existing namespace(s)?
} else if (CFG_NSENTER) {
char *nsnames = "user\0uts\0pid\0net\0mnt\0ipc";
diff --git a/toys/pending/traceroute.c b/toys/pending/traceroute.c
index 830331a..4a4f55d 100644
--- a/toys/pending/traceroute.c
+++ b/toys/pending/traceroute.c
@@ -254,7 +254,7 @@
struct ip *rcv_pkt = (struct ip*) toybuf;
struct icmp *ricmp;
- ricmp = (struct icmp *) ((void*)rcv_pkt + (rcv_pkt->ip_hl << 2));
+ ricmp = (struct icmp *) ((char*)rcv_pkt + (rcv_pkt->ip_hl << 2));
if (ricmp->icmp_code == ICMP_UNREACH_NEEDFRAG)
pmtu = ntohs(ricmp->icmp_nextmtu);
@@ -275,7 +275,7 @@
icmp_res = (ricmp->icmp_type == ICMP_TIMXCEED ? -1 :
ricmp->icmp_code);
} else {
- hicmp = (struct icmp *) ((void*)hip + (hip->ip_hl << 2));
+ hicmp = (struct icmp *) ((char*)hip + (hip->ip_hl << 2));
if (ricmp->icmp_type == ICMP_ECHOREPLY
&& ricmp->icmp_id == ntohs(TT.ident)
&& ricmp->icmp_seq == ntohs(seq))
@@ -481,7 +481,7 @@
int lsrr = 0, set = 1;
if(!(toys.optflags & FLAG_4) &&
- (inet_pton(AF_INET6, toys.optargs[0], (void*)&dest)))
+ (inet_pton(AF_INET6, toys.optargs[0], &dest)))
toys.optflags |= FLAG_6;
memset(&dest, 0, sizeof(dest));
@@ -562,7 +562,7 @@
if (!TT.istraceroute6) {
if ((toys.optflags & FLAG_t) &&
setsockopt(TT.snd_sock, IPPROTO_IP, IP_TOS, &tyser, sizeof(tyser)) < 0)
- perror_exit("IP_TOS %d failed ", TT.tos);
+ perror_exit("IP_TOS %ld failed ", TT.tos);
#ifdef IP_DONTFRAG
if ((toys.optflags & FLAG_F) &&
@@ -570,7 +570,7 @@
sizeof(set)) < 0)) perror_exit("IP_DONTFRAG failed ");
#endif
} else if (setsockopt(TT.snd_sock, IPPROTO_IPV6, IPV6_TCLASS, &TT.tos,
- sizeof(TT.tos)) < 0) perror_exit("IPV6_TCLASS %d failed ", TT.tos);
+ sizeof(TT.tos)) < 0) perror_exit("IPV6_TCLASS %ld failed ", TT.tos);
set_flag_dr(TT.snd_sock);
TT.packet = xzalloc(TT.msg_len);
@@ -592,7 +592,7 @@
}
if(TT.first_ttl > TT.max_ttl)
- error_exit("ERROR :Range for -f is 1 to %d (max ttl)", TT.max_ttl);
+ error_exit("ERROR :Range for -f is 1 to %ld (max ttl)", TT.max_ttl);
xprintf("traceroute to %s(%s)", toys.optargs[0],
inet_ntoa(((struct sockaddr_in *)&dest)->sin_addr));
diff --git a/toys/posix/cp.c b/toys/posix/cp.c
index f932ca4..cb7e6e3 100644
--- a/toys/posix/cp.c
+++ b/toys/posix/cp.c
@@ -177,7 +177,7 @@
if (!faccessat(cfd, catch, F_OK, 0) && !S_ISDIR(cst.st_mode)) {
char *s;
- if (S_ISDIR(try->st.st_dev)) {
+ if (S_ISDIR(try->st.st_mode)) {
error_msg("dir at '%s'", s = dirtree_path(try, 0));
free(s);
return 0;
diff --git a/toys/posix/find.c b/toys/posix/find.c
index e16c0df..febe688 100644
--- a/toys/posix/find.c
+++ b/toys/posix/find.c
@@ -8,7 +8,7 @@
* Parentheses can only stack 4096 deep
* Not treating two {} as an error, but only using last
*
- * TODO: -empty (dirs too!) -delete -execdir +
+ * TODO: -empty (dirs too!)
USE_FIND(NEWTOY(find, "?^HL[-HL]", TOYFLAG_USR|TOYFLAG_BIN))
@@ -46,6 +46,7 @@
-print Print match with newline -print0 Print match with null
-exec Run command with path -execdir Run command in file's dir
-ok Ask before exec -okdir Ask before execdir
+ -delete Remove matching file/dir
Commands substitute "{}" with matched file. End with ";" to run each file,
or "+" (next argument after "{}") to collect and run with multiple files.
@@ -277,7 +278,13 @@
} else s++;
if (!strcmp(s, "xdev")) TT.xdev = 1;
- else if (!strcmp(s, "depth")) TT.depth = 1;
+ else if (!strcmp(s, "delete")) {
+ // Delete forces depth first
+ TT.depth = 1;
+ if (new && check)
+ test = !unlinkat(dirtree_parentfd(new), new->name,
+ S_ISDIR(new->st.st_mode) ? AT_REMOVEDIR : 0);
+ } else if (!strcmp(s, "depth")) TT.depth = 1;
else if (!strcmp(s, "o") || !strcmp(s, "or")) {
if (not) goto error;
if (active) {
diff --git a/toys/posix/grep.c b/toys/posix/grep.c
index 1cb4f9c..92d723b 100644
--- a/toys/posix/grep.c
+++ b/toys/posix/grep.c
@@ -6,7 +6,7 @@
*
* TODO: -ABC
-USE_GREP(NEWTOY(grep, "ZzEFHabhinorsvwclqe*f*m#x[!wx][!EFw]", TOYFLAG_BIN))
+USE_GREP(NEWTOY(grep, "C#B#A#ZzEFHabhinorsvwclqe*f*m#x[!wx][!EFw]", TOYFLAG_BIN))
USE_EGREP(OLDTOY(egrep, grep, TOYFLAG_BIN))
USE_FGREP(OLDTOY(fgrep, grep, TOYFLAG_BIN))
@@ -14,7 +14,7 @@
bool "grep"
default y
help
- usage: grep [-EFivwcloqsHbhn] [-m MAX] [-e REGEX]... [-f REGFILE] [FILE]...
+ usage: grep [-EFivwcloqsHbhn] [-A NUM] [-m MAX] [-e REGEX]... [-f REGFILE] [FILE]...
Show lines matching regular expressions. If no -e, first argument is
regular expression to match. With no files (or "-" filename) read stdin.
@@ -24,11 +24,12 @@
-f File containing regular expressions to match.
match type:
- -E extended regex syntax -F fixed (match literal string)
- -i case insensitive -m stop after this many lines matched
- -r recursive (on dir) -v invert match
- -w whole word (implies -E) -x whole line
- -z input NUL terminated
+ -A Show NUM lines after -B Show NUM lines before match
+ -C NUM lines context (A+B) -E extended regex syntax
+ -F fixed (literal match) -i case insensitive
+ -m match MAX many lines -r recursive (on dir)
+ -v invert match -w whole word (implies -E)
+ -x whole line -z input NUL terminated
display modes: (default: matched line)
-c count of matching lines -l show matching filenames
@@ -58,15 +59,19 @@
long m;
struct arg_list *f;
struct arg_list *e;
+ long a;
+ long b;
+ long c;
)
// Show matches in one file
static void do_grep(int fd, char *name)
{
+ struct double_list *dlb = 0;
FILE *file = fdopen(fd, "r");
- long offset = 0;
+ long offset = 0, after = 0, before = 0;
int lcount = 0, mcount = 0;
- char indelim = '\n' * !(toys.optflags&FLAG_z),
+ char *bars = 0, indelim = '\n' * !(toys.optflags&FLAG_z),
outdelim = '\n' * !(toys.optflags&FLAG_Z);
if (!fd) name = "(standard input)";
@@ -164,6 +169,11 @@
matches.rm_so = 0;
} else if (rc) break;
+ // At least one line we didn't print since match while -ABC active
+ if (bars) {
+ xputs(bars);
+ bars = 0;
+ }
mmatch++;
toys.exitval = 0;
if (toys.optflags & FLAG_q) xexit();
@@ -183,10 +193,22 @@
if (toys.optflags & FLAG_b)
printf("%ld:", offset + (start-line) +
((toys.optflags & FLAG_o) ? matches.rm_so : 0));
- if (!(toys.optflags & FLAG_o)) xprintf("%s%c", line, outdelim);
- else {
- xprintf("%.*s%c", matches.rm_eo - matches.rm_so,
- start + matches.rm_so, outdelim);
+ if (!(toys.optflags & FLAG_o)) {
+ while (dlb) {
+ struct double_list *dl = dlist_pop(&dlb);
+
+ xprintf("%s%c", dl->data, outdelim);
+ free(dl->data);
+ free(dl);
+ before--;
+ }
+
+ while (before) xprintf("%s%c", line, outdelim);
+ xprintf("%s%c", line, outdelim);
+ if (TT.a) after = TT.a+1;
+ } else {
+ xprintf("%.*s%c", (int)(matches.rm_eo-matches.rm_so),
+ start+matches.rm_so, outdelim);
}
}
@@ -195,9 +217,30 @@
} while (*start);
offset += len;
+ if (mmatch) mcount++;
+ else {
+ int discard = (after || TT.b);
+
+ if (after && --after) {
+ xprintf("%s%c", line, outdelim);
+ discard = 0;
+ }
+ if (discard && TT.b) {
+ dlist_add(&dlb, line);
+ line = 0;
+ if (++before>TT.b) {
+ struct double_list *dl;
+
+ dl = dlist_pop(&dlb);
+ free(dl->data);
+ free(dl);
+ before--;
+ } else discard = 0;
+ }
+ if (discard && mcount) bars = "--";
+ }
free(line);
- if (mmatch) mcount++;
if ((toys.optflags & FLAG_m) && mcount >= TT.m) break;
}
@@ -292,6 +335,9 @@
{
char **ss = toys.optargs;
+ if (!TT.a) TT.a = TT.c;
+ if (!TT.b) TT.b = TT.c;
+
// Handle egrep and fgrep
if (*toys.which->name == 'e') toys.optflags |= FLAG_E;
if (*toys.which->name == 'f') toys.optflags |= FLAG_F;
diff --git a/toys/posix/ps.c b/toys/posix/ps.c
index b06be42..f99a216 100644
--- a/toys/posix/ps.c
+++ b/toys/posix/ps.c
@@ -29,18 +29,26 @@
* output argv[0] unmodified for -o comm or -o args (but procps violates
* posix for -o comm anyway, it's stat[2] not argv[0]).
*
+ * Note: iotop is STAYROOT so it can read other process's /proc/$PID/io
+ * files (why they're not globally readable when the rest of proc
+ * data is...?) and get a global I/O picture. Normal top is NOT,
+ * even though you can -o AIO there, to give sysadmins the option
+ * to reduce security exposure.)
+ *
* TODO: ps aux (att & bsd style "ps -ax" vs "ps ax" behavior difference)
* TODO: switch -fl to -y
* TODO: thread support /proc/$d/task/%d/stat (and -o stat has "l")
* TODO: iotop: Window size change: respond immediately. Why not padding
* at right edge? (Not adjusting to screen size at all? Header wraps?)
+ * TODO: top: thread support and SMP
+ * TODO: pgrep -f only searches the amount of cmdline that fits in toybuf.
-USE_PS(NEWTOY(ps, "k(sort)*P(ppid)*aAdeflno*p(pid)*s*t*u*U*g*G*wZ[!ol][+Ae]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
+USE_PS(NEWTOY(ps, "k(sort)*P(ppid)*aAdeflMno*O*p(pid)*s*t*u*U*g*G*wZ[!ol][+Ae]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
// stayroot because iotop needs root to read other process' proc/$$/io
-USE_TOP(NEWTOY(top, ">0m" "p*u*d#=3<1n#<1bq", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT|TOYFLAG_LOCALE))
-USE_IOTOP(NEWTOY(iotop, ">0Aako" "p*u*d#=3<1n#<1bq", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT|TOYFLAG_LOCALE))
-USE_PGREP(NEWTOY(pgrep, "?cld:u*U*t*s*P*g*G*fnovxL:", TOYFLAG_USR|TOYFLAG_BIN))
-USE_PKILL(NEWTOY(pkill, "Vu*U*t*s*P*g*G*fnovxl:", TOYFLAG_USR|TOYFLAG_BIN))
+USE_TOP(NEWTOY(top, ">0m" "k*o*p*u*s#<1=9d#=3<1n#<1bq", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
+USE_IOTOP(NEWTOY(iotop, ">0AaKO" "k*o*p*u*s#<1=7d#=3<1n#<1bq", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT|TOYFLAG_LOCALE))
+USE_PGREP(NEWTOY(pgrep, "?cld:u*U*t*s*P*g*G*fnovxL:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_PKILL(NEWTOY(pkill, "Vu*U*t*s*P*g*G*fnovxl:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
config PS
bool "ps"
@@ -68,6 +76,7 @@
Output modifiers:
-k Sort FIELDs in +increasing or -decreasting order (--sort)
+ -M Measure field widths (expanding as necessary)
-n Show numeric USER and GROUP
-w Wide output (don't truncate at terminal width)
@@ -75,7 +84,8 @@
-f Full listing (-o USER:8=UID,PID,PPID,C,STIME,TTY,TIME,CMD)
-l Long listing (-o F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD)
- -o Output the listed FIELDs, each with optional :size and/or =title
+ -o Output FIELDs instead of defaults, each with optional :size and =title
+ -O Add FIELDS to defaults
-Z Include LABEL
Available -o FIELDs:
@@ -114,41 +124,35 @@
help
usage: top [-m] [ -d seconds ] [ -n iterations ]
- Provide a view of process activity in real time.
- Keys
- N/M/P/T show CPU usage, sort by pid/mem/cpu/time
- S show memory
- R reverse sort
- H toggle threads
- C,1 toggle SMP
- Q,^C exit
+ Show process activity in real time.
- Options
- -n Iterations before exiting
- -d Delay between updates
- -m Same as 's' key
+ -k Fallback sort FIELDS (default -S,-%CPU,-ETIME,-PID)
+ -o Show FIELDS (def PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,CMDLINE)
+ -s Sort by field number (1-X, default 9)
# Requires CONFIG_IRQ_TIME_ACCOUNTING in the kernel for /proc/$$/io
config IOTOP
bool "iotop"
default y
help
- usage: iotop [-Aako]
+ usage: iotop [-AaKO]
Rank processes by I/O.
-A All I/O, not just disk
-a Accumulated I/O (not percentage)
- -k Kilobytes
- -o Only show processes doing I/O
-
- Cursor left/right to change sort, space to update, Q to exit.
+ -K Kilobytes
+ -k Fallback sort FIELDS (default -[D]IO,-ETIME,-PID)
+ -O Only show processes doing I/O
+ -o Show FIELDS (default PID,PR,USER,[D]READ,[D]WRITE,SWAP,[D]IO,COMM)
+ -s Sort by field number (0-X, default 6)
config TOP_COMMON
bool
default y
help
- usage: COMMON [-bq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,]
+ usage: COMMON [-bq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,] [-s SORT]
+
-b Batch mode (no tty)
-d Delay SECONDS between each cycle (default 3)
-n Exit after NUMBER iterations
@@ -156,6 +160,9 @@
-u Show these USERs
-q Quiet (no header lines)
+ Cursor LEFT/RIGHT to change sort, UP/DOWN move list, space to force
+ update, R to reverse sort, Q to exit.
+
config PGREP
bool "pgrep"
default y
@@ -213,6 +220,7 @@
struct arg_list *t;
struct arg_list *s;
struct arg_list *p;
+ struct arg_list *O;
struct arg_list *o;
struct arg_list *P;
struct arg_list *k;
@@ -220,8 +228,11 @@
struct {
long n;
long d;
+ long s;
struct arg_list *u;
struct arg_list *p;
+ struct arg_list *o;
+ struct arg_list *k;
} top;
struct{
char *L;
@@ -234,9 +245,9 @@
struct arg_list *u;
char *d;
- void *regexes;
+ void *regexes, *snapshot;
int signal;
- pid_t self;
+ pid_t self, match;
} pgrep;
};
@@ -245,8 +256,7 @@
unsigned width, height;
dev_t tty;
void *fields, *kfields;
- long long ticks, bits, ioread, iowrite, aioread, aiowrite;
- size_t header_len;
+ long long ticks, bits, time;
int kcount, forcek, sortpos;
int (*match_process)(long long *slot);
void (*show_process)(void *tb);
@@ -342,8 +352,6 @@
// Misc
{"STIME", 5, SLOT_starttime}, {"F", 1, 64|SLOT_flags}, {"S", -1, 64},
{"STAT", -5, 64},
-
-
);
// Return 0 to discard, nonzero to keep
@@ -533,7 +541,7 @@
else width -= printf("%*.*s", pad, len, out);
if (!width) break;
}
- xputc('\n');
+ xputc(TT.time ? '\r' : '\n');
}
// dirtree callback: read data about process to display, store, or discard it.
@@ -634,7 +642,7 @@
// systems (or ps | more) we re-fetch uptime as we fetch each /proc line.
sysinfo(&TT.si);
slot[SLOT_uptime] = TT.si.uptime;
- slot[SLOT_upticks] = slot[SLOT_uptime]*TT.ticks - slot[SLOT_rss];
+ slot[SLOT_upticks] = slot[SLOT_uptime]*TT.ticks - slot[SLOT_starttime];
// Do we need to read "statm"?
if (TT.bits&(_PS_VIRT|_PS_RES|_PS_SHR)) {
@@ -745,6 +753,7 @@
buf += strlen(buf)+1;
}
+ TT.kcount++;
if (TT.show_process) {
TT.show_process(tb);
@@ -755,7 +764,6 @@
s = xmalloc(buf-toybuf);
new->extra = (long)s;
memcpy(s, toybuf, buf-toybuf);
- TT.kcount++;
return DIRTREE_SAVE;
}
@@ -796,12 +804,10 @@
}
// Find type
- if (*(struct strawberry **)data == TT.kfields) {
- field->reverse = 1;
- if (*type == '-') field->reverse = -1;
- else if (*type != '+') type--;
- type++;
- }
+ field->reverse = 1;
+ if (*type == '-') field->reverse = -1;
+ else if (*type != '+') type--;
+ type++;
for (i = 0; i<ARRAY_LEN(typos); i++) {
field->which = i;
for (j = 0; j<2; j++) {
@@ -822,15 +828,21 @@
else if (typos[field->which].width<0) field->len *= -1;
dlist_add_nomalloc(data, (void *)field);
- // Print padded header for -o.
- if (*(struct strawberry **)data == TT.fields) {
- TT.header_len +=
- snprintf(toybuf + TT.header_len, sizeof(toybuf) - TT.header_len,
- " %*s" + (field == TT.fields), field->len, field->title);
- TT.bits |= 1LL<<field->which;
+ return 0;
+}
+
+long long get_headers(struct strawberry *fields, char *buf, int blen)
+{
+ long long bits = 0;
+ int len = 0;
+
+ for (; fields; fields = fields->next) {
+ len += snprintf(buf+len, blen-len, " %*s"+!bits, fields->len,
+ fields->title);
+ bits |= 1LL<<fields->which;
}
- return 0;
+ return bits;
}
// Parse -p -s -t -u -U -g -G
@@ -960,13 +972,22 @@
return tbsort;
}
+static void default_ko(char *s, void *fields, char *err, struct arg_list *arg)
+{
+ struct arg_list def;
+
+ memset(&def, 0, sizeof(struct arg_list));
+ def.arg = s;
+ comma_args(arg ? arg : &def, fields, err, parse_ko);
+}
+
static void shared_main(void)
{
int i;
TT.ticks = sysconf(_SC_CLK_TCK);
if (!TT.width) {
- TT.width = 80;
+ TT.width = (toys.which->name[1] == 's') ? 99999 : 80;
TT.height = 25;
terminal_size(&TT.width, &TT.height);
}
@@ -986,6 +1007,7 @@
void ps_main(void)
{
struct dirtree *dt;
+ char *s;
int i;
if (toys.optflags&FLAG_w) TT.width = 99999;
@@ -1003,56 +1025,63 @@
comma_args(TT.ps.k, &TT.kfields, "bad -k", parse_ko);
dlist_terminate(TT.kfields);
- // Parse manual field selection, or default/-f/-l, plus -Z,
- // constructing the header line in toybuf as we go.
- if (toys.optflags&FLAG_Z) {
- struct arg_list Z = { 0, "LABEL" };
-
- comma_args(&Z, &TT.fields, "-Z", parse_ko);
- }
- if (TT.ps.o) comma_args(TT.ps.o, &TT.fields, "bad -o field", parse_ko);
- else {
- struct arg_list al;
-
- al.next = 0;
- if (toys.optflags&FLAG_f)
- al.arg = "USER:8=UID,PID,PPID,C,STIME,TTY,TIME,CMD";
- else if (toys.optflags&FLAG_l)
- al.arg = "F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD";
- else if (CFG_TOYBOX_ON_ANDROID)
- al.arg = "USER,PID,PPID,VSIZE,RSS,WCHAN:10,ADDR:10=PC,S,CMDLINE";
- else al.arg = "PID,TTY,TIME,CMD";
-
- comma_args(&al, &TT.fields, 0, parse_ko);
+ // Parse manual field selection, or default/-f/-l, plus -Z and -O
+ if (toys.optflags&FLAG_Z) default_ko("LABEL", &TT.fields, 0, 0);
+ if (toys.optflags&FLAG_f) s = "USER:8=UID,PID,PPID,C,STIME,TTY,TIME,CMD";
+ else if (toys.optflags&FLAG_l)
+ s = "F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD";
+ else if (CFG_TOYBOX_ON_ANDROID)
+ s = "USER,PID,PPID,VSIZE,RSS,WCHAN:10,ADDR:10=PC,S,NAME";
+ else s = "PID,TTY,TIME,CMD";
+ default_ko(s, &TT.fields, "bad -o", TT.ps.o);
+ if (TT.ps.O) {
+ if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->prev;
+ comma_args(TT.ps.O, &TT.fields, "bad -O", parse_ko);
+ if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->next;
}
dlist_terminate(TT.fields);
- printf("%s\n", toybuf);
- // misunderstand fields the flags say to
+ // -f and -n change the meaning of some fields
if (toys.optflags&(FLAG_f|FLAG_n)) {
struct strawberry *ever;
for (ever = TT.fields; ever; ever = ever->next) {
- int alluc = ever->which;
-
- if ((toys.optflags&FLAG_f) && alluc==PS_CMD) alluc = PS_ARGS;
- if ((toys.optflags&FLAG_n) && alluc>=PS_UID && alluc<=PS_RGROUP
- && (typos[alluc].slot&64)) alluc--;
- if (alluc != ever->which) {
- TT.bits &= 1LL<<ever->which;
- TT.bits |= 1LL<<(ever->which = alluc);
- }
+ if ((toys.optflags&FLAG_f) && ever->which==PS_CMD) ever->which = PS_ARGS;
+ if ((toys.optflags&FLAG_n) && ever->which>=PS_UID
+ && ever->which<=PS_RGROUP && (typos[ever->which].slot&64))
+ ever->which--;
}
}
- if (!(toys.optflags&FLAG_k)) TT.show_process = (void *)show_ps;
+ // Calculate seen fields bit array, and if we aren't deferring printing
+ // print headers now (for low memory/nommu systems).
+ TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
+ if (!(toys.optflags&FLAG_M)) printf("%.*s\n", TT.width, toybuf);
+ if (!(toys.optflags&(FLAG_k|FLAG_M))) TT.show_process = (void *)show_ps;
TT.match_process = ps_match_process;
dt = dirtree_read("/proc", get_ps);
- if (toys.optflags&FLAG_k) {
+ if (toys.optflags&(FLAG_k|FLAG_M)) {
struct carveup **tbsort = collate(TT.kcount, dt, ksort);
- qsort(tbsort, TT.kcount, sizeof(struct carveup *), (void *)ksort);
+ if (toys.optflags&FLAG_M) {
+ for (i = 0; i<TT.kcount; i++) {
+ struct strawberry *field;
+
+ for (field = TT.fields; field; field = field->next) {
+ int len = strlen(string_field(tbsort[i], field));
+
+ if (abs(field->len)<len) field->len = (field->len<0) ? -len : len;
+ }
+ }
+
+ // Now that we've recalculated field widths, re-pad headers again
+ get_headers(TT.fields, toybuf, sizeof(toybuf));
+ printf("%.*s\n", TT.width, toybuf);
+ }
+
+ if (toys.optflags&FLAG_k)
+ qsort(tbsort, TT.kcount, sizeof(struct carveup *), (void *)ksort);
for (i = 0; i<TT.kcount; i++) {
show_ps(tbsort[i]);
free(tbsort[i]);
@@ -1086,7 +1115,7 @@
if (pos<0) pos = 0;
for (field = TT.fields; field; field = field->next) {
- if ((TT.sortpos = i++)<pos) continue;
+ if ((TT.sortpos = i++)<pos && field->next) continue;
going2 = TT.kfields;
going2->which = field->which;
going2->len = field->len;
@@ -1097,53 +1126,79 @@
// If we have both, adjust slot[deltas[]] to be relative to previous
// measurement rather than process start. Stomping old.data is fine
// because we free it after displaying.
-static int merge_deltas(long long *oslot, long long *nslot)
+static int merge_deltas(long long *oslot, long long *nslot, int milis)
{
- char deltas[] = {SLOT_utime2, SLOT_iobytes, SLOT_diobytes, SLOT_upticks,
- SLOT_rchar, SLOT_wchar, SLOT_rbytes, SLOT_wbytes, SLOT_swap};
+ char deltas[] = {SLOT_utime2, SLOT_iobytes, SLOT_diobytes, SLOT_rchar,
+ SLOT_wchar, SLOT_rbytes, SLOT_wbytes, SLOT_swap};
int i;
for (i = 0; i<ARRAY_LEN(deltas); i++)
oslot[deltas[i]] = nslot[deltas[i]] - oslot[deltas[i]];
+ oslot[SLOT_upticks] = (milis*TT.ticks)/1000;
return 1;
}
-static void top_common(char *header,
- int (*filter)(long long *oslot, long long *nslot))
+static int header_line(int line, int rev)
+{
+ if (!line) return 0;
+
+ printf("%s%*.*s%s\r\n", rev ? "\033[7m" : "",
+ (toys.optflags&FLAG_b) ? 0 : -TT.width, TT.width, toybuf,
+ rev ? "\033[0m" : "");
+
+ return line-1;
+}
+
+// Get current time in miliseconds
+static long long militime(void)
{
struct timespec ts;
- long long timeout = 0, now;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ return ts.tv_sec*1000+ts.tv_nsec/1000000;
+}
+
+static void top_common(
+ int (*filter)(long long *oslot, long long *nslot, int milis))
+{
+ long long timeout = 0, now, stats[16];
struct proclist {
struct carveup **tb;
int count;
+ long long whence;
} plist[2], *plold, *plnew, old, new, mix;
- char scratch[16];
+ char scratch[16], *pos, *cpufields[] = {"user", "nice", "sys", "idle",
+ "iow", "irq", "sirq", "host"};
+
unsigned tock = 0;
- int i, lines, done = 0;
+ int i, lines, topoff = 0, done = 0;
+ toys.signal = SIGWINCH;
+ TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
*scratch = 0;
- TT.top.d *= 1000;
- if (toys.optflags&FLAG_b) TT.width = TT.height = 99999;
- else {
- xset_terminal(0, 1, 0);
- sigatexit(tty_sigreset);
- }
- shared_main();
-
- comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest);
- comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest);
-
- TT.match_process = shared_match_process;
memset(plist, 0, sizeof(plist));
+ memset(stats, 0, sizeof(stats));
do {
- struct dirtree *dt = dirtree_read("/proc", get_ps);
+ struct dirtree *dt;
+ int recalc = 1;
plold = plist+(tock++&1);
plnew = plist+(tock&1);
+ plnew->whence = militime();
+ dt= dirtree_read("/proc", get_ps);
plnew->tb = collate(plnew->count = TT.kcount, dt, ksort);
TT.kcount = 0;
+ if (readfile("/proc/stat", pos = toybuf, sizeof(toybuf))) {
+ long long *st = stats+8*(tock&1);
+
+ // user nice system idle iowait irq softirq host
+ sscanf(pos, "cpu %lld %lld %lld %lld %lld %lld %lld %lld",
+ st, st+1, st+2, st+3, st+4, st+5, st+6, st+7);
+ }
+
// First time, wait a quarter of a second to collect a little delta data.
if (!plold->tb) {
msleep(250);
@@ -1171,7 +1226,7 @@
if (!old.count || *otb->slot > *ntb->slot) mix.tb[mix.count] = ntb;
else {
// Keep or discard
- if (filter(otb->slot, ntb->slot)) {
+ if (filter(otb->slot, ntb->slot, new.whence-old.whence)) {
mix.tb[mix.count] = otb;
mix.count++;
}
@@ -1182,35 +1237,113 @@
new.count--;
}
- // Will will re-fetch no data before its time. - Mork calling Orson Welles
+ // We will re-fetch no data before its time. - Mork calling Orson Welles
for (;;) {
- char was, is, *pos;
+ char was, is;
- qsort(mix.tb, mix.count, sizeof(struct carveup *), (void *)ksort);
-printf("cheese\n");
- if (!(toys.optflags&FLAG_b)) printf("\033[H\033[J");
- if (!(toys.optflags&FLAG_q)) {
- i = 0;
- strcpy(pos = toybuf, header);
+ if (recalc) {
+ qsort(mix.tb, mix.count, sizeof(struct carveup *), (void *)ksort);
+ if (!(toys.optflags&FLAG_b)) {
+ printf("\033[H\033[J");
+ if (toys.signal) {
+ toys.signal = 0;
+ terminal_probesize(&TT.width, &TT.height);
+ }
+ }
+ lines = TT.height;
+ }
+ if (recalc && !(toys.optflags&FLAG_q)) {
+ if (*toys.which->name == 't') {
+ struct strawberry alluc;
+ long long ll, up = 0;
+ long run[6];
+ int j;
- for (i=0, is = *pos; *pos; pos++) {
+ alluc.which = PS_S;
+ memset(run, 0, sizeof(run));
+ for (i = 0; i<mix.count; i++)
+ run[1+stridx("RSTZ", *string_field(mix.tb[i], &alluc))]++;
+
+ sprintf(toybuf,
+ "Tasks: %d total,%4ld running,%4ld sleeping,%4ld stopped,"
+ "%4ld zombie", mix.count, run[1], run[2], run[3], run[4]);
+ lines = header_line(lines, 0);
+
+ if (readfile("/proc/meminfo", toybuf, sizeof(toybuf))) {
+ for (i=0; i<6; i++) {
+ pos = strafter(toybuf, (char *[]){"MemTotal:","\nMemFree:",
+ "\nBuffers:","\nCached:","\nSwapTotal:","\nSwapFree:"}[i]);
+ run[i] = pos ? atol(pos) : 0;
+ }
+ sprintf(toybuf,
+ "Mem:%10ldk total,%9ldk used,%9ldk free,%9ldk buffers",
+ run[0], run[0]-run[1], run[1], run[2]);
+ lines = header_line(lines, 0);
+ sprintf(toybuf,
+ "Swap:%9ldk total,%9ldk used,%9ldk free,%9ldk cached",
+ run[4], run[4]-run[5], run[5], run[3]);
+ lines = header_line(lines, 0);
+ }
+
+ pos = toybuf;
+ i = sysconf(_SC_NPROCESSORS_CONF);
+ pos += sprintf(pos, "%d%%cpu", i*100);
+ j = 4+(i>10);
+
+ // If a processor goes idle it's powered down and its idle ticks don't
+ // advance, so calculate idle time as potential time - used.
+ if (mix.count) up = mix.tb[0]->slot[SLOT_upticks];
+ if (!up) up = 1;
+ now = up*i;
+ ll = stats[3] = stats[11] = 0;
+ for (i = 0; i<8; i++) ll += stats[i]-stats[i+8];
+ stats[3] = now - llabs(ll);
+
+ for (i = 0; i<8; i++) {
+ ll = (llabs(stats[i]-stats[i+8])*1000)/up;
+ pos += sprintf(pos, "% *lld%%%s", j, (ll+5)/10, cpufields[i]);
+ }
+ lines = header_line(lines, 0);
+ } else {
+ struct strawberry *fields;
+ struct carveup tb;
+
+ memset(&tb, 0, sizeof(struct carveup));
+ pos = stpcpy(toybuf, "Totals:");
+ for (fields = TT.fields; fields; fields = fields->next) {
+ long long ll, bits = 0;
+ int slot = typos[fields->which].slot&63;
+
+ if (fields->which<PS_C || fields->which>PS_DIO) continue;
+ ll = 1LL<<fields->which;
+ if (bits&ll) continue;
+ bits |= ll;
+ for (i=0; i<mix.count; i++)
+ tb.slot[slot] += mix.tb[i]->slot[slot];
+ pos += snprintf(pos, sizeof(toybuf)/2-(pos-toybuf),
+ " %s: %*s,", typos[fields->which].name,
+ fields->len, string_field(&tb, fields));
+ }
+ *--pos = 0;
+ lines = header_line(lines, 0);
+ }
+
+ get_headers(TT.fields, pos = toybuf, sizeof(toybuf));
+ for (i = 0, is = *pos; *pos; pos++) {
was = is;
is = *pos;
if (isspace(was) && !isspace(is) && i++==TT.sortpos) pos[-1] = '[';
if (!isspace(was) && isspace(is) && i==TT.sortpos+1) *pos = ']';
}
*pos = 0;
- printf("\033[7m%*.*s\033[0m\n\r",
- (toys.optflags&FLAG_b) ? 0 : -TT.width, TT.width, toybuf);
-
- if (!(toys.optflags&FLAG_b))
- terminal_probesize(&TT.width, &TT.height);
+ lines = header_line(lines, 1);
}
- lines = TT.height-2;
+ if (!recalc) printf("\033[%dH\033[J", 1+TT.height-lines);
+ recalc = 1;
- for (i=0; i<lines && i<mix.count; i++) {
- show_ps(mix.tb[i]);
- xputc('\r');
+ for (i = 0; i<lines && i+topoff<mix.count; i++) {
+ if (i) xputc('\n');
+ show_ps(mix.tb[i+topoff]);
}
if (TT.top.n && !--TT.top.n) {
@@ -1219,10 +1352,9 @@
}
// Get current time in miliseconds
- clock_gettime(CLOCK_MONOTONIC, &ts);
- now = ts.tv_sec*1000+ts.tv_nsec/1000000;
- if (timeout<=now) timeout += TT.top.d;
- if (timeout<=now) timeout = now+TT.top.d;
+ now = militime();
+ if (timeout<=now) timeout = new.whence+TT.top.d;
+ if (timeout<=now || timeout>now+TT.top.d) timeout = now+TT.top.d;
i = scan_key_getsize(scratch, timeout-now, &TT.width, &TT.height);
if (i==-1 || i==3 || toupper(i)=='Q') {
@@ -1234,12 +1366,25 @@
// Flush unknown escape sequences.
if (i==27) while (0<scan_key_getsize(scratch, 0, &TT.width, &TT.height));
else if (i==' ') {
- timeout = now;
+ timeout = 0;
break;
- } else {
+ } else if (toupper(i)=='R')
+ ((struct strawberry *)TT.kfields)->reverse *= -1;
+ else {
i -= 256;
if (i == KEY_LEFT) setsort(TT.sortpos-1);
else if (i == KEY_RIGHT) setsort(TT.sortpos+1);
+ // KEY_UP is 0, so at end of strchr
+ else if (strchr((char []){KEY_DOWN,KEY_PGUP,KEY_PGDN,KEY_UP}, i)) {
+ recalc = 0;
+
+ if (i == KEY_UP) topoff--;
+ else if (i == KEY_DOWN) topoff++;
+ else if (i == KEY_PGDN) topoff += lines;
+ else if (i == KEY_PGUP) topoff -= lines;
+ if (topoff<0) topoff = 0;
+ if (topoff>mix.count) topoff = mix.count;
+ }
}
continue;
}
@@ -1248,67 +1393,77 @@
for (i=0; i<plold->count; i++) free(plold->tb[i]);
free(plold->tb);
} while (!done);
+
if (!(toys.optflags&FLAG_b)) tty_reset();
}
+static void top_setup(char *defo, char *defk)
+{
+ int len;
+
+ TT.time = militime();
+ TT.top.d *= 1000;
+ if (toys.optflags&FLAG_b) TT.width = TT.height = 99999;
+ else {
+ xset_terminal(0, 1, 0);
+ sigatexit(tty_sigreset);
+ xsignal(SIGWINCH, generic_signal);
+ printf("\033[?25l\033[0m");
+ }
+ shared_main();
+
+ comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest);
+ comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest);
+ TT.match_process = shared_match_process;
+
+ default_ko(defo, &TT.fields, "bad -o", TT.top.o);
+ dlist_terminate(TT.fields);
+ len = strlen(toybuf);
+ if (toybuf[len-1]!=' ' && len<sizeof(toybuf)-1) strcpy(toybuf+len, " ");
+
+ // First (dummy) sort field is overwritten by setsort()
+ default_ko("-S", &TT.kfields, 0, 0);
+ default_ko(defk, &TT.kfields, "bad -k", TT.top.k);
+ dlist_terminate(TT.kfields);
+ setsort(TT.top.s-1);
+}
+
void top_main(void)
{
- struct arg_list al;
- char *header;
+ // usage: [-h HEADER] -o OUTPUT -k SORT
- // Display fields
- al.next = 0;
- al.arg = xstrdup("PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,CMDLINE");
- comma_args(&al, &TT.fields, 0, parse_ko);
- free(al.arg);
- dlist_terminate(TT.fields);
- header=xstrdup(toybuf);
-
- // Fallback sorts
- al.arg = xstrdup("-S,-%CPU,-ETIME,-PID");
- comma_args(&al, &TT.kfields, "bang", parse_ko);
- dlist_terminate(TT.kfields);
- setsort(8);
-
- top_common(header, merge_deltas);
+ top_setup(
+ "PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,ARGS",
+ "-%CPU,-ETIME,-PID");
+ top_common(merge_deltas);
}
#define CLEANUP_top
#define FOR_iotop
#include "generated/flags.h"
-static int iotop_filter(long long *oslot, long long *nslot)
+static int iotop_filter(long long *oslot, long long *nslot, int milis)
{
- if (!(toys.optflags&FLAG_a)) merge_deltas(oslot, nslot);
+ if (!(toys.optflags&FLAG_a)) merge_deltas(oslot, nslot, milis);
+ else oslot[SLOT_upticks] = ((militime()-TT.time)*TT.ticks)/1000;
- return !(toys.optflags&FLAG_o) || oslot[SLOT_iobytes+!(toys.optflags&FLAG_A)];
+ return !(toys.optflags&FLAG_o)||oslot[SLOT_iobytes+!(toys.optflags&FLAG_A)];
}
void iotop_main(void)
{
- struct arg_list al;
- char *header, *d = "D"+!!(toys.optflags&FLAG_A);
+ char *s1 = 0, *s2 = 0, *d = "D"+!!(toys.optflags&FLAG_A);
- if (toys.optflags&FLAG_k) TT.forcek++;
+ if (toys.optflags&FLAG_K) TT.forcek++;
- al.next = 0;
- al.arg = xmprintf("PID,PR,USER,%sREAD,%sWRITE,SWAP,%sIO,COMM", d, d, d);
- comma_args(&al, &TT.fields, 0, parse_ko);
- free(al.arg);
- dlist_terminate(TT.fields);
- header = strdup(toybuf);
-
- // Fallback sorts. First (dummy) field gets overwritten by setsort()
- al.arg = xmprintf("-S,-%sIO,-ETIME,-PID",d);
- comma_args(&al, &TT.kfields, 0, parse_ko);
- free(al.arg);
- dlist_terminate(TT.kfields);
- setsort(6);
-
- top_common(header, iotop_filter);
+ top_setup(s1 = xmprintf("PID,PR,USER,%sREAD,%sWRITE,SWAP,%sIO,COMM",d,d,d),
+ s2 = xmprintf("-%sIO,-ETIME,-PID",d));
+ free(s1);
+ free(s2);
+ top_common(iotop_filter);
}
-// pkill's plumbing wrap's pgrep's and thus mostly takes place in pgrep's flag
+// pkill's plumbing wraps pgrep's and thus mostly takes place in pgrep's flag
// context, so force pgrep's flags on even when building pkill standalone.
// (All the pgrep/pkill functions drop out when building ps standalone.)
#define FORCE_FLAGS
@@ -1321,17 +1476,34 @@
regex_t reg;
};
-static void show_pgrep(struct carveup *tb)
+static void do_pgk(struct carveup *tb)
+{
+ if (TT.pgrep.signal) {
+ if (kill(*tb->slot, TT.pgrep.signal)) {
+ char *s = num_to_sig(TT.pgrep.signal);
+
+ if (!s) sprintf(s = toybuf, "%d", TT.pgrep.signal);
+ perror_msg("%s->%lld", s, *tb->slot);
+ }
+ }
+ if (!(toys.optflags&FLAG_c) && (!TT.pgrep.signal || TT.tty)) {
+ printf("%lld", *tb->slot);
+ if (toys.optflags&FLAG_l)
+ printf(" %s", tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f));
+
+ printf("%s", TT.pgrep.d ? TT.pgrep.d : "\n");
+ }
+}
+
+static void match_pgrep(struct carveup *tb)
{
regmatch_t match;
struct regex_list *reg;
- char *name = tb->str;
+ char *name = tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f);;
// Never match ourselves.
if (TT.pgrep.self == *tb->slot) return;
- if (toys.optflags&FLAG_f) name += tb->offset[4];
-
if (TT.pgrep.regexes) {
for (reg = TT.pgrep.regexes; reg; reg = reg->next) {
if (regexec(®->reg, name, 1, &match, 0)) continue;
@@ -1344,20 +1516,15 @@
// Repurpose a field for -c count
TT.sortpos++;
- if (TT.pgrep.signal) {
- if (kill(*tb->slot, TT.pgrep.signal)) {
- char *s = num_to_sig(TT.pgrep.signal);
+ if (toys.optflags&(FLAG_n|FLAG_o)) {
+ long long ll = tb->slot[SLOT_starttime];
- if (!s) sprintf(s = toybuf, "%d", TT.pgrep.signal);
- perror_msg("%s->%lld", s, *tb->slot);
- }
- }
- if (!(toys.optflags&FLAG_c) && (!TT.pgrep.signal || TT.tty)) {
- printf("%lld", *tb->slot);
- if (toys.optflags&FLAG_l) printf(" %s", name);
-
- printf("%s", TT.pgrep.d ? TT.pgrep.d : "\n");
- }
+ if (toys.optflags&FLAG_o) ll *= -1;
+ if (TT.time && TT.time>ll) return;
+ TT.time = ll;
+ free(TT.pgrep.snapshot);
+ TT.pgrep.snapshot = xmemdup(toybuf, (name+strlen(name)+1)-toybuf);
+ } else do_pgk(tb);
}
static int pgrep_match_process(long long *slot)
@@ -1398,10 +1565,14 @@
TT.pgrep.regexes = reg;
}
TT.match_process = pgrep_match_process;
- TT.show_process = (void *)show_pgrep;
+ TT.show_process = (void *)match_pgrep;
dirtree_read("/proc", get_ps);
if (toys.optflags&FLAG_c) printf("%d\n", TT.sortpos);
+ if (TT.pgrep.snapshot) {
+ do_pgk(TT.pgrep.snapshot);
+ if (CFG_TOYBOX_FREE) free(TT.pgrep.snapshot);
+ }
if (TT.pgrep.d) xputc('\n');
}
diff --git a/toys/posix/sed.c b/toys/posix/sed.c
index a57891e..30d8a15 100644
--- a/toys/posix/sed.c
+++ b/toys/posix/sed.c
@@ -827,8 +827,7 @@
if (strchr("aiqr=", c) && i>1) break;
// Add step to pattern
- corwin = xmalloc(reg-toybuf);
- memcpy(corwin, toybuf, reg-toybuf);
+ corwin = xmemdup(toybuf, reg-toybuf);
reg = (reg-toybuf) + (char *)corwin;
// Parse arguments by command type