diff options
45 files changed, 1286 insertions, 424 deletions
@@ -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" @@ -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; @@ -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); @@ -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. @@ -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&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> |