OSDN Git Service

am 7e612cb8: (-s ours) am f41c7e0b: Merge remote-tracking branch \'toybox/master...
authorElliott Hughes <enh@google.com>
Thu, 27 Aug 2015 10:50:05 +0000 (10:50 +0000)
committerAndroid Git Automerger <android-git-automerger@android.com>
Thu, 27 Aug 2015 10:50:05 +0000 (10:50 +0000)
* commit '7e612cb856f90b83c0a7f48b0e087fdb32de24e1':

55 files changed:
.config
Android.mk
Config.in
generated/config.h
generated/flags.h
generated/globals.h
generated/help.h
generated/newtoys.h
lib/lib.c
lib/lib.h
lib/net.c
lib/portability.h
lib/xwrap.c
main.c
scripts/change.sh
scripts/mkflags.c
tests/date.test [new file with mode: 0644]
toys/other/acpi.c
toys/other/blkid.c
toys/other/bzcat.c
toys/other/chcon.c
toys/other/fsync.c [new file with mode: 0644]
toys/other/hexedit.c
toys/other/hostid.c [new file with mode: 0644]
toys/other/login.c
toys/other/lspci.c
toys/other/nbd_client.c
toys/other/reboot.c
toys/other/rev.c
toys/other/switch_root.c
toys/other/tac.c
toys/other/taskset.c
toys/other/vmstat.c
toys/pending/dd.c
toys/pending/init.c
toys/pending/mdev.c
toys/pending/modprobe.c
toys/pending/ps.c
toys/pending/telnet.c
toys/pending/tftp.c [new file with mode: 0644]
toys/posix/cal.c
toys/posix/cat.c
toys/posix/chmod.c
toys/posix/cmp.c
toys/posix/date.c
toys/posix/du.c
toys/posix/find.c
toys/posix/id.c
toys/posix/ls.c
toys/posix/nl.c
toys/posix/split.c
toys/posix/strings.c
www/header.html
www/news.html
www/roadmap.html

diff --git a/.config b/.config
index fb117b1..2d354d0 100644 (file)
--- a/.config
+++ b/.config
@@ -172,6 +172,7 @@ CONFIG_TAR=y
 # CONFIG_TELNET is not set
 # CONFIG_TELNETD is not set
 # CONFIG_TEST is not set
+# CONFIG_TFTP is not set
 # CONFIG_TFTPD is not set
 CONFIG_TOP=y
 CONFIG_TRACEROUTE=y
@@ -189,6 +190,7 @@ CONFIG_BASE64=y
 CONFIG_BLKID=y
 # CONFIG_FSTYPE is not set
 CONFIG_BLOCKDEV=y
+CONFIG_BUNZIP2=y
 CONFIG_BZCAT=y
 CONFIG_CHCON=y
 CONFIG_CHROOT=y
@@ -203,8 +205,10 @@ CONFIG_FALLOCATE=y
 CONFIG_FREE=y
 CONFIG_FREERAMDISK=y
 CONFIG_FSFREEZE=y
+# CONFIG_FSYNC is not set
 CONFIG_HELP=y
 CONFIG_HELP_EXTRAS=y
+# CONFIG_HOSTID is not set
 CONFIG_IFCONFIG=y
 CONFIG_INOTIFYD=y
 CONFIG_INSMOD=y
index 02865da..8c1f77f 100644 (file)
@@ -90,6 +90,7 @@ LOCAL_SRC_FILES := \
     toys/other/freeramdisk.c \
     toys/other/fsfreeze.c \
     toys/other/help.c \
+    toys/other/hwclock.c \
     toys/other/ifconfig.c \
     toys/other/inotifyd.c \
     toys/other/insmod.c \
@@ -132,7 +133,6 @@ LOCAL_SRC_FILES := \
     toys/other/yes.c \
     toys/pending/dd.c \
     toys/pending/expr.c \
-    toys/pending/hwclock.c \
     toys/pending/more.c \
     toys/pending/pgrep.c \
     toys/pending/netstat.c \
@@ -221,7 +221,7 @@ LOCAL_CXX_STL := none
 
 LOCAL_MODULE := toybox
 
-# dupes: dd df du ls renice
+# dupes: dd df du ls
 # useless?: freeramdisk fsfreeze install makedevs mkfifo nbd-client
 #           partprobe pivot_root pwdx rev rfkill switch_root tty vconfig
 # prefer BSD netcat instead?: nc netcat
@@ -229,6 +229,7 @@ LOCAL_MODULE := toybox
 
 ALL_TOOLS := \
     acpi \
+    base64 \
     basename \
     blockdev \
     bzcat \
@@ -268,8 +269,9 @@ ALL_TOOLS := \
     ifconfig \
     inotifyd \
     insmod \
+    ionice \
+    iorenice \
     kill \
-    killall \
     load_policy \
     ln \
     logname \
@@ -302,6 +304,7 @@ ALL_TOOLS := \
     pwd \
     readlink \
     realpath \
+    renice \
     restorecon \
     rm \
     rmdir \
@@ -345,6 +348,7 @@ ALL_TOOLS := \
     which \
     whoami \
     xargs \
+    xxd \
     yes \
 
 # Install the symlinks.
index 9d3a73b..a2443e2 100644 (file)
--- a/Config.in
+++ b/Config.in
@@ -117,7 +117,7 @@ config TOYBOX_DEBUG
        default n
        help
          Enable extra checks for debugging purposes. All of them catch
-          things that can only go wrong at development time, not runtime.
+         things that can only go wrong at development time, not runtime.
 
 config TOYBOX_UID_SYS
        int "First system UID"
index 4c698f7..3cfcdc0 100644 (file)
 #define USE_TELNETD(...)
 #define CFG_TEST 0
 #define USE_TEST(...)
+#define CFG_TFTP 0
+#define USE_TFTP(...)
 #define CFG_TFTPD 0
 #define USE_TFTPD(...)
 #define CFG_TOP 1
 #define USE_FSTYPE(...)
 #define CFG_BLOCKDEV 1
 #define USE_BLOCKDEV(...) __VA_ARGS__
+#define CFG_BUNZIP2 1
+#define USE_BUNZIP2(...) __VA_ARGS__
 #define CFG_BZCAT 1
 #define USE_BZCAT(...) __VA_ARGS__
 #define CFG_CHCON 1
 #define USE_FREERAMDISK(...) __VA_ARGS__
 #define CFG_FSFREEZE 1
 #define USE_FSFREEZE(...) __VA_ARGS__
+#define CFG_FSYNC 0
+#define USE_FSYNC(...)
 #define CFG_HELP 1
 #define USE_HELP(...) __VA_ARGS__
 #define CFG_HELP_EXTRAS 1
 #define USE_HELP_EXTRAS(...) __VA_ARGS__
+#define CFG_HOSTID 0
+#define USE_HOSTID(...)
 #define CFG_IFCONFIG 1
 #define USE_IFCONFIG(...) __VA_ARGS__
 #define CFG_INOTIFYD 1
index f70392b..2fb316f 100644 (file)
@@ -72,9 +72,9 @@
 #undef FOR_basename
 #endif
 
-// blkid <1 <1
+// blkid    
 #undef OPTSTR_blkid
-#define OPTSTR_blkid "<1"
+#define OPTSTR_blkid  0 
 #ifdef CLEANUP_blkid
 #undef CLEANUP_blkid
 #undef FOR_blkid
 #undef FOR_brctl
 #endif
 
+// bunzip2 cftkv cftkv
+#undef OPTSTR_bunzip2
+#define OPTSTR_bunzip2 "cftkv"
+#ifdef CLEANUP_bunzip2
+#undef CLEANUP_bunzip2
+#undef FOR_bunzip2
+#undef FLAG_v
+#undef FLAG_k
+#undef FLAG_t
+#undef FLAG_f
+#undef FLAG_c
+#endif
+
 // bzcat    
 #undef OPTSTR_bzcat
 #define OPTSTR_bzcat  0 
 #undef FLAG_b
 #endif
 
-// date d:s:r:u[!dr] d:s:r:u[!dr]
+// date d:D:r:u[!dr] d:D:r:u[!dr]
 #undef OPTSTR_date
-#define OPTSTR_date "d:s:r:u[!dr]"
+#define OPTSTR_date "d:D:r:u[!dr]"
 #ifdef CLEANUP_date
 #undef CLEANUP_date
 #undef FOR_date
 #undef FLAG_u
 #undef FLAG_r
-#undef FLAG_s
+#undef FLAG_D
 #undef FLAG_d
 #endif
 
 #undef FLAG_V
 #endif
 
-// dhcpd   >1P#<0>65535=67fi:S
+// dhcpd   >1P#<0>65535=67fi:S46[!46]
 #undef OPTSTR_dhcpd
 #define OPTSTR_dhcpd  0 
 #ifdef CLEANUP_dhcpd
 #undef CLEANUP_dhcpd
 #undef FOR_dhcpd
+#undef FLAG_6
+#undef FLAG_4
 #undef FLAG_S
 #undef FLAG_i
 #undef FLAG_f
 #undef FOR_fstype
 #endif
 
+// fsync   <1d
+#undef OPTSTR_fsync
+#define OPTSTR_fsync  0 
+#ifdef CLEANUP_fsync
+#undef CLEANUP_fsync
+#undef FOR_fsync
+#undef FLAG_d
+#endif
+
 // ftpget   <2cvu:p:P#<0=21>65535
 #undef OPTSTR_ftpget
 #define OPTSTR_ftpget  0 
 #undef FLAG_a
 #endif
 
+// hostid   >0
+#undef OPTSTR_hostid
+#define OPTSTR_hostid  0 
+#ifdef CLEANUP_hostid
+#undef CLEANUP_hostid
+#undef FOR_hostid
+#endif
+
 // hostname    
 #undef OPTSTR_hostname
 #define OPTSTR_hostname  0 
 #undef FLAG_s
 #endif
 
-// login   >1fph:
+// login   >1f:ph:
 #undef OPTSTR_login
 #define OPTSTR_login  0 
 #ifdef CLEANUP_login
 #undef FLAG_g
 #endif
 
-// mkfifo <1Zm: <1Zm:
+// mkfifo <1Z:m: <1Z:m:
 #undef OPTSTR_mkfifo
-#define OPTSTR_mkfifo "<1Zm:"
+#define OPTSTR_mkfifo "<1Z:m:"
 #ifdef CLEANUP_mkfifo
 #undef CLEANUP_mkfifo
 #undef FOR_mkfifo
 #undef FOR_realpath
 #endif
 
-// reboot   n
+// reboot   fn
 #undef OPTSTR_reboot
 #define OPTSTR_reboot  0 
 #ifdef CLEANUP_reboot
 #undef CLEANUP_reboot
 #undef FOR_reboot
 #undef FLAG_n
+#undef FLAG_f
 #endif
 
 // renice <1gpun#| <1gpun#|
 #undef FOR_test
 #endif
 
+// tftp   <1b#<8>65464r:l:g|p|[!gp]
+#undef OPTSTR_tftp
+#define OPTSTR_tftp  0 
+#ifdef CLEANUP_tftp
+#undef CLEANUP_tftp
+#undef FOR_tftp
+#undef FLAG_p
+#undef FLAG_g
+#undef FLAG_l
+#undef FLAG_r
+#undef FLAG_b
+#endif
+
 // tftpd   rcu:l
 #undef OPTSTR_tftpd
 #define OPTSTR_tftpd  0 
 #endif
 #endif
 
+#ifdef FOR_bunzip2
+#ifndef TT
+#define TT this.bunzip2
+#endif
+#define FLAG_v (1<<0)
+#define FLAG_k (1<<1)
+#define FLAG_t (1<<2)
+#define FLAG_f (1<<3)
+#define FLAG_c (1<<4)
+#endif
+
 #ifdef FOR_bzcat
 #ifndef TT
 #define TT this.bzcat
 #endif
 #define FLAG_u (1<<0)
 #define FLAG_r (1<<1)
-#define FLAG_s (1<<2)
+#define FLAG_D (1<<2)
 #define FLAG_d (1<<3)
 #endif
 
 #ifndef TT
 #define TT this.dhcpd
 #endif
-#define FLAG_S (FORCED_FLAG<<0)
-#define FLAG_i (FORCED_FLAG<<1)
-#define FLAG_f (FORCED_FLAG<<2)
-#define FLAG_P (FORCED_FLAG<<3)
+#define FLAG_6 (FORCED_FLAG<<0)
+#define FLAG_4 (FORCED_FLAG<<1)
+#define FLAG_S (FORCED_FLAG<<2)
+#define FLAG_i (FORCED_FLAG<<3)
+#define FLAG_f (FORCED_FLAG<<4)
+#define FLAG_P (FORCED_FLAG<<5)
 #endif
 
 #ifdef FOR_diff
 #endif
 #endif
 
+#ifdef FOR_fsync
+#ifndef TT
+#define TT this.fsync
+#endif
+#define FLAG_d (FORCED_FLAG<<0)
+#endif
+
 #ifdef FOR_ftpget
 #ifndef TT
 #define TT this.ftpget
 #define FLAG_a (FORCED_FLAG<<2)
 #endif
 
+#ifdef FOR_hostid
+#ifndef TT
+#define TT this.hostid
+#endif
+#endif
+
 #ifdef FOR_hostname
 #ifndef TT
 #define TT this.hostname
 #define TT this.reboot
 #endif
 #define FLAG_n (FORCED_FLAG<<0)
+#define FLAG_f (FORCED_FLAG<<1)
 #endif
 
 #ifdef FOR_renice
 #endif
 #endif
 
+#ifdef FOR_tftp
+#ifndef TT
+#define TT this.tftp
+#endif
+#define FLAG_p (FORCED_FLAG<<0)
+#define FLAG_g (FORCED_FLAG<<1)
+#define FLAG_l (FORCED_FLAG<<2)
+#define FLAG_r (FORCED_FLAG<<3)
+#define FLAG_b (FORCED_FLAG<<4)
+#endif
+
 #ifdef FOR_tftpd
 #ifndef TT
 #define TT this.tftpd
