summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Config.in2
-rw-r--r--lib/lib.c43
-rw-r--r--lib/lib.h7
-rw-r--r--lib/net.c30
-rw-r--r--lib/xwrap.c9
-rw-r--r--main.c7
-rwxr-xr-xscripts/change.sh1
-rw-r--r--scripts/mkflags.c2
-rw-r--r--tests/date.test22
-rw-r--r--toys/other/acpi.c17
-rw-r--r--toys/other/blkid.c32
-rw-r--r--toys/other/bzcat.c93
-rw-r--r--toys/other/chcon.c2
-rw-r--r--toys/other/fsync.c33
-rw-r--r--toys/other/hexedit.c2
-rw-r--r--toys/other/hostid.c23
-rw-r--r--toys/other/login.c246
-rw-r--r--toys/other/lspci.c2
-rw-r--r--toys/other/nbd_client.c16
-rw-r--r--toys/other/reboot.c12
-rw-r--r--toys/other/rev.c2
-rw-r--r--toys/other/switch_root.c25
-rw-r--r--toys/other/tac.c2
-rw-r--r--toys/other/taskset.c2
-rw-r--r--toys/other/vmstat.c2
-rw-r--r--toys/pending/dd.c4
-rw-r--r--toys/pending/init.c13
-rw-r--r--toys/pending/mdev.c33
-rw-r--r--toys/pending/modprobe.c12
-rw-r--r--toys/pending/ps.c2
-rw-r--r--toys/pending/telnet.c48
-rw-r--r--toys/pending/tftp.c454
-rw-r--r--toys/posix/cal.c12
-rw-r--r--toys/posix/chmod.c2
-rw-r--r--toys/posix/cmp.c2
-rw-r--r--toys/posix/date.c137
-rw-r--r--toys/posix/du.c2
-rw-r--r--toys/posix/find.c35
-rw-r--r--toys/posix/id.c2
-rw-r--r--toys/posix/ls.c13
-rw-r--r--toys/posix/nl.c2
-rw-r--r--toys/posix/split.c2
-rw-r--r--toys/posix/strings.c2
-rwxr-xr-xwww/news.html285
-rwxr-xr-xwww/roadmap.html14
45 files changed, 1286 insertions, 424 deletions
diff --git a/Config.in b/Config.in
index 9d3a73b..a2443e2 100644
--- a/Config.in
+++ b/Config.in
@@ -117,7 +117,7 @@ config TOYBOX_DEBUG
default n
help
Enable extra checks for debugging purposes. All of them catch
- things that can only go wrong at development time, not runtime.
+ things that can only go wrong at development time, not runtime.
config TOYBOX_UID_SYS
int "First system UID"
diff --git a/lib/lib.c b/lib/lib.c
index 05e377f..c16cffe 100644
--- a/lib/lib.c
+++ b/lib/lib.c
@@ -291,6 +291,41 @@ int stridx(char *haystack, char needle)
return off-haystack;
}
+char *strlower(char *s)
+{
+ char *try, *new;
+
+ if (!CFG_TOYBOX_I18N) {
+ try = new = xstrdup(s);
+ for (; *s; s++) *(new++) = tolower(*s);
+ } else {
+ // I can't guarantee the string _won't_ expand during reencoding, so...?
+ try = new = xmalloc(strlen(s)*2+1);
+
+ while (*s) {
+ wchar_t c;
+ int len = mbrtowc(&c, s, MB_CUR_MAX, 0);
+
+ if (len < 1) *(new++) = *(s++);
+ else {
+ s += len;
+ // squash title case too
+ c = towlower(c);
+
+ // if we had a valid utf8 sequence, convert it to lower case, and can't
+ // encode back to utf8, something is wrong with your libc. But just
+ // in case somebody finds an exploit...
+ len = wcrtomb(new, c, 0);
+ if (len < 1) error_exit("bad utf8 %x", (int)c);
+ new += len;
+ }
+ }
+ *new = 0;
+ }
+
+ return try;
+}
+
int unescape(char c)
{
char *from = "\\abefnrtv", *to = "\\\a\b\033\f\n\r\t\v";
@@ -540,7 +575,7 @@ void delete_tempfile(int fdin, int fdout, char **tempname)
{
close(fdin);
close(fdout);
- unlink(*tempname);
+ if (*tempname) unlink(*tempname);
tempfile2zap = (char *)1;
free(*tempname);
*tempname = NULL;
@@ -833,7 +868,7 @@ void names_to_pid(char **names, int (*callback)(pid_t pid, char *name))
// display first few digits of number with power of two units, except we're
// actually just counting decimal digits and showing mil/bil/trillions.
-int human_readable(char *buf, unsigned long long num)
+int human_readable(char *buf, unsigned long long num, int style)
{
int end, len;
@@ -846,9 +881,9 @@ int human_readable(char *buf, unsigned long long num)
buf[1] = '.';
end = 3;
}
- buf[end++] = ' ';
+ if (style & HR_SPACE) buf[end++] = ' ';
if (len) buf[end++] = " KMGTPE"[len];
- buf[end++] = 'B';
+ if (style & HR_B) buf[end++] = 'B';
buf[end++] = 0;
return end;
diff --git a/lib/lib.h b/lib/lib.h
index 3183f32..17a4a97 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -160,6 +160,7 @@ long xstrtol(char *str, char **end, int base);
long atolx(char *c);
long atolx_range(char *numstr, long low, long high);
int stridx(char *haystack, char needle);
+char *strlower(char *s);
int unescape(char c);
int strstart(char **a, char *b);
off_t fdlength(int fd);
@@ -176,7 +177,9 @@ void replace_tempfile(int fdin, int fdout, char **tempname);
void crc_init(unsigned int *crc_table, int little_endian);
void base64_init(char *p);
int yesno(char *prompt, int def);
-int human_readable(char *buf, unsigned long long num);
+#define HR_SPACE 1
+#define HR_B 2
+int human_readable(char *buf, unsigned long long num, int style);
int qstrcmp(const void *a, const void *b);
int xpoll(struct pollfd *fds, int nfds, int timeout);
@@ -204,6 +207,8 @@ void tty_sigreset(int i);
// net.c
int xsocket(int domain, int type, int protocol);
void xsetsockopt(int fd, int level, int opt, void *val, socklen_t len);
+int xconnect(char *host, char *port, int family, int socktype, int protocol,
+ int flags);
// password.c
int get_salt(char *salt, char * algo);
diff --git a/lib/net.c b/lib/net.c
index 5d3ea4a..48d0a5f 100644
--- a/lib/net.c
+++ b/lib/net.c
@@ -12,3 +12,33 @@ void xsetsockopt(int fd, int level, int opt, void *val, socklen_t len)
{
if (-1 == setsockopt(fd, level, opt, val, len)) perror_exit("setsockopt");
}
+
+int xconnect(char *host, char *port, int family, int socktype, int protocol,
+ int flags)
+{
+ struct addrinfo info, *ai, *ai2;
+ int fd;
+
+ memset(&info, 0, sizeof(struct addrinfo));
+ info.ai_family = family;
+ info.ai_socktype = socktype;
+ info.ai_protocol = protocol;
+ info.ai_flags = flags;
+
+ fd = getaddrinfo(host, port, &info, &ai);
+ if (fd || !ai)
+ error_exit("Connect '%s%s%s': %s", host, port ? ":" : "", port ? port : "",
+ fd ? gai_strerror(fd) : "not found");
+
+ // Try all the returned addresses. Report errors if last entry can't connect.
+ for (ai2 = ai; ai; ai = ai->ai_next) {
+ fd = (ai->ai_next ? socket : xsocket)(ai->ai_family, ai->ai_socktype,
+ ai->ai_protocol);
+ if (!connect(fd, ai->ai_addr, ai->ai_addrlen)) break;
+ else if (!ai2->ai_next) perror_exit("connect");
+ close(fd);
+ }
+ freeaddrinfo(ai2);
+
+ return fd;
+}
diff --git a/lib/xwrap.c b/lib/xwrap.c
index 54f2cbb..8086282 100644
--- a/lib/xwrap.c
+++ b/lib/xwrap.c
@@ -30,10 +30,10 @@ void xstrncat(char *dest, char *src, size_t size)
void xexit(void)
{
+ if (toys.rebound) longjmp(*toys.rebound, 1);
if (fflush(NULL) || ferror(stdout))
if (!toys.exitval) perror_msg("write");
- if (toys.rebound) longjmp(*toys.rebound, 1);
- else exit(toys.exitval);
+ exit(toys.exitval);
}
// Die unless we can allocate memory.
@@ -136,7 +136,10 @@ void xexec(char **argv)
if (CFG_TOYBOX && !CFG_TOYBOX_NORECURSE) toy_exec(argv);
execvp(argv[0], argv);
- perror_exit("exec %s", argv[0]);
+ perror_msg("exec %s", argv[0]);
+ toys.exitval = 127;
+ if (!CFG_TOYBOX_FORK) _exit(toys.exitval);
+ xexit();
}
// Spawn child process, capturing stdin/stdout.
diff --git a/main.c b/main.c
index c557030..0738a17 100644
--- a/main.c
+++ b/main.c
@@ -6,7 +6,7 @@
#include "toys.h"
#ifndef TOYBOX_VERSION
-#define TOYBOX_VERSION "0.5.2"
+#define TOYBOX_VERSION "0.6.0"
#endif
// Populate toy_list[].
@@ -163,7 +163,10 @@ void toybox_main(void)
xputs(TOYBOX_VERSION);
xexit();
}
- if (toys.argv[1][0] != '-') error_exit("Unknown command %s", toys.argv[1]);
+ if (toys.argv[1][0] != '-') {
+ toys.exitval = 127;
+ error_exit("Unknown command %s", toys.argv[1]);
+ }
}
// Output list of command.
diff --git a/scripts/change.sh b/scripts/change.sh
index dcd581e..99dcfde 100755
--- a/scripts/change.sh
+++ b/scripts/change.sh
@@ -18,3 +18,4 @@ do
scripts/single.sh $i > /dev/null 2>$PREFIX/${i}.bad &&
rm $PREFIX/${i}.bad || echo -n '*'
done
+echo
diff --git a/scripts/mkflags.c b/scripts/mkflags.c
index d87087b..7e57f84 100644
--- a/scripts/mkflags.c
+++ b/scripts/mkflags.c
@@ -6,10 +6,12 @@
// This is intentionally crappy code because we control the inputs. It leaks
// memory like a sieve and segfaults if malloc returns null, but does the job.
+#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
+#include <ctype.h>
struct flag {
struct flag *next;
diff --git a/tests/date.test b/tests/date.test
new file mode 100644
index 0000000..94a4157
--- /dev/null
+++ b/tests/date.test
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+# Test Unix date parsing.
+testing "date -d @0" "TZ=UTC date -d @0 2>&1" "Thu Jan 1 00:00:00 GMT 1970\n" "" ""
+testing "date -d @0x123" "TZ=UTC date -d @0x123 2>&1" "date: bad date '@0x123'\n" "" ""
+
+# Test basic date parsing.
+# Note that toybox's -d format is not the same as coreutils'.
+testing "date -d 06021234" "TZ=UTC date -d 06021234 2>&1" "Sun Jun 2 12:34:00 UTC 1900\n" "" ""
+testing "date -d 060212341982" "TZ=UTC date -d 060212341982 2>&1" "Sun Jun 2 12:34:00 UTC 1982\n" "" ""
+testing "date -d 123" "TZ=UTC date -d 123 2>&1" "date: bad date '123'\n" "" ""
+
+# Accidentally given a Unix time, we should trivially reject that.
+testing "date Unix time missing @" "TZ=UTC date 1438053157 2>&1" \
+ "date: bad date '1438053157'; Tue February 38 05:31:00 UTC 2057 != Sun Mar 10 05:31:00 UTC 2058\n" "" ""
+# But some invalid dates are more subtle, like Febuary 29th in a non-leap year.
+testing "date Feb 29th" "TZ=UTC date 022900001975 2>&1" \
+ "date: bad date '022900001975'; Tue Feb 29 00:00:00 UTC 2075 != Fri Mar 1 00:00:00 UTC 2075\n" "" ""
diff --git a/toys/other/acpi.c b/toys/other/acpi.c
index 44fd03b..363cfb9 100644
--- a/toys/other/acpi.c
+++ b/toys/other/acpi.c
@@ -25,10 +25,7 @@ config ACPI
#include "toys.h"
GLOBALS(
- int ac;
- int bat;
- int therm;
- int cool;
+ int ac, bat, therm, cool;
char *cpath;
)
@@ -44,7 +41,7 @@ int read_int_at(int dirfd, char *name)
return ret;
}
-int acpi_callback(struct dirtree *tree)
+static int acpi_callback(struct dirtree *tree)
{
int dfd, fd, len, on;
@@ -85,11 +82,11 @@ done:
return 0;
}
-int temp_callback(struct dirtree *tree)
+static int temp_callback(struct dirtree *tree)
{
int dfd, temp;
- if (tree->name[0]=='.') return 0;
+ if (*tree->name=='.') return 0;
if (!tree->parent || !tree->parent->parent)
return DIRTREE_RECURSE|DIRTREE_SYMFOLLOW;
errno = 0;
@@ -98,17 +95,17 @@ int temp_callback(struct dirtree *tree)
if ((0 < (temp = read_int_at(dfd, "temp"))) || !errno) {
//some tempertures are in milli-C, some in deci-C
//reputedly some are in deci-K, but I have not seen them
- if (((temp >= 1000) || (temp <= -1000)) && (temp%100 == 0))
- temp /= 100;
+ if (((temp >= 1000) || (temp <= -1000)) && (temp%100 == 0)) temp /= 100;
printf("Thermal %d: %d.%d degrees C\n", TT.therm++, temp/10, temp%10);
}
close(dfd);
}
free(TT.cpath);
+
return 0;
}
-int cool_callback(struct dirtree *tree)
+static int cool_callback(struct dirtree *tree)
{
int dfd=5, cur, max;
diff --git a/toys/other/blkid.c b/toys/other/blkid.c
index 725f163..fad1159 100644
--- a/toys/other/blkid.c
+++ b/toys/other/blkid.c
@@ -4,7 +4,7 @@
*
* See ftp://ftp.kernel.org/pub/linux/utils/util-linux/v2.24/libblkid-docs/api-index-full.html
-USE_BLKID(NEWTOY(blkid, "<1", TOYFLAG_BIN))
+USE_BLKID(NEWTOY(blkid, 0, TOYFLAG_BIN))
USE_FSTYPE(NEWTOY(fstype, "<1", TOYFLAG_BIN))
config BLKID
@@ -34,6 +34,7 @@ struct fstype {
};
static const struct fstype fstypes[] = {
+ {"swap", 0x4341505350415753, 8, 4086, 1036, 15, 1052},
{"ext2", 0xEF53, 2, 1080, 1128, 16, 1144}, // keep this first for ext3/4 check
// NTFS label actually 8/16 0x4d80 but horrible: 16 bit wide characters via
// codepage, something called a uuid that's only 8 bytes long...
@@ -56,8 +57,7 @@ static const struct fstype fstypes[] = {
{"vfat", 0x31544146, 4, 54, 39+(4<<24), 11, 43} // fat1
};
-/* TODO if no args use proc/partitions */
-void do_blkid(int fd, char *name)
+static void do_blkid(int fd, char *name)
{
int off, i, j;
char *type;
@@ -89,7 +89,7 @@ void do_blkid(int fd, char *name)
if (test == fstypes[i].magic) break;
}
- if (i == sizeof(fstypes)/sizeof(struct fstype)) {
+ if (i == ARRAY_LEN(fstypes)) {
off += len;
if (pass) continue;
return;
@@ -136,10 +136,30 @@ void do_blkid(int fd, char *name)
void blkid_main(void)
{
- loopfiles(toys.optargs, do_blkid);
+ if (*toys.optargs) loopfiles(toys.optargs, do_blkid);
+ else {
+ unsigned int ma, mi, sz, fd;
+ char *name = toybuf, *buffer = toybuf+1024, device[32];
+ FILE *fp = xfopen("/proc/partitions", "r");
+
+ while (fgets(buffer, 1024, fp)) {
+ *name = 0;
+ if (sscanf(buffer, " %u %u %u %[^\n ]", &ma, &mi, &sz, name) != 4)
+ continue;
+
+ sprintf(device, "/dev/%.20s", name);
+ if (-1 == (fd = open(device, O_RDONLY))) {
+ if (errno != ENOMEDIUM) perror_msg("%s", device);
+ } else {
+ do_blkid(fd, device);
+ close(fd);
+ }
+ }
+ if (CFG_TOYBOX_FREE) fclose(fp);
+ }
}
void fstype_main(void)
{
- blkid_main();
+ loopfiles(toys.optargs, do_blkid);
}
diff --git a/toys/other/bzcat.c b/toys/other/bzcat.c
index 642590d..850c51c 100644
--- a/toys/other/bzcat.c
+++ b/toys/other/bzcat.c
@@ -1,4 +1,4 @@
-/* bzcat.c - decompress stdin to stdout using bunzip2.
+/* bzcat.c - bzip2 decompression
*
* Copyright 2003, 2007 Rob Landley <rob@landley.net>
*
@@ -12,16 +12,33 @@
USE_BZCAT(NEWTOY(bzcat, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+USE_BUNZIP2(NEWTOY(bunzip2, "cftkv", TOYFLAG_USR|TOYFLAG_BIN))
+
+config BUNZIP2
+ bool "bunzip2"
+ default y
+ help
+ usage: bunzip2 [-cftkv] [FILE...]
+
+ Decompress listed files (file.bz becomes file) deleting archive file(s).
+ Read from stdin if no files listed.
+
+ -c force output to stdout
+ -f force decompression. (If FILE doesn't end in .bz, replace original.)
+ -k keep input files (-c and -t imply this)
+ -t test integrity
+ -v verbose
config BZCAT
bool "bzcat"
default y
help
- usage: bzcat [filename...]
+ usage: bzcat [FILE...]
Decompress listed files to stdout. Use stdin if no files listed.
*/
+#define FOR_bunzip2
#include "toys.h"
#define THREADS 1
@@ -421,7 +438,7 @@ static int read_huffman_data(struct bunzip_data *bd, struct bwdata *bw)
}
// Flush output buffer to disk
-void flush_bunzip_outbuf(struct bunzip_data *bd, int out_fd)
+static void flush_bunzip_outbuf(struct bunzip_data *bd, int out_fd)
{
if (bd->outbufPos) {
if (write(out_fd, bd->outbuf, bd->outbufPos) != bd->outbufPos)
@@ -430,7 +447,7 @@ void flush_bunzip_outbuf(struct bunzip_data *bd, int out_fd)
}
}
-void burrows_wheeler_prep(struct bunzip_data *bd, struct bwdata *bw)
+static void burrows_wheeler_prep(struct bunzip_data *bd, struct bwdata *bw)
{
int ii, jj;
unsigned int *dbuf = bw->dbuf;
@@ -474,7 +491,7 @@ void burrows_wheeler_prep(struct bunzip_data *bd, struct bwdata *bw)
}
// Decompress a block of text to intermediate buffer
-int read_bunzip_data(struct bunzip_data *bd)
+static int read_bunzip_data(struct bunzip_data *bd)
{
int rc = read_block_header(bd, bd->bwdata);
if (!rc) rc=read_huffman_data(bd, bd->bwdata);
@@ -493,7 +510,8 @@ int read_bunzip_data(struct bunzip_data *bd)
// http://dogma.net/markn/articles/bwt/bwt.htm
// http://marknelson.us/1996/09/01/bwt/
-int write_bunzip_data(struct bunzip_data *bd, struct bwdata *bw, int out_fd, char *outbuf, int len)
+static int write_bunzip_data(struct bunzip_data *bd, struct bwdata *bw,
+ int out_fd, char *outbuf, int len)
{
unsigned int *dbuf = bw->dbuf;
int count, pos, current, run, copies, outbyte, previous, gotcount = 0;
@@ -584,7 +602,8 @@ dataus_interruptus:
// Allocate the structure, read file header. If !len, src_fd contains
// filehandle to read from. Else inbuf contains data.
-int start_bunzip(struct bunzip_data **bdp, int src_fd, char *inbuf, int len)
+static int start_bunzip(struct bunzip_data **bdp, int src_fd, char *inbuf,
+ int len)
{
struct bunzip_data *bd;
unsigned int i;
@@ -622,10 +641,10 @@ int start_bunzip(struct bunzip_data **bdp, int src_fd, char *inbuf, int len)
// Example usage: decompress src_fd to dst_fd. (Stops at end of bzip data,
// not end of file.)
-void bunzipStream(int src_fd, int dst_fd)
+static char *bunzipStream(int src_fd, int dst_fd)
{
struct bunzip_data *bd;
- char *bunzip_errors[]={NULL, "not bzip", "bad data", "old format"};
+ char *bunzip_errors[] = {0, "not bzip", "bad data", "old format"};
int i, j;
if (!(i = start_bunzip(&bd,src_fd, 0, 0))) {
@@ -636,15 +655,67 @@ void bunzipStream(int src_fd, int dst_fd)
for (j=0; j<THREADS; j++) free(bd->bwdata[j].dbuf);
free(bd);
- if (i) error_exit(bunzip_errors[-i]);
+
+ return bunzip_errors[-i];
}
static void do_bzcat(int fd, char *name)
{
- bunzipStream(fd, 1);
+ char *err = bunzipStream(fd, 1);
+
+ if (err) error_exit(err);
}
void bzcat_main(void)
{
loopfiles(toys.optargs, do_bzcat);
}
+
+static void do_bunzip2(int fd, char *name)
+{
+ int outfd = 1, rename = 0, len = strlen(name);
+ char *tmp, *err, *dotbz = 0;
+
+ // Trim off .bz or .bz2 extension
+ dotbz = name+len-3;
+ if ((len>3 && !strcmp(dotbz, ".bz")) || (len>4 && !strcmp(--dotbz, ".bz2")))
+ dotbz = 0;
+
+ // For - no replace
+ if (toys.optflags&FLAG_t) outfd = xopen("/dev/null", O_WRONLY);
+ else if ((fd || strcmp(name, "-")) && !(toys.optflags&FLAG_c)) {
+ if (toys.optflags&FLAG_k) {
+ if (!dotbz || !access(name, X_OK)) {
+ error_msg("%s exists", name);
+
+ return;
+ }
+ }
+ outfd = copy_tempfile(fd, name, &tmp);
+ rename++;
+ }
+
+ if (toys.optflags&FLAG_v) printf("%s:", name);
+ err = bunzipStream(fd, outfd);
+ if (toys.optflags&FLAG_v) {
+ printf("%s\n", err ? err : "ok");
+ toys.exitval |= !!err;
+ } else if (err) error_msg(err);
+
+ // can't test outfd==1 because may have been called with stdin+stdout closed
+ if (rename) {
+ if (toys.optflags&FLAG_k) {
+ free(tmp);
+ tmp = 0;
+ } else {
+ if (dotbz) *dotbz = '.';
+ if (!unlink(name)) perror_msg("%s", name);
+ }
+ (err ? delete_tempfile : replace_tempfile)(-1, outfd, &tmp);
+ }
+}
+
+void bunzip2_main(void)
+{
+ loopfiles(toys.optargs, do_bunzip2);
+}
diff --git a/toys/other/chcon.c b/toys/other/chcon.c
index a2bbb66..6dbdd13 100644
--- a/toys/other/chcon.c
+++ b/toys/other/chcon.c
@@ -21,7 +21,7 @@ config CHCON
#define FOR_chcon
#include "toys.h"
-int do_chcon(struct dirtree *try)
+static int do_chcon(struct dirtree *try)
{
char *path, *con = *toys.optargs;
diff --git a/toys/other/fsync.c b/toys/other/fsync.c
new file mode 100644
index 0000000..e6f6c8d
--- /dev/null
+++ b/toys/other/fsync.c
@@ -0,0 +1,33 @@
+/* fsync.c - Synchronize a file's in-core state with storage device.
+ *
+ * Copyright 2015 Ranjan Kumar <ranjankumar.bth@gmail.comi>
+ *
+ * No Standard.
+
+USE_FSYNC(NEWTOY(fsync, "<1d", TOYFLAG_BIN))
+
+config FSYNC
+ bool "fsync"
+ default y
+ help
+ usage: fsync [-d] [FILE...]
+
+ Synchronize a file's in-core state with storage device.
+
+ -d Avoid syncing metadata.
+*/
+
+#define FOR_fsync
+#include "toys.h"
+
+static void do_fsync(int fd, char *name)
+{
+ if (((toys.optflags & FLAG_d) ? fdatasync(fd) : fsync(fd)))
+ perror_msg("can't sync '%s'", name);
+}
+
+void fsync_main(void)
+{
+ loopfiles_rw(toys.optargs, O_RDONLY|O_NOATIME|O_NOCTTY|O_CLOEXEC,
+ 0, 0, do_fsync);
+}
diff --git a/toys/other/hexedit.c b/toys/other/hexedit.c
index 1f6b42e..a52d66d 100644
--- a/toys/other/hexedit.c
+++ b/toys/other/hexedit.c
@@ -137,6 +137,8 @@ void hexedit_main(void)
fd = xopen(*toys.optargs, ro ? O_RDONLY : O_RDWR);
char keybuf[16];
+ *keybuf = 0;
+
// Terminal setup
TT.height = 25;
terminal_size(0, &TT.height);
diff --git a/toys/other/hostid.c b/toys/other/hostid.c
new file mode 100644
index 0000000..883ac3c
--- /dev/null
+++ b/toys/other/hostid.c
@@ -0,0 +1,23 @@
+/* hostid.c - Print the numeric identifier for the current host.
+ *
+ * Copyright 2015 Ranjan Kumar <ranjankumar.bth@gmail.com>
+ *
+ * No Standard.
+
+USE_HOSTID(NEWTOY(hostid, ">0", TOYFLAG_USR|TOYFLAG_BIN))
+
+config HOSTID
+ bool "hostid"
+ default y
+ help
+ usage: hostid
+
+ Print the numeric identifier for the current host.
+*/
+#define FOR_hostid
+#include "toys.h"
+
+void hostid_main(void)
+{
+ xprintf("%08lx\n", gethostid());
+}
diff --git a/toys/other/login.c b/toys/other/login.c
index b728286..c727bf9 100644
--- a/toys/other/login.c
+++ b/toys/other/login.c
@@ -8,30 +8,28 @@
* TODO: this command predates "pending" but needs cleanup. It #defines
* random stuff, calls exit() form a signal handler... yeah.
-USE_LOGIN(NEWTOY(login, ">1fph:", TOYFLAG_BIN))
+USE_LOGIN(NEWTOY(login, ">1f:ph:", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
config LOGIN
bool "login"
default y
depends on TOYBOX_SHADOW
help
- usage: login [-p] [-h host] [[-f] username]
+ usage: login [-p] [-h host] [-f USERNAME] [USERNAME]
- Establish a new session with the system.
+ Log in as a user, prompting for username and password if necessary.
-p Preserve environment
-h The name of the remote host for this login
- -f Do not perform authentication
+ -f login as USERNAME without authentication
*/
#define FOR_login
#include "toys.h"
-#define USER_NAME_MAX_SIZE 32
-#define HOSTNAME_SIZE 32
-
GLOBALS(
char *hostname;
+ char *username;
int login_timeout, login_fail_timeout;
)
@@ -42,184 +40,128 @@ static void login_timeout_handler(int sig __attribute__((unused)))
exit(0);
}
-static char *forbid[] = {
- "BASH_ENV", "ENV", "HOME", "IFS", "LD_LIBRARY_PATH", "LD_PRELOAD",
- "LD_TRACE_LOADED_OBJECTS", "LD_BIND_NOW", "LD_AOUT_LIBRARY_PATH",
- "LD_AOUT_PRELOAD", "LD_NOWARN", "LD_KEEPDIR", "SHELL", NULL
-};
-
-int verify_password(char * pwd)
-{
- char *pass;
-
- if (read_password(toybuf, sizeof(toybuf), "Password: ")) return 1;
- if (!pwd) return 1;
- if (pwd[0] == '!' || pwd[0] == '*') return 1;
-
- pass = crypt(toybuf, pwd);
- if (pass && !strcmp(pass, pwd)) return 0;
-
- return 1;
-}
-
-void read_user(char * buff, int size)
-{
- char hostname[HOSTNAME_SIZE+1];
- int i = 0;
-
- hostname[HOSTNAME_SIZE] = 0;
- if (!gethostname(hostname, HOSTNAME_SIZE)) fputs(hostname, stdout);
-
- fputs(" login: ", stdout);
- fflush(stdout);
-
- do {
- int c = getchar();
- if (c == EOF) exit(EXIT_FAILURE);
- *buff = c;
- } while (isblank(*buff));
-
- if (*buff != '\n') if(!fgets(&buff[1], HOSTNAME_SIZE-1, stdin)) _exit(1);
-
- while(i<HOSTNAME_SIZE-1 && isgraph(buff[i])) i++;
- buff[i] = 0;
-}
-
-void handle_nologin(void)
-{
- int fd = open("/etc/nologin", O_RDONLY);
- int size;
-
- if (fd == -1) return;
-
- size = readall(fd, toybuf,sizeof(toybuf)-1);
- toybuf[size] = 0;
- if (!size) puts("System closed for routine maintenance\n");
- else puts(toybuf);
-
- close(fd);
- fflush(stdout);
- exit(1);
-}
-
-void handle_motd(void)
-{
- int fd = open("/etc/motd", O_RDONLY);
- int size;
- if (fd == -1) return;
-
- size = readall(fd, toybuf,sizeof(toybuf)-1);
- toybuf[size] = 0;
- puts(toybuf);
-
- close(fd);
- fflush(stdout);
-}
-
-void spawn_shell(const char *shell)
-{
- const char * exec_name = strrchr(shell,'/');
- if (exec_name) exec_name++;
- else exec_name = shell;
-
- snprintf(toybuf,sizeof(toybuf)-1, "-%s", shell);
- execl(shell, toybuf, NULL);
- error_exit("Failed to spawn shell");
-}
-
-void setup_environment(const struct passwd *pwd, int clear_env)
-{
- if (chdir(pwd->pw_dir)) printf("bad home dir: %s\n", pwd->pw_dir);
-
- if (clear_env) {
- const char *term = getenv("TERM");
- clearenv();
- if (term) setenv("TERM", term, 1);
- }
-
- setenv("USER", pwd->pw_name, 1);
- setenv("LOGNAME", pwd->pw_name, 1);
- setenv("HOME", pwd->pw_dir, 1);
- setenv("SHELL", pwd->pw_shell, 1);
-}
-
void login_main(void)
{
- int f_flag = toys.optflags & FLAG_f;
- int h_flag = toys.optflags & FLAG_h;
- char username[33], *pass = NULL, **ss;
- struct passwd * pwd = NULL;
- struct spwd * spwd = NULL;
- int auth_fail_cnt = 0;
-
- if (f_flag && toys.optc != 1) error_exit("-f requires username");
+ char *forbid[] = {
+ "BASH_ENV", "ENV", "HOME", "IFS", "LD_LIBRARY_PATH", "LD_PRELOAD",
+ "LD_TRACE_LOADED_OBJECTS", "LD_BIND_NOW", "LD_AOUT_LIBRARY_PATH",
+ "LD_AOUT_PRELOAD", "LD_NOWARN", "LD_KEEPDIR", "SHELL"
+ };
+ int hh = toys.optflags&FLAG_h, count, tty;
+ char uu[33], *username, *pass = 0, *ss;
+ struct passwd *pwd = 0;
- if (geteuid()) error_exit("not root");
+ for (tty=0; tty<3; tty++) if (isatty(tty)) break;
+ if (tty == 3) error_exit("no tty");
- if (!isatty(0) || !isatty(1) || !isatty(2)) error_exit("no tty");
+ for (count = 0; count < ARRAY_LEN(forbid); count++) unsetenv(forbid[count]);
openlog("login", LOG_PID | LOG_CONS, LOG_AUTH);
xsignal(SIGALRM, login_timeout_handler);
- alarm(TT.login_timeout = 60);
- for (ss = forbid; *ss; ss++) unsetenv(*ss);
-
- while (1) {
+ if (TT.username) username = TT.username;
+ else username = *toys.optargs;
+ for (count = 0; count < 3; count++) {
+ alarm(TT.login_timeout = 60);
tcflush(0, TCIFLUSH);
- username[sizeof(username)-1] = 0;
- if (*toys.optargs) xstrncpy(username, *toys.optargs, sizeof(username));
- else {
- read_user(username, sizeof(username));
- if (!*username) continue;
+ if (!username) {
+ int i;
+
+ memset(username = uu, 0, sizeof(uu));
+ gethostname(uu, sizeof(uu)-1);
+ printf("%s%slogin: ", *uu ? uu : "", *uu ? " " : "");
+ fflush(stdout);
+
+ if(!fgets(uu, sizeof(uu)-1, stdin)) _exit(1);
+
+ // Remove trailing \n and so on
+ for (i = 0; i<sizeof(uu); i++) if (uu[i]<=' ' || uu[i]==':') uu[i]=0;
+ if (!*uu) {
+ username = 0;
+ continue;
+ }
}
+ // If user exists and isn't locked
pwd = getpwnam(username);
- if (!pwd) goto query_pass; // Non-existing user
+ if (pwd && *pwd->pw_passwd != '!' && *pwd->pw_passwd != '*') {
- if (pwd->pw_passwd[0] == '!' || pwd->pw_passwd[0] == '*')
- goto query_pass; // Locked account
+ // Pre-authenticated or passwordless
+ if (TT.username || !*pwd->pw_passwd) break;
- if (f_flag) break; // Pre-authenticated
+ // fetch shadow password if necessary
+ if (*(pass = pwd->pw_passwd) == 'x') {
+ struct spwd *spwd = getspnam (username);
- if (!pwd->pw_passwd[0]) break; // Password-less account
+ if (spwd) pass = spwd->sp_pwdp;
+ }
+ } else if (TT.username) error_exit("bad -f '%s'", TT.username);
- pass = pwd->pw_passwd;
- if (pwd->pw_passwd[0] == 'x') {
- spwd = getspnam (username);
- if (spwd) pass = spwd->sp_pwdp;
- }
+ // Verify password. (Prompt for password _before_ checking disable state.)
+ if (!read_password(toybuf, sizeof(toybuf), "Password: ")) {
+ int x = pass && (ss = crypt(toybuf, pass)) && !strcmp(pass, ss);
-query_pass:
- if (!verify_password(pass)) break;
+ // password go bye-bye now.
+ memset(toybuf, 0, sizeof(toybuf));
+ if (x) break;
+ }
- f_flag = 0;
- syslog(LOG_WARNING, "invalid password for '%s' on %s %s %s", username,
- ttyname(0), h_flag?"from":"", h_flag?TT.hostname:"");
+ syslog(LOG_WARNING, "invalid password for '%s' on %s %s%s", pwd->pw_name,
+ ttyname(tty), hh ? "from " : "", hh ? TT.hostname : "");
sleep(3);
puts("Login incorrect");
- if (++auth_fail_cnt == 3)
- error_exit("Maximum number of tries exceeded (3)\n");
-
- *username = 0;
+ username = 0;
pwd = 0;
- spwd = 0;
}
alarm(0);
+ // This had password data in it, and we reuse for motd below
+ memset(toybuf, 0, sizeof(toybuf));
+
+ if (!pwd) error_exit("max retries (3)");
- if (pwd->pw_uid) handle_nologin();
+ // Check twice because "this file exists" is a security test, and in
+ // theory filehandle exhaustion or other error could make open/read fail.
+ if (pwd->pw_uid && !access("/etc/nologin", R_OK)) {
+ ss = readfile("/etc/nologin", toybuf, sizeof(toybuf));
+ puts ((ss && *ss) ? ss : "nologin");
+ free(ss);
+ toys.exitval = 1;
+
+ return;
+ }
xsetuser(pwd);
- setup_environment(pwd, !(toys.optflags & FLAG_p));
+ if (chdir(pwd->pw_dir)) printf("bad $HOME: %s\n", pwd->pw_dir);
+
+ if (!(toys.optflags&FLAG_p)) {
+ char *term = getenv("TERM");
+
+ clearenv();
+ if (term) setenv("TERM", term, 1);
+ }
+
+ setenv("USER", pwd->pw_name, 1);
+ setenv("LOGNAME", pwd->pw_name, 1);
+ setenv("HOME", pwd->pw_dir, 1);
+ setenv("SHELL", pwd->pw_shell, 1);
- handle_motd();
+ // Message of the day
+ if ((ss = readfile("/etc/motd", 0, 0))) {
+ puts(ss);
+ free(ss);
+ }
syslog(LOG_INFO, "%s logged in on %s %s %s", pwd->pw_name,
- ttyname(0), h_flag?"from":"", h_flag?TT.hostname:"");
+ ttyname(tty), hh ? "from" : "", hh ? TT.hostname : "");
- spawn_shell(pwd->pw_shell);
+ // can't xexec here because name doesn't match argv[0]
+ snprintf(toybuf, sizeof(toybuf)-1, "-%s", basename_r(pwd->pw_shell));
+ toy_exec((char *[]){toybuf, 0});
+ execl(pwd->pw_shell, toybuf, NULL);
+ error_exit("Failed to spawn shell");
}
diff --git a/toys/other/lspci.c b/toys/other/lspci.c
index c9b22ab..077ce75 100644
--- a/toys/other/lspci.c
+++ b/toys/other/lspci.c
@@ -37,7 +37,7 @@ GLOBALS(
FILE *db;
)
-int do_lspci(struct dirtree *new)
+static int do_lspci(struct dirtree *new)
{
char *p = toybuf, *vendor = toybuf+9, *device = toybuf+18,
driver[256], *vbig = 0, *dbig = 0, **fields;
diff --git a/toys/other/nbd_client.c b/toys/other/nbd_client.c
index c16585a..a82ff7c 100644
--- a/toys/other/nbd_client.c
+++ b/toys/other/nbd_client.c
@@ -40,7 +40,6 @@ void nbd_client_main(void)
{
int sock = -1, nbd, flags;
unsigned long timeout = 0;
- struct addrinfo *addr, *p;
char *host=toys.optargs[0], *port=toys.optargs[1], *device=toys.optargs[2];
uint64_t devsize;
@@ -49,23 +48,10 @@ void nbd_client_main(void)
nbd = xopen(device, O_RDWR);
for (;;) {
int temp;
- struct addrinfo hints;
// Find and connect to server
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = PF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
- if (getaddrinfo(host, port, &hints, &addr)) addr = 0;
- for (p = addr; p; p = p->ai_next) {
- sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
- if (-1 != connect(sock, p->ai_addr, p->ai_addrlen)) break;
- close(sock);
- }
- freeaddrinfo(addr);
-
- if (!p) perror_exit("%s:%s", host, port);
-
+ sock = xconnect(host, port, AF_UNSPEC, SOCK_STREAM, 0, 0);
temp = 1;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &temp, sizeof(int));
diff --git a/toys/other/reboot.c b/toys/other/reboot.c
index a135888..1e8f5e9 100644
--- a/toys/other/reboot.c
+++ b/toys/other/reboot.c
@@ -2,7 +2,7 @@
*
* Copyright 2013 Elie De Brauwer <eliedebrauwer@gmail.com>
-USE_REBOOT(NEWTOY(reboot, "n", TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
+USE_REBOOT(NEWTOY(reboot, "fn", TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
USE_REBOOT(OLDTOY(halt, reboot, TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
USE_REBOOT(OLDTOY(poweroff, reboot, TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
@@ -10,10 +10,11 @@ config REBOOT
bool "reboot"
default y
help
- usage: reboot/halt/poweroff [-n]
+ usage: reboot/halt/poweroff [-fn]
Restart, halt or powerdown the system.
+ -f Don't signal init
-n Don't sync before stopping the system.
*/
@@ -23,9 +24,12 @@ config REBOOT
void reboot_main(void)
{
- int types[] = {RB_AUTOBOOT, RB_HALT_SYSTEM, RB_POWER_OFF};
+ int types[] = {RB_AUTOBOOT, RB_HALT_SYSTEM, RB_POWER_OFF},
+ sigs[] = {SIGINT, SIGUSR1, SIGUSR2}, idx;
if (!(toys.optflags & FLAG_n)) sync();
- toys.exitval = reboot(types[stridx("hp", *toys.which->name)+1]);
+ idx = stridx("hp", *toys.which->name)+1;
+ if (toys.optflags & FLAG_f) toys.exitval = reboot(types[idx]);
+ else toys.exitval = kill(1, sigs[idx]);
}
diff --git a/toys/other/rev.c b/toys/other/rev.c
index b5720a3..4cf7214 100644
--- a/toys/other/rev.c
+++ b/toys/other/rev.c
@@ -15,7 +15,7 @@ config REV
#include "toys.h"
-void do_rev(int fd, char *name)
+static void do_rev(int fd, char *name)
{
char *c;
diff --git a/toys/other/switch_root.c b/toys/other/switch_root.c
index 0861c70..acbae2b 100644
--- a/toys/other/switch_root.c
+++ b/toys/other/switch_root.c
@@ -67,8 +67,13 @@ void switch_root_main(void)
}
TT.rootdev=st2.st_dev;
+ // trim any / characters from the init cmdline, as we want to test it with
+ // stat(), relative to newroot. *cmdline is also used below, but by that
+ // point we are in the chroot, so a relative path is still OK.
+ while (**cmdline == '/') (*cmdline)++;
+
// init program must exist and be an executable file
- if (stat("init", &st1) || !S_ISREG(st1.st_mode) || !(st1.st_mode&0100)) {
+ if (stat(*cmdline, &st1) || !S_ISREG(st1.st_mode) || !(st1.st_mode&0100)) {
error_msg("bad init");
goto panic;
}
@@ -81,6 +86,24 @@ void switch_root_main(void)
// Ok, enough safety checks: wipe root partition.
dirtree_read("/", del_node);
+ // Fix the appearance of the mount table in the newroot chroot
+ if (mount(".", "/", NULL, MS_MOVE, NULL)) {
+ perror_msg("mount");
+ goto panic;
+ }
+
+ // Enter the new root before starting init
+ if (chroot(".")) {
+ perror_msg("chroot");
+ goto panic;
+ }
+
+ // Make sure cwd does not point outside of the chroot
+ if (chdir("/")) {
+ perror_msg("chdir");
+ goto panic;
+ }
+
if (TT.console) {
int i;
for (i=0; i<3; i++) if (console != i) dup2(console, i);
diff --git a/toys/other/tac.c b/toys/other/tac.c
index 538d1b0..d5f72fd 100644
--- a/toys/other/tac.c
+++ b/toys/other/tac.c
@@ -15,7 +15,7 @@ config TAC
#include "toys.h"
-void do_tac(int fd, char *name)
+static void do_tac(int fd, char *name)
{
struct arg_list *list = NULL;
char *c;
diff --git a/toys/other/taskset.c b/toys/other/taskset.c
index 2851923..4ade5f1 100644
--- a/toys/other/taskset.c
+++ b/toys/other/taskset.c
@@ -120,7 +120,7 @@ void taskset_main(void)
}
}
-int do_nproc(struct dirtree *new)
+static int do_nproc(struct dirtree *new)
{
if (!new->parent) return DIRTREE_RECURSE;
if (!strncmp(new->name, "cpu", 3) && isdigit(new->name[3])) TT.nproc++;
diff --git a/toys/other/vmstat.c b/toys/other/vmstat.c
index c11e46b..9a38e45 100644
--- a/toys/other/vmstat.c
+++ b/toys/other/vmstat.c
@@ -39,7 +39,7 @@ struct vmstat_proc {
// All the elements of vmstat_proc are the same size, so we can populate it as
// a big array, then read the elements back out by name
-void get_vmstat_proc(struct vmstat_proc *vmstat_proc)
+static void get_vmstat_proc(struct vmstat_proc *vmstat_proc)
{
char *vmstuff[] = { "/proc/stat", "cpu ", 0, 0, 0, 0, 0, 0,
"intr ", "ctxt ", "procs_running ", "procs_blocked ", "/proc/meminfo",
diff --git a/toys/pending/dd.c b/toys/pending/dd.c
index 3449104..366d3c5 100644
--- a/toys/pending/dd.c
+++ b/toys/pending/dd.c
@@ -133,9 +133,9 @@ static void summary()
//out to STDERR
fprintf(stderr,"%llu+%llu records in\n%llu+%llu records out\n", st.in_full, st.in_part,
st.out_full, st.out_part);
- human_readable(toybuf, st.bytes);
+ human_readable(toybuf, st.bytes, HR_SPACE|HR_B);
fprintf(stderr, "%llu bytes (%s) copied, ",st.bytes, toybuf);
- human_readable(toybuf, st.bytes/seconds);
+ human_readable(toybuf, st.bytes/seconds, HR_SPACE|HR_B);
fprintf(stderr, "%f s, %s/s\n", seconds, toybuf);
}
diff --git a/toys/pending/init.c b/toys/pending/init.c
index 529c1b9..d9e78ff 100644
--- a/toys/pending/init.c
+++ b/toys/pending/init.c
@@ -348,6 +348,7 @@ static void halt_poweroff_reboot_handler(int sig_no)
reboot_magic_no=RB_POWER_OFF;
break;
case SIGTERM:
+ case SIGINT:
error_msg("Requesting system reboot");
reboot_magic_no=RB_AUTOBOOT;
break;
@@ -415,7 +416,7 @@ static void pause_handler(int sig_no)
errno_backup = errno;
signal_backup = caught_signal;
- signal(SIGCONT, catch_signal);
+ xsignal(SIGCONT, catch_signal);
while(1) {
if (caught_signal == SIGCONT) break;
@@ -460,10 +461,11 @@ void init_main(void)
putenv("USER=root");
inittab_parsing();
- signal(SIGUSR1, halt_poweroff_reboot_handler);//halt
- signal(SIGUSR2, halt_poweroff_reboot_handler);//poweroff
- signal(SIGTERM, halt_poweroff_reboot_handler);//reboot
- signal(SIGQUIT, restart_init_handler);//restart init
+ xsignal(SIGUSR1, halt_poweroff_reboot_handler);//halt
+ xsignal(SIGUSR2, halt_poweroff_reboot_handler);//poweroff
+ xsignal(SIGTERM, halt_poweroff_reboot_handler);//reboot
+ xsignal(SIGINT, halt_poweroff_reboot_handler);//reboot
+ xsignal(SIGQUIT, restart_init_handler);//restart init
memset(&sig_act, 0, sizeof(sig_act));
sigfillset(&sig_act.sa_mask);
sigdelset(&sig_act.sa_mask, SIGCONT);
@@ -471,7 +473,6 @@ void init_main(void)
sigaction(SIGTSTP, &sig_act, NULL);
memset(&sig_act, 0, sizeof(sig_act));
sig_act.sa_handler = catch_signal;
- sigaction(SIGINT, &sig_act, NULL);
sigaction(SIGHUP, &sig_act, NULL);
run_action_from_list(SYSINIT);
check_if_pending_signals();
diff --git a/toys/pending/mdev.c b/toys/pending/mdev.c
index 0c49633..a13a53d 100644
--- a/toys/pending/mdev.c
+++ b/toys/pending/mdev.c
@@ -35,7 +35,7 @@ config MDEV_CONF
static void make_device(char *path)
{
char *device_name = NULL, *s, *temp;
- int major, minor, type, len, fd;
+ int major = 0, minor = 0, type, len, fd;
int mode = 0660;
uid_t uid = 0;
gid_t gid = 0;
@@ -46,24 +46,21 @@ static void make_device(char *path)
temp = strrchr(path, '/');
fd = open(path, O_RDONLY);
*temp=0;
- temp = toybuf;
- len = read(fd, temp, 64);
+ len = read(fd, toybuf, 64);
close(fd);
if (len<1) return;
- temp[len] = 0;
+ toybuf[len] = 0;
// Determine device type, major and minor
type = path[5]=='c' ? S_IFCHR : S_IFBLK;
- major = minor = 0;
- sscanf(temp, "%u:%u", &major, &minor);
+ sscanf(toybuf, "%u:%u", &major, &minor);
} else {
// if (!path), do hotplug
if (!(temp = getenv("SUBSYSTEM")))
return;
type = strcmp(temp, "block") ? S_IFCHR : S_IFBLK;
- major = minor = 0;
if (!(temp = getenv("MAJOR")))
return;
sscanf(temp, "%u", &major);
@@ -74,11 +71,15 @@ static void make_device(char *path)
device_name = getenv("DEVNAME");
if (!path)
return;
- temp = toybuf;
}
if (!device_name)
device_name = strrchr(path, '/') + 1;
+ // as in linux/drivers/base/core.c, device_get_devnode()
+ while ((temp = strchr(device_name, '!'))) {
+ *temp = '/';
+ }
+
// If we have a config file, look up permissions for this device
if (CFG_MDEV_CONF) {
@@ -185,22 +186,22 @@ found_device:
}
}
- sprintf(temp, "/dev/%s", device_name);
+ sprintf(toybuf, "/dev/%s", device_name);
- if (getenv("ACTION") && !strcmp(getenv("ACTION"), "remove")) {
- unlink(temp);
+ if ((temp=getenv("ACTION")) && !strcmp(temp, "remove")) {
+ unlink(toybuf);
return;
}
if (strchr(device_name, '/'))
- mkpathat(AT_FDCWD, temp, 0, 2);
- if (mknod(temp, mode | type, makedev(major, minor)) && errno != EEXIST)
- perror_exit("mknod %s failed", temp);
+ mkpathat(AT_FDCWD, toybuf, 0, 2);
+ if (mknod(toybuf, mode | type, makedev(major, minor)) && errno != EEXIST)
+ perror_exit("mknod %s failed", toybuf);
- if (type == S_IFBLK) close(open(temp, O_RDONLY)); // scan for partitions
+ if (type == S_IFBLK) close(open(toybuf, O_RDONLY)); // scan for partitions
- if (CFG_MDEV_CONF) mode=chown(temp, uid, gid);
+ if (CFG_MDEV_CONF) mode=chown(toybuf, uid, gid);
}
static int callback(struct dirtree *node)
diff --git a/toys/pending/modprobe.c b/toys/pending/modprobe.c
index 5431cb3..07c53fc 100644
--- a/toys/pending/modprobe.c
+++ b/toys/pending/modprobe.c
@@ -65,14 +65,12 @@ struct module_s {
static char *path2mod(char *file, char *mod)
{
int i;
- char *from, *lslash;
+ char *from;
if (!file) return NULL;
if (!mod) mod = xmalloc(MODNAME_LEN);
- lslash = strrchr(file, '/');
- if (!lslash || (lslash == file && !lslash[1])) from = file;
- else from = lslash + 1;
+ from = basename_r(file);
for (i = 0; i < (MODNAME_LEN-1) && from[i] && from[i] != '.'; i++)
mod[i] = (from[i] == '-') ? '_' : from[i];
@@ -277,7 +275,8 @@ static int config_action(struct dirtree *node)
get_mod(tokens[1], 1)->flags |= MOD_BLACKLIST;
else if (!strcmp(tokens[0], "install")) continue;
else if (!strcmp(tokens[0], "remove")) continue;
- else error_msg("Invalid option %s found in file %s", tokens[0], filename);
+ else if (toys.optflags & FLAG_q)
+ error_msg("Invalid option %s found in file %s", tokens[0], filename);
}
fclose(fc);
free(filename);
@@ -381,7 +380,6 @@ static int ins_mod(char *modules, char *flags)
}
res = syscall(__NR_init_module, buf, len, toybuf);
if (CFG_TOYBOX_FREE && buf != toybuf) free(buf);
- if (res) perror_exit("failed to load %s ", toys.optargs[0]);
return res;
}
@@ -428,7 +426,7 @@ static int go_probe(struct module_s *m)
int rc = 0, first = 1;
if (!(m->flags & MOD_FNDDEPMOD)) {
- if (!(toys.optflags & FLAG_s))
+ if (!(toys.optflags & FLAG_q))
error_msg("module %s not found in modules.dep", m->name);
return -ENOENT;
}
diff --git a/toys/pending/ps.c b/toys/pending/ps.c
index cb0f32c..29111d5 100644
--- a/toys/pending/ps.c
+++ b/toys/pending/ps.c
@@ -312,7 +312,7 @@ void ps_main(void)
if (j!=2) break;
}
if (i == ARRAY_LEN(typos)) error_exit("bad -o %.*s", end-type, type);
- if (!field->title) strcpy(field->title, typos[field->which]);
+ if (!*field->title) strcpy(field->title, typos[field->which]);
dlist_add_nomalloc((void *)&TT.fields, (void *)field);
}
}
diff --git a/toys/pending/telnet.c b/toys/pending/telnet.c
index f113cbb..dc3487a 100644
--- a/toys/pending/telnet.c
+++ b/toys/pending/telnet.c
@@ -48,39 +48,6 @@ GLOBALS(
#define UF_ECHO 0x01
#define UF_SGA 0x02
-/*
- * creates a socket of family INET/INET6 and protocol TCP and connects
- * it to HOST at PORT.
- * if successful then returns SOCK othrwise error
- */
-static int xconnect_inet_tcp(char *host, int port)
-{
- int ret;
- struct addrinfo *info, *rp;
- char buf[32];
-
- rp = xzalloc(sizeof(struct addrinfo));
- rp->ai_family = AF_UNSPEC;
- rp->ai_socktype = SOCK_STREAM;
- rp->ai_protocol = IPPROTO_TCP;
- sprintf(buf, "%d", port);
-
- ret = getaddrinfo(host, buf, rp, &info);
- if(ret || !info) perror_exit("BAD ADDRESS: can't find : %s ", host);
- free(rp);
-
- for (rp = info; rp; rp = rp->ai_next)
- if ( (rp->ai_family == AF_INET) || (rp->ai_family == AF_INET6)) break;
-
- if (!rp) error_exit("Invalid IP %s", host);
-
- ret = xsocket(rp->ai_family, SOCK_STREAM, IPPROTO_TCP);
- if(connect(ret, rp->ai_addr, rp->ai_addrlen) == -1) perror_exit("connect");
-
- freeaddrinfo(info);
- return ret;
-}
-
// sets terminal mode: LINE or CHARACTER based om internal stat.
static char const es[] = "\r\nEscape character is ";
static void set_mode(void)
@@ -135,7 +102,7 @@ static void handle_esc(void)
char input;
if(toys.signal && TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.raw_term);
- write(1,"\r\nConsole escape. Commands are:\r\n\n"
+ xwrite(1,"\r\nConsole escape. Commands are:\r\n\n"
" l go to line mode\r\n"
" c go to character mode\r\n"
" z suspend telnet\r\n"
@@ -178,7 +145,7 @@ static void handle_esc(void)
default: break;
}
- write(1, "continuing...\r\n", 15);
+ xwrite(1, "continuing...\r\n", 15);
if (toys.signal && TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term);
ret:
@@ -292,7 +259,7 @@ static int read_server(int len)
}
} while (TT.pbuff < len);
- if (i) write(STDIN_FILENO, toybuf, i);
+ if (i) xwrite(STDIN_FILENO, toybuf, i);
return 0;
}
@@ -314,20 +281,19 @@ static void write_server(int len)
if (*c == IAC) toybuf[i++] = *c; /* IAC -> IAC IAC */
else if (*c == '\r') toybuf[i++] = '\0'; /* CR -> CR NUL */
}
- if(i) write(TT.sfd, toybuf, i);
+ if(i) xwrite(TT.sfd, toybuf, i);
}
void telnet_main(void)
{
+ char *port = "23";
int set = 1, len;
struct pollfd pfds[2];
- TT.port = 23; //TELNET_PORT
TT.win_width = 80; //columns
TT.win_height = 24; //rows
- if(toys.optc == 2) TT.port = atoi(toys.optargs[1]);
- if(TT.port <= 0 || TT.port > 65535) error_exit("bad PORT (1-65535)");
+ if (toys.optc == 2) port = toys.optargs[1];
TT.ttype = getenv("TERM");
if(!TT.ttype) TT.ttype = "";
@@ -340,7 +306,7 @@ void telnet_main(void)
}
terminal_size(&TT.win_width, &TT.win_height);
- TT.sfd = xconnect_inet_tcp(toys.optargs[0], TT.port);
+ TT.sfd = xconnect(*toys.optargs, port, 0, SOCK_STREAM, IPPROTO_TCP, 0);
setsockopt(TT.sfd, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(set));
setsockopt(TT.sfd, SOL_SOCKET, SO_KEEPALIVE, &set, sizeof(set));
diff --git a/toys/pending/tftp.c b/toys/pending/tftp.c
new file mode 100644
index 0000000..60d5f17
--- /dev/null
+++ b/toys/pending/tftp.c
@@ -0,0 +1,454 @@
+/* tftp.c - TFTP client.
+ *
+ * Copyright 2012 Madhur Verma <mad.flexi@gmail.com>
+ * Copyright 2015 Sameer Prakash Pradhan <sameer.p.pradhan@gmail.com>
+ *
+ * No Standard.
+
+USE_TFTP(NEWTOY(tftp, "<1b#<8>65464r:l:g|p|[!gp]", TOYFLAG_USR|TOYFLAG_BIN))
+
+config TFTP
+ bool "tftp"
+ default n
+ help
+ usage: tftp [OPTIONS] HOST [PORT]
+
+ Transfer file from/to tftp server.
+
+ -l FILE Local FILE
+ -r FILE Remote FILE
+ -g Get file
+ -p Put file
+ -b SIZE Transfer blocks of SIZE octets(8 <= SIZE <= 65464)
+*/
+#define FOR_tftp
+#include "toys.h"
+
+GLOBALS(
+ char *local_file;
+ char *remote_file;
+ long block_size;
+
+ struct sockaddr_storage inaddr;
+ int af;
+)
+
+#define TFTP_BLKSIZE 512
+#define TFTP_RETRIES 3
+#define TFTP_DATAHEADERSIZE 4
+#define TFTP_MAXPACKETSIZE (TFTP_DATAHEADERSIZE + TFTP_BLKSIZE)
+#define TFTP_PACKETSIZE TFTP_MAXPACKETSIZE
+#define TFTP_DATASIZE (TFTP_PACKETSIZE-TFTP_DATAHEADERSIZE)
+#define TFTP_IOBUFSIZE (TFTP_PACKETSIZE+8)
+
+#define TFTP_OP_RRQ 1 /* Read Request RFC 1350, RFC 2090 */
+#define TFTP_OP_WRQ 2 /* Write Request RFC 1350 */
+#define TFTP_OP_DATA 3 /* Data chunk RFC 1350 */
+#define TFTP_OP_ACK 4 /* Acknowledgement RFC 1350 */
+#define TFTP_OP_ERR 5 /* Error Message RFC 1350 */
+#define TFTP_OP_OACK 6 /* Option acknowledgment RFC 2347 */
+
+#define TFTP_ER_ILLEGALOP 4 /* Illegal TFTP operation */
+#define TFTP_ER_UNKID 5 /* Unknown transfer ID */
+
+#define TFTP_ES_NOSUCHFILE "File not found"
+#define TFTP_ES_ACCESS "Access violation"
+#define TFTP_ES_FULL "Disk full or allocation exceeded"
+#define TFTP_ES_ILLEGALOP "Illegal TFTP operation"
+#define TFTP_ES_UNKID "Unknown transfer ID"
+#define TFTP_ES_EXISTS "File already exists"
+#define TFTP_ES_UNKUSER "No such user"
+#define TFTP_ES_NEGOTIATE "Terminate transfer due to option negotiation"
+
+// Initializes SERVER with ADDR and returns socket.
+static int init_tftp(struct sockaddr_storage *server)
+{
+ struct timeval to = { .tv_sec = 10, //Time out
+ .tv_usec = 0 };
+ const int set = 1;
+ int port = 69, sd = xsocket(TT.af, SOCK_DGRAM, IPPROTO_UDP);
+
+ xsetsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (void *)&to, sizeof(struct timeval));
+ xsetsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (void *)&set, sizeof(set));
+
+ if(toys.optc == 2) port = atolx_range(toys.optargs[1], 1, 65535);
+ memset(server, 0, sizeof(struct sockaddr_storage));
+ if (TT.af == AF_INET6) {
+ ((struct sockaddr_in6 *)server)->sin6_family = AF_INET6;
+ ((struct sockaddr_in6 *)server)->sin6_addr =
+ ((struct sockaddr_in6 *)&TT.inaddr)->sin6_addr;
+ ((struct sockaddr_in6 *)server)->sin6_port = htons(port);
+ }
+ else {
+ ((struct sockaddr_in *)server)->sin_family = AF_INET;
+ ((struct sockaddr_in *)server)->sin_addr.s_addr =
+ ((struct sockaddr_in *)&TT.inaddr)->sin_addr.s_addr;
+ ((struct sockaddr_in *)server)->sin_port = htons(port);
+ }
+ return sd;
+}
+
+/*
+ * Makes a request packet in BUFFER with OPCODE and file PATH of MODE
+ * and returns length of packet.
+ */
+static int mkpkt_request(uint8_t *buffer, int opcode, char *path, int mode)
+{
+ buffer[0] = opcode >> 8;
+ buffer[1] = opcode & 0xff;
+ if(strlen(path) > TFTP_BLKSIZE) error_exit("path too long");
+ return sprintf((char*) &buffer[2], "%s%c%s", path, 0,
+ (mode ? "octet" : "netascii")) + 3;
+}
+
+/*
+ * Makes an acknowledgement packet in BUFFER of BLOCNO
+ * and returns packet length.
+ */
+static int mkpkt_ack(uint8_t *buffer, uint16_t blockno)
+{
+ buffer[0] = TFTP_OP_ACK >> 8;
+ buffer[1] = TFTP_OP_ACK & 0xff;
+ buffer[2] = blockno >> 8;
+ buffer[3] = blockno & 0xff;
+ return 4;
+}
+
+/*
+ * Makes an error packet in BUFFER with ERRORCODE and ERRORMSG.
+ * and returns packet length.
+ */
+static int mkpkt_err(uint8_t *buffer, uint16_t errorcode, char *errormsg)
+{
+ buffer[0] = TFTP_OP_ERR >> 8;
+ buffer[1] = TFTP_OP_ERR & 0xff;
+ buffer[2] = errorcode >> 8;
+ buffer[3] = errorcode & 0xff;
+ strcpy((char*) &buffer[4], errormsg);
+ return strlen(errormsg) + 5;
+}
+
+/*
+ * Recieves data from server in BUFF with socket SD and updates FROM
+ * and returns read length.
+ */
+static ssize_t read_server(int sd, void *buf, size_t len,
+ struct sockaddr_storage *from)
+{
+ socklen_t alen;
+ ssize_t nb;
+
+ for (;;) {
+ memset(buf, 0, len);
+ alen = sizeof(struct sockaddr_storage);
+ nb = recvfrom(sd, buf, len, 0, (struct sockaddr *) from, &alen);
+ if (nb < 0) {
+ if (errno == EAGAIN) {
+ perror_msg("server read timed out");
+ return nb;
+ }else if (errno != EINTR) {
+ perror_msg("server read failed");
+ return nb;
+ }
+ }else return nb;
+ }
+ return nb;
+}
+
+/*
+ * sends data to server TO from BUFF of length LEN through socket SD
+ * and returns successfully send bytes number.
+ */
+static ssize_t write_server(int sd, void *buf, size_t len,
+ struct sockaddr_storage *to)
+{
+ ssize_t nb;
+
+ for (;;) {
+ nb = sendto(sd, buf, len, 0, (struct sockaddr *)to,
+ sizeof(struct sockaddr_storage));
+ if (nb < 0) {
+ if (errno != EINTR) {
+ perror_msg("server write failed");
+ return nb;
+ }
+ } else return nb;
+ }
+ return nb;
+}
+
+// checks packet for data and updates block no
+static inline int check_data( uint8_t *packet, uint16_t *opcode,
+ uint16_t *blockno)
+{
+ *opcode = (uint16_t) packet[0] << 8 | (uint16_t) packet[1];
+ if (*opcode == TFTP_OP_DATA) {
+ *blockno = (uint16_t) packet[2] << 8 | (uint16_t) packet[3];
+ return 0;
+ }
+ return -1;
+}
+
+// Makes data packet through FD from file OFFSET in buffer PACKET of BLOCKNO
+static int mkpkt_data(int fd, off_t offset, uint8_t *packet, uint16_t blockno)
+{
+ off_t tmp;
+ int nbytesread;
+
+ packet[0] = TFTP_OP_DATA >> 8;
+ packet[1] = TFTP_OP_DATA & 0xff;
+ packet[2] = blockno >> 8;
+ packet[3] = blockno & 0xff;
+ tmp = lseek(fd, offset, SEEK_SET);
+ if (tmp == (off_t) -1) {
+ perror_msg("lseek failed");
+ return -1;
+ }
+ nbytesread = readall(fd, &packet[TFTP_DATAHEADERSIZE], TFTP_DATASIZE);
+ if (nbytesread < 0) return -1;
+ return nbytesread + TFTP_DATAHEADERSIZE;
+}
+
+// Receives ACK responses from server and updates blockno
+static int read_ack(int sd, uint8_t *packet, struct sockaddr_storage *server,
+ uint16_t *port, uint16_t *blockno)
+{
+ struct sockaddr_storage from;
+ ssize_t nbytes;
+ uint16_t opcode, rblockno;
+ int packetlen, retry;
+
+ for (retry = 0; retry < TFTP_RETRIES; retry++) {
+ for (;;) {
+ nbytes = read_server(sd, packet, TFTP_IOBUFSIZE, &from);
+ if (nbytes < 4) { // Ack headersize = 4
+ if (nbytes == 0) error_msg("Connection lost.");
+ else if (nbytes > 0) error_msg("Short packet: %d bytes", nbytes);
+ else error_msg("Server read ACK failure.");
+ break;
+ } else {
+ if (!*port) {
+ *port = ((struct sockaddr_in *)&from)->sin_port;
+ ((struct sockaddr_in *)server)->sin_port =
+ ((struct sockaddr_in *)&from)->sin_port;
+ }
+ if (((struct sockaddr_in *)server)->sin_addr.s_addr !=
+ ((struct sockaddr_in *)&from)->sin_addr.s_addr) {
+ error_msg("Invalid address in DATA.");
+ continue;
+ }
+ if (*port != ((struct sockaddr_in *)server)->sin_port) {
+ error_msg("Invalid port in DATA.");
+ packetlen = mkpkt_err(packet, TFTP_ER_UNKID, TFTP_ES_UNKID);
+ (void) write_server(sd, packet, packetlen, server);
+ continue;
+ }
+ opcode = (uint16_t) packet[0] << 8 | (uint16_t) packet[1];
+ rblockno = (uint16_t) packet[2] << 8 | (uint16_t) packet[3];
+
+ if (opcode != TFTP_OP_ACK) {
+ error_msg("Bad opcode.");
+ if (opcode > 5) {
+ packetlen = mkpkt_err(packet, TFTP_ER_ILLEGALOP, TFTP_ES_ILLEGALOP);
+ (void) write_server(sd, packet, packetlen, server);
+ }
+ break;
+ }
+ if (blockno) *blockno = rblockno;
+ return 0;
+ }
+ }
+ }
+ error_msg("Timeout, Waiting for ACK.");
+ return -1;
+}
+
+// receives file from server.
+static int file_get(void)
+{
+ struct sockaddr_storage server, from;
+ uint8_t *packet;
+ uint16_t blockno = 0, opcode, rblockno = 0;
+ int len, sd, fd, retry, nbytesrecvd = 0, ndatabytes, ret, result = -1;
+
+ sd = init_tftp(&server);
+
+ packet = (uint8_t*) xzalloc(TFTP_IOBUFSIZE);
+ fd = xcreate(TT.local_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+
+ len = mkpkt_request(packet, TFTP_OP_RRQ, TT.remote_file, 1);
+ ret = write_server(sd, packet, len, &server);
+ if (ret != len){
+ unlink(TT.local_file);
+ goto errout_with_sd;
+ }
+ if (TT.af == AF_INET6) ((struct sockaddr_in6 *)&server)->sin6_port = 0;
+ else ((struct sockaddr_in *)&server)->sin_port = 0;
+
+ do {
+ blockno++;
+ for (retry = 0 ; retry < TFTP_RETRIES; retry++) {
+ nbytesrecvd = read_server(sd, packet, TFTP_IOBUFSIZE, &from);
+ if (nbytesrecvd > 0) {
+ if ( ((TT.af == AF_INET) &&
+ memcmp(&((struct sockaddr_in *)&server)->sin_addr,
+ &((struct sockaddr_in *)&from)->sin_addr,
+ sizeof(struct in_addr))) ||
+ ((TT.af == AF_INET6) &&
+ memcmp(&((struct sockaddr_in6 *)&server)->sin6_addr,
+ &((struct sockaddr_in6 *)&from)->sin6_addr,
+ sizeof(struct in6_addr)))) {
+ error_msg("Invalid address in DATA.");
+ retry--;
+ continue;
+ }
+ if ( ((TT.af == AF_INET) && ((struct sockaddr_in *)&server)->sin_port
+ && (((struct sockaddr_in *)&server)->sin_port !=
+ ((struct sockaddr_in *)&from)->sin_port)) ||
+ ((TT.af == AF_INET6) && ((struct sockaddr_in6 *)&server)->sin6_port
+ && (((struct sockaddr_in6 *)&server)->sin6_port !=
+ ((struct sockaddr_in6 *)&from)->sin6_port))) {
+ error_msg("Invalid port in DATA.");
+ len = mkpkt_err(packet, TFTP_ER_UNKID, TFTP_ES_UNKID);
+ ret = write_server(sd, packet, len, &from);
+ retry--;
+ continue;
+ }
+ if (nbytesrecvd < TFTP_DATAHEADERSIZE) {
+ error_msg("Tiny data packet ignored.");
+ continue;
+ }
+ if (check_data(packet, &opcode, &rblockno) != 0
+ || blockno != rblockno) {
+
+ if (opcode == TFTP_OP_ERR) {
+ char *message = "DATA Check failure.";
+ char *arr[] = {TFTP_ES_NOSUCHFILE, TFTP_ES_ACCESS,
+ TFTP_ES_FULL, TFTP_ES_ILLEGALOP,
+ TFTP_ES_UNKID, TFTP_ES_EXISTS,
+ TFTP_ES_UNKUSER, TFTP_ES_NEGOTIATE};
+ if (rblockno && (rblockno < 9)) message = arr[rblockno - 1];
+ error_msg(message);
+ }
+ if (opcode > 5) {
+ len = mkpkt_err(packet, TFTP_ER_ILLEGALOP, TFTP_ES_ILLEGALOP);
+ ret = write_server(sd, packet, len, &from);
+ }
+ continue;
+ }
+ if ((TT.af == AF_INET6) && !((struct sockaddr_in6 *)&server)->sin6_port)
+ ((struct sockaddr_in6 *)&server)->sin6_port =
+ ((struct sockaddr_in6 *)&from)->sin6_port;
+ else if ((TT.af == AF_INET) && !((struct sockaddr_in *)&server)->sin_port)
+ ((struct sockaddr_in *)&server)->sin_port =
+ ((struct sockaddr_in *)&from)->sin_port;
+ break;
+ }
+ }
+ if (retry == TFTP_RETRIES) {
+ error_msg("Retry limit exceeded.");
+ unlink(TT.local_file);
+ goto errout_with_sd;
+ }
+ ndatabytes = nbytesrecvd - TFTP_DATAHEADERSIZE;
+ if (writeall(fd, packet + TFTP_DATAHEADERSIZE, ndatabytes) < 0){
+ unlink(TT.local_file);
+ goto errout_with_sd;
+ }
+ len = mkpkt_ack(packet, blockno);
+ ret = write_server(sd, packet, len, &server);
+ if (ret != len){
+ unlink(TT.local_file);
+ goto errout_with_sd;
+ }
+ } while (ndatabytes >= TFTP_DATASIZE);
+
+ result = 0;
+
+errout_with_sd: xclose(sd);
+ free(packet);
+ return result;
+}
+
+// Sends file to server.
+int file_put(void)
+{
+ struct sockaddr_storage server;
+ uint8_t *packet;
+ off_t offset = 0;
+ uint16_t blockno = 1, rblockno, port = 0;
+ int packetlen, sd, fd, retry = 0, ret, result = -1;
+
+ sd = init_tftp(&server);
+ packet = (uint8_t*)xzalloc(TFTP_IOBUFSIZE);
+ fd = xopen(TT.local_file, O_RDONLY);
+
+ for (;;) { //first loop for request send and confirmation from server.
+ packetlen = mkpkt_request(packet, TFTP_OP_WRQ, TT.remote_file, 1);
+ ret = write_server(sd, packet, packetlen, &server);
+ if (ret != packetlen) goto errout_with_sd;
+ if (read_ack(sd, packet, &server, &port, NULL) == 0) break;
+ if (++retry > TFTP_RETRIES) {
+ error_msg("Retry count exceeded.");
+ goto errout_with_sd;
+ }
+ }
+ for (;;) { // loop for data sending and receving ack from server.
+ packetlen = mkpkt_data(fd, offset, packet, blockno);
+ if (packetlen < 0) goto errout_with_sd;
+
+ ret = write_server(sd, packet, packetlen, &server);
+ if (ret != packetlen) goto errout_with_sd;
+
+ if (read_ack(sd, packet, &server, &port, &rblockno) == 0) {
+ if (rblockno == blockno) {
+ if (packetlen < TFTP_PACKETSIZE) break;
+ blockno++;
+ offset += TFTP_DATASIZE;
+ retry = 0;
+ continue;
+ }
+ }
+ if (++retry > TFTP_RETRIES) {
+ error_msg("Retry count exceeded.");
+ goto errout_with_sd;
+ }
+ }
+ result = 0;
+
+errout_with_sd: close(sd);
+ free(packet);
+ return result;
+}
+
+void tftp_main(void)
+{
+ struct addrinfo *info, rp, *res=0;
+ int ret;
+
+ if (toys.optflags & FLAG_r) {
+ if (!(toys.optflags & FLAG_l)) {
+ char *slash = strrchr(TT.remote_file, '/');
+ TT.local_file = (slash) ? slash + 1 : TT.remote_file;
+ }
+ } else if (toys.optflags & FLAG_l) TT.remote_file = TT.local_file;
+ else error_exit("Please provide some files.");
+
+ memset(&rp, 0, sizeof(rp));
+ rp.ai_family = AF_UNSPEC;
+ rp.ai_socktype = SOCK_STREAM;
+ ret = getaddrinfo(toys.optargs[0], toys.optargs[1], &rp, &info);
+ if (!ret) {
+ for (res = info; res; res = res->ai_next)
+ if ( (res->ai_family == AF_INET) || (res->ai_family == AF_INET6)) break;
+ }
+ if (!res)
+ error_exit("bad address '%s' : %s", toys.optargs[0], gai_strerror(ret));
+ TT.af = info->ai_family;
+
+ memcpy((void *)&TT.inaddr, info->ai_addr, info->ai_addrlen);
+ freeaddrinfo(info);
+
+ if (toys.optflags & FLAG_g) file_get();
+ if (toys.optflags & FLAG_p) file_put();
+}
diff --git a/toys/posix/cal.c b/toys/posix/cal.c
index bb476df..bd3afad 100644
--- a/toys/posix/cal.c
+++ b/toys/posix/cal.c
@@ -64,14 +64,6 @@ static char *calstrings(char *buf, struct tm *tm)
return buf;
}
-void xcheckrange(long val, long low, long high)
-{
- char *err = "%ld %s than %ld";
-
- if (val < low) error_exit(err, val, "less", low);
- if (val > high) error_exit(err, val, "greater", high);
-}
-
// Worst case scenario toybuf usage: sizeof(struct tm) plus 21 bytes/line
// plus 8 lines/month plus 12 months, comes to a bit over 2k of our 4k buffer.
@@ -86,12 +78,12 @@ void cal_main(void)
buf += sizeof(struct tm);
// Last argument is year, one before that (if any) is month.
- xcheckrange(tm->tm_year = atol(toys.optargs[--toys.optc]),1,9999);
+ tm->tm_year = atolx_range(toys.optargs[--toys.optc], 1, 9999);
tm->tm_year -= 1900;
tm->tm_mday = 1;
tm->tm_hour = 12; // noon to avoid timezone weirdness
if (toys.optc) {
- xcheckrange(tm->tm_mon = atol(toys.optargs[--toys.optc]),1,12);
+ tm->tm_mon = atolx_range(toys.optargs[--toys.optc], 1, 12);
tm->tm_mon--;
// Print 12 months of the year
diff --git a/toys/posix/chmod.c b/toys/posix/chmod.c
index 64369b6..4292439 100644
--- a/toys/posix/chmod.c
+++ b/toys/posix/chmod.c
@@ -39,7 +39,7 @@ GLOBALS(
char *mode;
)
-int do_chmod(struct dirtree *try)
+static int do_chmod(struct dirtree *try)
{
mode_t mode;
diff --git a/toys/posix/cmp.c b/toys/posix/cmp.c
index 2dae113..829da75 100644
--- a/toys/posix/cmp.c
+++ b/toys/posix/cmp.c
@@ -28,7 +28,7 @@ GLOBALS(
// This handles opening the file and
-void do_cmp(int fd, char *name)
+static void do_cmp(int fd, char *name)
{
int i, len1, len2, min_len, size = sizeof(toybuf)/2;
long byte_no = 1, line_no = 1;
diff --git a/toys/posix/date.c b/toys/posix/date.c
index d4c4524..a42de50 100644
--- a/toys/posix/date.c
+++ b/toys/posix/date.c
@@ -7,22 +7,23 @@
* Note: setting a 2 year date is 50 years back/forward from today,
* not posix's hardwired magic dates.
-USE_DATE(NEWTOY(date, "d:s:r:u[!dr]", TOYFLAG_BIN))
+USE_DATE(NEWTOY(date, "d:D:r:u[!dr]", TOYFLAG_BIN))
config DATE
bool "date"
default y
help
- usage: date [-u] [-r FILE] [-d DATE] [+DISPLAY_FORMAT] [-s SET_FORMAT] [SET]
+ usage: date [-u] [-r FILE] [-d DATE] [+DISPLAY_FORMAT] [-D SET_FORMAT] [SET]
Set/get the current date/time. With no SET shows the current date.
Default SET format is "MMDDhhmm[[CC]YY][.ss]", that's (2 digits each)
month, day, hour (0-23), and minute. Optionally century, year, and second.
+ Also accepts "@UNIXTIME[.FRACTION]" as seconds since midnight Jan 1 1970.
-d Show DATE instead of current time (convert date format)
+ -D +FORMAT for SET or -d (instead of MMDDhhmm[[CC]YY][.ss])
-r Use modification time of FILE instead of current date
- -s +FORMAT for SET or -d (instead of MMDDhhmm[[CC]YY][.ss])
-u Use UTC instead of current timezone
+FORMAT specifies display format string using these escapes:
@@ -49,15 +50,79 @@ GLOBALS(
char *file;
char *setfmt;
char *showdate;
+
+ char *tz;
+ unsigned nano;
)
-// Handle default posix date format: mmddhhmm[[cc]yy]
+// mktime(3) normalizes the struct tm fields, but date(1) shouldn't.
+static time_t chkmktime(struct tm *tm, const char *str, const char* fmt)
+{
+ struct tm tm0 = *tm;
+ struct tm tm1;
+ time_t t = mktime(tm);
+
+ if (t == -1 || !localtime_r(&t, &tm1) ||
+ tm0.tm_sec != tm1.tm_sec || tm0.tm_min != tm1.tm_min ||
+ tm0.tm_hour != tm1.tm_hour || tm0.tm_mday != tm1.tm_mday ||
+ tm0.tm_mon != tm1.tm_mon) {
+ int len;
+
+ strftime(toybuf, sizeof(toybuf), fmt, &tm0);
+ len = strlen(toybuf) + 1;
+ strftime(toybuf + len, sizeof(toybuf) - len, fmt, &tm1);
+ error_exit("bad date '%s'; %s != %s", str, toybuf, toybuf + len);
+ }
+ return t;
+}
+
+static void utzset(void)
+{
+ if (!(TT.tz = getenv("TZ"))) TT.tz = (char *)1;
+ setenv("TZ", "UTC", 1);
+ tzset();
+}
+
+static void utzreset(void)
+{
+ if (TT.tz) {
+ if (TT.tz != (char *)1) setenv("TZ", TT.tz, 1);
+ else unsetenv("TZ");
+ tzset();
+ }
+}
+
+// Handle default posix date format (mmddhhmm[[cc]yy]) or @UNIX[.FRAC]
// returns 0 success, nonzero for error
-int parse_posixdate(char *str, struct tm *tm)
+static int parse_default(char *str, struct tm *tm)
{
- int len;
+ int len = 0;
+
+ // Parse @UNIXTIME[.FRACTION]
+ if (*str == '@') {
+ long long ll;
+ time_t tt;
+
+ // Collect seconds and nanoseconds
+ // Note: struct tm hasn't got a fractional seconds field, thus strptime()
+ // doesn't support it, so store nanoseconds out of band (in globals).
+ // tt and ll are separate because we can't guarantee time_t is 64 bit (yet).
+ sscanf(str, "@%lld%n", &ll, &len);
+ if (str[len]=='.') {
+ str += len+1;
+ for (len = 0; len<9; len++) {
+ TT.nano *= 10;
+ if (isdigit(str[len])) TT.nano += str[len]-'0';
+ }
+ }
+ if (str[len]) return 1;
+ tt = ll;
+ gmtime_r(&tt, tm);
+
+ return 0;
+ }
- len = 0;
+ // Posix format
sscanf(str, "%2u%2u%2u%2u%n", &tm->tm_mon, &tm->tm_mday, &tm->tm_hour,
&tm->tm_min, &len);
if (len != 8) return 1;
@@ -78,40 +143,38 @@ int parse_posixdate(char *str, struct tm *tm)
// 2 digit years, next 50 years are "future", last 50 years are "past".
// A "future" date in past is a century ahead.
// A non-future date in the future is a century behind.
- if ((r1 < r2) ? (r1 < year && year < r2) : (year < r1 || year > r2)) {
- if (year < r1) year += 100;
- } else if (year > r1) year -= 100;
+ if (len == 2) {
+ if ((r1 < r2) ? (r1 < year && year < r2) : (year < r1 || year > r2)) {
+ if (year < r1) year += 100;
+ } else if (year > r1) year -= 100;
+ }
tm->tm_year = year + century;
}
if (*str == '.') {
len = 0;
sscanf(str, ".%u%n", &tm->tm_sec, &len);
str += len;
- }
+ } else tm->tm_sec = 0;
return *str;
}
void date_main(void)
{
- char *setdate = *toys.optargs, *format_string = "%a %b %e %H:%M:%S %Z %Y",
- *tz = 0;
+ char *setdate = *toys.optargs, *format_string = "%a %b %e %H:%M:%S %Z %Y";
struct tm tm;
+ memset(&tm, 0, sizeof(struct tm));
+
// We can't just pass a timezone to mktime because posix.
- if (toys.optflags & FLAG_u) {
- if (CFG_TOYBOX_FREE) tz = getenv("TZ");
- setenv("TZ", "UTC", 1);
- tzset();
- }
+ if (toys.optflags & FLAG_u) utzset();
if (TT.showdate) {
- setdate = TT.showdate;
if (TT.setfmt) {
char *s = strptime(TT.showdate, TT.setfmt+(*TT.setfmt=='+'), &tm);
- if (!s || *s) goto bad_date;
- } else if (parse_posixdate(TT.showdate, &tm)) goto bad_date;
+ if (!s || *s) goto bad_showdate;
+ } else if (parse_default(TT.showdate, &tm)) goto bad_showdate;
} else {
time_t now;
@@ -125,7 +188,6 @@ void date_main(void)
((toys.optflags & FLAG_u) ? gmtime_r : localtime_r)(&now, &tm);
}
- setdate = *toys.optargs;
// Fall through if no arguments
if (!setdate);
// Display the date?
@@ -137,39 +199,26 @@ void date_main(void)
} else if (setdate) {
struct timeval tv;
- if (parse_posixdate(setdate, &tm)) goto bad_date;
+ if (parse_default(setdate, &tm)) error_exit("bad date '%s'", setdate);
if (toys.optflags & FLAG_u) {
- char *tz = CFG_TOYBOX_FREE ? getenv("TZ") : 0;
-
// We can't just pass a timezone to mktime because posix.
- setenv("TZ", "UTC", 1);
- tzset();
- tv.tv_sec = mktime(&tm);
- if (CFG_TOYBOX_FREE) {
- if (tz) setenv("TZ", tz, 1);
- else unsetenv("TZ");
- tzset();
- }
- } else tv.tv_sec = mktime(&tm);
- if (tv.tv_sec == (time_t)-1) goto bad_date;
+ utzset();
+ tv.tv_sec = chkmktime(&tm, setdate, format_string);
+ utzreset();
+ } else tv.tv_sec = chkmktime(&tm, setdate, format_string);
- tv.tv_usec = 0;
+ tv.tv_usec = TT.nano/1000;
if (settimeofday(&tv, NULL) < 0) perror_msg("cannot set date");
}
- if (toys.optflags & FLAG_u) {
- if (tz) setenv("TZ", tz, 1);
- else unsetenv("TZ");
- tzset();
- }
-
+ utzreset();
if (!strftime(toybuf, sizeof(toybuf), format_string, &tm))
perror_exit("bad format '%s'", format_string);
puts(toybuf);
return;
-bad_date:
- error_exit("bad date '%s'", setdate);
+bad_showdate:
+ error_exit("bad date '%s'", TT.showdate);
}
diff --git a/toys/posix/du.c b/toys/posix/du.c
index 4302997..77c7b6e 100644
--- a/toys/posix/du.c
+++ b/toys/posix/du.c
@@ -57,7 +57,7 @@ static void print(long long size, struct dirtree *node)
if (TT.maxdepth && TT.depth > TT.maxdepth) return;
if (toys.optflags & FLAG_h) {
- human_readable(toybuf, size);
+ human_readable(toybuf, size, 0);
printf("%s", toybuf);
} else {
int bits = 10;
diff --git a/toys/posix/find.c b/toys/posix/find.c
index a11a910..99cf5e2 100644
--- a/toys/posix/find.c
+++ b/toys/posix/find.c
@@ -141,41 +141,6 @@ static void do_print(struct dirtree *new, char c)
free(s);
}
-char *strlower(char *s)
-{
- char *try, *new;
-
- if (!CFG_TOYBOX_I18N) {
- try = new = xstrdup(s);
- for (; *s; s++) *(new++) = tolower(*s);
- } else {
- // I can't guarantee the string _won't_ expand during reencoding, so...?
- try = new = xmalloc(strlen(s)*2+1);
-
- while (*s) {
- wchar_t c;
- int len = mbrtowc(&c, s, MB_CUR_MAX, 0);
-
- if (len < 1) *(new++) = *(s++);
- else {
- s += len;
- // squash title case too
- c = towlower(c);
-
- // if we had a valid utf8 sequence, convert it to lower case, and can't
- // encode back to utf8, something is wrong with your libc. But just
- // in case somebody finds an exploit...
- len = wcrtomb(new, c, 0);
- if (len < 1) error_exit("bad utf8 %x", (int)c);
- new += len;
- }
- }
- *new = 0;
- }
-
- return try;
-}
-
// Call this with 0 for first pass argument parsing and syntax checking (which
// populates argdata). Later commands traverse argdata (in order) when they
// need "do once" results.
diff --git a/toys/posix/id.c b/toys/posix/id.c
index aa43072..01610bc 100644
--- a/toys/posix/id.c
+++ b/toys/posix/id.c
@@ -82,7 +82,7 @@ static void showid(char *header, unsigned u, char *s)
printf("%s%u(%s)", header, u, s);
}
-void do_id(char *username)
+static void do_id(char *username)
{
int flags, i, ngroups;
struct passwd *pw;
diff --git a/toys/posix/ls.c b/toys/posix/ls.c
index 44915fa..f951198 100644
--- a/toys/posix/ls.c
+++ b/toys/posix/ls.c
@@ -422,6 +422,13 @@ static void listfiles(int dirfd, struct dirtree *indir)
mode_to_string(mode, perm);
printf("%s% *ld", perm, totals[2]+1, (long)st->st_nlink);
+ // print user
+ if (!(flags&FLAG_g)) {
+ if (flags&FLAG_n) sprintf(ss = thyme, "%u", (unsigned)st->st_uid);
+ else strwidth(ss = getusername(st->st_uid));
+ printf(" %*s", (int)totals[3], ss);
+ }
+
// print group
if (!(flags&FLAG_o)) {
if (flags&FLAG_n) sprintf(ss = thyme, "%u", (unsigned)st->st_gid);
@@ -429,12 +436,6 @@ static void listfiles(int dirfd, struct dirtree *indir)
printf(" %*s", (int)totals[4], ss);
}
- if (!(flags&FLAG_g)) {
- if (flags&FLAG_n) sprintf(ss = thyme, "%u", (unsigned)st->st_uid);
- else strwidth(ss = getusername(st->st_uid));
- printf(" %*s", (int)totals[3], ss);
- }
-
if (flags & FLAG_Z)
printf(" %*s", -(int)totals[7], (char *)sort[next]->extra);
diff --git a/toys/posix/nl.c b/toys/posix/nl.c
index c7e7b92..60c0a52 100644
--- a/toys/posix/nl.c
+++ b/toys/posix/nl.c
@@ -40,7 +40,7 @@ GLOBALS(
long lcount;
)
-void do_nl(int fd, char *name)
+static void do_nl(int fd, char *name)
{
FILE *f = xfdopen(fd, "r");
int w = TT.w, slen = strlen(TT.s);
diff --git a/toys/posix/split.c b/toys/posix/split.c
index aabf931..075a414 100644
--- a/toys/posix/split.c
+++ b/toys/posix/split.c
@@ -35,7 +35,7 @@ GLOBALS(
char *outfile;
)
-void do_split(int infd, char *in)
+static void do_split(int infd, char *in)
{
unsigned long bytesleft, linesleft, filenum, len, pos;
int outfd = -1;
diff --git a/toys/posix/strings.c b/toys/posix/strings.c
index a872cf6..e87ccde 100644
--- a/toys/posix/strings.c
+++ b/toys/posix/strings.c
@@ -29,7 +29,7 @@ GLOBALS(
long num;
)
-void do_strings(int fd, char *filename)
+static void do_strings(int fd, char *filename)
{
int nread, i, wlen = TT.num, count = 0;
off_t offset = 0;
diff --git a/www/news.html b/www/news.html
index 084d753..0c5da77 100755
--- a/www/news.html
+++ b/www/news.html
@@ -8,7 +8,245 @@ a development environment. See the links on the left for details.</p>
<h2>News</h2>
-<hr><b>April 5, 2015</b>
+<a name="23-07-2015" /><a href="#23-07-2015"><hr><h2><b>July 23, 2015</b></h2></a>
+<p>I recreated the <a href=downloads/toybox-0.6.0.tar.gz>0.6.0 source tarball</a>
+(new sha1sum 08fb1c23f520c25a15f262a8a95ea5b676a98d54)
+because I forgot to add --prefix to the git archive command when I updated
+my release script from mercurial, so the files weren't in an enclosing
+directory. (Ooops.)</p>
+
+<a name="19-07-2015" /><a href="#19-07-2015"><hr><h2><b>July 19, 2015</b></h2></a>
+<blockquote><p>
+The reason why it was published in the form of a micro sub meson electronic
+component is that if it were printed in normal book form, an interstellar
+hitchhiker would require several inconveniently large buildings to carry it
+around in." - The Hitchhiker's Guide to the Galaxy </p></blockquote>
+
+<p><a href=downloads/toybox-0.6.0.tar.gz>Toybox 0.6.0</a>
+(<a href=https://github.com/landley/toybox/releases/tag/0.6.0>git commit</a>)
+is out. (Yes, git. See the <a href=#05-04-2015>previous news entry</a>.)</p>
+
+<p>Sorry for the unusually long gap between releases. Since last release Ye
+Olde Project Maintainer traveled to japan twice and had two more "once
+a century" floods at home. (Probably a coincidence.) Still catching up.</p>
+
+<h3><b>CELF/ELC talk and Wikipedia[citation needed] article</b></h3>
+
+<p>I gave another State Of The Toybox talk
+(<a href=https://www.youtube.com/watch?v=04XwAbtPmAg>video</a>
+<a href=http://landley.net/talks/celf-2015.txt>outline</a>), in which I
+repeat my <a href=http://landley.net/notes-2013.html#07-11-2013>perennial</a>
+<a href=https://twitter.com/landley/status/557309224535851009>complaint</a>
+that Wikipedia[citation needed]
+<a href=http://en.wikipedia.org/wiki/Toybox>still</a>
+<a href=https://en.wikipedia.org/wiki/BusyBox#Controversy_over_Toybox>says</a>
+toybox was relicensed before its hiatus, when relicensing was why
+the hiatus ended.</p>
+
+<p>Since Wikipedia[citation needed] seems unable to do the
+<a href=#15-11-2011>most</a>
+<a href=http://landley.net/hg/toybox/log/tip/LICENSE>basic</a>
+<a href=http://landley.net/notes-2011.html#13-11-2011>research</a> on
+this point, and has stuck to an incorrect sequence of events for years,
+I've been gradually escalating my attempts to correct them. Toybox
+came out of mothballs in November 2011 <b>because</b> it could be
+relicensed. That's what opened up a new niche busybox wasn't already
+filling with a 10 year headstart.</p>
+
+<a name="asterisk_back" />
+<p>The article has plenty of smaller issues<a href=#asterisk>*</a>, but
+given that I gave an entire talk at Ohio LinuxFest in 2013
+(<a href=http://landley.net/talks/ohio-2013.txt>outline</a>,
+<a href=https://archive.org/download/OhioLinuxfest2013/24-Rob_Landley-The_Rise_and_Fall_of_Copyleft.mp3>audio</a>) on why I switched away from GPL for
+my projects, that one bugs me.</p>
+
+<h3><b>New stuff this release</b></h3>
+
+<p>There's a new android menu in menuconfig, and rather a lot of Linux
+Security Module support (Smack for Tizen from Xavier Roche and José Bollo,
+and SELinux for Android from Elliott Hughes; see
+the Security Blanket menu under global settings in menuconfig) has
+trickled in, although there's still more to come.</p>
+
+<p><b>New commands:</b> Added reset, nproc, ionice, and iorenice.
+Elliott Hughes contributed xxd, runcon,
+restorecon, load_policy, getenforce, setenforce, getprop, and setprop.
+Promoted shred, nsenter, and hwclock.</p>
+
+<p>You can once again build catv now the flag infrastructure's been updated to
+let it coexist with cat -v.
+And on a long plane flight I wrote
+hexedit, an interactive hex editor that implements the start of
+cursor control infrastructure (for eventual use by less and vi and shell
+command history and so on).</p>
+
+<p><b>New options:</b> Added sed -E as a BSD-compatible synonym for -r.
+Upgraded oneit with -r (restart), -3 (send exiting PID values to child),
+and signal handling. Added -v option to timeout, -m to mknod, -u to shred,
+-t to dmesg, and -123 to head and tail. Added implicit "." to grep -r without
+any files to work on. Hyejin Kim requested prefix support for truncate -s.
+Greg Hackman added -inum to find.
+Jan Cybulski added the smack side of ls -Z support. Various patches also
+added -Z to mkdir, mknod, and mkfifo.
+Basic cp --preserve support went in, but not yet the xattr/LSM parts.</p>
+
+<p>The toybox command now has a --version option,
+which uses "git describe" if available.</p>
+
+<p><b>Build infrastructure:</b>
+The "make change" target now saves the output of each failed standalone
+command build in a .bad file, and "make defconfig" is quieter now.</p>
+
+<p>Paul Barker submitted a large patch changing command install paths so
+"toybox can be installed alongside busybox without confusing
+update-alternatives". (There's some argument over
+what the right paths should be, and I'm waiting for
+people to tell me what else needs fixing because I have no idea. I've
+been symlinking /bin to /usr/bin since 2002
+<a href=http://landley.net/writing/hackermonthly-issue022-pg33.pdf>for
+historical reasons</a>.)</p>
+
+<p><b>Docs:</b> The repository link now goes to github, with another link
+to the commit rss feed.</p>
+
+<p>Elliott Hughes updated the Android section of the roadmap
+(and he would know). Redid bits of scripts/mkstatus.py to make updating
+status.html easier, and the README is larger.</p>
+
+<p>More description of option parsing in code.html, which now describes the
+FLAG_x macros, switching flag macro sets with FOR_newcommand, how
+configuration zeroes flag macros and using FORCE_FLAGS to suppress the
+zeroing of options shared between commands. Also added description of ";"
+to make --longopts take an optional =value part, and more about TOYBOX_DEBUG
+to check NEWTOY() option strings (otherwise a bad option string makes
+lib/args.c obviously segfault, but doesn't explain why).</p>
+
+<p>Added a "Why 0BSD?" section to license.html when submitting zero clause bsd
+to SPDX (according to the pending license spreadsheet, it's been approved for
+SPDX 2.2).</p>
+
+<p>The old list of commands needing cleanup but not in pending was
+removed from toys/pending/README and instead the issues were added
+as TODO comments in the individual commands.</p>
+
+<p><b>Bugfixes:</b>
+Fixed mount -a segfaulting without -O (reported by Janus Troelsen),
+and made it try a "become rw" ioctl() on the block device before falling
+back to mounting read only (because Android expects that).
+Fixed printf -- and printf ---. Lots of tweaks to ls -l spacing with
+different options. Make touch -d and -t actually set time when you don't
+specify nanoseconds.
+Fixed a subtle bug where recursive calls (toybox commands that run other
+toybox commands) weren't resetting all their state. (This manifested as
+a "no }" error from "find | xargs sed", but could cause other problems.)
+And David Halls reported another sed bug trying to compile libiconv (which
+left extra \ at the start of lines in a generated shell script, breaking
+the build). Output an error message for "cat /mnt".</p>
+
+<p>Kylie McClain reported that mktemp broke when $TMPDIR was set to an empty
+string (which is not the same as unset), that install/find didn't support
+numeric uid/gids, and that sort -z affects both input and output.
+Isabella Parakiss fixed a printf.c bug.
+David Halls fixed bugs in install -D and find -exec. Samuel Holland
+fixed unshare -r. Hyejin Kim fixed makedevs with a count of 1, fold -w
+range checking, an error path in scripts/mkflags.c, added -i to dhcpd,
+and stopped su from prompting the root user for the new user's password.
+Jan Cybulski spotted wrong indentation when combining ls -s and -i with -C and
+-x. José Bollo fixed stat %G. Sameer Pradhan fixed a bug in mkfifo -Z.</p>
+
+<p>Elliott Hughes asked for a default SIGPIPE handler to disable
+the signal handler bionic's dynamic loader installs (yes really). Still not
+100% sure what the correct behavior is there. (Posix is
+(<a href=http://permalink.gmane.org/gmane.comp.standards.posix.austin.general/10915>actively unhelpful</a>, but at least they're taking
+<a href=http://austingroupbugs.net/view.php?id=789#c1976>years to
+make up their mind</a>. Elliott also sent patches to fix a typo in
+useradd.test, add missing arguments to error_exit() calls and clean up
+printf() format strings, fix an off by one error in human_readable(),
+fix dmesg -c error reporting, fix a segfault in comma_scan where the option
+was the last item in optlist (triggered by mount -o ro,remount), fix
+hwclock -w, made ifconfig print lowercase MAC addresses (it was bothering
+him), and make terminal_size() read the right environment variable
+(LINES, not ROWS). And he suggested the test suite notice high command exit
+values (corresponding to segfault or other signals).</p>
+
+<p>People are apparently using toys/pending commands, despite the police tape
+and flashing lights, so added louder warnings to toys/pending/README.
+Elliott Hughes fixed various problems with tar, dd, more, and top.
+Hyejin Kim cleaned up syslogd and dumpleases. Isaac Dunham added hotplug
+support to mdev. Yeongdeok Suh added RFC-3315 ipv6 support to dhcpd.</p>
+
+<p>I rewrote ps.c from scratch (in pending), but it's not ready for real use
+yet.</p>
+
+<p><b>Portability:</b>
+On the portability front Bernhard Rosenkranzer fixed a problem where the
+menuconfig code wouldn't compile in C99 mode. (This led to me documenting
+the craptacular nature of kconfig in a README, and the plan to replace it
+sometime before 1.0.) Some extra flags to shut up overzealous llvm warnings
+were added (and have to be probed for because gcc complains about
+arguments it doesn't recognize even when they switch stuff _off_ using
+a standard syntax). Don't depend on malloc(0) to return non-null in ls.
+David Halls fixed some mac/ios portability issues,
+implying somebody's built at least part of toybox on a mac.</p>
+
+<p>Added basename_r() to lib/lib.c because the posix semantics for basename()
+are stupid but what the gnu guys did to it was appalling.
+Turns out bionic already had a basename_r(), but posix still doesn't.
+Fixed it up in portability.h, but this
+could break more stuff in future. (Correct fix is to lobby posix to add it,
+which would probably take about 15 years...)</p>
+
+<p><b>Infrastructure:</b>
+The build now checks $LDFLAGS for linker-only flags, and allows the strip
+command to fail (binflt toolchains provide a strip that doesn't work).
+Since time.c uses floating point, added TOYBOX_FLOAT dependency in config.</p>
+
+<p>There's a lib/lsm.h defining varous inline functions for linux
+security modules stuff, if (lsm_enabled()) should turn into a compile-time
+constant 0 and let code drop out when TOYBOX_LSM_NONE selected, but
+testing against CFG_TOYBOX_LSM_NONE or derived symbols is still useful
+becuase when it _is_ enabled the probe turns into a system call you
+don't want to repeat too much.</p>
+
+<p>Switched a bunch of commands from signal() to xsignal(). Factored out
+xgetgrnamid() and xgetpwnamid() into xwrap.c. Make time.c depend on
+TOYBOX_FLOAT (since it always uses float so shouldn't be available on
+build targets without even software float). Added readfileat() to lib/lib.c.</p>
+
+<p>The dirtree infrastructure now passes in full flags for the old symlink
+field, and the new DIRTREE_SHUTUP flag disables warnings if a file vanishes
+out from under you during traverse. New dirtree_start() wrapper to
+create dirtree root with only two arguments.</p>
+
+<p>The not-curses infrastructure introduced by hexedit mostly moved to
+lib/interestingtimes.c.</p>
+
+<a name="asterisk" />
+<a href="#asterisk_back" />Asterisk:</a> such when
+Tim contacted me (my blog says a couple days before nov 13, 2011, I.E.
+11/11/11 not some specific day 2 months later) to ask if I wanted to work
+on a new project he was proposing called
+<a href=http://www.elinux.org/Busybox_replacement_project>BentoBox</a>
+(because I used to do busybox, he'd forgotten toybox existed
+until I brought it up). And don't ask me what "focuses not on compatibility
+with its GNU counterparts" means when CP_MORE adds 7 non-posix options
+and toys/other has 84 commands in neither posix nor LSB. I think they're
+struggling to explain the difference having dismissed "licensing" as being
+the reason it started up again after a long hiatus? The reason I don't think
+GNU is special is there are a half-dozen other independent
+implementations of the same unix command tools out there (AT&amp;T,
+BSD, Coherent, Minix, plan 9, busybox, toybox, and several more analyzed in
+the <a href=roadmap.html>roadmap</a>, and that's ignoring the implementations
+written for DOS or in assembly over the years). But I do care what
+Linux From Scratch expects, and if it's
+<a href=http://archive.linuxfromscratch.org/lfs-museum/7.6/LFS-BOOK-7.6-NOCHUNKS.html#ch-tools-gcc-pass1>calling mv -v</a>
+then I impelement mv -v
+even if <a href=http://landley.net/toybox/roadmap.html>posix hasn't got
+it</a>. And I don't know why "gnu counterparts" would describe this when
+util-linux isn't a gnu package, nor are info-zip, e2fsprogs, kmod, less,
+procps, shadow, sysklogd, vim, zlib, sudo, dhcpcd...</p>
+
+<a name="05-04-2015" /><a href="#05-04-2015"><hr><h2><b>April 5, 2015</b></h2></a>
<p>Since <a href=https://android.googlesource.com/platform/external/toybox/>android</a> and
<a href=https://git.tizen.org/cgit/platform/upstream/toybox.git>tizen</a>
and <a href=https://github.com/kraj/meta-musl/tree/master/recipes-core/toybox>openembedded</a>
@@ -18,7 +256,7 @@ than the mercurial repository, I bit the bullet and switched the project's repo
<a href=https://github.com/landley/toybox>to git</a>. Georgi's
<a href=https://github.com/gfto/toybox>mirror</a> is now pulling from that.</p>
-<hr><b>February 25, 2015</b>
+<a name="25-02-2015" /><a href="#25-02-2015"><hr><h2><b>February 25, 2015</b></h2></a>
<blockquote><p>"A common mistake that people make when trying to design
something completely foolproof is to underestimate the ingenuity of
complete fools."</p><p>- The Hitchhiker's Guide to the Galaxy.</p></blockquote>
@@ -138,7 +376,7 @@ supported, so stop using it.</p>
<p>Fixed toy_exec() to detect when argc is in optargs, so we don't
need a separate xexec_optargs().</p>
-<hr><b>February 18, 2015</b>
+<a name="18-02-2015" /><a href="#18-02-2015"><hr><h2><b>February 18, 2015</b></h2></a>
<p>Dreamhost continues to be unable to make mailing list archives work, so
here's <a href=http://www.mail-archive.com/toybox@lists.landley.net/>another
list archive</a> with a less awkward interface than gmane.</p>
@@ -155,7 +393,7 @@ web archive, from Dec 15-Jan 3, and then another hole from Jan 16-Feb 18.
The relevant messages are in both of the other archives. Here's hoping
the chronic archive constipation problem won't happen a sixth time.</p>
-<hr><b>December 30, 2014</b>
+<a name="30-12-2014" /><a href="#30-12-2014"><hr><h2><b>December 30, 2014</b></h2></a>
<p>Due to Dreamhost's <a href=http://landley.net/dreamhost.txt>ongoing</a>
<a href=http://landley.net/dreamhost2.txt>inability</a> to make mailman
work reliably, I've added a link to a backup web archive at
@@ -167,7 +405,7 @@ on the left.</p>
<p>Update (January 27, 2015): they're <a href=https://twitter.com/landley/status/558428839462703104>still working on it</a>.</p>
-<hr><b>November 19, 2014</b>
+<a name="19-11-2014" /><a href="#19-11-2014"><hr><h2><b>November 19, 2014</b></h2></a>
<blockquote><p>"This time it was right, it would work, and no one would have to get nailed to anything." - The Hitchhiker's Guide to the Galaxy.</p></blockquote>
@@ -246,7 +484,7 @@ close each supplied filehandle itself.)</p>
<p>The printf-style escape parsing ("\n" and friends) got factored out into
a new unescape() function.</p>
-<hr><b>October 2, 2014</b>
+<a name="02-10-2014" /><a href="#02-10-2014"><hr><h2><b>October 2, 2014</b></h2></a>
<blockquote><p>"There is an art, it says, or rather, a knack to flying.
The knack lies in learning how to throw yourself at the ground and miss...
Clearly, it is this second part, the missing, which presents the
@@ -397,7 +635,7 @@ sort on the host broke in non-C locales.</p>
<p>Divya Kothari submitted tests for chmod, link, tar, bzcat, xzcat, zcat,
and hostname. (And more, but that's all that's merged so far.)</p>
-<hr><b>July 7, 2014</b>
+<a name="07-07-2014" /><a href="#07-07-2014"><hr><h2><b>July 7, 2014</b></h2></a>
<blockquote><p>"This planet has - or rather had - a problem, which was this:
most of the people living on it were unhappy for pretty much of the time. Many
solutions were suggested for this problem, but most of these were largely
@@ -519,7 +757,7 @@ bytes). The build infrastructure now notices duplicate commands (so if you
cp toys/pending/command.c toys/other/command.c and forget to delete the
first one, the build break is now more informative).</p>
-<hr><b>April 20, 2014</b>
+<a name="20-04-2014" /><a href="#20-04-2014"><hr><h2><b>April 20, 2014</b></h2></a>
<blockquote><p>And to this end they built themselves a stupendous supercomputer
which was so amazingly intelligent that even before the data banks
had been connected up it had started from "I think therefore I am" and got as
@@ -605,7 +843,7 @@ of different kinds of simplicity, and why comments aren't a substitute for
good code. The README no longer trails off into obvious unfinished confusion
at the end. Each page on the website should now have its own title.</p>
-<hr><b>November 18, 2013</b>
+<a name="18-11-2013" /><a href="#18-11-2013"><hr><h2><b>November 18, 2013</b></h2></a>
<blockquote><p>"Space," it says, "is big. Really big. You just won't believe how vastly, hugely, mindbogglingly big it is. I mean, you may think it's a long way down the street to the chemist's, but that's just peanuts to space." -
The Hitchhiker's Guide to the Galaxy.</p></blockquote>
@@ -642,7 +880,7 @@ The "toynet.h" file got folded into toys.h since musl supports it and
micromanging uClibc options isn't very interesting anymore. The test suite
now uses scripts/single.sh when testing a single command.</p>
-<hr><b>September 17, 2013</b>
+<a name="17-09-2013" /><a href="#17-09-2013"><hr><h2><b>September 17, 2013</b></h2></a>
<blockquote><p>"Think of a number," said the computer, "any number."
Arthur told the computer the telephone number of King's Cross railway
station passenger inquiries, on the grounds that it must have some function,
@@ -771,13 +1009,13 @@ delete sub and didn't exit with an error either. Neither was correct, rm
should now be fixed.</p>
<p>
-<hr><b>July 26, 2013</b>
+<a name="26-07-2013" /><a href="#26-07-2013"><hr><h2><b>July 26, 2013</b></h2></a>
<p>Georgi Chorbadzhiyski maintains a <a href=https://github.com/gfto/toybox>git
mirror</a> of the repository on github, automatically updated from the
mercurial every 6 hours. The mirror is read only, but you can generate patches
against it and post them to the list.</p>
-<hr><b>July 2, 2013</b>
+<a name="02-07-2013" /><a href="#02-07-2013"><hr><h2><b>July 2, 2013</b></h2></a>
<blockquote><p>"Time is an illusion. Lunchtime doubly so." "Very deep. You
should send that in to the Reader's Digest. They've got a page for people
like you." -
@@ -856,7 +1094,7 @@ the GPL but "all copies must include this magic text blob" somehow don't?</p>
or less public domain with a liability disclaimer, but we're still calling it
BSD (sometimes "0 clause BSD") to avoid explaining.</p>
-<hr><b>March 21, 2013</b>
+<a name="21-03-2013" /><a href="#21-03-2013"><hr><h2><b>March 21, 2013</b></h2></a>
<p>Video of my ELC talk
"<a href=http://youtu.be/SGmtP5Lg_t0>Why is Toybox?</a>"
is up on youtube. Related materials include the
@@ -891,7 +1129,7 @@ the ads, it's The Linux Foundation.)</p>
</span>
-<hr><b>March 14, 2013</b>
+<a name="14-03-2013" /><a href="#14-03-2013"><hr><h2><b>March 14, 2013</b></h2></a>
<blockquote><p>"Ford, you're turning into a penguin. Stop it." -
The Hitchhiker's Guide to the Galaxy.</p></blockquote>
@@ -914,7 +1152,7 @@ should at least compile (although defconfig is still what's useful).</p>
<p>Significant roadmap updates, checking several other multicall binaries
(klibc, sash, sbase, s6...) to see what commands they include.</p>
-<hr><b>January 18, 2013</b>
+<a name="18-01-2013" /><a href="#18-01-2013"><hr><h2><b>January 18, 2013</b></h2></a>
<blockquote><p>This must be Thursday. I never could get the hang of Thursdays. - The Hitchhiker's Guide to the Galaxy.</p></blockquote>
<p><a href=downloads/toybox-0.4.3.tar.bz2>Toybox 0.4.3</a> is based on
@@ -955,7 +1193,7 @@ it now correctly detects "/trailing/slash/" which the previous code didn't.</p>
disabled compiler optimization, so the binary size bloated a bit. It's back
to -Os by default now.</p>
-<hr><b>December 15, 2012</b>
+<a name="15-12-2012" /><a href="#15-12-2012"><hr><h2><b>December 15, 2012</b></h2></a>
<blockquote><p>"The major difference between a thing that might go wrong and a
thing that cannot possibly go wrong is that when a thing that cannot possibly
go wrong goes wrong it usually turns out to be impossible to get at or repair."
@@ -1006,7 +1244,7 @@ to do it Posix's way, which is more brittle and needs extra security checks,
but am waiting for somebody to complain first. The default "ulimit -n" is 1024
filehandles, so drilling down over 1000 nested subdirectories).</p>
-<hr><b>November 13, 2012</b>
+<a name="13-11-2012" /><a href="#13-11-2012"><hr><h2><b>November 13, 2012</b></h2></a>
<blockquote><p>"Rule Six: The winning team shall be the first team that wins."
- The Hitchhiker's Guide to the Galaxy.</p></blockquote>
@@ -1070,7 +1308,7 @@ a separate <a href=oldnews.html>oldnews</a> page.</p>
back now.</p>
</span>
-<hr><b>July 23, 2012</b>
+<a name="23-07-2012" /><a href="#23-07-2012"><hr><h2><b>July 23, 2012</b></h2></a>
<blockquote><p>"Ford", Arthur said. "There's an infinite number of monkeys
out here who want to talk to us about this script for Hamlet they've worked
out." - The Hitchhiker's Guide to the Galaxy.</p></blockquote>
@@ -1095,7 +1333,7 @@ corresponding executable bit wasn't set, and worked around a longstanding
glibc bug where static linking prevents stdout from automatically flushing
pending output on exit.</p>
-<hr><b>June 25, 2012</b>
+<a name="25-06-2012" /><a href="#25-06-2012"><hr><h2><b>June 25, 2012</b></h2></a>
<blockquote><p>"For a moment, nothing happened. Then, after a second or so, nothing continued to happen." - The Hitchhiker's Guide to the Galaxy.</p></blockquote>
<p><a href=downloads/toybox-0.3.1.tar.bz2>Toybox 0.3.1</a> is based on commit
@@ -1111,7 +1349,7 @@ it is. The roadmap and documentation are a bit behind, and I've got ~40
pending submissions to review. I need to catch up...</p>
</span>
-<hr><b>June 12, 2012</b>
+<a name="12-06-2012" /><a href="#12-06-2012"><hr><h2><b>June 12, 2012</b></h2></a>
<blockquote><p>"For instance, on the planet Earth, man had always assumed that
he was more intelligent than dolphins because he had achieved so much - the
wheel, New York, wars and so on - whilst all the dolphins had ever done was
@@ -1176,7 +1414,7 @@ yet, but if I wait until everything works we won't have a release before
1.0, so here's a checkpoint.)</p>
-<hr><b>March 3, 2012</b>
+<a name="03-03-2012" /><a href="#03-03-2012"><hr><h2><b>March 3, 2012</b></h2></a>
<blockquote><p>"They went unnoticed at Goonhilly, passed over Cape Canaveral
without a blip, and Woomera and Jodrell Bank looked straight through them.
@@ -1216,7 +1454,7 @@ dirname, unshare, and various infrastructure tweaks, but it took me 3 months
and those guys did their stuff in a week or so.)</p>
-<hr><b>February 12, 2012</b>
+<a name="12-02-2012" /><a href="#12-02-2012"><hr><h2><b>February 12, 2012</b></h2></a>
<blockquote><p>
"for though it has many omissions and contains much that is apocryphal, or at
least wildly inaccurate, it scores over the older, more pedestrian work in two
@@ -1233,7 +1471,8 @@ patches pending on the mailing list I need to review and merge.</p>
<p>More to come...</p>
<hr>
-<p><b>November 15, 2011</b> - Back from the dead, Toybox is now under a 2
+<a name="15-11-2011" /><a href="#15-11-2011"><hr><h2><b>November 15, 2011</b></h2></a>
+- Back from the dead, Toybox is now under a 2
clause BSD license, and aiming to become the default command line
implementation of Android systems everywhere.</p>
diff --git a/www/roadmap.html b/www/roadmap.html
index bba08b1..e5105b8 100755
--- a/www/roadmap.html
+++ b/www/roadmap.html
@@ -244,15 +244,13 @@ sendevent start stop top uptime watchprops
<h3>Other Android core commands</h3>
<p>Other than the toolbox directory, the currently interesting
-subdirectories in the core repository are gpttool, init,
-logcat, logwrapper, mkbootimg, reboot, and run-as.</p>
+subdirectories in the core repository are init,
+logcat, logwrapper, reboot, and run-as.</p>
<ul>
-<li><b>gpttool</b> - subset of fdisk</li>
<li><b>init</b> - Android's PID 1</li>
<li><b>logcat</b> - read android log format</li>
<li><b>logwrapper</b> - redirect stdio to android log</li>
-<li><b>mkbootimg</b> - create signed boot image</li>
<li><b>reboot</b> - Android's reboot(1)</li>
<li><b>run-as</b> - subset of sudo</li>
</ul>
@@ -261,17 +259,13 @@ logcat, logwrapper, mkbootimg, reboot, and run-as.</p>
different user interface. We may want to provide that interface, but
implementing the full commands (fdisk, init, and sudo) come first.</p>
-<p>Also, gpttool and mkbootimg are install tools.
-These aren't a priority if android wants to use its own
-bespoke code to install itself.</p>
-
<h3>Analysis</h3>
<p>For reference, combining everything listed above, we get:</p>
<blockquote><b>
-dd du df getevent gpttool iftop init ioctl ionice
-log logcat logwrapper ls lsof mkbootimg mount nandread
+dd du df getevent iftop init ioctl ionice
+log logcat logwrapper ls lsof mount nandread
newfs_msdos ps prlimit reboot renice run-as
sendevent start stop top uptime watchprops
</b></blockquote>