# 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
CONFIG_BLKID=y
# CONFIG_FSTYPE is not set
CONFIG_BLOCKDEV=y
+CONFIG_BUNZIP2=y
CONFIG_BZCAT=y
CONFIG_CHCON=y
CONFIG_CHROOT=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
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 \
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 \
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
ALL_TOOLS := \
acpi \
+ base64 \
basename \
blockdev \
bzcat \
ifconfig \
inotifyd \
insmod \
+ ionice \
+ iorenice \
kill \
- killall \
load_policy \
ln \
logname \
pwd \
readlink \
realpath \
+ renice \
restorecon \
rm \
rmdir \
which \
whoami \
xargs \
+ xxd \
yes \
# Install the symlinks.
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"
#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
#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
// toys/other/acpi.c
struct acpi_data {
- int ac;
- int bat;
- int therm;
- int cool;
+ int ac, bat, therm, cool;
char *cpath;
};
unsigned height;
};
+// toys/other/hwclock.c
+
+struct hwclock_data {
+ char *fname;
+
+ int utc;
+};
+
// toys/other/ifconfig.c
struct ifconfig_data {
struct login_data {
char *hostname;
+ char *username;
int login_timeout, login_fail_timeout;
};
char *type_str;
};
-// toys/pending/hwclock.c
-
-struct hwclock_data {
- char *fname;
-
- int utc;
-};
-
// toys/pending/iconv.c
struct iconv_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 {
char *file;
char *setfmt;
char *showdate;
+
+ char *tz;
+ unsigned nano;
};
// toys/posix/df.c
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;
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;
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;
#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"
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))
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))
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))
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))
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))
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))
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))
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))
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";
{
close(fdin);
close(fdout);
- unlink(*tempname);
+ if (*tempname) unlink(*tempname);
tempfile2zap = (char *)1;
free(*tempname);
*tempname = NULL;
// 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;
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;
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);
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);
// 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);
{
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;
+}
// 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
#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__
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.
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.
#include "toys.h"
#ifndef TOYBOX_VERSION
-#define TOYBOX_VERSION "0.5.2"
+#define TOYBOX_VERSION "0.6.0"
#endif
// Populate toy_list[].
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.
scripts/single.sh $i > /dev/null 2>$PREFIX/${i}.bad &&
rm $PREFIX/${i}.bad || echo -n '*'
done
+echo
// 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;
--- /dev/null
+#!/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" "" ""
#include "toys.h"
GLOBALS(
- int ac;
- int bat;
- int therm;
- int cool;
+ int ac, bat, therm, cool;
char *cpath;
)
return ret;
}
-int acpi_callback(struct dirtree *tree)
+static int acpi_callback(struct dirtree *tree)
{
int dfd, fd, len, on;
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;
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;
*
* 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
};
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...
{"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;
if (test == fstypes[i].magic) break;
}
- if (i == sizeof(fstypes)/sizeof(struct fstype)) {
+ if (i == ARRAY_LEN(fstypes)) {
off += len;
if (pass) continue;
return;
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);
}
-/* 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
}
// 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)
}
}
-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;
}
// 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);
// 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;
// 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;
// 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))) {
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);
+}
#define FOR_chcon
#include "toys.h"
-int do_chcon(struct dirtree *try)
+static int do_chcon(struct dirtree *try)
{
char *path, *con = *toys.optargs;
--- /dev/null
+/* 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);
+}
fd = xopen(*toys.optargs, ro ? O_RDONLY : O_RDWR);
char keybuf[16];
+ *keybuf = 0;
+
// Terminal setup
TT.height = 25;
terminal_size(0, &TT.height);
--- /dev/null
+/* 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());
+}
* 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;
)
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");
}
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;
{
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;
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));
*
* 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))
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.
*/
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]);
}
#include "toys.h"
-void do_rev(int fd, char *name)
+static void do_rev(int fd, char *name)
{
char *c;
}
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;
}
// 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);
#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;
}
}
-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++;
// 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",
//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);
}
reboot_magic_no=RB_POWER_OFF;
break;
case SIGTERM:
+ case SIGINT:
error_msg("Requesting system reboot");
reboot_magic_no=RB_AUTOBOOT;
break;
errno_backup = errno;
signal_backup = caught_signal;
- signal(SIGCONT, catch_signal);
+ xsignal(SIGCONT, catch_signal);
while(1) {
if (caught_signal == SIGCONT) break;
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);
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();
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;
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);
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) {
}
}
- 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)
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];
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);
}
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;
}
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;
}
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);
}
}
#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)
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"
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:
}
} while (TT.pbuff < len);
- if (i) write(STDIN_FILENO, toybuf, i);
+ if (i) xwrite(STDIN_FILENO, toybuf, i);
return 0;
}
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 = "";
}
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));
--- /dev/null
+/* 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();
+}
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.
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
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++) {
char *mode;
)
-int do_chmod(struct dirtree *try)
+static int do_chmod(struct dirtree *try)
{
mode_t mode;
// 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;
* 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:
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;
// 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;
((toys.optflags & FLAG_u) ? gmtime_r : localtime_r)(&now, &tm);
}
- setdate = *toys.optargs;
// Fall through if no arguments
if (!setdate);
// Display the date?
} 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);
}
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;
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.
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;
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);
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);
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);
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;
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;
<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>
<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&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>
<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>
<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>
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
<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>
<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
<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
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
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>
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,
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." -
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
</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>
<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
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."
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>
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>
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
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
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.
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
<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>
<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>
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>