index d089c9e..607cd31 100644 (file)
@@ -123,10 +123,7 @@ struct umount_data {
 // toys/other/acpi.c
 
 struct acpi_data {
-  int ac;
-  int bat;
-  int therm;
-  int cool;
+  int ac, bat, therm, cool;
   char *cpath;
 };
 
@@ -170,6 +167,14 @@ struct hexedit_data {
   unsigned height;
 };
 
+// toys/other/hwclock.c
+
+struct hwclock_data {
+  char *fname;
+
+  int utc;
+};
+
 // toys/other/ifconfig.c
 
 struct ifconfig_data {
@@ -188,6 +193,7 @@ struct ionice_data {
 
 struct login_data {
   char *hostname;
+  char *username;
 
   int login_timeout, login_fail_timeout;
 };
@@ -555,14 +561,6 @@ struct host_data {
   char *type_str;
 };
 
-// toys/pending/hwclock.c
-
-struct hwclock_data {
-  char *fname;
-
-  int utc;
-};
-
 // toys/pending/iconv.c
 
 struct iconv_data {
@@ -802,6 +800,17 @@ struct telnetd_data {
     pid_t fork_pid;
 };
 
+// toys/pending/tftp.c
+
+struct tftp_data {
+  char *local_file;
+  char *remote_file;
+  long block_size;
+
+  struct sockaddr_storage inaddr;
+  int af;
+};
+
 // toys/pending/tftpd.c
 
 struct tftpd_data {
@@ -954,6 +963,9 @@ struct date_data {
   char *file;
   char *setfmt;
   char *showdate;
+
+  char *tz;
+  unsigned nano;
 };
 
 // toys/posix/df.c
@@ -1233,6 +1245,7 @@ extern union global_union {
        struct fallocate_data fallocate;
        struct free_data free;
        struct hexedit_data hexedit;
+       struct hwclock_data hwclock;
        struct ifconfig_data ifconfig;
        struct ionice_data ionice;
        struct login_data login;
@@ -1273,7 +1286,6 @@ extern union global_union {
        struct getty_data getty;
        struct groupadd_data groupadd;
        struct host_data host;
-       struct hwclock_data hwclock;
        struct iconv_data iconv;
        struct ip_data ip;
        struct ipcrm_data ipcrm;
@@ -1296,6 +1308,7 @@ extern union global_union {
        struct tcpsvd_data tcpsvd;
        struct telnet_data telnet;
        struct telnetd_data telnetd;
+       struct tftp_data tftp;
        struct tftpd_data tftpd;
        struct top_data top;
        struct tr_data tr;
index 299b329..c07c86c 100644 (file)
@@ -2,7 +2,7 @@
 
 #define help_toybox_uid_sys "When commands like useradd/groupadd allocate system IDs, start here.\n\n"
 
-#define help_toybox_debug "Enable extra checks for debugging purposes. All of them catch\n       things that can only go wrong at development time, not runtime.\n\n"
+#define help_toybox_debug "Enable extra checks for debugging purposes. All of them catch\nthings that can only go wrong at development time, not runtime.\n\n"
 
 #define help_toybox_norecurse "When one toybox command calls another, usually it just calls the new\ncommand's main() function rather than searching the $PATH and calling\nexec on another file (which is much slower).\n\nThis disables that optimization, so toybox will run external commands\n       even when it has a built-in version of that command. This requires\n       toybox symlinks to be installed in the $PATH, or re-invoking the\n       \"toybox\" multiplexer command by name.\n\n"
 
 
 #define help_reset "usage: reset\n\nreset the terminal\n\n"
 
-#define help_reboot "usage: reboot/halt/poweroff [-n]\n\nRestart, halt or powerdown the system.\n\n-n  Don't sync before stopping the system.\n\n"
+#define help_reboot "usage: reboot/halt/poweroff [-fn]\n\nRestart, halt or powerdown the system.\n\n-f Don't signal init\n-n   Don't sync before stopping the system.\n\n"
 
 #define help_realpath "usage: realpath FILE...\n\nDisplay the canonical absolute pathname\n\n"
 
 
 #define help_losetup "usage: losetup [-cdrs] [-o OFFSET] [-S SIZE] {-d DEVICE...|-j FILE|-af|{DEVICE FILE}}\n\nAssociate a loopback device with a file, or show current file (if any)\nassociated with a loop device.\n\nInstead of a device:\n-a      Iterate through all loopback devices\n-f        Find first unused loop device (may create one)\n-j      Iterate through all loopback devices associated with FILE\n\nexisting:\n-c      Check capacity (file size changed)\n-d  Detach loopback device\n\nnew:\n-s      Show device name (alias --show)\n-o     Start assocation at OFFSET into FILE\n-r        Read only\n-S   Limit SIZE of loopback association (alias --sizelimit)\n\n"
 
-#define help_login "usage: login [-p] [-h host] [[-f] username]\n\nEstablish a new session with the system.\n\n-p      Preserve environment\n-h        The name of the remote host for this login\n-f  Do not perform authentication\n\n"
+#define help_login "usage: login [-p] [-h host] [-f USERNAME] [USERNAME]\n\nLog in as a user, prompting for username and password if necessary.\n\n-p  Preserve environment\n-h        The name of the remote host for this login\n-f  login as USERNAME without authentication\n\n"
 
 #define help_iorenice "usage: iorenice PID [CLASS] [PRIORITY]\n\nDisplay or change I/O priority of existing process. CLASS can be\n\"rt\" for realtime, \"be\" for best effort, \"idle\" for only when idle, or\n\"none\" to leave it alone. PRIORITY can be 0-7 (0 is highest, default 4).\n\n"
 
 
 #define help_ifconfig "usage: ifconfig [-a] [INTERFACE [ACTION...]]\n\nDisplay or configure network interface.\n\nWith no arguments, display active interfaces. First argument is interface\nto operate on, one argument by itself displays that interface.\n\n-a      Show all interfaces, not just active ones\n\nAdditional arguments are actions to perform on the interface:\n\nADDRESS[/NETMASK] - set IPv4 address (1.2.3.4/5)\ndefault - unset ipv4 address\nadd|del ADDRESS[/PREFIXLEN] - add/remove IPv6 address (1111::8888/128)\nup - enable interface\ndown - disable interface\n\nnetmask|broadcast|pointopoint ADDRESS - set more IPv4 characteristics\nhw ether|infiniband ADDRESS - set LAN hardware address (AA:BB:CC...)\ntxqueuelen LEN - number of buffered packets before output blocks\nmtu LEN - size of outgoing packets (Maximum Transmission Unit)\n\nFlags you can set on an interface (or -remove by prefixing with -):\narp - don't use Address Resolution Protocol to map LAN routes\npromisc - don't discard packets that aren't to this LAN hardware address\nmulticast - force interface into multicast mode if the driver doesn't\nallmulti - promisc for multicast packets\n\nObsolete fields included for historical purposes:\nirq|io_addr|mem_start ADDR - micromanage obsolete hardware\noutfill|keepalive INTEGER - SLIP analog dialup line quality monitoring\nmetric INTEGER - added to Linux 0.9.10 with comment \"never used\", still true\n\n"
 
+#define help_hwclock "usage: hwclock [-rswtluf]\n\n-f FILE Use specified device file instead of /dev/rtc (--rtc)\n-l      Hardware clock uses localtime (--localtime)\n-r      Show hardware clock time (--show)\n-s      Set system time from hardware clock (--hctosys)\n-t      Set the system time based on the current timezone (--systz)\n-u      Hardware clock uses UTC (--utc)\n-w      Set hardware clock from system time (--systohc)\n\n"
+
+#define help_hostid "usage: hostid\n\nPrint the numeric identifier for the current host.\n\n"
+
 #define help_hexedit "usage: hexedit FILENAME\n\nHexadecimal file editor.\n\n-r        Read only (display but don't edit)\n\n"
 
 #define help_help "usage: help [-ah] [command]\n\nShow usage information for toybox commands.\nRun \"toybox\" with no arguments for a list of available commands.\n\n-h        HTML output\n-a All commands\n"
 
+#define help_fsync "usage: fsync [-d] [FILE...]\n\nSynchronize a file's in-core state with storage device.\n\n-d       Avoid syncing metadata.\n\n"
+
 #define help_fsfreeze "usage: fsfreeze {-f | -u} MOUNTPOINT\n\nFreeze or unfreeze a filesystem.\n\n-f  freeze\n-u      unfreeze\n\n"
 
 #define help_freeramdisk "usage: freeramdisk [RAM device]\n\nFree all memory allocated to specified ramdisk\n\n"
 
 #define help_chcon "usage: chcon [-hRv] CONTEXT FILE...\n\nChange the SELinux security context of listed file[s].\n\n-h change symlinks instead of what they point to.\n-R recurse into subdirectories.\n-v verbose output.\n\n"
 
-#define help_bzcat "usage: bzcat [filename...]\n\nDecompress listed files to stdout. Use stdin if no files listed.\n\n"
+#define help_bzcat "usage: bzcat [FILE...]\n\nDecompress listed files to stdout. Use stdin if no files listed.\n\n"
+
+#define help_bunzip2 "usage: bunzip2 [-cftkv] [FILE...]\n\nDecompress listed files (file.bz becomes file) deleting archive file(s).\nRead from stdin if no files listed.\n\n-c force output to stdout\n-f      force decompression. (If FILE doesn't end in .bz, replace original.)\n-k        keep input files (-c and -t imply this)\n-t  test integrity\n-v verbose\n\n"
 
 #define help_blockdev "usage: blockdev --OPTION... BLOCKDEV...\n\nCall ioctl(s) on each listed block device\n\nOPTIONs:\n--setro               Set read only\n--setrw          Set read write\n--getro         Get read only\n--getss          Get sector size\n--getbsz       Get block size\n--setbsz        BYTES   Set block size\n--getsz         Get device size in 512-byte sectors\n--getsize  Get device size in sectors (deprecated)\n--getsize64    Get device size in bytes\n--flushbufs   Flush buffers\n--rereadpt       Reread partition table\n\n"
 
 
 #define help_tftpd "usage: tftpd [-cr] [-u USER] [DIR]\n\nTransfer file from/to tftp server.\n\n-r     read only\n-c   Allow file creation via upload\n-u      run as USER\n-l Log to syslog (inetd mode requires this)\n\n"
 
+#define help_tftp "usage: tftp [OPTIONS] HOST [PORT]\n\nTransfer file from/to tftp server.\n\n-l FILE Local FILE\n-r FILE Remote FILE\n-g    Get file\n-p    Put file\n-b SIZE Transfer blocks of SIZE octets(8 <= SIZE <= 65464)\n\n"
+
 #define help_test "usage: test [-bcdefghLPrSsuwx PATH] [-nz STRING] [-t FD] [X ?? Y]\n\nReturn true or false by performing tests. (With no arguments return false.)\n\n--- Tests with a single argument (after the option):\nPATH is/has:\n  -b  block device   -f  regular file   -p  fifo           -u  setuid bit\n  -c  char device    -g  setgid         -r  read bit       -w  write bit\n  -d  directory      -h  symlink        -S  socket         -x  execute bit\n  -e  exists         -L  symlink        -s  nonzero size\nSTRING is:\n  -n  nonzero size   -z  zero size      (STRING by itself implies -n)\nFD (integer file descriptor) is:\n  -t  a TTY\n\n--- Tests with one argument on each side of an operator:\nTwo strings:\n  =  are identical    !=  differ\nTwo integers:\n  -eq  equal         -gt  first > second    -lt  first < second\n  -ne  not equal     -ge  first >= second   -le  first <= second\n\n--- Modify or combine tests:\n  ! EXPR     not (swap true/false)   EXPR -a EXPR    and (are both true)\n  ( EXPR )   evaluate this first     EXPR -o EXPR    or (is either true)\n\n"
 
 #define help_telnetd "Handle incoming telnet connections\n\n-l LOGIN  Exec LOGIN on connect\n-f ISSUE_FILE Display ISSUE_FILE instead of /etc/issue\n-K Close connection as soon as login exits\n-p PORT   Port to listen on\n-b ADDR[:PORT]  Address to bind to\n-F Run in foreground\n-i Inetd mode\n-w SEC    Inetd 'wait' mode, linger time SEC\n-S Log to syslog (implied by -i or without -F and -w)\n\n"
 
 #define help_iconv "usage: iconv [-f FROM] [-t TO] [FILE...]\n\nConvert character encoding of files.\n\n-f  convert from (default utf8)\n-t  convert to   (default utf8)\n\n"
 
-#define help_hwclock "usage: hwclock [-rswtluf]\n\n-f FILE Use specified device file instead of /dev/rtc (--rtc)\n-l      Hardware clock uses localtime (--localtime)\n-r      Show hardware clock time (--show)\n-s      Set system time from hardware clock (--hctosys)\n-t      Set the system time based on the current timezone (--systz)\n-u      Hardware clock uses UTC (--utc)\n-w      Set hardware clock from system time (--systohc)\n\n"
-
 #define help_host "usage: host [-av] [-t TYPE] NAME [SERVER]\n\nPerform DNS lookup on NAME, which can be a domain name to lookup,\nor an ipv4 dotted or ipv6 colon seprated address to reverse lookup.\nSERVER (if present) is the DNS server to use.\n\n-a    no idea\n-t     not a clue\n-v  verbose\n\n"
 
 #define help_groupdel "usage: groupdel [USER] GROUP\n\nDelete a group or remove a user from a group\n\n"
 
 #define help_diff "usage: diff [-abBdiNqrTstw] [-L LABEL] [-S FILE] [-U LINES] FILE1 FILE2\n\n-a  Treat all files as text\n-b  Ignore changes in the amount of whitespace\n-B  Ignore changes whose lines are all blank\n-d  Try hard to find a smaller set of changes\n-i  Ignore case differences\n-L  Use LABEL instead of the filename in the unified header\n-N  Treat absent files as empty\n-q  Output only whether files differ\n-r  Recurse\n-S  Start with FILE when comparing directories\n-T  Make tabs line up by prefixing a tab when necessary\n-s  Report when two files are the same\n-t  Expand tabs to spaces in output\n-U  Output LINES lines of context\n-w  Ignore all whitespace\n\n"
 
-#define help_dhcpd "usage: dhcpd [-fS] [-i IFACE] [-P N] [CONFFILE]\n\n -f    Run in foreground\n -i    Interface to use\n -S    Log to syslog too\n -P N  Use port N (default 67)\n\n"
+#define help_dhcpd "usage: dhcpd [-46fS] [-i IFACE] [-P N] [CONFFILE]\n\n -f    Run in foreground\n -i Interface to use\n -S    Log to syslog too\n -P N  Use port N (default ipv4 67, ipv6 547)\n -4, -6    Run as a DHCPv4 or DHCPv6 server\n\n"
 
 #define help_dhcp "usage: dhcp [-fbnqvoCRB] [-i IFACE] [-r IP] [-s PROG] [-p PIDFILE]\n            [-H HOSTNAME] [-V VENDOR] [-x OPT:VAL] [-O OPT]\n\n     Configure network dynamicaly using DHCP.\n\n   -i Interface to use (default eth0)\n   -p Create pidfile\n   -s Run PROG at DHCP events (default /usr/share/dhcp/default.script)\n   -B Request broadcast replies\n   -t Send up to N discover packets\n   -T Pause between packets (default 3 seconds)\n   -A Wait N seconds after failure (default 20)\n   -f Run in foreground\n   -b Background if lease is not obtained\n   -n Exit if lease is not obtained\n   -q Exit after obtaining lease\n   -R Release IP on exit\n   -S Log to syslog too\n   -a Use arping to validate offered address\n   -O Request option OPT from server (cumulative)\n   -o Don't request any options (unless -O is given)\n   -r Request this IP address\n   -x OPT:VAL  Include option OPT in sent packets (cumulative)\n   -F Ask server to update DNS mapping for NAME\n   -H Send NAME as client hostname (default none)\n   -V VENDOR Vendor identifier (default 'toybox VERSION')\n   -C Don't send MAC as client identifier\n   -v Verbose\n\n   Signals:\n   USR1  Renew current lease\n   USR2  Release current lease\n\n\n"
 
 
 #define help_grep "usage: grep [-EFivwcloqsHbhn] [-m MAX] [-e REGEX]... [-f REGFILE] [FILE]...\n\nShow lines matching regular expressions. If no -e, first argument is\nregular expression to match. With no files (or \"-\" filename) read stdin.\nReturns 0 if matched, 1 if no match found.\n\n-e  Regex to match. (May be repeated.)\n-f  File containing regular expressions to match.\n\nmatch type:\n-E  extended regex syntax    -F  fixed (match literal string)\n-i  case insensitive         -m  stop after this many lines matched\n-r  recursive (on dir)       -v  invert match\n-w  whole word (implies -E)  -x  whole line\n-z  input NUL terminated\n\ndisplay modes: (default: matched line)\n-c  count of matching lines  -l  show matching filenames\n-o  only matching part       -q  quiet (errors only)\n-s  silent (no error msg)    -Z  output NUL terminated\n\noutput prefix (default: filename if checking more than 1 file)\n-H  force filename           -b  byte offset of match\n-h  hide filename            -n  line number of match\n\n"
 
-#define help_find "usage: find [-HL] [DIR...] [<options>]\n\nSearch directories for matching files.\nDefault: search \".\" match all -print all matches.\n\n-H  Follow command line symlinks         -L  Follow all symlinks\n\nMatch filters:\n-name  PATTERN filename with wildcards   -iname      case insensitive -name\n-path  PATTERN path name with wildcards  -ipath      case insensitive -path\n-user  UNAME   belongs to user UNAME     -nouser     user not in /etc/passwd\n-group GROUP   belongs to group GROUP    -nogroup    group not in /etc/group\n-perm  [-]MODE permissons (-=at least)   -prune      ignore contents of dir\n-size  N[c]    512 byte blocks (c=bytes) -xdev       stay in this filesystem\n-links N       hardlink count            -atime N    accessed N days ago\n-ctime N       created N days ago        -mtime N    modified N days ago\n-newer FILE    newer mtime than FILE     -mindepth # at least # dirs down\n-depth         ignore contents of dir    -maxdepth # at most # dirs down\n-type [bcdflps] (block, char, dir, file, symlink, pipe, socket)\n\nNumbers N may be prefixed by a - (less than) or + (greater than):\n\nCombine matches with:\n!, -a, -o, ( )    not, and, or, group expressions\n\nActions:\n-print   Print match with newline  -print0    Print match with null\n-exec    Run command with path     -execdir   Run command in file's dir\n-ok      Ask before exec           -okdir     Ask before execdir\n\nCommands substitute \"{}\" with matched file. End with \";\" to run each file,\nor \"+\" (next argument after \"{}\") to collect and run with multiple files.\n\n"
+#define help_find "usage: find [-HL] [DIR...] [<options>]\n\nSearch directories for matching files.\nDefault: search \".\" match all -print all matches.\n\n-H  Follow command line symlinks         -L  Follow all symlinks\n\nMatch filters:\n-name  PATTERN filename with wildcards   -iname      case insensitive -name\n-path  PATTERN path name with wildcards  -ipath      case insensitive -path\n-user  UNAME   belongs to user UNAME     -nouser     user not in /etc/passwd\n-group GROUP   belongs to group GROUP    -nogroup    group not in /etc/group\n-perm  [-]MODE permissons (-=at least)   -prune      ignore contents of dir\n-size  N[c]    512 byte blocks (c=bytes) -xdev       stay in this filesystem\n-links N       hardlink count            -atime N    accessed N days ago\n-ctime N       created N days ago        -mtime N    modified N days ago\n-newer FILE    newer mtime than FILE     -mindepth # at least # dirs down\n-depth         ignore contents of dir    -maxdepth # at most # dirs down\n-inum  N       inode number N\n-type [bcdflps] (block, char, dir, file, symlink, pipe, socket)\n\nNumbers N may be prefixed by a - (less than) or + (greater than):\n\nCombine matches with:\n!, -a, -o, ( )    not, and, or, group expressions\n\nActions:\n-print   Print match with newline  -print0    Print match with null\n-exec    Run command with path     -execdir   Run command in file's dir\n-ok      Ask before exec           -okdir     Ask before execdir\n\nCommands substitute \"{}\" with matched file. End with \";\" to run each file,\nor \"+\" (next argument after \"{}\") to collect and run with multiple files.\n\n"
 
 #define help_false "Return nonzero.\n\n"
 
 
 #define help_df "usage: df [-t type] [FILESYSTEM ...]\n\nThe \"disk free\" command shows total/used/available disk space for\neach filesystem listed on the command line, or all currently mounted\nfilesystems.\n\n-P The SUSv3 \"Pedantic\" option\n-k       Sets units back to 1024 bytes (the default without -P)\n-t type Display only filesystems of this type.\n\nPedantic provides a slightly less useful output format dictated by Posix,\nand sets the units to 512 bytes instead of the default 1024 bytes.\n\n"
 
-#define help_date "usage: date [-u] [-r FILE] [-d DATE] [+DISPLAY_FORMAT] [-s SET_FORMAT] [SET]\n\nSet/get the current date/time. With no SET shows the current date.\n\nDefault SET format is \"MMDDhhmm[[CC]YY][.ss]\", that's (2 digits each)\nmonth, day, hour (0-23), and minute. Optionally century, year, and second.\n\n-d     Show DATE instead of current time (convert date format)\n-r     Use modification time of FILE instead of current date\n-s       +FORMAT for SET or -d (instead of MMDDhhmm[[CC]YY][.ss])\n-u    Use UTC instead of current timezone\n\n+FORMAT specifies display format string using these escapes:\n\n%% literal %             %n newline              %t tab\n%S seconds (00-60)       %M minute (00-59)       %m month (01-12)\n%H hour (0-23)           %I hour (01-12)         %p AM/PM\n%y short year (00-99)    %Y year                 %C century\n%a short weekday name    %A weekday name         %u day of week (1-7, 1=mon)\n%b short month name      %B month name           %Z timezone name\n%j day of year (001-366) %d day of month (01-31) %e day of month ( 1-31)\n\n%U Week of year (0-53 start sunday)   %W Week of year (0-53 start monday)\n%V Week of year (1-53 start monday, week < 4 days not part of this year)\n\n%D = \"%m/%d/%y\"    %r = \"%I : %M : %S %p\"   %T = \"%H:%M:%S\"   %h = \"%b\"\n%x locale date     %X locale time           %c locale date/time\n\n"
+#define help_date "usage: date [-u] [-r FILE] [-d DATE] [+DISPLAY_FORMAT] [-D SET_FORMAT] [SET]\n\nSet/get the current date/time. With no SET shows the current date.\n\nDefault SET format is \"MMDDhhmm[[CC]YY][.ss]\", that's (2 digits each)\nmonth, day, hour (0-23), and minute. Optionally century, year, and second.\nAlso accepts \"@UNIXTIME[.FRACTION]\" as seconds since midnight Jan 1 1970.\n\n-d        Show DATE instead of current time (convert date format)\n-D     +FORMAT for SET or -d (instead of MMDDhhmm[[CC]YY][.ss])\n-r    Use modification time of FILE instead of current date\n-u       Use UTC instead of current timezone\n\n+FORMAT specifies display format string using these escapes:\n\n%% literal %             %n newline              %t tab\n%S seconds (00-60)       %M minute (00-59)       %m month (01-12)\n%H hour (0-23)           %I hour (01-12)         %p AM/PM\n%y short year (00-99)    %Y year                 %C century\n%a short weekday name    %A weekday name         %u day of week (1-7, 1=mon)\n%b short month name      %B month name           %Z timezone name\n%j day of year (001-366) %d day of month (01-31) %e day of month ( 1-31)\n\n%U Week of year (0-53 start sunday)   %W Week of year (0-53 start monday)\n%V Week of year (1-53 start monday, week < 4 days not part of this year)\n\n%D = \"%m/%d/%y\"    %r = \"%I : %M : %S %p\"   %T = \"%H:%M:%S\"   %h = \"%b\"\n%x locale date     %X locale time           %c locale date/time\n\n"
 
 #define help_cut "usage: cut OPTION... [FILE]...\n\nPrint selected parts of lines from each FILE to standard output.\n\n-b LIST        select only these bytes from LIST.\n-c LIST     select only these characters from LIST.\n-f LIST        select only these fields.\n-d DELIM     use DELIM instead of TAB for field delimiter.\n-s       do not print lines not containing delimiters.\n-n       don't split multibyte characters (Ignored).\n\n"
 
index c503b41..e6af5be 100644 (file)
@@ -7,10 +7,11 @@ USE_ARP(NEWTOY(arp, "vi:nDsdap:A:H:[+Ap][!sd]", TOYFLAG_USR|TOYFLAG_BIN))
 USE_ARPING(NEWTOY(arping, "<1>1s:I:w#<0c#<0AUDbqf[+AU][+Df]", TOYFLAG_USR|TOYFLAG_SBIN))
 USE_BASE64(NEWTOY(base64, "diw#<1[!dw]", TOYFLAG_USR|TOYFLAG_BIN))
 USE_BASENAME(NEWTOY(basename, "<1>2", TOYFLAG_USR|TOYFLAG_BIN))
-USE_BLKID(NEWTOY(blkid, "<1", TOYFLAG_BIN))
+USE_BLKID(NEWTOY(blkid, 0, TOYFLAG_BIN))
 USE_BLOCKDEV(NEWTOY(blockdev, "<1>1(setro)(setrw)(getro)(getss)(getbsz)(setbsz)#<0(getsz)(getsize)(getsize64)(flushbufs)(rereadpt)",TOYFLAG_USR|TOYFLAG_BIN))
 USE_BOOTCHARTD(NEWTOY(bootchartd, 0, TOYFLAG_STAYROOT|TOYFLAG_USR|TOYFLAG_BIN))
 USE_BRCTL(NEWTOY(brctl, "<1", TOYFLAG_USR|TOYFLAG_SBIN))
+USE_BUNZIP2(NEWTOY(bunzip2, "cftkv", TOYFLAG_USR|TOYFLAG_BIN))
 USE_BZCAT(NEWTOY(bzcat, NULL, TOYFLAG_USR|TOYFLAG_BIN))
 USE_CAL(NEWTOY(cal, ">2", TOYFLAG_USR|TOYFLAG_BIN))
 USE_CAT(NEWTOY(cat, "u"USE_CAT_V("vte"), TOYFLAG_BIN))
@@ -34,14 +35,14 @@ USE_CPIO(NEWTOY(cpio, "mduH:p:|i|t|F:v(verbose)o|[!pio][!pot][!pF]", TOYFLAG_BIN
 USE_CROND(NEWTOY(crond, "fbSl#<0=8d#<0L:c:[-bf][-LS][-ld]", TOYFLAG_USR|TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
 USE_CRONTAB(NEWTOY(crontab, "c:u:elr[!elr]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT))
 USE_CUT(NEWTOY(cut, "b:|c:|f:|d:sn[!cbf]", TOYFLAG_USR|TOYFLAG_BIN))
-USE_DATE(NEWTOY(date, "d:s:r:u[!dr]", TOYFLAG_BIN))
+USE_DATE(NEWTOY(date, "d:D:r:u[!dr]", TOYFLAG_BIN))
 USE_DD(NEWTOY(dd, NULL, TOYFLAG_USR|TOYFLAG_BIN))
 USE_DEALLOCVT(NEWTOY(deallocvt, ">1", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_NEEDROOT))
 USE_GROUPDEL(OLDTOY(delgroup, groupdel, TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
 USE_USERDEL(OLDTOY(deluser, userdel, TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
 USE_DF(NEWTOY(df, "Pkt*a[-Pk]", TOYFLAG_SBIN))
 USE_DHCP(NEWTOY(dhcp, "V:H:F:x*r:O*A#<0T#<0t#<0s:p:i:SBRCaovqnbf", TOYFLAG_SBIN|TOYFLAG_ROOTONLY))
-USE_DHCPD(NEWTOY(dhcpd, ">1P#<0>65535=67fi:S", TOYFLAG_SBIN|TOYFLAG_ROOTONLY))
+USE_DHCPD(NEWTOY(dhcpd, ">1P#<0>65535=67fi:S46[!46]", TOYFLAG_SBIN|TOYFLAG_ROOTONLY))
 USE_DIFF(NEWTOY(diff, "<2>2B(ignore-blank-lines)d(minimal)b(ignore-space-change)ut(expand-tabs)w(ignore-all-space)i(ignore-case)T(initial-tab)s(report-identical-files)q(brief)a(text)L(label)*S(starting-file):N(new-file)r(recursive)U(unified)#<0=3", TOYFLAG_USR|TOYFLAG_BIN))
 USE_DIRNAME(NEWTOY(dirname, "<1", TOYFLAG_USR|TOYFLAG_BIN))
 USE_DMESG(NEWTOY(dmesg, "trs#<1n#c[!tr]", TOYFLAG_BIN))
@@ -67,6 +68,7 @@ USE_FREERAMDISK(NEWTOY(freeramdisk, "<1>1", TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
 USE_FSCK(NEWTOY(fsck, "?t:ANPRTVsC#", TOYFLAG_USR|TOYFLAG_BIN))
 USE_FSFREEZE(NEWTOY(fsfreeze, "<1>1f|u|[!fu]", TOYFLAG_USR|TOYFLAG_SBIN))
 USE_FSTYPE(NEWTOY(fstype, "<1", TOYFLAG_BIN))
+USE_FSYNC(NEWTOY(fsync, "<1d", TOYFLAG_BIN))
 USE_FTPGET(NEWTOY(ftpget, "<2cvu:p:P#<0=21>65535", TOYFLAG_BIN))
 USE_FTPGET(OLDTOY(ftpput, ftpget, TOYFLAG_BIN))
 USE_GETENFORCE(NEWTOY(getenforce, ">0", TOYFLAG_USR|TOYFLAG_SBIN))
@@ -84,6 +86,7 @@ USE_HELLO(NEWTOY(hello, 0, TOYFLAG_USR|TOYFLAG_BIN))
 USE_HELP(NEWTOY(help, ""USE_HELP_EXTRAS("ah"), TOYFLAG_BIN))
 USE_HEXEDIT(NEWTOY(hexedit, "<1>1r", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
 USE_HOST(NEWTOY(host, "<1>2avt:", TOYFLAG_USR|TOYFLAG_BIN))
+USE_HOSTID(NEWTOY(hostid, ">0", TOYFLAG_USR|TOYFLAG_BIN))
 USE_HOSTNAME(NEWTOY(hostname, NULL, TOYFLAG_BIN))
 USE_HWCLOCK(NEWTOY(hwclock, ">0(fast)f(rtc):u(utc)l(localtime)t(systz)s(hctosys)r(show)w(systohc)[-ul][!rtsw]", TOYFLAG_USR|TOYFLAG_BIN))
 USE_ICONV(NEWTOY(iconv, "cst:f:", TOYFLAG_USR|TOYFLAG_BIN))
@@ -112,7 +115,7 @@ USE_LINK(NEWTOY(link, "<2>2", TOYFLAG_USR|TOYFLAG_BIN))
 USE_LN(NEWTOY(ln, "<1vnfs", TOYFLAG_BIN))
 USE_LOAD_POLICY(NEWTOY(load_policy, "<1>1", TOYFLAG_USR|TOYFLAG_SBIN))
 USE_LOGGER(NEWTOY(logger, "st:p:", TOYFLAG_USR|TOYFLAG_BIN))
-USE_LOGIN(NEWTOY(login, ">1fph:", TOYFLAG_BIN))
+USE_LOGIN(NEWTOY(login, ">1f:ph:", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
 USE_LOGNAME(NEWTOY(logname, ">0", TOYFLAG_USR|TOYFLAG_BIN))
 USE_LOSETUP(NEWTOY(losetup, ">2S(sizelimit)#s(show)ro#j:fdca[!afj]", TOYFLAG_SBIN))
 USE_LS(NEWTOY(ls, USE_LS_COLOR("(color):;")"ZgoACFHLRSacdfiklmnpqrstux1[-Cxm1][-Cxml][-Cxmo][-Cxmg][-cu][-ftS][-HL]", TOYFLAG_BIN|TOYFLAG_LOCALE))
@@ -126,7 +129,7 @@ USE_MDEV(NEWTOY(mdev, "s", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_UMASK))
 USE_MIX(NEWTOY(mix, "c:d:l#r#", TOYFLAG_USR|TOYFLAG_BIN))
 USE_MKDIR(NEWTOY(mkdir, "<1"USE_MKDIR_Z("Z:")"vpm:", TOYFLAG_BIN|TOYFLAG_UMASK))
 USE_MKE2FS(NEWTOY(mke2fs, "<1>2g:Fnqm#N#i#b#", TOYFLAG_SBIN))
-USE_MKFIFO(NEWTOY(mkfifo, "<1"USE_MKFIFO_Z("Z")"m:", TOYFLAG_USR|TOYFLAG_BIN))
+USE_MKFIFO(NEWTOY(mkfifo, "<1"USE_MKFIFO_Z("Z:")"m:", TOYFLAG_USR|TOYFLAG_BIN))
 USE_MKNOD(NEWTOY(mknod, "<2>4m(mode):"USE_MKNOD_Z("Z:"), TOYFLAG_BIN|TOYFLAG_UMASK))
 USE_MKPASSWD(NEWTOY(mkpasswd, ">2S:m:P#=0<0", TOYFLAG_USR|TOYFLAG_BIN))
 USE_MKSWAP(NEWTOY(mkswap, "<1>1", TOYFLAG_SBIN))
@@ -169,7 +172,7 @@ USE_PWDX(NEWTOY(pwdx, "<1a", TOYFLAG_USR|TOYFLAG_BIN))
 USE_READAHEAD(NEWTOY(readahead, NULL, TOYFLAG_BIN))
 USE_READLINK(NEWTOY(readlink, "<1>1fenq[-fe]", TOYFLAG_USR|TOYFLAG_BIN))
 USE_REALPATH(NEWTOY(realpath, "<1", TOYFLAG_USR|TOYFLAG_BIN))
-USE_REBOOT(NEWTOY(reboot, "n", TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
+USE_REBOOT(NEWTOY(reboot, "fn", TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
 USE_RENICE(NEWTOY(renice, "<1gpun#|", TOYFLAG_USR|TOYFLAG_BIN))
 USE_RESET(NEWTOY(reset, 0, TOYFLAG_USR|TOYFLAG_BIN))
 USE_RESTORECON(NEWTOY(restorecon, "<1DFnRrv", TOYFLAG_USR|TOYFLAG_SBIN))
@@ -212,6 +215,7 @@ USE_TEE(NEWTOY(tee, "ia", TOYFLAG_USR|TOYFLAG_BIN))
 USE_TELNET(NEWTOY(telnet, "<1>2", TOYFLAG_BIN))
 USE_TELNETD(NEWTOY(telnetd, "w#<0b:p#<0>65535=23f:l:FSKi[!wi]", TOYFLAG_USR|TOYFLAG_BIN))
 USE_TEST(NEWTOY(test, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+USE_TFTP(NEWTOY(tftp, "<1b#<8>65464r:l:g|p|[!gp]", TOYFLAG_USR|TOYFLAG_BIN))
 USE_TFTPD(NEWTOY(tftpd, "rcu:l", TOYFLAG_BIN))
 USE_TIME(NEWTOY(time, "<1^p", TOYFLAG_USR|TOYFLAG_BIN))
 USE_TIMEOUT(NEWTOY(timeout, "<2^vk:s: ", TOYFLAG_BIN))
index 05e377f..c16cffe 100644 (file)
--- a/lib/lib.c
+++ b/lib/lib.c
@@ -291,6 +291,41 @@ int stridx(char *haystack, char needle)
   return off-haystack;
 }
 
+char *strlower(char *s)
+{
+  char *try, *new;
+
+  if (!CFG_TOYBOX_I18N) {
+    try = new = xstrdup(s);
+    for (; *s; s++) *(new++) = tolower(*s);
+  } else {
+    // I can't guarantee the string _won't_ expand during reencoding, so...?
+    try = new = xmalloc(strlen(s)*2+1);
+
+    while (*s) {
+      wchar_t c;
+      int len = mbrtowc(&c, s, MB_CUR_MAX, 0);
+
+      if (len < 1) *(new++) = *(s++);
+      else {
+        s += len;
+        // squash title case too
+        c = towlower(c);
+
+        // if we had a valid utf8 sequence, convert it to lower case, and can't
+        // encode back to utf8, something is wrong with your libc. But just
+        // in case somebody finds an exploit...
+        len = wcrtomb(new, c, 0);
+        if (len < 1) error_exit("bad utf8 %x", (int)c);
+        new += len;
+      }
+    }
+    *new = 0;
+  }
+
+  return try;
+}
+
 int unescape(char c)
 {
   char *from = "\\abefnrtv", *to = "\\\a\b\033\f\n\r\t\v";
@@ -540,7 +575,7 @@ void delete_tempfile(int fdin, int fdout, char **tempname)
 {
   close(fdin);
   close(fdout);
-  unlink(*tempname);
+  if (*tempname) unlink(*tempname);
   tempfile2zap = (char *)1;
   free(*tempname);
   *tempname = NULL;
@@ -833,7 +868,7 @@ void names_to_pid(char **names, int (*callback)(pid_t pid, char *name))
 
 // display first few digits of number with power of two units, except we're
 // actually just counting decimal digits and showing mil/bil/trillions.
-int human_readable(char *buf, unsigned long long num)
+int human_readable(char *buf, unsigned long long num, int style)
 {
   int end, len;
 
@@ -846,9 +881,9 @@ int human_readable(char *buf, unsigned long long num)
     buf[1] = '.';
     end = 3;
   }
-  buf[end++] = ' ';
+  if (style & HR_SPACE) buf[end++] = ' ';
   if (len) buf[end++] = " KMGTPE"[len];
-  buf[end++] = 'B';
+  if (style & HR_B) buf[end++] = 'B';
   buf[end++] = 0;
 
   return end;
index 3183f32..17a4a97 100644 (file)
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -160,6 +160,7 @@ long xstrtol(char *str, char **end, int base);
 long atolx(char *c);
 long atolx_range(char *numstr, long low, long high);
 int stridx(char *haystack, char needle);
+char *strlower(char *s);
 int unescape(char c);
 int strstart(char **a, char *b);
 off_t fdlength(int fd);
@@ -176,7 +177,9 @@ void replace_tempfile(int fdin, int fdout, char **tempname);
 void crc_init(unsigned int *crc_table, int little_endian);
 void base64_init(char *p);
 int yesno(char *prompt, int def);
-int human_readable(char *buf, unsigned long long num);
+#define HR_SPACE 1
+#define HR_B 2
+int human_readable(char *buf, unsigned long long num, int style);
 int qstrcmp(const void *a, const void *b);
 int xpoll(struct pollfd *fds, int nfds, int timeout);
 
@@ -204,6 +207,8 @@ void tty_sigreset(int i);
 // net.c
 int xsocket(int domain, int type, int protocol);
 void xsetsockopt(int fd, int level, int opt, void *val, socklen_t len);
+int xconnect(char *host, char *port, int family, int socktype, int protocol,
+  int flags);
 
 // password.c
 int get_salt(char *salt, char * algo);
index 5d3ea4a..48d0a5f 100644 (file)
--- a/lib/net.c
+++ b/lib/net.c
@@ -12,3 +12,33 @@ void xsetsockopt(int fd, int level, int opt, void *val, socklen_t len)
 {
   if (-1 == setsockopt(fd, level, opt, val, len)) perror_exit("setsockopt");
 }
+
+int xconnect(char *host, char *port, int family, int socktype, int protocol,
+             int flags)
+{
+  struct addrinfo info, *ai, *ai2;
+  int fd;
+
+  memset(&info, 0, sizeof(struct addrinfo));
+  info.ai_family = family;
+  info.ai_socktype = socktype;
+  info.ai_protocol = protocol;
+  info.ai_flags = flags;
+
+  fd = getaddrinfo(host, port, &info, &ai);
+  if (fd || !ai)
+    error_exit("Connect '%s%s%s': %s", host, port ? ":" : "", port ? port : "",
+      fd ? gai_strerror(fd) : "not found");
+
+  // Try all the returned addresses. Report errors if last entry can't connect.
+  for (ai2 = ai; ai; ai = ai->ai_next) {
+    fd = (ai->ai_next ? socket : xsocket)(ai->ai_family, ai->ai_socktype,
+      ai->ai_protocol);
+    if (!connect(fd, ai->ai_addr, ai->ai_addrlen)) break;
+    else if (!ai2->ai_next) perror_exit("connect");
+    close(fd);
+  }
+  freeaddrinfo(ai2);
+
+  return fd;
+}
index f83cab6..ff22fa5 100644 (file)
@@ -79,7 +79,7 @@ char *strptime(const char *buf, const char *format, struct tm *tm);
 // correct name to the broken name.
 
 char *dirname(char *path);
-char *__xpg_basename (char *path);
+char *__xpg_basename(char *path);
 static inline char *basename(char *path) { return __xpg_basename(path); }
 
 // uClibc pretends to be glibc and copied a lot of its bugs, but has a few more
@@ -154,11 +154,19 @@ int utimensat(int fd, const char *path, const struct timespec times[2], int flag
 
 #endif // glibc in general
 
-#ifndef __GLIBC__
+#if !defined(__GLIBC__) && !defined(__BIONIC__)
 // POSIX basename.
 #include <libgen.h>
 #endif
 
+// glibc was handled above; for 32-bit bionic we need to avoid a collision
+// with toybox's basename_r so we can't include <libgen.h> even though that
+// would give us a POSIX basename(3).
+#if defined(__BIONIC__)
+char *basename(char *path);
+char *dirname(char *path);
+#endif
+
 // Work out how to do endianness
 
 #ifndef __APPLE__
index 54f2cbb..8086282 100644 (file)
@@ -30,10 +30,10 @@ void xstrncat(char *dest, char *src, size_t size)
 
 void xexit(void)
 {
+  if (toys.rebound) longjmp(*toys.rebound, 1);
   if (fflush(NULL) || ferror(stdout))
     if (!toys.exitval) perror_msg("write");
-  if (toys.rebound) longjmp(*toys.rebound, 1);
-  else exit(toys.exitval);
+  exit(toys.exitval);
 }
 
 // Die unless we can allocate memory.
@@ -136,7 +136,10 @@ void xexec(char **argv)
   if (CFG_TOYBOX && !CFG_TOYBOX_NORECURSE) toy_exec(argv);
   execvp(argv[0], argv);
 
-  perror_exit("exec %s", argv[0]);
+  perror_msg("exec %s", argv[0]);
+  toys.exitval = 127;
+  if (!CFG_TOYBOX_FORK) _exit(toys.exitval);
+  xexit();
 }
 
 // Spawn child process, capturing stdin/stdout.
diff --git a/main.c b/main.c
index c557030..0738a17 100644 (file)
--- a/main.c
+++ b/main.c
@@ -6,7 +6,7 @@
 #include "toys.h"
 
 #ifndef TOYBOX_VERSION
-#define TOYBOX_VERSION "0.5.2"
+#define TOYBOX_VERSION "0.6.0"
 #endif
 
 // Populate toy_list[].
@@ -163,7 +163,10 @@ void toybox_main(void)
       xputs(TOYBOX_VERSION);
       xexit();
     }
-    if (toys.argv[1][0] != '-') error_exit("Unknown command %s", toys.argv[1]);
+    if (toys.argv[1][0] != '-') {
+      toys.exitval = 127;
+      error_exit("Unknown command %s", toys.argv[1]);
+    }
   }
 
   // Output list of command.
index dcd581e..99dcfde 100755 (executable)
@@ -18,3 +18,4 @@ do
   scripts/single.sh $i > /dev/null 2>$PREFIX/${i}.bad &&
     rm $PREFIX/${i}.bad || echo -n '*'
 done
+echo
index d87087b..7e57f84 100644 (file)
@@ -6,10 +6,12 @@
 // This is intentionally crappy code because we control the inputs. It leaks
 // memory like a sieve and segfaults if malloc returns null, but does the job.
 
+#include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
+#include <ctype.h>
 
 struct flag {
   struct flag *next;
diff --git a/tests/date.test b/tests/date.test
new file mode 100644 (file)
index 0000000..94a4157
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+# Test Unix date parsing.
+testing "date -d @0" "TZ=UTC date -d @0 2>&1" "Thu Jan  1 00:00:00 GMT 1970\n" "" ""
+testing "date -d @0x123" "TZ=UTC date -d @0x123 2>&1" "date: bad date '@0x123'\n" "" ""
+
+# Test basic date parsing.
+# Note that toybox's -d format is not the same as coreutils'.
+testing "date -d 06021234" "TZ=UTC date -d 06021234 2>&1" "Sun Jun  2 12:34:00 UTC 1900\n" "" ""
+testing "date -d 060212341982" "TZ=UTC date -d 060212341982 2>&1" "Sun Jun  2 12:34:00 UTC 1982\n" "" ""
+testing "date -d 123" "TZ=UTC date -d 123 2>&1" "date: bad date '123'\n" "" ""
+
+# Accidentally given a Unix time, we should trivially reject that.
+testing "date Unix time missing @" "TZ=UTC date 1438053157 2>&1" \
+  "date: bad date '1438053157'; Tue February 38 05:31:00 UTC 2057 != Sun Mar 10 05:31:00 UTC 2058\n" "" ""
+# But some invalid dates are more subtle, like Febuary 29th in a non-leap year.
+testing "date Feb 29th" "TZ=UTC date 022900001975 2>&1" \
+  "date: bad date '022900001975'; Tue Feb 29 00:00:00 UTC 2075 != Fri Mar  1 00:00:00 UTC 2075\n" "" ""
index 44fd03b..363cfb9 100644 (file)
@@ -25,10 +25,7 @@ config ACPI
 #include "toys.h"
 
 GLOBALS(
-  int ac;
-  int bat;
-  int therm;
-  int cool;
+  int ac, bat, therm, cool;
   char *cpath;
 )
 
@@ -44,7 +41,7 @@ int read_int_at(int dirfd, char *name)
   return ret;
 }
 
-int acpi_callback(struct dirtree *tree)
+static int acpi_callback(struct dirtree *tree)
 {
   int dfd, fd, len, on;
 
@@ -85,11 +82,11 @@ done:
   return 0;
 }
 
-int temp_callback(struct dirtree *tree)
+static int temp_callback(struct dirtree *tree)
 {
   int dfd, temp;
 
-  if (tree->name[0]=='.') return 0;
+  if (*tree->name=='.') return 0;
   if (!tree->parent || !tree->parent->parent)
     return DIRTREE_RECURSE|DIRTREE_SYMFOLLOW;
   errno = 0;
@@ -98,17 +95,17 @@ int temp_callback(struct dirtree *tree)
     if ((0 < (temp = read_int_at(dfd, "temp"))) || !errno) {
       //some tempertures are in milli-C, some in deci-C
       //reputedly some are in deci-K, but I have not seen them
-      if (((temp >= 1000) || (temp <= -1000)) && (temp%100 == 0))
-        temp /= 100;
+      if (((temp >= 1000) || (temp <= -1000)) && (temp%100 == 0)) temp /= 100;
       printf("Thermal %d: %d.%d degrees C\n", TT.therm++, temp/10, temp%10);
     }
     close(dfd);
   }
   free(TT.cpath);
+
   return 0;
 }
 
-int cool_callback(struct dirtree *tree)
+static int cool_callback(struct dirtree *tree)
 {
   int dfd=5, cur, max;
 
index 725f163..fad1159 100644 (file)
@@ -4,7 +4,7 @@
  *
  * See ftp://ftp.kernel.org/pub/linux/utils/util-linux/v2.24/libblkid-docs/api-index-full.html
 
-USE_BLKID(NEWTOY(blkid, "<1", TOYFLAG_BIN))
+USE_BLKID(NEWTOY(blkid, 0, TOYFLAG_BIN))
 USE_FSTYPE(NEWTOY(fstype, "<1", TOYFLAG_BIN))
 
 config BLKID
@@ -34,6 +34,7 @@ struct fstype {
 };
 
 static const struct fstype fstypes[] = {
+  {"swap", 0x4341505350415753, 8, 4086, 1036, 15, 1052},
   {"ext2", 0xEF53, 2, 1080, 1128, 16, 1144}, // keep this first for ext3/4 check
   // NTFS label actually 8/16 0x4d80 but horrible: 16 bit wide characters via
   // codepage, something called a uuid that's only 8 bytes long...
@@ -56,8 +57,7 @@ static const struct fstype fstypes[] = {
   {"vfat", 0x31544146, 4, 54, 39+(4<<24), 11, 43}     // fat1
 };
 
-/* TODO if no args use proc/partitions */
-void do_blkid(int fd, char *name)
+static void do_blkid(int fd, char *name)
 {
   int off, i, j;
   char *type;
@@ -89,7 +89,7 @@ void do_blkid(int fd, char *name)
       if (test == fstypes[i].magic) break;
     }
 
-    if (i == sizeof(fstypes)/sizeof(struct fstype)) {
+    if (i == ARRAY_LEN(fstypes)) {
       off += len;
       if (pass) continue;
       return;
@@ -136,10 +136,30 @@ void do_blkid(int fd, char *name)
 
 void blkid_main(void)
 {
-  loopfiles(toys.optargs, do_blkid);
+  if (*toys.optargs) loopfiles(toys.optargs, do_blkid);
+  else {
+    unsigned int ma, mi, sz, fd;
+    char *name = toybuf, *buffer = toybuf+1024, device[32];
+    FILE *fp = xfopen("/proc/partitions", "r");
+
+    while (fgets(buffer, 1024, fp)) {
+      *name = 0;
+      if (sscanf(buffer, " %u %u %u %[^\n ]", &ma, &mi, &sz, name) != 4)
+        continue;
+
+      sprintf(device, "/dev/%.20s", name);
+      if (-1 == (fd = open(device, O_RDONLY))) {
+        if (errno != ENOMEDIUM) perror_msg("%s", device);
+      } else {
+        do_blkid(fd, device);
+        close(fd);
+      }
+    }
+    if (CFG_TOYBOX_FREE) fclose(fp);
+  }
 }
 
 void fstype_main(void)
 {
-  blkid_main();
+  loopfiles(toys.optargs, do_blkid);
 }
index 642590d..850c51c 100644 (file)
@@ -1,4 +1,4 @@
-/* bzcat.c - decompress stdin to stdout using bunzip2.
+/* bzcat.c - bzip2 decompression
  *
  * Copyright 2003, 2007 Rob Landley <rob@landley.net>
  *
 
 
 USE_BZCAT(NEWTOY(bzcat, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+USE_BUNZIP2(NEWTOY(bunzip2, "cftkv", TOYFLAG_USR|TOYFLAG_BIN))
+
+config BUNZIP2
+  bool "bunzip2"
+  default y
+  help
+    usage: bunzip2 [-cftkv] [FILE...]
+
+    Decompress listed files (file.bz becomes file) deleting archive file(s).
+    Read from stdin if no files listed.
+
+    -c force output to stdout
+    -f force decompression. (If FILE doesn't end in .bz, replace original.)
+    -k keep input files (-c and -t imply this)
+    -t  test integrity
+    -v verbose
 
 config BZCAT
   bool "bzcat"
   default y
   help
-    usage: bzcat [filename...]
+    usage: bzcat [FILE...]
 
     Decompress listed files to stdout. Use stdin if no files listed.
 */
 
+#define FOR_bunzip2
 #include "toys.h"
 
 #define THREADS 1
@@ -421,7 +438,7 @@ static int read_huffman_data(struct bunzip_data *bd, struct bwdata *bw)
 }
 
 // Flush output buffer to disk
-void flush_bunzip_outbuf(struct bunzip_data *bd, int out_fd)
+static void flush_bunzip_outbuf(struct bunzip_data *bd, int out_fd)
 {
   if (bd->outbufPos) {
     if (write(out_fd, bd->outbuf, bd->outbufPos) != bd->outbufPos)
@@ -430,7 +447,7 @@ void flush_bunzip_outbuf(struct bunzip_data *bd, int out_fd)
   }
 }
 
-void burrows_wheeler_prep(struct bunzip_data *bd, struct bwdata *bw)
+static void burrows_wheeler_prep(struct bunzip_data *bd, struct bwdata *bw)
 {
   int ii, jj;
   unsigned int *dbuf = bw->dbuf;
@@ -474,7 +491,7 @@ void burrows_wheeler_prep(struct bunzip_data *bd, struct bwdata *bw)
 }
 
 // Decompress a block of text to intermediate buffer
-int read_bunzip_data(struct bunzip_data *bd)
+static int read_bunzip_data(struct bunzip_data *bd)
 {
   int rc = read_block_header(bd, bd->bwdata);
   if (!rc) rc=read_huffman_data(bd, bd->bwdata);
@@ -493,7 +510,8 @@ int read_bunzip_data(struct bunzip_data *bd)
 // http://dogma.net/markn/articles/bwt/bwt.htm
 // http://marknelson.us/1996/09/01/bwt/
 
-int write_bunzip_data(struct bunzip_data *bd, struct bwdata *bw, int out_fd, char *outbuf, int len)
+static int write_bunzip_data(struct bunzip_data *bd, struct bwdata *bw,
+  int out_fd, char *outbuf, int len)
 {
   unsigned int *dbuf = bw->dbuf;
   int count, pos, current, run, copies, outbyte, previous, gotcount = 0;
@@ -584,7 +602,8 @@ dataus_interruptus:
 
 // Allocate the structure, read file header. If !len, src_fd contains
 // filehandle to read from. Else inbuf contains data.
-int start_bunzip(struct bunzip_data **bdp, int src_fd, char *inbuf, int len)
+static int start_bunzip(struct bunzip_data **bdp, int src_fd, char *inbuf,
+  int len)
 {
   struct bunzip_data *bd;
   unsigned int i;
@@ -622,10 +641,10 @@ int start_bunzip(struct bunzip_data **bdp, int src_fd, char *inbuf, int len)
 
 // Example usage: decompress src_fd to dst_fd. (Stops at end of bzip data,
 // not end of file.)
-void bunzipStream(int src_fd, int dst_fd)
+static char *bunzipStream(int src_fd, int dst_fd)
 {
   struct bunzip_data *bd;
-  char *bunzip_errors[]={NULL, "not bzip", "bad data", "old format"};
+  char *bunzip_errors[] = {0, "not bzip", "bad data", "old format"};
   int i, j;
 
   if (!(i = start_bunzip(&bd,src_fd, 0, 0))) {
@@ -636,15 +655,67 @@ void bunzipStream(int src_fd, int dst_fd)
 
   for (j=0; j<THREADS; j++) free(bd->bwdata[j].dbuf);
   free(bd);
-  if (i) error_exit(bunzip_errors[-i]);
+
+  return bunzip_errors[-i];
 }
 
 static void do_bzcat(int fd, char *name)
 {
-  bunzipStream(fd, 1);
+  char *err = bunzipStream(fd, 1);
+
+  if (err) error_exit(err);
 }
 
 void bzcat_main(void)
 {
   loopfiles(toys.optargs, do_bzcat);
 }
+
+static void do_bunzip2(int fd, char *name)
+{
+  int outfd = 1, rename = 0, len = strlen(name);
+  char *tmp, *err, *dotbz = 0;
+
+  // Trim off .bz or .bz2 extension
+  dotbz = name+len-3;
+  if ((len>3 && !strcmp(dotbz, ".bz")) || (len>4 && !strcmp(--dotbz, ".bz2")))
+    dotbz = 0;
+
+  // For - no replace
+  if (toys.optflags&FLAG_t) outfd = xopen("/dev/null", O_WRONLY);
+  else if ((fd || strcmp(name, "-")) && !(toys.optflags&FLAG_c)) {
+    if (toys.optflags&FLAG_k) {
+      if (!dotbz || !access(name, X_OK)) {
+        error_msg("%s exists", name);
+
+        return;
+      }
+    }
+    outfd = copy_tempfile(fd, name, &tmp);
+    rename++;
+  }
+
+  if (toys.optflags&FLAG_v) printf("%s:", name);
+  err = bunzipStream(fd, outfd);
+  if (toys.optflags&FLAG_v) {
+    printf("%s\n", err ? err : "ok");
+    toys.exitval |= !!err;
+  } else if (err) error_msg(err);
+
+  // can't test outfd==1 because may have been called with stdin+stdout closed
+  if (rename) {
+    if (toys.optflags&FLAG_k) {
+      free(tmp);
+      tmp = 0;
+    } else {
+      if (dotbz) *dotbz = '.';
+      if (!unlink(name)) perror_msg("%s", name);
+    }
+    (err ? delete_tempfile : replace_tempfile)(-1, outfd, &tmp);
+  }
+}
+
+void bunzip2_main(void)
+{
+  loopfiles(toys.optargs, do_bunzip2);
+}
index a2bbb66..6dbdd13 100644 (file)
@@ -21,7 +21,7 @@ config CHCON
 #define FOR_chcon
 #include "toys.h"
 
-int do_chcon(struct dirtree *try)
+static int do_chcon(struct dirtree *try)
 {
   char *path, *con = *toys.optargs;
 
diff --git a/toys/other/fsync.c b/toys/other/fsync.c
new file mode 100644 (file)
index 0000000..e6f6c8d
--- /dev/null
@@ -0,0 +1,33 @@
+/* fsync.c - Synchronize a file's in-core state with storage device.
+ *
+ * Copyright 2015 Ranjan Kumar <ranjankumar.bth@gmail.comi>
+ *
+ * No Standard.
+
+USE_FSYNC(NEWTOY(fsync, "<1d", TOYFLAG_BIN))
+
+config FSYNC
+  bool "fsync"
+  default y
+  help
+    usage: fsync [-d] [FILE...]
+
+    Synchronize a file's in-core state with storage device.
+
+    -d Avoid syncing metadata.
+*/
+
+#define FOR_fsync
+#include "toys.h"
+
+static void do_fsync(int fd, char *name)
+{
+  if (((toys.optflags & FLAG_d) ? fdatasync(fd) : fsync(fd)))
+    perror_msg("can't sync '%s'", name);
+}
+
+void fsync_main(void)
+{
+  loopfiles_rw(toys.optargs, O_RDONLY|O_NOATIME|O_NOCTTY|O_CLOEXEC,
+      0, 0, do_fsync);
+}
index 1f6b42e..a52d66d 100644 (file)
@@ -137,6 +137,8 @@ void hexedit_main(void)
       fd = xopen(*toys.optargs, ro ? O_RDONLY : O_RDWR);
   char keybuf[16];
 
+  *keybuf = 0;
+
   // Terminal setup
   TT.height = 25;
   terminal_size(0, &TT.height);
diff --git a/toys/other/hostid.c b/toys/other/hostid.c
new file mode 100644 (file)
index 0000000..883ac3c
--- /dev/null
@@ -0,0 +1,23 @@
+/* hostid.c - Print the numeric identifier for the current host.
+ *
+ * Copyright 2015 Ranjan Kumar <ranjankumar.bth@gmail.com>
+ *
+ * No Standard.
+
+USE_HOSTID(NEWTOY(hostid, ">0", TOYFLAG_USR|TOYFLAG_BIN))
+
+config HOSTID
+  bool "hostid"
+  default y
+  help
+    usage: hostid
+
+    Print the numeric identifier for the current host.
+*/
+#define FOR_hostid
+#include "toys.h"
+
+void hostid_main(void)
+{
+  xprintf("%08lx\n", gethostid());
+}
index b728286..c727bf9 100644 (file)
@@ -8,30 +8,28 @@
  * TODO: this command predates "pending" but needs cleanup. It #defines
  * random stuff, calls exit() form a signal handler... yeah.
 
-USE_LOGIN(NEWTOY(login, ">1fph:", TOYFLAG_BIN))
+USE_LOGIN(NEWTOY(login, ">1f:ph:", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
 
 config LOGIN
   bool "login"
   default y
   depends on TOYBOX_SHADOW
   help
-    usage: login [-p] [-h host] [[-f] username]
+    usage: login [-p] [-h host] [-f USERNAME] [USERNAME]
 
-    Establish a new session with the system.
+    Log in as a user, prompting for username and password if necessary.
 
     -p Preserve environment
     -h The name of the remote host for this login
-    -f Do not perform authentication
+    -f login as USERNAME without authentication
 */
 
 #define FOR_login
 #include "toys.h"
 
-#define USER_NAME_MAX_SIZE 32
-#define HOSTNAME_SIZE 32
-
 GLOBALS(
   char *hostname;
+  char *username;
 
   int login_timeout, login_fail_timeout;
 )
@@ -42,184 +40,128 @@ static void login_timeout_handler(int sig __attribute__((unused)))
   exit(0);
 }
 
-static char *forbid[] = {
-  "BASH_ENV", "ENV", "HOME", "IFS", "LD_LIBRARY_PATH", "LD_PRELOAD",
-  "LD_TRACE_LOADED_OBJECTS", "LD_BIND_NOW", "LD_AOUT_LIBRARY_PATH",
-  "LD_AOUT_PRELOAD", "LD_NOWARN", "LD_KEEPDIR", "SHELL", NULL
-};
-
-int verify_password(char * pwd)
-{
-  char *pass;
-
-  if (read_password(toybuf, sizeof(toybuf), "Password: ")) return 1;
-  if (!pwd) return 1;
-  if (pwd[0] == '!' || pwd[0] == '*') return 1;
-
-  pass = crypt(toybuf, pwd);
-  if (pass && !strcmp(pass, pwd)) return 0;
-
-  return 1;
-}
-
-void read_user(char * buff, int size)
-{
-  char hostname[HOSTNAME_SIZE+1];
-  int i = 0;
-
-  hostname[HOSTNAME_SIZE] = 0;
-  if (!gethostname(hostname, HOSTNAME_SIZE)) fputs(hostname, stdout);
-
-  fputs(" login: ", stdout);
-  fflush(stdout);
-
-  do {
-    int c = getchar();
-    if (c == EOF) exit(EXIT_FAILURE);
-    *buff = c;
-  } while (isblank(*buff));
-
-  if (*buff != '\n') if(!fgets(&buff[1], HOSTNAME_SIZE-1, stdin)) _exit(1);
-
-  while(i<HOSTNAME_SIZE-1 && isgraph(buff[i])) i++;
-  buff[i] = 0;
-}
-
-void handle_nologin(void)
-{
-  int fd = open("/etc/nologin", O_RDONLY);
-  int size;
-
-  if (fd == -1) return;
-
-  size = readall(fd, toybuf,sizeof(toybuf)-1);
-  toybuf[size] = 0;
-  if (!size) puts("System closed for routine maintenance\n");
-  else puts(toybuf);
-
-  close(fd);
-  fflush(stdout);
-  exit(1);
-}
-
-void handle_motd(void)
-{
-  int fd = open("/etc/motd", O_RDONLY);
-  int size;
-  if (fd == -1) return;
-
-  size = readall(fd, toybuf,sizeof(toybuf)-1);
-  toybuf[size] = 0;
-  puts(toybuf);
-
-  close(fd);
-  fflush(stdout);
-}
-
-void spawn_shell(const char *shell)
-{
-  const char * exec_name = strrchr(shell,'/');
-  if (exec_name) exec_name++;
-  else exec_name = shell;
-
-  snprintf(toybuf,sizeof(toybuf)-1, "-%s", shell);
-  execl(shell, toybuf, NULL);
-  error_exit("Failed to spawn shell");
-}
-
-void setup_environment(const struct passwd *pwd, int clear_env)
-{
-  if (chdir(pwd->pw_dir)) printf("bad home dir: %s\n", pwd->pw_dir);
-
-  if (clear_env) {
-    const char *term = getenv("TERM");
-    clearenv();
-    if (term) setenv("TERM", term, 1);
-  }
-
-  setenv("USER", pwd->pw_name, 1);
-  setenv("LOGNAME", pwd->pw_name, 1);
-  setenv("HOME", pwd->pw_dir, 1);
-  setenv("SHELL", pwd->pw_shell, 1);
-}
-
 void login_main(void)
 {
-  int f_flag = toys.optflags & FLAG_f;
-  int h_flag = toys.optflags & FLAG_h;
-  char username[33], *pass = NULL, **ss;
-  struct passwd * pwd = NULL;
-  struct spwd * spwd = NULL;
-  int auth_fail_cnt = 0;
-
-  if (f_flag && toys.optc != 1) error_exit("-f requires username");
+  char *forbid[] = {
+    "BASH_ENV", "ENV", "HOME", "IFS", "LD_LIBRARY_PATH", "LD_PRELOAD",
+    "LD_TRACE_LOADED_OBJECTS", "LD_BIND_NOW", "LD_AOUT_LIBRARY_PATH",
+    "LD_AOUT_PRELOAD", "LD_NOWARN", "LD_KEEPDIR", "SHELL"
+  };
+  int hh = toys.optflags&FLAG_h, count, tty;
+  char uu[33], *username, *pass = 0, *ss;
+  struct passwd *pwd = 0;
 
-  if (geteuid()) error_exit("not root");
+  for (tty=0; tty<3; tty++) if (isatty(tty)) break;
+  if (tty == 3) error_exit("no tty");
 
-  if (!isatty(0) || !isatty(1) || !isatty(2)) error_exit("no tty");
+  for (count = 0; count < ARRAY_LEN(forbid); count++) unsetenv(forbid[count]);
 
   openlog("login", LOG_PID | LOG_CONS, LOG_AUTH);
   xsignal(SIGALRM, login_timeout_handler);
-  alarm(TT.login_timeout = 60);
 
-  for (ss = forbid; *ss; ss++) unsetenv(*ss);
-
-  while (1) {
+  if (TT.username) username = TT.username;
+  else username = *toys.optargs;
+  for (count = 0; count < 3; count++) {
+    alarm(TT.login_timeout = 60);
     tcflush(0, TCIFLUSH);
 
-    username[sizeof(username)-1] = 0;
-    if (*toys.optargs) xstrncpy(username, *toys.optargs, sizeof(username));
-    else {
-      read_user(username, sizeof(username));
-      if (!*username) continue;
+    if (!username) {
+      int i;
+
+      memset(username = uu, 0, sizeof(uu));
+      gethostname(uu, sizeof(uu)-1);
+      printf("%s%slogin: ", *uu ? uu : "", *uu ? " " : "");
+      fflush(stdout);
+
+      if(!fgets(uu, sizeof(uu)-1, stdin)) _exit(1);
+
+      // Remove trailing \n and so on
+      for (i = 0; i<sizeof(uu); i++) if (uu[i]<=' ' || uu[i]==':') uu[i]=0;
+      if (!*uu) {
+        username = 0;
+        continue;
+      }
     }
 
+    // If user exists and isn't locked
     pwd = getpwnam(username);
-    if (!pwd) goto query_pass; // Non-existing user
+    if (pwd && *pwd->pw_passwd != '!' && *pwd->pw_passwd != '*') {
 
-    if (pwd->pw_passwd[0] == '!' || pwd->pw_passwd[0] == '*')
-      goto query_pass;  // Locked account
+      // Pre-authenticated or passwordless
+      if (TT.username || !*pwd->pw_passwd) break;
 
-    if (f_flag) break; // Pre-authenticated
+      // fetch shadow password if necessary
+      if (*(pass = pwd->pw_passwd) == 'x') {
+        struct spwd *spwd = getspnam (username);
 
-    if (!pwd->pw_passwd[0]) break; // Password-less account
+        if (spwd) pass = spwd->sp_pwdp;
+      }
+    } else if (TT.username) error_exit("bad -f '%s'", TT.username);
 
-    pass = pwd->pw_passwd;
-    if (pwd->pw_passwd[0] == 'x') {
-      spwd = getspnam (username);
-      if (spwd) pass = spwd->sp_pwdp;
-    }
+    // Verify password. (Prompt for password _before_ checking disable state.)
+    if (!read_password(toybuf, sizeof(toybuf), "Password: ")) {
+      int x = pass && (ss = crypt(toybuf, pass)) && !strcmp(pass, ss);
 
-query_pass:
-    if (!verify_password(pass)) break;
+      // password go bye-bye now.
+      memset(toybuf, 0, sizeof(toybuf));
+      if (x) break;
+    }
 
-    f_flag = 0;
-    syslog(LOG_WARNING, "invalid password for '%s' on %s %s %s", username,
-      ttyname(0), h_flag?"from":"", h_flag?TT.hostname:"");
+    syslog(LOG_WARNING, "invalid password for '%s' on %s %s%s", pwd->pw_name,
+      ttyname(tty), hh ? "from " : "", hh ? TT.hostname : "");
 
     sleep(3);
     puts("Login incorrect");
 
-    if (++auth_fail_cnt == 3)
-      error_exit("Maximum number of tries exceeded (3)\n");
-
-    *username = 0;
+    username = 0;
     pwd = 0;
-    spwd = 0;
   }
 
   alarm(0);
+  // This had password data in it, and we reuse for motd below
+  memset(toybuf, 0, sizeof(toybuf));
+
+  if (!pwd) error_exit("max retries (3)");
 
-  if (pwd->pw_uid) handle_nologin();
+  // Check twice because "this file exists" is a security test, and in
+  // theory filehandle exhaustion or other error could make open/read fail.
+  if (pwd->pw_uid && !access("/etc/nologin", R_OK)) {
+    ss = readfile("/etc/nologin", toybuf, sizeof(toybuf));
+    puts ((ss && *ss) ? ss : "nologin");
+    free(ss);
+    toys.exitval = 1;
+
+    return;
+  }
 
   xsetuser(pwd);
 
-  setup_environment(pwd, !(toys.optflags & FLAG_p));
+  if (chdir(pwd->pw_dir)) printf("bad $HOME: %s\n", pwd->pw_dir);
+
+  if (!(toys.optflags&FLAG_p)) {
+    char *term = getenv("TERM");
+
+    clearenv();
+    if (term) setenv("TERM", term, 1);
+  }
+
+  setenv("USER", pwd->pw_name, 1);
+  setenv("LOGNAME", pwd->pw_name, 1);
+  setenv("HOME", pwd->pw_dir, 1);
+  setenv("SHELL", pwd->pw_shell, 1);
 
-  handle_motd();
+  // Message of the day
+  if ((ss = readfile("/etc/motd", 0, 0))) {
+    puts(ss);
+    free(ss);
+  }
 
   syslog(LOG_INFO, "%s logged in on %s %s %s", pwd->pw_name,
-    ttyname(0), h_flag?"from":"", h_flag?TT.hostname:"");
+    ttyname(tty), hh ? "from" : "", hh ? TT.hostname : "");
 
-  spawn_shell(pwd->pw_shell);
+  // can't xexec here because name doesn't match argv[0]
+  snprintf(toybuf, sizeof(toybuf)-1, "-%s", basename_r(pwd->pw_shell));
+  toy_exec((char *[]){toybuf, 0});
+  execl(pwd->pw_shell, toybuf, NULL);
+  error_exit("Failed to spawn shell");
 }
index c9b22ab..077ce75 100644 (file)
@@ -37,7 +37,7 @@ GLOBALS(
   FILE *db;
 )
 
-int do_lspci(struct dirtree *new)
+static int do_lspci(struct dirtree *new)
 {
   char *p = toybuf, *vendor = toybuf+9, *device = toybuf+18,
        driver[256], *vbig = 0, *dbig = 0, **fields;
index c16585a..a82ff7c 100644 (file)
@@ -40,7 +40,6 @@ void nbd_client_main(void)
 {
   int sock = -1, nbd, flags;
   unsigned long timeout = 0;
-  struct addrinfo *addr, *p;
   char *host=toys.optargs[0], *port=toys.optargs[1], *device=toys.optargs[2];
   uint64_t devsize;
 
@@ -49,23 +48,10 @@ void nbd_client_main(void)
   nbd = xopen(device, O_RDWR);
   for (;;) {
     int temp;
-    struct addrinfo hints;
 
     // Find and connect to server
 
-    memset(&hints, 0, sizeof(hints));
-    hints.ai_family = PF_UNSPEC;
-    hints.ai_socktype = SOCK_STREAM;
-    if (getaddrinfo(host, port, &hints, &addr)) addr = 0;
-    for (p = addr; p; p = p->ai_next) {
-      sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
-      if (-1 != connect(sock, p->ai_addr, p->ai_addrlen)) break;
-      close(sock);
-    }
-    freeaddrinfo(addr);
-
-    if (!p) perror_exit("%s:%s", host, port);
-
+    sock = xconnect(host, port, AF_UNSPEC, SOCK_STREAM, 0, 0);
     temp = 1;
     setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &temp, sizeof(int));
 
index a135888..1e8f5e9 100644 (file)
@@ -2,7 +2,7 @@
  *
  * Copyright 2013 Elie De Brauwer <eliedebrauwer@gmail.com>
 
-USE_REBOOT(NEWTOY(reboot, "n", TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
+USE_REBOOT(NEWTOY(reboot, "fn", TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
 USE_REBOOT(OLDTOY(halt, reboot, TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
 USE_REBOOT(OLDTOY(poweroff, reboot, TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
 
@@ -10,10 +10,11 @@ config REBOOT
   bool "reboot"
   default y
   help
-    usage: reboot/halt/poweroff [-n]
+    usage: reboot/halt/poweroff [-fn]
 
     Restart, halt or powerdown the system.
 
+    -f Don't signal init
     -n Don't sync before stopping the system.
 */
 
@@ -23,9 +24,12 @@ config REBOOT
 
 void reboot_main(void)
 {
-  int types[] = {RB_AUTOBOOT, RB_HALT_SYSTEM, RB_POWER_OFF};
+  int types[] = {RB_AUTOBOOT, RB_HALT_SYSTEM, RB_POWER_OFF},
+      sigs[] = {SIGINT, SIGUSR1, SIGUSR2}, idx;
 
   if (!(toys.optflags & FLAG_n)) sync();
 
-  toys.exitval = reboot(types[stridx("hp", *toys.which->name)+1]);
+  idx = stridx("hp", *toys.which->name)+1;
+  if (toys.optflags & FLAG_f) toys.exitval = reboot(types[idx]);
+  else toys.exitval = kill(1, sigs[idx]);
 }
index b5720a3..4cf7214 100644 (file)
@@ -15,7 +15,7 @@ config REV
 
 #include "toys.h"
 
-void do_rev(int fd, char *name)
+static void do_rev(int fd, char *name)
 {
   char *c;
 
index 0861c70..acbae2b 100644 (file)
@@ -67,8 +67,13 @@ void switch_root_main(void)
   }
   TT.rootdev=st2.st_dev;
 
+  // trim any / characters from the init cmdline, as we want to test it with
+  // stat(), relative to newroot. *cmdline is also used below, but by that
+  // point we are in the chroot, so a relative path is still OK.
+  while (**cmdline == '/') (*cmdline)++;
+
   // init program must exist and be an executable file
-  if (stat("init", &st1) || !S_ISREG(st1.st_mode) || !(st1.st_mode&0100)) {
+  if (stat(*cmdline, &st1) || !S_ISREG(st1.st_mode) || !(st1.st_mode&0100)) {
     error_msg("bad init");
     goto panic;
   }
@@ -81,6 +86,24 @@ void switch_root_main(void)
   // Ok, enough safety checks: wipe root partition.
   dirtree_read("/", del_node);
 
+  // Fix the appearance of the mount table in the newroot chroot
+  if (mount(".", "/", NULL, MS_MOVE, NULL)) {
+    perror_msg("mount");
+    goto panic;
+  }
+
+  // Enter the new root before starting init
+  if (chroot(".")) {
+    perror_msg("chroot");
+    goto panic;
+  }
+
+  // Make sure cwd does not point outside of the chroot
+  if (chdir("/")) {
+    perror_msg("chdir");
+    goto panic;
+  }
+
   if (TT.console) {
     int i;
     for (i=0; i<3; i++) if (console != i) dup2(console, i);
index 538d1b0..d5f72fd 100644 (file)
@@ -15,7 +15,7 @@ config TAC
 
 #include "toys.h"
 
-void do_tac(int fd, char *name)
+static void do_tac(int fd, char *name)
 {
   struct arg_list *list = NULL;
   char *c;
index 2851923..4ade5f1 100644 (file)
@@ -120,7 +120,7 @@ void taskset_main(void)
   }
 }
 
-int do_nproc(struct dirtree *new)
+static int do_nproc(struct dirtree *new)
 {
   if (!new->parent) return DIRTREE_RECURSE;
   if (!strncmp(new->name, "cpu", 3) && isdigit(new->name[3])) TT.nproc++;
index c11e46b..9a38e45 100644 (file)
@@ -39,7 +39,7 @@ struct vmstat_proc {
 
 // All the elements of vmstat_proc are the same size, so we can populate it as
 // a big array, then read the elements back out by name
-void get_vmstat_proc(struct vmstat_proc *vmstat_proc)
+static void get_vmstat_proc(struct vmstat_proc *vmstat_proc)
 {
   char *vmstuff[] = { "/proc/stat", "cpu ", 0, 0, 0, 0, 0, 0,
     "intr ", "ctxt ", "procs_running ", "procs_blocked ", "/proc/meminfo",
index 3449104..366d3c5 100644 (file)
@@ -133,9 +133,9 @@ static void summary()
   //out to STDERR
   fprintf(stderr,"%llu+%llu records in\n%llu+%llu records out\n", st.in_full, st.in_part,
       st.out_full, st.out_part);
-  human_readable(toybuf, st.bytes);
+  human_readable(toybuf, st.bytes, HR_SPACE|HR_B);
   fprintf(stderr, "%llu bytes (%s) copied, ",st.bytes, toybuf);
-  human_readable(toybuf, st.bytes/seconds);
+  human_readable(toybuf, st.bytes/seconds, HR_SPACE|HR_B);
   fprintf(stderr, "%f s, %s/s\n", seconds, toybuf);
 }
 
index 529c1b9..d9e78ff 100644 (file)
@@ -348,6 +348,7 @@ static void halt_poweroff_reboot_handler(int sig_no)
       reboot_magic_no=RB_POWER_OFF;
       break;
     case SIGTERM:  
+    case SIGINT:
       error_msg("Requesting system reboot");
       reboot_magic_no=RB_AUTOBOOT;
       break;
@@ -415,7 +416,7 @@ static void pause_handler(int sig_no)
 
   errno_backup = errno;
   signal_backup = caught_signal;
-  signal(SIGCONT, catch_signal);
+  xsignal(SIGCONT, catch_signal);
 
   while(1) {
     if (caught_signal == SIGCONT) break;
@@ -460,10 +461,11 @@ void init_main(void)
   putenv("USER=root");
 
   inittab_parsing();  
-  signal(SIGUSR1, halt_poweroff_reboot_handler);//halt
-  signal(SIGUSR2, halt_poweroff_reboot_handler);//poweroff
-  signal(SIGTERM, halt_poweroff_reboot_handler);//reboot
-  signal(SIGQUIT, restart_init_handler);//restart init
+  xsignal(SIGUSR1, halt_poweroff_reboot_handler);//halt
+  xsignal(SIGUSR2, halt_poweroff_reboot_handler);//poweroff
+  xsignal(SIGTERM, halt_poweroff_reboot_handler);//reboot
+  xsignal(SIGINT, halt_poweroff_reboot_handler);//reboot
+  xsignal(SIGQUIT, restart_init_handler);//restart init
   memset(&sig_act, 0, sizeof(sig_act));
   sigfillset(&sig_act.sa_mask);
   sigdelset(&sig_act.sa_mask, SIGCONT);
@@ -471,7 +473,6 @@ void init_main(void)
   sigaction(SIGTSTP, &sig_act, NULL);
   memset(&sig_act, 0, sizeof(sig_act));
   sig_act.sa_handler = catch_signal;
-  sigaction(SIGINT, &sig_act, NULL);
   sigaction(SIGHUP, &sig_act, NULL);  
   run_action_from_list(SYSINIT);
   check_if_pending_signals();
index 0c49633..a13a53d 100644 (file)
@@ -35,7 +35,7 @@ config MDEV_CONF
 static void make_device(char *path)
 {
   char *device_name = NULL, *s, *temp;
-  int major, minor, type, len, fd;
+  int major = 0, minor = 0, type, len, fd;
   int mode = 0660;
   uid_t uid = 0;
   gid_t gid = 0;
@@ -46,24 +46,21 @@ static void make_device(char *path)
     temp = strrchr(path, '/');
     fd = open(path, O_RDONLY);
     *temp=0;
-    temp = toybuf;
-    len = read(fd, temp, 64);
+    len = read(fd, toybuf, 64);
     close(fd);
     if (len<1) return;
-    temp[len] = 0;
+    toybuf[len] = 0;
 
     // Determine device type, major and minor
 
     type = path[5]=='c' ? S_IFCHR : S_IFBLK;
-    major = minor = 0;
-    sscanf(temp, "%u:%u", &major, &minor);
+    sscanf(toybuf, "%u:%u", &major, &minor);
   } else {
     // if (!path), do hotplug
 
     if (!(temp = getenv("SUBSYSTEM")))
       return;
     type = strcmp(temp, "block") ? S_IFCHR : S_IFBLK;
-    major = minor = 0;
     if (!(temp = getenv("MAJOR")))
       return;
     sscanf(temp, "%u", &major);
@@ -74,11 +71,15 @@ static void make_device(char *path)
     device_name = getenv("DEVNAME");
     if (!path)
       return;
-    temp = toybuf;
   }
   if (!device_name)
     device_name = strrchr(path, '/') + 1;
 
+  // as in linux/drivers/base/core.c, device_get_devnode()
+  while ((temp = strchr(device_name, '!'))) {
+    *temp = '/';
+  }
+
   // If we have a config file, look up permissions for this device
 
   if (CFG_MDEV_CONF) {
@@ -185,22 +186,22 @@ found_device:
     }
   }
 
-  sprintf(temp, "/dev/%s", device_name);
+  sprintf(toybuf, "/dev/%s", device_name);
 
-  if (getenv("ACTION") && !strcmp(getenv("ACTION"), "remove")) {
-    unlink(temp);
+  if ((temp=getenv("ACTION")) && !strcmp(temp, "remove")) {
+    unlink(toybuf);
     return;
   }
 
   if (strchr(device_name, '/'))
-    mkpathat(AT_FDCWD, temp, 0, 2);
-  if (mknod(temp, mode | type, makedev(major, minor)) && errno != EEXIST)
-    perror_exit("mknod %s failed", temp);
+    mkpathat(AT_FDCWD, toybuf, 0, 2);
+  if (mknod(toybuf, mode | type, makedev(major, minor)) && errno != EEXIST)
+    perror_exit("mknod %s failed", toybuf);
 
  
-  if (type == S_IFBLK) close(open(temp, O_RDONLY)); // scan for partitions
+  if (type == S_IFBLK) close(open(toybuf, O_RDONLY)); // scan for partitions
 
-  if (CFG_MDEV_CONF) mode=chown(temp, uid, gid);
+  if (CFG_MDEV_CONF) mode=chown(toybuf, uid, gid);
 }
 
 static int callback(struct dirtree *node)
index 5431cb3..07c53fc 100644 (file)
@@ -65,14 +65,12 @@ struct module_s {
 static char *path2mod(char *file, char *mod)
 {
   int i;
-  char *from, *lslash;
+  char *from;
 
   if (!file) return NULL;
   if (!mod) mod = xmalloc(MODNAME_LEN);
        
-  lslash = strrchr(file, '/');
-  if (!lslash || (lslash == file && !lslash[1])) from = file;
-  else from = lslash + 1;
+  from = basename_r(file);
   
   for (i = 0; i < (MODNAME_LEN-1) && from[i] && from[i] != '.'; i++)
     mod[i] = (from[i] == '-') ? '_' : from[i];
@@ -277,7 +275,8 @@ static int config_action(struct dirtree *node)
       get_mod(tokens[1], 1)->flags |= MOD_BLACKLIST;
     else if (!strcmp(tokens[0], "install")) continue;
     else if (!strcmp(tokens[0], "remove")) continue;
-    else error_msg("Invalid option %s found in file %s", tokens[0], filename);
+    else if (toys.optflags & FLAG_q)
+      error_msg("Invalid option %s found in file %s", tokens[0], filename);
   }
   fclose(fc);
   free(filename);
@@ -381,7 +380,6 @@ static int ins_mod(char *modules, char *flags)
   }
   res = syscall(__NR_init_module, buf, len, toybuf);
   if (CFG_TOYBOX_FREE && buf != toybuf) free(buf);
-  if (res) perror_exit("failed to load %s ", toys.optargs[0]);
   return res;
 }
 
@@ -428,7 +426,7 @@ static int go_probe(struct module_s *m)
   int rc = 0, first = 1;
 
   if (!(m->flags & MOD_FNDDEPMOD)) {
-    if (!(toys.optflags & FLAG_s))
+    if (!(toys.optflags & FLAG_q))
       error_msg("module %s not found in modules.dep", m->name);
     return -ENOENT;
   }
index cb0f32c..29111d5 100644 (file)
@@ -312,7 +312,7 @@ void ps_main(void)
           if (j!=2) break;
         }
         if (i == ARRAY_LEN(typos)) error_exit("bad -o %.*s", end-type, type);
-        if (!field->title) strcpy(field->title, typos[field->which]);
+        if (!*field->title) strcpy(field->title, typos[field->which]);
         dlist_add_nomalloc((void *)&TT.fields, (void *)field);
       }
     }
index f113cbb..dc3487a 100644 (file)
@@ -48,39 +48,6 @@ GLOBALS(
 #define UF_ECHO     0x01
 #define UF_SGA      0x02
 
-/*
- * creates a socket of family INET/INET6 and protocol TCP and connects
- * it to HOST at PORT.
- * if successful then returns SOCK othrwise error
- */
-static int xconnect_inet_tcp(char *host, int port)
-{
-  int ret;
-  struct addrinfo *info, *rp;
-  char buf[32];
-
-  rp = xzalloc(sizeof(struct addrinfo));
-  rp->ai_family = AF_UNSPEC;
-  rp->ai_socktype = SOCK_STREAM;
-  rp->ai_protocol = IPPROTO_TCP;
-  sprintf(buf, "%d", port);
-
-  ret = getaddrinfo(host, buf, rp, &info);
-  if(ret || !info) perror_exit("BAD ADDRESS: can't find : %s ", host);
-  free(rp);
-
-  for (rp = info; rp; rp = rp->ai_next) 
-    if ( (rp->ai_family == AF_INET) || (rp->ai_family == AF_INET6)) break;
-
-  if (!rp) error_exit("Invalid IP %s", host);
-
-  ret = xsocket(rp->ai_family, SOCK_STREAM, IPPROTO_TCP);
-  if(connect(ret, rp->ai_addr, rp->ai_addrlen) == -1) perror_exit("connect");
-
-  freeaddrinfo(info);
-  return ret;
-}
-
 // sets terminal mode: LINE or CHARACTER based om internal stat.
 static char const es[] = "\r\nEscape character is ";
 static void set_mode(void)
@@ -135,7 +102,7 @@ static void handle_esc(void)
   char input;
 
   if(toys.signal && TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.raw_term);
-  write(1,"\r\nConsole escape. Commands are:\r\n\n"
+  xwrite(1,"\r\nConsole escape. Commands are:\r\n\n"
       " l  go to line mode\r\n"
       " c  go to character mode\r\n"
       " z  suspend telnet\r\n"
@@ -178,7 +145,7 @@ static void handle_esc(void)
   default: break;
   }
 
-  write(1, "continuing...\r\n", 15);
+  xwrite(1, "continuing...\r\n", 15);
   if (toys.signal && TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term);
 
 ret:
@@ -292,7 +259,7 @@ static int read_server(int len)
     }
   } while (TT.pbuff < len);
 
-  if (i) write(STDIN_FILENO, toybuf, i);
+  if (i) xwrite(STDIN_FILENO, toybuf, i);
   return 0;
 }
 
@@ -314,20 +281,19 @@ static void write_server(int len)
     if (*c == IAC) toybuf[i++] = *c; /* IAC -> IAC IAC */
     else if (*c == '\r') toybuf[i++] = '\0'; /* CR -> CR NUL */
   }
-  if(i) write(TT.sfd, toybuf, i);
+  if(i) xwrite(TT.sfd, toybuf, i);
 }
 
 void telnet_main(void)
 {
+  char *port = "23";
   int set = 1, len;
   struct pollfd pfds[2];
 
-  TT.port = 23; //TELNET_PORT
   TT.win_width = 80; //columns
   TT.win_height = 24; //rows
 
-  if(toys.optc == 2) TT.port = atoi(toys.optargs[1]);
-  if(TT.port <= 0 || TT.port > 65535) error_exit("bad PORT (1-65535)");
+  if (toys.optc == 2) port = toys.optargs[1];
 
   TT.ttype = getenv("TERM");
   if(!TT.ttype) TT.ttype = "";
@@ -340,7 +306,7 @@ void telnet_main(void)
   }
   terminal_size(&TT.win_width, &TT.win_height);
 
-  TT.sfd = xconnect_inet_tcp(toys.optargs[0], TT.port);
+  TT.sfd = xconnect(*toys.optargs, port, 0, SOCK_STREAM, IPPROTO_TCP, 0);
   setsockopt(TT.sfd, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(set));
   setsockopt(TT.sfd, SOL_SOCKET, SO_KEEPALIVE, &set, sizeof(set));
 
diff --git a/toys/pending/tftp.c b/toys/pending/tftp.c
new file mode 100644 (file)
index 0000000..60d5f17
--- /dev/null
@@ -0,0 +1,454 @@
+/* tftp.c - TFTP client.
+ *
+ * Copyright 2012 Madhur Verma <mad.flexi@gmail.com>
+ * Copyright 2015 Sameer Prakash Pradhan <sameer.p.pradhan@gmail.com>
+ *
+ * No Standard.
+
+USE_TFTP(NEWTOY(tftp, "<1b#<8>65464r:l:g|p|[!gp]", TOYFLAG_USR|TOYFLAG_BIN))
+
+config TFTP
+  bool "tftp"
+  default n
+  help
+    usage: tftp [OPTIONS] HOST [PORT]
+
+    Transfer file from/to tftp server.
+
+    -l FILE Local FILE
+    -r FILE Remote FILE
+    -g    Get file
+    -p    Put file
+    -b SIZE Transfer blocks of SIZE octets(8 <= SIZE <= 65464)
+*/
+#define FOR_tftp
+#include "toys.h"
+
+GLOBALS(
+  char *local_file;
+  char *remote_file;
+  long block_size;
+
+  struct sockaddr_storage inaddr;
+  int af;
+)
+
+#define TFTP_BLKSIZE    512
+#define TFTP_RETRIES    3
+#define TFTP_DATAHEADERSIZE 4
+#define TFTP_MAXPACKETSIZE  (TFTP_DATAHEADERSIZE + TFTP_BLKSIZE)
+#define TFTP_PACKETSIZE    TFTP_MAXPACKETSIZE
+#define TFTP_DATASIZE    (TFTP_PACKETSIZE-TFTP_DATAHEADERSIZE)
+#define TFTP_IOBUFSIZE    (TFTP_PACKETSIZE+8)
+
+#define TFTP_OP_RRQ      1  /* Read Request      RFC 1350, RFC 2090 */
+#define TFTP_OP_WRQ      2  /* Write Request     RFC 1350 */
+#define TFTP_OP_DATA    3  /* Data chunk      RFC 1350 */
+#define TFTP_OP_ACK      4  /* Acknowledgement     RFC 1350 */
+#define TFTP_OP_ERR      5  /* Error Message     RFC 1350 */
+#define TFTP_OP_OACK    6  /* Option acknowledgment RFC 2347 */
+
+#define TFTP_ER_ILLEGALOP  4  /* Illegal TFTP operation */
+#define TFTP_ER_UNKID    5  /* Unknown transfer ID */
+
+#define TFTP_ES_NOSUCHFILE  "File not found"
+#define TFTP_ES_ACCESS    "Access violation"
+#define TFTP_ES_FULL    "Disk full or allocation exceeded"
+#define TFTP_ES_ILLEGALOP  "Illegal TFTP operation"
+#define TFTP_ES_UNKID    "Unknown transfer ID"
+#define TFTP_ES_EXISTS    "File already exists"
+#define TFTP_ES_UNKUSER    "No such user"
+#define TFTP_ES_NEGOTIATE  "Terminate transfer due to option negotiation"
+
+// Initializes SERVER with ADDR and returns socket.
+static int init_tftp(struct sockaddr_storage *server)
+{
+  struct timeval to = { .tv_sec = 10, //Time out
+                        .tv_usec = 0 };
+  const int set = 1;
+  int port = 69, sd = xsocket(TT.af, SOCK_DGRAM, IPPROTO_UDP);
+
+  xsetsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (void *)&to, sizeof(struct timeval));
+  xsetsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (void *)&set, sizeof(set));
+
+  if(toys.optc == 2) port = atolx_range(toys.optargs[1], 1, 65535);
+  memset(server, 0, sizeof(struct sockaddr_storage));
+  if (TT.af == AF_INET6) {
+      ((struct sockaddr_in6 *)server)->sin6_family = AF_INET6;
+      ((struct sockaddr_in6 *)server)->sin6_addr =
+        ((struct sockaddr_in6 *)&TT.inaddr)->sin6_addr;
+      ((struct sockaddr_in6 *)server)->sin6_port = htons(port);
+  }
+  else {
+      ((struct sockaddr_in *)server)->sin_family = AF_INET;
+      ((struct sockaddr_in *)server)->sin_addr.s_addr =
+        ((struct sockaddr_in *)&TT.inaddr)->sin_addr.s_addr;
+      ((struct sockaddr_in *)server)->sin_port = htons(port);
+  }
+  return sd;
+}
+
+/*
+ * Makes a request packet in BUFFER with OPCODE and file PATH of MODE
+ * and returns length of packet.
+ */
+static int mkpkt_request(uint8_t *buffer, int opcode, char *path, int mode)
+{
+  buffer[0] = opcode >> 8;
+  buffer[1] = opcode & 0xff;
+  if(strlen(path) > TFTP_BLKSIZE) error_exit("path too long");
+  return sprintf((char*) &buffer[2], "%s%c%s", path, 0, 
+    (mode ? "octet" : "netascii")) + 3;
+}
+
+/*
+ * Makes an acknowledgement packet in BUFFER of BLOCNO
+ * and returns packet length.
+ */
+static int mkpkt_ack(uint8_t *buffer, uint16_t blockno)
+{
+  buffer[0] = TFTP_OP_ACK >> 8;
+  buffer[1] = TFTP_OP_ACK & 0xff;
+  buffer[2] = blockno >> 8;
+  buffer[3] = blockno & 0xff;
+  return 4;
+}
+
+/*
+ * Makes an error packet in BUFFER with ERRORCODE and ERRORMSG.
+ * and returns packet length.
+ */
+static int mkpkt_err(uint8_t *buffer, uint16_t errorcode, char *errormsg)
+{
+  buffer[0] = TFTP_OP_ERR >> 8;
+  buffer[1] = TFTP_OP_ERR & 0xff;
+  buffer[2] = errorcode >> 8;
+  buffer[3] = errorcode & 0xff;
+  strcpy((char*) &buffer[4], errormsg);
+  return strlen(errormsg) + 5;
+}
+
+/*
+ * Recieves data from server in BUFF with socket SD and updates FROM
+ * and returns read length.
+ */
+static ssize_t read_server(int sd, void *buf, size_t len,
+  struct sockaddr_storage *from)
+{
+  socklen_t alen;
+  ssize_t nb;
+  
+  for (;;) {
+    memset(buf, 0, len);
+    alen = sizeof(struct sockaddr_storage);
+    nb = recvfrom(sd, buf, len, 0, (struct sockaddr *) from, &alen);
+    if (nb < 0) {
+      if (errno == EAGAIN) {
+        perror_msg("server read timed out");
+        return nb;
+      }else if (errno != EINTR) {
+        perror_msg("server read failed");
+        return nb;
+      }
+    }else return nb;
+  }
+  return nb;
+}
+
+/*
+ * sends data to server TO from BUFF of length LEN through socket SD
+ * and returns successfully send bytes number.
+ */
+static ssize_t write_server(int sd, void *buf, size_t len,
+  struct sockaddr_storage *to)
+{
+  ssize_t nb;
+  
+  for (;;) {
+    nb = sendto(sd, buf, len, 0, (struct sockaddr *)to,
+            sizeof(struct sockaddr_storage));
+    if (nb < 0) {
+      if (errno != EINTR) {
+        perror_msg("server write failed");
+        return nb;
+      }
+    } else return nb;
+  }
+  return nb;
+}
+
+// checks packet for data and updates block no
+static inline int check_data( uint8_t *packet, uint16_t *opcode, 
+  uint16_t *blockno)
+{
+  *opcode = (uint16_t) packet[0] << 8 | (uint16_t) packet[1];
+  if (*opcode == TFTP_OP_DATA) {
+    *blockno = (uint16_t) packet[2] << 8 | (uint16_t) packet[3];
+    return 0;
+  }
+  return -1;
+}
+
+// Makes data packet through FD from file OFFSET in buffer PACKET of BLOCKNO
+static int mkpkt_data(int fd, off_t offset, uint8_t *packet, uint16_t blockno)
+{
+  off_t tmp;
+  int nbytesread;
+
+  packet[0] = TFTP_OP_DATA >> 8;
+  packet[1] = TFTP_OP_DATA & 0xff;
+  packet[2] = blockno >> 8;
+  packet[3] = blockno & 0xff;
+  tmp = lseek(fd, offset, SEEK_SET);
+  if (tmp == (off_t) -1) {
+    perror_msg("lseek failed");
+    return -1;
+  }
+  nbytesread = readall(fd, &packet[TFTP_DATAHEADERSIZE], TFTP_DATASIZE);
+  if (nbytesread < 0) return -1;
+  return nbytesread + TFTP_DATAHEADERSIZE;
+}
+
+// Receives ACK responses from server and updates blockno
+static int read_ack(int sd, uint8_t *packet, struct sockaddr_storage *server,
+  uint16_t *port, uint16_t *blockno)
+{
+  struct sockaddr_storage from;
+  ssize_t nbytes;
+  uint16_t opcode, rblockno;
+  int packetlen, retry;
+
+  for (retry = 0; retry < TFTP_RETRIES; retry++) {
+    for (;;) {
+      nbytes = read_server(sd, packet, TFTP_IOBUFSIZE, &from);
+      if (nbytes < 4) { // Ack headersize = 4
+        if (nbytes == 0) error_msg("Connection lost.");
+        else if (nbytes > 0) error_msg("Short packet: %d bytes", nbytes);
+        else error_msg("Server read ACK failure.");
+        break;
+      } else {
+        if (!*port) {
+          *port = ((struct sockaddr_in *)&from)->sin_port;
+          ((struct sockaddr_in *)server)->sin_port =
+                  ((struct sockaddr_in *)&from)->sin_port;
+        }
+        if (((struct sockaddr_in *)server)->sin_addr.s_addr !=
+                ((struct sockaddr_in *)&from)->sin_addr.s_addr) {
+          error_msg("Invalid address in DATA.");
+          continue;
+        }
+        if (*port != ((struct sockaddr_in *)server)->sin_port) {
+          error_msg("Invalid port in DATA.");
+          packetlen = mkpkt_err(packet, TFTP_ER_UNKID, TFTP_ES_UNKID);
+          (void) write_server(sd, packet, packetlen, server);
+          continue;
+        }
+        opcode = (uint16_t) packet[0] << 8 | (uint16_t) packet[1];
+        rblockno = (uint16_t) packet[2] << 8 | (uint16_t) packet[3];
+
+        if (opcode != TFTP_OP_ACK) {
+          error_msg("Bad opcode.");
+          if (opcode > 5) {
+            packetlen = mkpkt_err(packet, TFTP_ER_ILLEGALOP, TFTP_ES_ILLEGALOP);
+            (void) write_server(sd, packet, packetlen, server);
+          }
+          break;
+        }
+        if (blockno) *blockno = rblockno;
+        return 0;
+      }
+    }
+  }
+  error_msg("Timeout, Waiting for ACK.");
+  return -1;
+}
+
+// receives file from server.
+static int file_get(void)
+{
+  struct sockaddr_storage server, from;
+  uint8_t *packet;
+  uint16_t blockno = 0, opcode, rblockno = 0;
+  int len, sd, fd, retry, nbytesrecvd = 0, ndatabytes, ret, result = -1;
+
+  sd = init_tftp(&server);
+
+  packet = (uint8_t*) xzalloc(TFTP_IOBUFSIZE);
+  fd = xcreate(TT.local_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+
+  len = mkpkt_request(packet, TFTP_OP_RRQ, TT.remote_file, 1);
+  ret = write_server(sd, packet, len, &server);
+  if (ret != len){
+    unlink(TT.local_file);
+    goto errout_with_sd;
+  }
+  if (TT.af == AF_INET6) ((struct sockaddr_in6 *)&server)->sin6_port = 0;
+  else ((struct sockaddr_in *)&server)->sin_port = 0;
+
+  do {
+    blockno++;
+    for (retry = 0 ; retry < TFTP_RETRIES; retry++) {
+      nbytesrecvd = read_server(sd, packet, TFTP_IOBUFSIZE, &from);
+      if (nbytesrecvd > 0) {
+        if ( ((TT.af == AF_INET) &&
+                memcmp(&((struct sockaddr_in *)&server)->sin_addr,
+                &((struct sockaddr_in *)&from)->sin_addr,
+                sizeof(struct in_addr))) ||
+             ((TT.af == AF_INET6) &&
+                memcmp(&((struct sockaddr_in6 *)&server)->sin6_addr,
+                &((struct sockaddr_in6 *)&from)->sin6_addr,
+                sizeof(struct in6_addr)))) {
+          error_msg("Invalid address in DATA.");
+          retry--;
+          continue;
+        }
+        if ( ((TT.af == AF_INET) && ((struct sockaddr_in *)&server)->sin_port
+                && (((struct sockaddr_in *)&server)->sin_port !=
+                ((struct sockaddr_in *)&from)->sin_port)) ||
+             ((TT.af == AF_INET6) && ((struct sockaddr_in6 *)&server)->sin6_port
+                && (((struct sockaddr_in6 *)&server)->sin6_port !=
+                ((struct sockaddr_in6 *)&from)->sin6_port))) {
+          error_msg("Invalid port in DATA.");
+          len = mkpkt_err(packet, TFTP_ER_UNKID, TFTP_ES_UNKID);
+          ret = write_server(sd, packet, len, &from);
+          retry--;
+          continue;
+        }
+        if (nbytesrecvd < TFTP_DATAHEADERSIZE) {
+          error_msg("Tiny data packet ignored.");
+          continue;
+        }
+        if (check_data(packet, &opcode, &rblockno) != 0
+            || blockno != rblockno) {
+
+        if (opcode == TFTP_OP_ERR) {
+          char *message = "DATA Check failure.";
+            char *arr[] = {TFTP_ES_NOSUCHFILE, TFTP_ES_ACCESS,
+              TFTP_ES_FULL, TFTP_ES_ILLEGALOP,
+              TFTP_ES_UNKID, TFTP_ES_EXISTS,
+              TFTP_ES_UNKUSER, TFTP_ES_NEGOTIATE};
+            if (rblockno && (rblockno < 9)) message = arr[rblockno - 1];
+            error_msg(message);
+        }
+        if (opcode > 5) {
+          len = mkpkt_err(packet, TFTP_ER_ILLEGALOP, TFTP_ES_ILLEGALOP);
+          ret = write_server(sd, packet, len, &from);
+        }
+        continue;
+        }
+        if ((TT.af == AF_INET6) && !((struct sockaddr_in6 *)&server)->sin6_port)
+          ((struct sockaddr_in6 *)&server)->sin6_port =
+            ((struct sockaddr_in6 *)&from)->sin6_port;
+        else if ((TT.af == AF_INET) && !((struct sockaddr_in *)&server)->sin_port)
+          ((struct sockaddr_in *)&server)->sin_port =
+            ((struct sockaddr_in *)&from)->sin_port;
+        break;
+      }
+    }
+    if (retry == TFTP_RETRIES) {
+      error_msg("Retry limit exceeded.");
+      unlink(TT.local_file);
+      goto errout_with_sd;
+    }
+    ndatabytes = nbytesrecvd - TFTP_DATAHEADERSIZE;
+    if (writeall(fd, packet + TFTP_DATAHEADERSIZE, ndatabytes) < 0){
+      unlink(TT.local_file);
+      goto errout_with_sd;
+    }
+    len = mkpkt_ack(packet, blockno);
+    ret = write_server(sd, packet, len, &server);
+    if (ret != len){
+      unlink(TT.local_file);
+      goto errout_with_sd;
+    }
+  } while (ndatabytes >= TFTP_DATASIZE);
+
+  result = 0;
+
+errout_with_sd: xclose(sd);
+  free(packet);
+  return result;
+}
+
+// Sends file to server.
+int file_put(void)
+{
+  struct sockaddr_storage server;
+  uint8_t *packet;
+  off_t offset = 0;
+  uint16_t blockno = 1, rblockno, port = 0;
+  int packetlen, sd, fd, retry = 0, ret, result = -1;
+
+  sd = init_tftp(&server);
+  packet = (uint8_t*)xzalloc(TFTP_IOBUFSIZE);
+  fd = xopen(TT.local_file, O_RDONLY);
+
+  for (;;) {  //first loop for request send and confirmation from server.
+    packetlen = mkpkt_request(packet, TFTP_OP_WRQ, TT.remote_file, 1);
+    ret = write_server(sd, packet, packetlen, &server);
+    if (ret != packetlen) goto errout_with_sd;
+    if (read_ack(sd, packet, &server, &port, NULL) == 0) break;
+    if (++retry > TFTP_RETRIES) {
+      error_msg("Retry count exceeded.");
+      goto errout_with_sd;
+    }
+  }
+  for (;;) {  // loop for data sending and receving ack from server.
+    packetlen = mkpkt_data(fd, offset, packet, blockno);
+    if (packetlen < 0) goto errout_with_sd;
+
+    ret = write_server(sd, packet, packetlen, &server);
+    if (ret != packetlen) goto errout_with_sd;
+
+    if (read_ack(sd, packet, &server, &port, &rblockno) == 0) {
+      if (rblockno == blockno) {
+        if (packetlen < TFTP_PACKETSIZE) break;
+        blockno++;
+        offset += TFTP_DATASIZE;
+        retry = 0;
+        continue;
+      }
+    }
+    if (++retry > TFTP_RETRIES) {
+      error_msg("Retry count exceeded.");
+      goto errout_with_sd;
+    }
+  }
+  result = 0;
+
+errout_with_sd: close(sd);
+  free(packet);
+  return result;
+}
+
+void tftp_main(void)
+{
+  struct addrinfo *info, rp, *res=0;
+  int ret;
+
+  if (toys.optflags & FLAG_r) {
+    if (!(toys.optflags & FLAG_l)) {
+      char *slash = strrchr(TT.remote_file, '/');
+      TT.local_file = (slash) ? slash + 1 : TT.remote_file;
+    }
+  } else if (toys.optflags & FLAG_l) TT.remote_file = TT.local_file;
+  else error_exit("Please provide some files.");
+
+  memset(&rp, 0, sizeof(rp));
+  rp.ai_family = AF_UNSPEC;
+  rp.ai_socktype = SOCK_STREAM;
+  ret = getaddrinfo(toys.optargs[0], toys.optargs[1], &rp, &info);
+  if (!ret) {
+    for (res = info; res; res = res->ai_next)
+    if ( (res->ai_family == AF_INET) || (res->ai_family == AF_INET6)) break;
+  }
+  if (!res)
+    error_exit("bad address '%s' : %s", toys.optargs[0], gai_strerror(ret));
+  TT.af = info->ai_family;
+
+  memcpy((void *)&TT.inaddr, info->ai_addr, info->ai_addrlen);
+  freeaddrinfo(info);
+
+  if (toys.optflags & FLAG_g) file_get();
+  if (toys.optflags & FLAG_p) file_put();
+}
index bb476df..bd3afad 100644 (file)
@@ -64,14 +64,6 @@ static char *calstrings(char *buf, struct tm *tm)
   return buf;
 }
 
-void xcheckrange(long val, long low, long high)
-{
-  char *err = "%ld %s than %ld";
-
-  if (val < low) error_exit(err, val, "less", low);
-  if (val > high) error_exit(err, val, "greater", high);
-}
-
 // Worst case scenario toybuf usage: sizeof(struct tm) plus 21 bytes/line
 // plus 8 lines/month plus 12 months, comes to a bit over 2k of our 4k buffer.
 
@@ -86,12 +78,12 @@ void cal_main(void)
     buf += sizeof(struct tm);
 
     // Last argument is year, one before that (if any) is month.
-    xcheckrange(tm->tm_year = atol(toys.optargs[--toys.optc]),1,9999);
+    tm->tm_year = atolx_range(toys.optargs[--toys.optc], 1, 9999);
     tm->tm_year -= 1900;
     tm->tm_mday = 1;
     tm->tm_hour = 12;  // noon to avoid timezone weirdness
     if (toys.optc) {
-      xcheckrange(tm->tm_mon = atol(toys.optargs[--toys.optc]),1,12);
+      tm->tm_mon = atolx_range(toys.optargs[--toys.optc], 1, 12);
       tm->tm_mon--;
 
     // Print 12 months of the year
index 01134a7..3aae4a1 100644 (file)
@@ -57,7 +57,10 @@ static void do_cat(int fd, char *name)
 
   for(;;) {
     len = read(fd, toybuf, size);
-    if (len < 0) toys.exitval = EXIT_FAILURE;
+    if (len < 0) {
+      toys.exitval = EXIT_FAILURE;
+      perror_msg("%s", name);
+    }
     if (len < 1) break;
     if ((CFG_CAT_V || CFG_CATV) && (toys.optflags&~FLAG_u)) {
       for (i=0; i<len; i++) {
index 64369b6..4292439 100644 (file)
@@ -39,7 +39,7 @@ GLOBALS(
   char *mode;
 )
 
-int do_chmod(struct dirtree *try)
+static int do_chmod(struct dirtree *try)
 {
   mode_t mode;
 
index 2dae113..829da75 100644 (file)
@@ -28,7 +28,7 @@ GLOBALS(
 
 // This handles opening the file and
 
-void do_cmp(int fd, char *name)
+static void do_cmp(int fd, char *name)
 {
   int i, len1, len2, min_len, size = sizeof(toybuf)/2;
   long byte_no = 1, line_no = 1;
index d4c4524..a42de50 100644 (file)
@@ -7,22 +7,23 @@
  * Note: setting a 2 year date is 50 years back/forward from today,
  * not posix's hardwired magic dates.
 
-USE_DATE(NEWTOY(date, "d:s:r:u[!dr]", TOYFLAG_BIN))
+USE_DATE(NEWTOY(date, "d:D:r:u[!dr]", TOYFLAG_BIN))
 
 config DATE
   bool "date"
   default y
   help
-    usage: date [-u] [-r FILE] [-d DATE] [+DISPLAY_FORMAT] [-s SET_FORMAT] [SET]
+    usage: date [-u] [-r FILE] [-d DATE] [+DISPLAY_FORMAT] [-D SET_FORMAT] [SET]
 
     Set/get the current date/time. With no SET shows the current date.
 
     Default SET format is "MMDDhhmm[[CC]YY][.ss]", that's (2 digits each)
     month, day, hour (0-23), and minute. Optionally century, year, and second.
+    Also accepts "@UNIXTIME[.FRACTION]" as seconds since midnight Jan 1 1970.
 
     -d Show DATE instead of current time (convert date format)
+    -D +FORMAT for SET or -d (instead of MMDDhhmm[[CC]YY][.ss])
     -r Use modification time of FILE instead of current date
-    -s +FORMAT for SET or -d (instead of MMDDhhmm[[CC]YY][.ss])
     -u Use UTC instead of current timezone
 
     +FORMAT specifies display format string using these escapes:
@@ -49,15 +50,79 @@ GLOBALS(
   char *file;
   char *setfmt;
   char *showdate;
+
+  char *tz;
+  unsigned nano;
 )
 
-// Handle default posix date format: mmddhhmm[[cc]yy]
+// mktime(3) normalizes the struct tm fields, but date(1) shouldn't.
+static time_t chkmktime(struct tm *tm, const char *str, const char* fmt)
+{
+  struct tm tm0 = *tm;
+  struct tm tm1;
+  time_t t = mktime(tm);
+
+  if (t == -1 || !localtime_r(&t, &tm1) ||
+      tm0.tm_sec != tm1.tm_sec || tm0.tm_min != tm1.tm_min ||
+      tm0.tm_hour != tm1.tm_hour || tm0.tm_mday != tm1.tm_mday ||
+      tm0.tm_mon != tm1.tm_mon) {
+    int len;
+
+    strftime(toybuf, sizeof(toybuf), fmt, &tm0);
+    len = strlen(toybuf) + 1;
+    strftime(toybuf + len, sizeof(toybuf) - len, fmt, &tm1);
+    error_exit("bad date '%s'; %s != %s", str, toybuf, toybuf + len);
+  }
+  return t;
+}
+
+static void utzset(void)
+{
+  if (!(TT.tz = getenv("TZ"))) TT.tz = (char *)1;
+  setenv("TZ", "UTC", 1);
+  tzset();
+}
+
+static void utzreset(void)
+{
+  if (TT.tz) {
+    if (TT.tz != (char *)1) setenv("TZ", TT.tz, 1);
+    else unsetenv("TZ");
+    tzset();
+  }
+}
+
+// Handle default posix date format (mmddhhmm[[cc]yy]) or @UNIX[.FRAC]
 // returns 0 success, nonzero for error
-int parse_posixdate(char *str, struct tm *tm)
+static int parse_default(char *str, struct tm *tm)
 {
-  int len;
+  int len = 0;
+
+  // Parse @UNIXTIME[.FRACTION]
+  if (*str == '@') {
+    long long ll;
+    time_t tt;
+
+    // Collect seconds and nanoseconds
+    // Note: struct tm hasn't got a fractional seconds field, thus strptime()
+    // doesn't support it, so store nanoseconds out of band (in globals).
+    // tt and ll are separate because we can't guarantee time_t is 64 bit (yet).
+    sscanf(str, "@%lld%n", &ll, &len);
+    if (str[len]=='.') {
+      str += len+1;
+      for (len = 0; len<9; len++) {
+        TT.nano *= 10;
+        if (isdigit(str[len])) TT.nano += str[len]-'0';
+      }
+    }
+    if (str[len]) return 1;
+    tt = ll;
+    gmtime_r(&tt, tm);
+
+    return 0;
+  }
 
-  len = 0;
+  // Posix format
   sscanf(str, "%2u%2u%2u%2u%n", &tm->tm_mon, &tm->tm_mday, &tm->tm_hour,
     &tm->tm_min, &len);
   if (len != 8) return 1;
@@ -78,40 +143,38 @@ int parse_posixdate(char *str, struct tm *tm)
     // 2 digit years, next 50 years are "future", last 50 years are "past".
     // A "future" date in past is a century ahead.
     // A non-future date in the future is a century behind.
-    if ((r1 < r2) ? (r1 < year && year < r2) : (year < r1 || year > r2)) {
-      if (year < r1) year += 100;
-    } else if (year > r1) year -= 100;
+    if (len == 2) {
+      if ((r1 < r2) ? (r1 < year && year < r2) : (year < r1 || year > r2)) {
+        if (year < r1) year += 100;
+      } else if (year > r1) year -= 100;
+    }
     tm->tm_year = year + century;
   }
   if (*str == '.') {
     len = 0;
     sscanf(str, ".%u%n", &tm->tm_sec, &len);
     str += len;
-  }
+  } else tm->tm_sec = 0;
 
   return *str;
 }
 
 void date_main(void)
 {
-  char *setdate = *toys.optargs, *format_string = "%a %b %e %H:%M:%S %Z %Y",
-       *tz = 0;
+  char *setdate = *toys.optargs, *format_string = "%a %b %e %H:%M:%S %Z %Y";
   struct tm tm;
 
+  memset(&tm, 0, sizeof(struct tm));
+
   // We can't just pass a timezone to mktime because posix.
-  if (toys.optflags & FLAG_u) {
-    if (CFG_TOYBOX_FREE) tz = getenv("TZ");
-    setenv("TZ", "UTC", 1);
-    tzset();
-  }
+  if (toys.optflags & FLAG_u) utzset();
 
   if (TT.showdate) {
-    setdate = TT.showdate;
     if (TT.setfmt) {
       char *s = strptime(TT.showdate, TT.setfmt+(*TT.setfmt=='+'), &tm);
 
-      if (!s || *s) goto bad_date;
-    } else if (parse_posixdate(TT.showdate, &tm)) goto bad_date;
+      if (!s || *s) goto bad_showdate;
+    } else if (parse_default(TT.showdate, &tm)) goto bad_showdate;
   } else {
     time_t now;
 
@@ -125,7 +188,6 @@ void date_main(void)
     ((toys.optflags & FLAG_u) ? gmtime_r : localtime_r)(&now, &tm);
   }
 
-  setdate = *toys.optargs;
   // Fall through if no arguments
   if (!setdate);
   // Display the date?
@@ -137,39 +199,26 @@ void date_main(void)
   } else if (setdate) {
     struct timeval tv;
 
-    if (parse_posixdate(setdate, &tm)) goto bad_date;
+    if (parse_default(setdate, &tm)) error_exit("bad date '%s'", setdate);
 
     if (toys.optflags & FLAG_u) {
-      char *tz = CFG_TOYBOX_FREE ? getenv("TZ") : 0;
-
       // We can't just pass a timezone to mktime because posix.
-      setenv("TZ", "UTC", 1);
-      tzset();
-      tv.tv_sec = mktime(&tm);
-      if (CFG_TOYBOX_FREE) {
-        if (tz) setenv("TZ", tz, 1);
-        else unsetenv("TZ");
-        tzset();
-      }
-    } else tv.tv_sec = mktime(&tm);
-    if (tv.tv_sec == (time_t)-1) goto bad_date;
+      utzset();
+      tv.tv_sec = chkmktime(&tm, setdate, format_string);
+      utzreset();
+    } else tv.tv_sec = chkmktime(&tm, setdate, format_string);
 
-    tv.tv_usec = 0;
+    tv.tv_usec = TT.nano/1000;
     if (settimeofday(&tv, NULL) < 0) perror_msg("cannot set date");
   }
 
-  if (toys.optflags & FLAG_u) {
-    if (tz) setenv("TZ", tz, 1);
-    else unsetenv("TZ");
-    tzset();
-  }
-
+  utzreset();
   if (!strftime(toybuf, sizeof(toybuf), format_string, &tm))
     perror_exit("bad format '%s'", format_string);
   puts(toybuf);
 
   return;
 
-bad_date:
-  error_exit("bad date '%s'", setdate);
+bad_showdate:
+  error_exit("bad date '%s'", TT.showdate);
 }
index 4302997..77c7b6e 100644 (file)
@@ -57,7 +57,7 @@ static void print(long long size, struct dirtree *node)
   if (TT.maxdepth && TT.depth > TT.maxdepth) return;
 
   if (toys.optflags & FLAG_h) {
-    human_readable(toybuf, size);
+    human_readable(toybuf, size, 0);
     printf("%s", toybuf);
   } else {
     int bits = 10;
index a11a910..99cf5e2 100644 (file)
@@ -141,41 +141,6 @@ static void do_print(struct dirtree *new, char c)
   free(s);
 }
 
-char *strlower(char *s)
-{
-  char *try, *new;
-
-  if (!CFG_TOYBOX_I18N) {
-    try = new = xstrdup(s);
-    for (; *s; s++) *(new++) = tolower(*s);
-  } else {
-    // I can't guarantee the string _won't_ expand during reencoding, so...?
-    try = new = xmalloc(strlen(s)*2+1);
-
-    while (*s) {
-      wchar_t c;
-      int len = mbrtowc(&c, s, MB_CUR_MAX, 0);
-
-      if (len < 1) *(new++) = *(s++);
-      else {
-        s += len;
-        // squash title case too
-        c = towlower(c);
-
-        // if we had a valid utf8 sequence, convert it to lower case, and can't
-        // encode back to utf8, something is wrong with your libc. But just
-        // in case somebody finds an exploit...
-        len = wcrtomb(new, c, 0);
-        if (len < 1) error_exit("bad utf8 %x", (int)c);
-        new += len;
-      }
-    }
-    *new = 0;
-  }
-
-  return try;
-}
-
 // Call this with 0 for first pass argument parsing and syntax checking (which
 // populates argdata). Later commands traverse argdata (in order) when they
 // need "do once" results.
index aa43072..01610bc 100644 (file)
@@ -82,7 +82,7 @@ static void showid(char *header, unsigned u, char *s)
   printf("%s%u(%s)", header, u, s);
 }
 
-void do_id(char *username)
+static void do_id(char *username)
 {
   int flags, i, ngroups;
   struct passwd *pw;
index 44915fa..f951198 100644 (file)
@@ -422,6 +422,13 @@ static void listfiles(int dirfd, struct dirtree *indir)
       mode_to_string(mode, perm);
       printf("%s% *ld", perm, totals[2]+1, (long)st->st_nlink);
 
+      // print user
+      if (!(flags&FLAG_g)) {
+        if (flags&FLAG_n) sprintf(ss = thyme, "%u", (unsigned)st->st_uid);
+        else strwidth(ss = getusername(st->st_uid));
+        printf(" %*s", (int)totals[3], ss);
+      }
+
       // print group
       if (!(flags&FLAG_o)) {
         if (flags&FLAG_n) sprintf(ss = thyme, "%u", (unsigned)st->st_gid);
@@ -429,12 +436,6 @@ static void listfiles(int dirfd, struct dirtree *indir)
         printf(" %*s", (int)totals[4], ss);
       }
 
-      if (!(flags&FLAG_g)) {
-        if (flags&FLAG_n) sprintf(ss = thyme, "%u", (unsigned)st->st_uid);
-        else strwidth(ss = getusername(st->st_uid));
-        printf(" %*s", (int)totals[3], ss);
-      }
-
       if (flags & FLAG_Z)
         printf(" %*s", -(int)totals[7], (char *)sort[next]->extra);
 
index c7e7b92..60c0a52 100644 (file)
@@ -40,7 +40,7 @@ GLOBALS(
   long lcount;
 )
 
-void do_nl(int fd, char *name)
+static void do_nl(int fd, char *name)
 {
   FILE *f = xfdopen(fd, "r");
   int w = TT.w, slen = strlen(TT.s);
index aabf931..075a414 100644 (file)
@@ -35,7 +35,7 @@ GLOBALS(
   char *outfile;
 )
 
-void do_split(int infd, char *in)
+static void do_split(int infd, char *in)
 {
   unsigned long bytesleft, linesleft, filenum, len, pos;
   int outfd = -1;
index a872cf6..e87ccde 100644 (file)
@@ -29,7 +29,7 @@ GLOBALS(
   long num;
 )
 
-void do_strings(int fd, char *filename)
+static void do_strings(int fd, char *filename)
 {
   int nread, i, wlen = TT.num, count = 0;
   off_t offset = 0;
index 0059dac..56396d5 100644 (file)
@@ -32,6 +32,7 @@
     <li><a href="code.html">Source walkthrough</a></li>
     <li><a href="http://lists.landley.net/listinfo.cgi/toybox-landley.net">Mailing List</a> (<a href=http://news.gmane.org/gmane.linux.toybox>backup archive</a>)</li>
     <li>IRC #toybox on freenode.net</li>
+    <li><a href=https://github.com/landley/toybox/commits/master.atom>Commit RSS feed</a></li>
     <li><a href="/notes.html">Maintainer's Blog</a></li>
     <li><a href=cleanup.html>Cleanup</a></li>
     <li><a href=http://www.ohloh.net/p/toybox-landley>Statistics</a></li>
index 084d753..0c5da77 100755 (executable)
@@ -8,7 +8,245 @@ a development environment. See the links on the left for details.</p>
 
 <h2>News</h2>
 
-<hr><b>April 5, 2015</b>
+<a name="23-07-2015" /><a href="#23-07-2015"><hr><h2><b>July 23, 2015</b></h2></a>
+<p>I recreated the <a href=downloads/toybox-0.6.0.tar.gz>0.6.0 source tarball</a>
+(new sha1sum 08fb1c23f520c25a15f262a8a95ea5b676a98d54)
+because I forgot to add --prefix to the git archive command when I updated
+my release script from mercurial, so the files weren't in an enclosing
+directory. (Ooops.)</p>
+
+<a name="19-07-2015" /><a href="#19-07-2015"><hr><h2><b>July 19, 2015</b></h2></a>
+<blockquote><p>
+The reason why it was published in the form of a micro sub meson electronic
+component is that if it were printed in normal book form, an interstellar
+hitchhiker would require several inconveniently large buildings to carry it
+around in." - The Hitchhiker's Guide to the Galaxy </p></blockquote>
+
+<p><a href=downloads/toybox-0.6.0.tar.gz>Toybox 0.6.0</a>
+(<a href=https://github.com/landley/toybox/releases/tag/0.6.0>git commit</a>)
+is out. (Yes, git. See the <a href=#05-04-2015>previous news entry</a>.)</p>
+
+<p>Sorry for the unusually long gap between releases. Since last release Ye
+Olde Project Maintainer traveled to japan twice and had two more "once
+a century" floods at home. (Probably a coincidence.) Still catching up.</p>
+
+<h3><b>CELF/ELC talk and Wikipedia[citation needed] article</b></h3>
+
+<p>I gave another State Of The Toybox talk
+(<a href=https://www.youtube.com/watch?v=04XwAbtPmAg>video</a>
+<a href=http://landley.net/talks/celf-2015.txt>outline</a>), in which I
+repeat my <a href=http://landley.net/notes-2013.html#07-11-2013>perennial</a>
+<a href=https://twitter.com/landley/status/557309224535851009>complaint</a>
+that Wikipedia[citation needed]
+<a href=http://en.wikipedia.org/wiki/Toybox>still</a>
+<a href=https://en.wikipedia.org/wiki/BusyBox#Controversy_over_Toybox>says</a>
+toybox was relicensed before its hiatus, when relicensing was why
+the hiatus ended.</p>
+
+<p>Since Wikipedia[citation needed] seems unable to do the
+<a href=#15-11-2011>most</a>
+<a href=http://landley.net/hg/toybox/log/tip/LICENSE>basic</a>
+<a href=http://landley.net/notes-2011.html#13-11-2011>research</a> on
+this point, and has stuck to an incorrect sequence of events for years,
+I've been gradually escalating my attempts to correct them. Toybox
+came out of mothballs in November 2011 <b>because</b> it could be
+relicensed. That's what opened up a new niche busybox wasn't already
+filling with a 10 year headstart.</p>
+
+<a name="asterisk_back" />
+<p>The article has plenty of smaller issues<a href=#asterisk>*</a>, but
+given that I gave an entire talk at Ohio LinuxFest in 2013
+(<a href=http://landley.net/talks/ohio-2013.txt>outline</a>,
+<a href=https://archive.org/download/OhioLinuxfest2013/24-Rob_Landley-The_Rise_and_Fall_of_Copyleft.mp3>audio</a>) on why I switched away from GPL for
+my projects, that one bugs me.</p>
+
+<h3><b>New stuff this release</b></h3>
+
+<p>There's a new android menu in menuconfig, and rather a lot of Linux
+Security Module support (Smack for Tizen from Xavier Roche and José Bollo,
+and SELinux for Android from Elliott Hughes; see
+the Security Blanket menu under global settings in menuconfig) has
+trickled in, although there's still more to come.</p>
+
+<p><b>New commands:</b> Added reset, nproc, ionice, and iorenice.
+Elliott Hughes contributed xxd, runcon,
+restorecon, load_policy, getenforce, setenforce, getprop, and setprop.
+Promoted shred, nsenter, and hwclock.</p>
+
+<p>You can once again build catv now the flag infrastructure's been updated to
+let it coexist with cat -v.
+And on a long plane flight I wrote
+hexedit, an interactive hex editor that implements the start of
+cursor control infrastructure (for eventual use by less and vi and shell
+command history and so on).</p>
+
+<p><b>New options:</b> Added sed -E as a BSD-compatible synonym for -r.
+Upgraded oneit with -r (restart), -3 (send exiting PID values to child),
+and signal handling. Added -v option to timeout, -m to mknod, -u to shred,
+-t to dmesg, and -123 to head and tail. Added implicit "." to grep -r without
+any files to work on. Hyejin Kim requested prefix support for truncate -s.
+Greg Hackman added -inum to find.
+Jan Cybulski added the smack side of ls -Z support. Various patches also
+added -Z to mkdir, mknod, and mkfifo.
+Basic cp --preserve support went in, but not yet the xattr/LSM parts.</p>
+
+<p>The toybox command now has a --version option,
+which uses "git describe" if available.</p>
+
+<p><b>Build infrastructure:</b>
+The "make change" target now saves the output of each failed standalone
+command build in a .bad file, and "make defconfig" is quieter now.</p>
+
+<p>Paul Barker submitted a large patch changing command install paths so
+"toybox can be installed alongside busybox without confusing
+update-alternatives". (There's some argument over
+what the right paths should be, and I'm waiting for
+people to tell me what else needs fixing because I have no idea. I've
+been symlinking /bin to /usr/bin since 2002
+<a href=http://landley.net/writing/hackermonthly-issue022-pg33.pdf>for
+historical reasons</a>.)</p>
+
+<p><b>Docs:</b> The repository link now goes to github, with another link
+to the commit rss feed.</p>
+
+<p>Elliott Hughes updated the Android section of the roadmap
+(and he would know). Redid bits of scripts/mkstatus.py to make updating
+status.html easier, and the README is larger.</p>
+
+<p>More description of option parsing in code.html, which now describes the
+FLAG_x macros, switching flag macro sets with FOR_newcommand, how
+configuration zeroes flag macros and using FORCE_FLAGS to suppress the
+zeroing of options shared between commands. Also added description of ";"
+to make --longopts take an optional =value part, and more about TOYBOX_DEBUG
+to check NEWTOY() option strings (otherwise a bad option string makes
+lib/args.c obviously segfault, but doesn't explain why).</p>
+
+<p>Added a "Why 0BSD?" section to license.html when submitting zero clause bsd
+to SPDX (according to the pending license spreadsheet, it's been approved for
+SPDX 2.2).</p>
+
+<p>The old list of commands needing cleanup but not in pending was
+removed from toys/pending/README and instead the issues were added
+as TODO comments in the individual commands.</p>
+
+<p><b>Bugfixes:</b>
+Fixed mount -a segfaulting without -O (reported by Janus Troelsen),
+and made it try a "become rw" ioctl() on the block device before falling
+back to mounting read only (because Android expects that).
+Fixed printf -- and printf ---. Lots of tweaks to ls -l spacing with
+different options. Make touch -d and -t actually set time when you don't
+specify nanoseconds.
+Fixed a subtle bug where recursive calls (toybox commands that run other
+toybox commands) weren't resetting all their state. (This manifested as
+a "no }" error from "find | xargs sed", but could cause other problems.)
+And David Halls reported another sed bug trying to compile libiconv (which
+left extra \ at the start of lines in a generated shell script, breaking
+the build). Output an error message for "cat /mnt".</p>
+
+<p>Kylie McClain reported that mktemp broke when $TMPDIR was set to an empty
+string (which is not the same as unset), that install/find didn't support
+numeric uid/gids, and that sort -z affects both input and output.
+Isabella Parakiss fixed a printf.c bug.
+David Halls fixed bugs in install -D and find -exec. Samuel Holland
+fixed unshare -r. Hyejin Kim fixed makedevs with a count of 1, fold -w
+range checking, an error path in scripts/mkflags.c, added -i to dhcpd,
+and stopped su from prompting the root user for the new user's password.
+Jan Cybulski spotted wrong indentation when combining ls -s and -i with -C and
+-x. José Bollo fixed stat %G. Sameer Pradhan fixed a bug in mkfifo -Z.</p>
+
+<p>Elliott Hughes asked for a default SIGPIPE handler to disable
+the signal handler bionic's dynamic loader installs (yes really). Still not
+100% sure what the correct behavior is there. (Posix is
+(<a href=http://permalink.gmane.org/gmane.comp.standards.posix.austin.general/10915>actively unhelpful</a>, but at least they're taking
+<a href=http://austingroupbugs.net/view.php?id=789#c1976>years to
+make up their mind</a>. Elliott also sent patches to fix a typo in
+useradd.test, add missing arguments to error_exit() calls and clean up
+printf() format strings, fix an off by one error in human_readable(),
+fix dmesg -c error reporting, fix a segfault in comma_scan where the option
+was the last item in optlist (triggered by mount -o ro,remount), fix
+hwclock -w, made ifconfig print lowercase MAC addresses (it was bothering
+him), and make terminal_size() read the right environment variable
+(LINES, not ROWS). And he suggested the test suite notice high command exit
+values (corresponding to segfault or other signals).</p>
+
+<p>People are apparently using toys/pending commands, despite the police tape
+and flashing lights, so added louder warnings to toys/pending/README.
+Elliott Hughes fixed various problems with tar, dd, more, and top.
+Hyejin Kim cleaned up syslogd and dumpleases. Isaac Dunham added hotplug
+support to mdev. Yeongdeok Suh added RFC-3315 ipv6 support to dhcpd.</p>
+
+<p>I rewrote ps.c from scratch (in pending), but it's not ready for real use
+yet.</p>
+
+<p><b>Portability:</b>
+On the portability front Bernhard Rosenkranzer fixed a problem where the
+menuconfig code wouldn't compile in C99 mode. (This led to me documenting
+the craptacular nature of kconfig in a README, and the plan to replace it
+sometime before 1.0.) Some extra flags to shut up overzealous llvm warnings
+were added (and have to be probed for because gcc complains about
+arguments it doesn't recognize even when they switch stuff _off_ using
+a standard syntax). Don't depend on malloc(0) to return non-null in ls.
+David Halls fixed some mac/ios portability issues,
+implying somebody's built at least part of toybox on a mac.</p>
+
+<p>Added basename_r() to lib/lib.c because the posix semantics for basename()
+are stupid but what the gnu guys did to it was appalling.
+Turns out bionic already had a basename_r(), but posix still doesn't.
+Fixed it up in portability.h, but this
+could break more stuff in future. (Correct fix is to lobby posix to add it,
+which would probably take about 15 years...)</p>
+
+<p><b>Infrastructure:</b>
+The build now checks $LDFLAGS for linker-only flags, and allows the strip
+command to fail (binflt toolchains provide a strip that doesn't work).
+Since time.c uses floating point, added TOYBOX_FLOAT dependency in config.</p>
+
+<p>There's a lib/lsm.h defining varous inline functions for linux
+security modules stuff, if (lsm_enabled()) should turn into a compile-time
+constant 0 and let code drop out when TOYBOX_LSM_NONE selected, but
+testing against CFG_TOYBOX_LSM_NONE or derived symbols is still useful
+becuase when it _is_ enabled the probe turns into a system call you
+don't want to repeat too much.</p>
+
+<p>Switched a bunch of commands from signal() to xsignal(). Factored out
+xgetgrnamid() and xgetpwnamid() into xwrap.c. Make time.c depend on
+TOYBOX_FLOAT (since it always uses float so shouldn't be available on
+build targets without even software float). Added readfileat() to lib/lib.c.</p>
+
+<p>The dirtree infrastructure now passes in full flags for the old symlink
+field, and the new DIRTREE_SHUTUP flag disables warnings if a file vanishes
+out from under you during traverse. New dirtree_start() wrapper to
+create dirtree root with only two arguments.</p>
+
+<p>The not-curses infrastructure introduced by hexedit mostly moved to
+lib/interestingtimes.c.</p>
+
+<a name="asterisk" />
+<a href="#asterisk_back" />Asterisk:</a> such when
+Tim contacted me (my blog says a couple days before nov 13, 2011, I.E.
+11/11/11 not some specific day 2 months later) to ask if I wanted to work
+on a new project he was proposing called
+<a href=http://www.elinux.org/Busybox_replacement_project>BentoBox</a>
+(because I used to do busybox, he'd forgotten toybox existed
+until I brought it up). And don't ask me what "focuses not on compatibility
+with its GNU counterparts" means when CP_MORE adds 7 non-posix options
+and toys/other has 84 commands in neither posix nor LSB. I think they're
+struggling to explain the difference having dismissed "licensing" as being
+the reason it started up again after a long hiatus? The reason I don't think
+GNU is special is there are a half-dozen other independent
+implementations of the same unix command tools out there (AT&amp;T,
+BSD, Coherent, Minix, plan 9, busybox, toybox, and several more analyzed in
+the <a href=roadmap.html>roadmap</a>, and that's ignoring the implementations
+written for DOS or in assembly over the years). But I do care what
+Linux From Scratch expects, and if it's
+<a href=http://archive.linuxfromscratch.org/lfs-museum/7.6/LFS-BOOK-7.6-NOCHUNKS.html#ch-tools-gcc-pass1>calling mv -v</a>
+then I impelement mv -v
+even if <a href=http://landley.net/toybox/roadmap.html>posix hasn't got
+it</a>. And I don't know why "gnu counterparts" would describe this when
+util-linux isn't a gnu package, nor are info-zip, e2fsprogs, kmod, less,
+procps, shadow, sysklogd, vim, zlib, sudo, dhcpcd...</p>
+
+<a name="05-04-2015" /><a href="#05-04-2015"><hr><h2><b>April 5, 2015</b></h2></a>
 <p>Since <a href=https://android.googlesource.com/platform/external/toybox/>android</a> and
 <a href=https://git.tizen.org/cgit/platform/upstream/toybox.git>tizen</a>
 and <a href=https://github.com/kraj/meta-musl/tree/master/recipes-core/toybox>openembedded</a>
@@ -18,7 +256,7 @@ than the mercurial repository, I bit the bullet and switched the project's repo
 <a href=https://github.com/landley/toybox>to git</a>. Georgi's
 <a href=https://github.com/gfto/toybox>mirror</a> is now pulling from that.</p>
 
-<hr><b>February 25, 2015</b>
+<a name="25-02-2015" /><a href="#25-02-2015"><hr><h2><b>February 25, 2015</b></h2></a>
 <blockquote><p>"A common mistake that people make when trying to design
 something completely foolproof is to underestimate the ingenuity of
 complete fools."</p><p>- The Hitchhiker's Guide to the Galaxy.</p></blockquote>
@@ -138,7 +376,7 @@ supported, so stop using it.</p>
 <p>Fixed toy_exec() to detect when argc is in optargs, so we don't
 need a separate xexec_optargs().</p>
 
-<hr><b>February 18, 2015</b>
+<a name="18-02-2015" /><a href="#18-02-2015"><hr><h2><b>February 18, 2015</b></h2></a>
 <p>Dreamhost continues to be unable to make mailing list archives work, so
 here's <a href=http://www.mail-archive.com/toybox@lists.landley.net/>another
 list archive</a> with a less awkward interface than gmane.</p>
@@ -155,7 +393,7 @@ web archive, from Dec 15-Jan 3, and then another hole from Jan 16-Feb 18.
 The relevant messages are in both of the other archives. Here's hoping
 the chronic archive constipation problem won't happen a sixth time.</p>
 
-<hr><b>December 30, 2014</b>
+<a name="30-12-2014" /><a href="#30-12-2014"><hr><h2><b>December 30, 2014</b></h2></a>
 <p>Due to Dreamhost's <a href=http://landley.net/dreamhost.txt>ongoing</a>
 <a href=http://landley.net/dreamhost2.txt>inability</a> to make mailman
 work reliably, I've added a link to a backup web archive at
@@ -167,7 +405,7 @@ on the left.</p>
 
 <p>Update (January 27, 2015): they're <a href=https://twitter.com/landley/status/558428839462703104>still working on it</a>.</p>
 
-<hr><b>November 19, 2014</b>
+<a name="19-11-2014" /><a href="#19-11-2014"><hr><h2><b>November 19, 2014</b></h2></a>
 
 <blockquote><p>"This time it was right, it would work, and no one would have to get nailed to anything." - The Hitchhiker's Guide to the Galaxy.</p></blockquote>
 
@@ -246,7 +484,7 @@ close each supplied filehandle itself.)</p>
 <p>The printf-style escape parsing ("\n" and friends) got factored out into
 a new unescape() function.</p>
 
-<hr><b>October 2, 2014</b>
+<a name="02-10-2014" /><a href="#02-10-2014"><hr><h2><b>October 2, 2014</b></h2></a>
 <blockquote><p>"There is an art, it says, or rather, a knack to flying.
 The knack lies in learning how to throw yourself at the ground and miss...
 Clearly, it is this second part, the missing, which presents the
@@ -397,7 +635,7 @@ sort on the host broke in non-C locales.</p>
 <p>Divya Kothari submitted tests for chmod, link, tar, bzcat, xzcat, zcat,
 and hostname. (And more, but that's all that's merged so far.)</p>
 
-<hr><b>July 7, 2014</b>
+<a name="07-07-2014" /><a href="#07-07-2014"><hr><h2><b>July 7, 2014</b></h2></a>
 <blockquote><p>"This planet has - or rather had - a problem, which was this:
 most of the people living on it were unhappy for pretty much of the time. Many
 solutions were suggested for this problem, but most of these were largely
@@ -519,7 +757,7 @@ bytes). The build infrastructure now notices duplicate commands (so if you
 cp toys/pending/command.c toys/other/command.c and forget to delete the
 first one, the build break is now more informative).</p>
 
-<hr><b>April 20, 2014</b>
+<a name="20-04-2014" /><a href="#20-04-2014"><hr><h2><b>April 20, 2014</b></h2></a>
 <blockquote><p>And to this end they built themselves a stupendous supercomputer
 which was so amazingly intelligent that even before the data banks
 had been connected up it had started from "I think therefore I am" and got as
@@ -605,7 +843,7 @@ of different kinds of simplicity, and why comments aren't a substitute for
 good code. The README no longer trails off into obvious unfinished confusion
 at the end. Each page on the website should now have its own title.</p>
 
-<hr><b>November 18, 2013</b>
+<a name="18-11-2013" /><a href="#18-11-2013"><hr><h2><b>November 18, 2013</b></h2></a>
 <blockquote><p>"Space," it says, "is big. Really big. You just won't believe how vastly, hugely, mindbogglingly big it is. I mean, you may think it's a long way down the street to the chemist's, but that's just peanuts to space." -
 The Hitchhiker's Guide to the Galaxy.</p></blockquote>
 
@@ -642,7 +880,7 @@ The "toynet.h" file got folded into toys.h since musl supports it and
 micromanging uClibc options isn't very interesting anymore. The test suite
 now uses scripts/single.sh when testing a single command.</p>
 
-<hr><b>September 17, 2013</b>
+<a name="17-09-2013" /><a href="#17-09-2013"><hr><h2><b>September 17, 2013</b></h2></a>
 <blockquote><p>"Think of a number," said the computer, "any number."
 Arthur told the computer the telephone number of King's Cross railway
 station passenger inquiries, on the grounds that it must have some function,
@@ -771,13 +1009,13 @@ delete sub and didn't exit with an error either. Neither was correct, rm
 should now be fixed.</p>
 
 <p>
-<hr><b>July 26, 2013</b>
+<a name="26-07-2013" /><a href="#26-07-2013"><hr><h2><b>July 26, 2013</b></h2></a>
 <p>Georgi Chorbadzhiyski maintains a <a href=https://github.com/gfto/toybox>git
 mirror</a> of the repository on github, automatically updated from the
 mercurial every 6 hours. The mirror is read only, but you can generate patches
 against it and post them to the list.</p>
 
-<hr><b>July 2, 2013</b>
+<a name="02-07-2013" /><a href="#02-07-2013"><hr><h2><b>July 2, 2013</b></h2></a>
 <blockquote><p>"Time is an illusion. Lunchtime doubly so." "Very deep. You
 should send that in to the Reader's Digest. They've got a page for people
 like you." -
@@ -856,7 +1094,7 @@ the GPL but "all copies must include this magic text blob" somehow don't?</p>
 or less public domain with a liability disclaimer, but we're still calling it
 BSD (sometimes "0 clause BSD") to avoid explaining.</p>
 
-<hr><b>March 21, 2013</b>
+<a name="21-03-2013" /><a href="#21-03-2013"><hr><h2><b>March 21, 2013</b></h2></a>
 <p>Video of my ELC talk
 "<a href=http://youtu.be/SGmtP5Lg_t0>Why is Toybox?</a>"
 is up on youtube. Related materials include the
@@ -891,7 +1129,7 @@ the ads, it's The Linux Foundation.)</p>
 </span>
 
 
-<hr><b>March 14, 2013</b>
+<a name="14-03-2013" /><a href="#14-03-2013"><hr><h2><b>March 14, 2013</b></h2></a>
 <blockquote><p>"Ford, you're turning into a penguin. Stop it." -
 The Hitchhiker's Guide to the Galaxy.</p></blockquote>
 
@@ -914,7 +1152,7 @@ should at least compile (although defconfig is still what's useful).</p>
 <p>Significant roadmap updates, checking several other multicall binaries
 (klibc, sash, sbase, s6...) to see what commands they include.</p>
 
-<hr><b>January 18, 2013</b>
+<a name="18-01-2013" /><a href="#18-01-2013"><hr><h2><b>January 18, 2013</b></h2></a>
 <blockquote><p>This must be Thursday. I never could get the hang of Thursdays. - The Hitchhiker's Guide to the Galaxy.</p></blockquote>
 
 <p><a href=downloads/toybox-0.4.3.tar.bz2>Toybox 0.4.3</a> is based on
@@ -955,7 +1193,7 @@ it now correctly detects "/trailing/slash/" which the previous code didn't.</p>
 disabled compiler optimization, so the binary size bloated a bit. It's back
 to -Os by default now.</p>
 
-<hr><b>December 15, 2012</b>
+<a name="15-12-2012" /><a href="#15-12-2012"><hr><h2><b>December 15, 2012</b></h2></a>
 <blockquote><p>"The major difference between a thing that might go wrong and a
 thing that cannot possibly go wrong is that when a thing that cannot possibly
 go wrong goes wrong it usually turns out to be impossible to get at or repair."
@@ -1006,7 +1244,7 @@ to do it Posix's way, which is more brittle and needs extra security checks,
 but am waiting for somebody to complain first. The default "ulimit -n" is 1024
 filehandles, so drilling down over 1000 nested subdirectories).</p>
 
-<hr><b>November 13, 2012</b>
+<a name="13-11-2012" /><a href="#13-11-2012"><hr><h2><b>November 13, 2012</b></h2></a>
 <blockquote><p>"Rule Six: The winning team shall be the first team that wins."
 - The Hitchhiker's Guide to the Galaxy.</p></blockquote>
 
@@ -1070,7 +1308,7 @@ a separate <a href=oldnews.html>oldnews</a> page.</p>
 back now.</p>
 </span>
 
-<hr><b>July 23, 2012</b>
+<a name="23-07-2012" /><a href="#23-07-2012"><hr><h2><b>July 23, 2012</b></h2></a>
 <blockquote><p>"Ford", Arthur said. "There's an infinite number of monkeys
 out here who want to talk to us about this script for Hamlet they've worked
 out." - The Hitchhiker's Guide to the Galaxy.</p></blockquote>
@@ -1095,7 +1333,7 @@ corresponding executable bit wasn't set, and worked around a longstanding
 glibc bug where static linking prevents stdout from automatically flushing
 pending output on exit.</p>
 
-<hr><b>June 25, 2012</b>
+<a name="25-06-2012" /><a href="#25-06-2012"><hr><h2><b>June 25, 2012</b></h2></a>
 <blockquote><p>"For a moment, nothing happened. Then, after a second or so, nothing continued to happen." - The Hitchhiker's Guide to the Galaxy.</p></blockquote>
 
 <p><a href=downloads/toybox-0.3.1.tar.bz2>Toybox 0.3.1</a> is based on commit
@@ -1111,7 +1349,7 @@ it is. The roadmap and documentation are a bit behind, and I've got ~40
 pending submissions to review. I need to catch up...</p>
 </span>
 
-<hr><b>June 12, 2012</b>
+<a name="12-06-2012" /><a href="#12-06-2012"><hr><h2><b>June 12, 2012</b></h2></a>
 <blockquote><p>"For instance, on the planet Earth, man had always assumed that
 he was more intelligent than dolphins because he had achieved so much - the
 wheel, New York, wars and so on - whilst all the dolphins had ever done was
@@ -1176,7 +1414,7 @@ yet, but if I wait until everything works we won't have a release before
 1.0, so here's a checkpoint.)</p>
 
 
-<hr><b>March 3, 2012</b>
+<a name="03-03-2012" /><a href="#03-03-2012"><hr><h2><b>March 3, 2012</b></h2></a>
 
 <blockquote><p>"They went unnoticed at Goonhilly, passed over Cape Canaveral
 without a blip, and Woomera and Jodrell Bank looked straight through them.
@@ -1216,7 +1454,7 @@ dirname, unshare, and various infrastructure tweaks, but it took me 3 months
 and those guys did their stuff in a week or so.)</p>
 
 
-<hr><b>February 12, 2012</b>
+<a name="12-02-2012" /><a href="#12-02-2012"><hr><h2><b>February 12, 2012</b></h2></a>
 <blockquote><p>
 "for though it has many omissions and contains much that is apocryphal, or at
 least wildly inaccurate, it scores over the older, more pedestrian work in two
@@ -1233,7 +1471,8 @@ patches pending on the mailing list I need to review and merge.</p>
 <p>More to come...</p>
 
 <hr>
-<p><b>November 15, 2011</b> - Back from the dead, Toybox is now under a 2
+<a name="15-11-2011" /><a href="#15-11-2011"><hr><h2><b>November 15, 2011</b></h2></a>
+- Back from the dead, Toybox is now under a 2
 clause BSD license, and aiming to become the default command line
 implementation of Android systems everywhere.</p>
 
index bba08b1..e5105b8 100755 (executable)
@@ -244,15 +244,13 @@ sendevent start stop top uptime watchprops
 <h3>Other Android core commands</h3>
 
 <p>Other than the toolbox directory, the currently interesting
-subdirectories in the core repository are gpttool, init,
-logcat, logwrapper, mkbootimg, reboot, and run-as.</p>
+subdirectories in the core repository are init,
+logcat, logwrapper, reboot, and run-as.</p>
 
 <ul>
-<li><b>gpttool</b> - subset of fdisk</li>
 <li><b>init</b> - Android's PID 1</li>
 <li><b>logcat</b> - read android log format</li>
 <li><b>logwrapper</b> - redirect stdio to android log</li>
-<li><b>mkbootimg</b> - create signed boot image</li>
 <li><b>reboot</b> - Android's reboot(1)</li>
 <li><b>run-as</b> - subset of sudo</li>
 </ul>
@@ -261,17 +259,13 @@ logcat, logwrapper, mkbootimg, reboot, and run-as.</p>
 different user interface. We may want to provide that interface, but
 implementing the full commands (fdisk, init, and sudo) come first.</p>
 
-<p>Also, gpttool and mkbootimg are install tools.
-These aren't a priority if android wants to use its own
-bespoke code to install itself.</p>
-
 <h3>Analysis</h3>
 
 <p>For reference, combining everything listed above, we get:</p>
 
 <blockquote><b>
-dd du df getevent gpttool iftop init ioctl ionice
-log logcat logwrapper ls lsof mkbootimg mount nandread
+dd du df getevent iftop init ioctl ionice
+log logcat logwrapper ls lsof mount nandread
 newfs_msdos ps prlimit reboot renice run-as
 sendevent start stop top uptime watchprops
 </b></blockquote>