aboutsummaryrefslogtreecommitdiff
path: root/toys/other
diff options
context:
space:
mode:
Diffstat (limited to 'toys/other')
-rw-r--r--toys/other/bzcat.c30
-rw-r--r--toys/other/catv.c67
-rw-r--r--toys/other/chroot.c28
-rw-r--r--toys/other/chvt.c51
-rw-r--r--toys/other/clear.c23
-rw-r--r--toys/other/count.c36
-rw-r--r--toys/other/dos2unix.c70
-rw-r--r--toys/other/free.c60
-rw-r--r--toys/other/hello.c60
-rw-r--r--toys/other/help.c48
-rw-r--r--toys/other/insmod.c47
-rw-r--r--toys/other/login.c246
-rw-r--r--toys/other/lsmod.c42
-rw-r--r--toys/other/mdev.c213
-rw-r--r--toys/other/mke2fs.c662
-rw-r--r--toys/other/mkswap.c42
-rw-r--r--toys/other/modinfo.c103
-rw-r--r--toys/other/mountpoint.c56
-rw-r--r--toys/other/netcat.c232
-rw-r--r--toys/other/oneit.c80
-rw-r--r--toys/other/printenv.c47
-rw-r--r--toys/other/readlink.c45
-rw-r--r--toys/other/realpath.c31
-rw-r--r--toys/other/rmmod.c51
-rw-r--r--toys/other/setsid.c33
-rw-r--r--toys/other/sha1sum.c185
-rw-r--r--toys/other/swapoff.c26
-rw-r--r--toys/other/swapon.c37
-rw-r--r--toys/other/tac.c55
-rw-r--r--toys/other/taskset.c110
-rw-r--r--toys/other/toysh.c382
-rw-r--r--toys/other/truncate.c47
-rw-r--r--toys/other/unshare.c45
-rw-r--r--toys/other/uptime.c52
-rw-r--r--toys/other/usleep.c32
-rw-r--r--toys/other/vmstat.c191
-rw-r--r--toys/other/w.c36
-rw-r--r--toys/other/which.c71
-rw-r--r--toys/other/whoami.c32
-rw-r--r--toys/other/yes.c33
40 files changed, 3737 insertions, 0 deletions
diff --git a/toys/other/bzcat.c b/toys/other/bzcat.c
new file mode 100644
index 00000000..b8e16958
--- /dev/null
+++ b/toys/other/bzcat.c
@@ -0,0 +1,30 @@
+/* vi: set sw=4 ts=4:
+ *
+ * bzcat.c - decompress stdin to stdout using bunzip2.
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+ *
+ * Not in SUSv3.
+
+USE_BZCAT(NEWTOY(bzcat, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config BZCAT
+ bool "bzcat"
+ default y
+ help
+ usage: bzcat [filename...]
+
+ Decompress listed files to stdout. Use stdin if no files listed.
+*/
+
+#include "toys.h"
+
+static void do_bzcat(int fd, char *name)
+{
+ bunzipStream(fd, 1);
+}
+
+void bzcat_main(void)
+{
+ loopfiles(toys.optargs, do_bzcat);
+}
diff --git a/toys/other/catv.c b/toys/other/catv.c
new file mode 100644
index 00000000..c4479d9c
--- /dev/null
+++ b/toys/other/catv.c
@@ -0,0 +1,67 @@
+/* vi: set sw=4 ts=4:
+ *
+ * cat -v implementation for toybox
+ *
+ * Copyright (C) 2006, 2007 Rob Landley <rob@landley.net>
+ *
+ * Not in SUSv3, but see "Cat -v considered harmful" at
+ * http://cm.bell-labs.com/cm/cs/doc/84/kp.ps.gz
+
+USE_CATV(NEWTOY(catv, "vte", TOYFLAG_USR|TOYFLAG_BIN))
+
+config CATV
+ bool "catv"
+ default y
+ help
+ usage: catv [-evt] [filename...]
+
+ Display nonprinting characters as escape sequences. Use M-x for
+ high ascii characters (>127), and ^x for other nonprinting chars.
+
+ -e Mark each newline with $
+ -t Show tabs as ^I
+ -v Don't use ^x or M-x escapes.
+*/
+
+#include "toys.h"
+
+// Callback function for loopfiles()
+
+static void do_catv(int fd, char *name)
+{
+ for(;;) {
+ int i, len;
+
+ len = read(fd, toybuf, sizeof(toybuf));
+ if (len < 0) toys.exitval = EXIT_FAILURE;
+ if (len < 1) break;
+ for (i=0; i<len; i++) {
+ char c=toybuf[i];
+
+ if (c > 126 && (toys.optflags & 4)) {
+ if (c == 127) {
+ printf("^?");
+ continue;
+ } else {
+ printf("M-");
+ c -= 128;
+ }
+ }
+ if (c < 32) {
+ if (c == 10) {
+ if (toys.optflags & 1) xputc('$');
+ } else if (toys.optflags & (c==9 ? 2 : 4)) {
+ printf("^%c", c+'@');
+ continue;
+ }
+ }
+ xputc(c);
+ }
+ }
+}
+
+void catv_main(void)
+{
+ toys.optflags^=4;
+ loopfiles(toys.optargs, do_catv);
+}
diff --git a/toys/other/chroot.c b/toys/other/chroot.c
new file mode 100644
index 00000000..06823796
--- /dev/null
+++ b/toys/other/chroot.c
@@ -0,0 +1,28 @@
+/* vi: set sw=4 ts=4:
+ *
+ * chroot.c - Run command in new root directory.
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+ *
+ * Not in SUSv3.
+
+USE_CHROOT(NEWTOY(chroot, "^<1", TOYFLAG_USR|TOYFLAG_SBIN))
+
+config CHROOT
+ bool "chroot"
+ default y
+ help
+ usage: chroot NEWPATH [commandline...]
+
+ Run command within a new root directory. If no command, run /bin/sh.
+*/
+
+#include "toys.h"
+
+void chroot_main(void)
+{
+ char *binsh[] = {"/bin/sh", "-i", 0};
+ if (chdir(*toys.optargs) || chroot("."))
+ perror_exit("%s", *toys.optargs);
+ xexec(toys.optargs[1] ? toys.optargs+1 : binsh);
+}
diff --git a/toys/other/chvt.c b/toys/other/chvt.c
new file mode 100644
index 00000000..9afb059b
--- /dev/null
+++ b/toys/other/chvt.c
@@ -0,0 +1,51 @@
+/* vi: set sw=4 ts=4:
+ *
+ * chvt.c - switch virtual terminals
+ *
+ * Copyright (C) 2008 David Anders <danders@amltd.com>
+ *
+ * Not in SUSv3.
+
+USE_CHVT(NEWTOY(chvt, "<1", TOYFLAG_USR|TOYFLAG_SBIN))
+
+config CHVT
+ bool "chvt"
+ default y
+ help
+ usage: chvt N
+
+ Change to virtual terminal number N. (This only works in text mode.)
+
+ Virtual terminals are the Linux VGA text mode displays, ordinarily
+ switched between via alt-F1, alt-F2, etc. Use ctrl-alt-F1 to switch
+ from X to a virtual terminal, and alt-F6 (or F7, or F8) to get back.
+*/
+
+#include "toys.h"
+
+/* Note: get_console_fb() will need to be moved into a seperate lib section */
+int get_console_fd()
+{
+ int fd;
+ char *consoles[]={"/dev/console", "/dev/vc/0", "/dev/tty", NULL}, **cc;
+
+ cc = consoles;
+ while (*cc) {
+ fd = open(*cc++, O_RDWR);
+ if (fd >= 0) return fd;
+ }
+
+ return -1;
+}
+
+void chvt_main(void)
+{
+ int vtnum, fd;
+
+ vtnum=atoi(*toys.optargs);
+
+ fd=get_console_fd();
+ // These numbers are VT_ACTIVATE and VT_WAITACTIVE from linux/vt.h
+ if (fd < 0 || ioctl(fd, 0x5606, vtnum) || ioctl(fd, 0x5607, vtnum))
+ perror_exit(NULL);
+}
diff --git a/toys/other/clear.c b/toys/other/clear.c
new file mode 100644
index 00000000..8cfceff3
--- /dev/null
+++ b/toys/other/clear.c
@@ -0,0 +1,23 @@
+/* vi: set sw=4 ts=4:
+ *
+ * clear.c - clear the screen
+ *
+ * Copyright 2012 Rob Landley <rob@landley.net>
+ *
+ * Not in SUSv4.
+
+USE_CLEAR(NEWTOY(clear, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config CLEAR
+ bool "clear"
+ default y
+ help
+ Clear the screen.
+*/
+
+#include "toys.h"
+
+void clear_main(void)
+{
+ write(1, "\e[2J\e[H", 7);
+}
diff --git a/toys/other/count.c b/toys/other/count.c
new file mode 100644
index 00000000..0ec3919d
--- /dev/null
+++ b/toys/other/count.c
@@ -0,0 +1,36 @@
+/* vi: set sw=4 ts=4:
+ *
+ * count.c - Progress indicator from stdin to stdout
+ *
+ * Copyright 2002 Rob Landley <rob@landley.net>
+ *
+ * Not in SUSv3.
+
+USE_COUNT(NEWTOY(count, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config COUNT
+ bool "count"
+ default y
+ help
+ usage: count
+
+ Copy stdin to stdout, displaying simple progress indicator to stderr.
+*/
+
+#include "toys.h"
+
+void count_main(void)
+{
+ uint64_t size = 0;
+ int len;
+ char buf[32];
+
+ for (;;) {
+ len = xread(0, toybuf, sizeof(toybuf));
+ if (!len) break;
+ size += len;
+ xwrite(1, toybuf, len);
+ xwrite(2, buf, sprintf(buf, "%"PRIu64" bytes\r", size));
+ }
+ xwrite(2, "\n", 1);
+}
diff --git a/toys/other/dos2unix.c b/toys/other/dos2unix.c
new file mode 100644
index 00000000..01c90536
--- /dev/null
+++ b/toys/other/dos2unix.c
@@ -0,0 +1,70 @@
+/* vi: set sw=4 ts=4:
+ *
+ * dos2unix.c - convert newline format
+ *
+ * Copyright 2012 Rob Landley <rob@landley.net>
+ *
+ * No standard
+
+USE_DOS2UNIX(NEWTOY(dos2unix, NULL, TOYFLAG_BIN))
+USE_DOS2UNIX(OLDTOY(unix2dos, dos2unix, NULL, TOYFLAG_BIN))
+
+config DOS2UNIX
+ bool "dos2unix/unix2dos"
+ default y
+ help
+ usage: dos2unix/unix2dos [file...]
+
+ Convert newline format between dos (\r\n) and unix (just \n)
+ If no files listed copy from stdin, "-" is a synonym for stdin.
+*/
+
+#include "toys.h"
+
+DEFINE_GLOBALS(
+ char *tempfile;
+)
+
+#define TT this.dos2unix
+
+static void do_dos2unix(int fd, char *name)
+{
+ char c = toys.which->name[0];
+ int outfd = 1, catch = 0;
+
+ if (fd) outfd = copy_tempfile(fd, name, &TT.tempfile);
+
+ for (;;) {
+ int len, in, out;
+
+ len = read(fd, toybuf+(sizeof(toybuf)/2), sizeof(toybuf)/2);
+ if (len<0) {
+ perror_msg("%s",name);
+ toys.exitval = 1;
+ }
+ if (len<1) break;
+
+ for (in = out = 0; in < len; in++) {
+ char x = toybuf[in+sizeof(toybuf)/2];
+
+ // Drop \r only if followed by \n in dos2unix mode
+ if (catch) {
+ if (c == 'u' || x != '\n') toybuf[out++] = '\r';
+ catch = 0;
+ // Add \r only if \n not after \r in unix2dos mode
+ } else if (c == 'u' && x == '\n') toybuf[out++] = '\r';
+
+ if (x == '\r') catch++;
+ else toybuf[out++] = x;
+ }
+ xwrite(outfd, toybuf, out);
+ }
+ if (catch) xwrite(outfd, "\r", 1);
+
+ if (fd) replace_tempfile(-1, outfd, &TT.tempfile);
+}
+
+void dos2unix_main(void)
+{
+ loopfiles(toys.optargs, do_dos2unix);
+}
diff --git a/toys/other/free.c b/toys/other/free.c
new file mode 100644
index 00000000..99dca86d
--- /dev/null
+++ b/toys/other/free.c
@@ -0,0 +1,60 @@
+/* vi: set sw=4 ts=4:
+ *
+ * free.c - Display amount of free and used memory in the system.
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_FREE(NEWTOY(free, "gmkb", TOYFLAG_USR|TOYFLAG_BIN))
+
+config FREE
+ bool "free"
+ default y
+ help
+ usage: free [-bkmg]
+
+ Display the total, free and used amount of physical memory and
+ swap space.
+
+ -bkmg Output in bytes (default), KB, MB or GB
+*/
+
+#include "toys.h"
+
+static unsigned long long convert(unsigned long d, unsigned int iscale,
+ unsigned int oscale)
+{
+ return ((unsigned long long)d*iscale)>>oscale;
+}
+
+void free_main(void)
+{
+ struct sysinfo info;
+ unsigned int iscale = 1;
+ unsigned int oscale = 0;
+
+ sysinfo(&info);
+ if (info.mem_unit) iscale = info.mem_unit;
+ if (toys.optflags & 1) oscale = 0;
+ if (toys.optflags & 2) oscale = 10;
+ if (toys.optflags & 4) oscale = 20;
+ if (toys.optflags & 8) oscale = 30;
+
+ xprintf("\t\ttotal used free shared buffers\n");
+ xprintf("Mem:%17llu%12llu%12llu%12llu%12llu\n",
+ convert(info.totalram, iscale, oscale),
+ convert(info.totalram-info.freeram, iscale, oscale),
+ convert(info.freeram, iscale, oscale),
+ convert(info.sharedram, iscale, oscale),
+ convert(info.bufferram, iscale, oscale));
+
+ xprintf("-/+ buffers/cache:%15llu%12llu\n",
+ convert(info.totalram - info.freeram - info.bufferram, iscale, oscale),
+ convert(info.freeram + info.bufferram, iscale, oscale));
+
+ xprintf("Swap:%16llu%12llu%12llu\n",
+ convert(info.totalswap, iscale, oscale),
+ convert(info.totalswap - info.freeswap, iscale, oscale),
+ convert(info.freeswap, iscale, oscale));
+}
diff --git a/toys/other/hello.c b/toys/other/hello.c
new file mode 100644
index 00000000..06aa470b
--- /dev/null
+++ b/toys/other/hello.c
@@ -0,0 +1,60 @@
+/* vi: set sw=4 ts=4:
+ *
+ * hello.c - A hello world program.
+ *
+ * Copyright 2012 Rob Landley <rob@landley.net>
+ *
+ * Not in SUSv4/LSB.
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/
+ * See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/cmdbehav.html
+
+USE_HELLO(NEWTOY(hello, "e@d*c#b:a", TOYFLAG_USR|TOYFLAG_BIN))
+
+config HELLO
+ bool "hello"
+ default n
+ help
+ usage: hello [-a] [-b string] [-c number] [-d list] [-e count] [...]
+
+ A hello world program. You don't need this.
+
+ Mostly used as an example/skeleton file for adding new commands,
+ occasionally nice to test kernel booting via "init=/bin/hello".
+*/
+
+#include "toys.h"
+
+// Hello doesn't use these globals, they're here for example/skeleton purposes.
+
+DEFINE_GLOBALS(
+ char *b_string;
+ long c_number;
+ struct arg_list *d_list;
+ long e_count;
+
+ int more_globals;
+)
+
+#define TT this.hello
+
+#define FLAG_a 1
+#define FLAG_b 2
+#define FLAG_c 4
+#define FLAG_d 8
+#define FLAG_e 16
+
+void hello_main(void)
+{
+ printf("Hello world\n");
+
+ if (toys.optflags & FLAG_a) printf("Saw a\n");
+ if (toys.optflags & FLAG_b) printf("b=%s\n", TT.b_string);
+ if (toys.optflags & FLAG_c) printf("c=%ld\n", TT.c_number);
+ while (TT.d_list) {
+ printf("d=%s\n", TT.d_list->arg);
+ TT.d_list = TT.d_list->next;
+ }
+ if (TT.e_count) printf("e was seen %ld times", TT.e_count);
+
+ while (*toys.optargs) printf("optarg=%s\n", *(toys.optargs++));
+}
diff --git a/toys/other/help.c b/toys/other/help.c
new file mode 100644
index 00000000..5e2b6ec4
--- /dev/null
+++ b/toys/other/help.c
@@ -0,0 +1,48 @@
+/* vi: set sw=4 ts=4:
+ *
+ * help.c - Show help for toybox
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+ *
+ * Not in SUSv3, but exists as a bash builtin.
+
+USE_HELP(NEWTOY(help, "<1", TOYFLAG_BIN))
+
+config HELP
+ bool "help"
+ default y
+ help
+ usage: help [command]
+
+ Show usage information for toybox commands.
+ Run "toybox" with no arguments for a list of available commands.
+*/
+
+
+#include "toys.h"
+#include "generated/help.h"
+
+#undef NEWTOY
+#undef OLDTOY
+#define NEWTOY(name,opt,flags) help_##name "\0"
+#define OLDTOY(name,oldname,opts,flags) "\xff" #oldname "\0"
+static char *help_data =
+#include "generated/newtoys.h"
+;
+
+void help_main(void)
+{
+ struct toy_list *t = toy_find(*toys.optargs);
+ int i = t-toy_list;
+ char *s = help_data;
+
+ if (!t) error_exit("Unknown command '%s'", *toys.optargs);
+ for (;;) {
+ while (i--) s += strlen(s) + 1;
+ if (*s != 255) break;
+ i = toy_find(++s)-toy_list;
+ s = help_data;
+ }
+
+ fprintf(toys.exithelp ? stderr : stdout, "%s", s);
+}
diff --git a/toys/other/insmod.c b/toys/other/insmod.c
new file mode 100644
index 00000000..e794828c
--- /dev/null
+++ b/toys/other/insmod.c
@@ -0,0 +1,47 @@
+/* vi: set sw=4 ts=4:
+ *
+ * insmod.c - Load a module into the Linux kernel.
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_INSMOD(NEWTOY(insmod, "<1", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
+
+config INSMOD
+ bool "insmod"
+ default y
+ help
+ usage: insmod MODULE [MODULE_OPTIONS]
+
+ Load the module named MODULE passing options if given.
+*/
+
+#include "toys.h"
+
+#include <sys/syscall.h>
+#define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts)
+
+void insmod_main(void)
+{
+ char * buf = NULL;
+ int len, res, i;
+ int fd = xopen(toys.optargs[0], O_RDONLY);
+
+ len = fdlength(fd);
+ buf = xmalloc(len);
+ xreadall(fd, buf, len);
+
+ i = 1;
+ while(toys.optargs[i] &&
+ strlen(toybuf) + strlen(toys.optargs[i]) + 2 < sizeof(toybuf)) {
+ strcat(toybuf, toys.optargs[i++]);
+ strcat(toybuf, " ");
+ }
+
+ res = init_module(buf, len, toybuf);
+ if (CFG_TOYBOX_FREE && buf != toybuf) free(buf);
+
+ if (res)
+ perror_exit("failed to load %s", toys.optargs[0]);
+}
diff --git a/toys/other/login.c b/toys/other/login.c
new file mode 100644
index 00000000..c2c9ba38
--- /dev/null
+++ b/toys/other/login.c
@@ -0,0 +1,246 @@
+/* vi: set sw=4 ts=4:
+ *
+ * login.c - Start a session on the system.
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+ *
+ * Not in SUSv4.
+ * No support for PAM/securetty/selinux/login script/issue/utmp
+ * Relies on libcrypt for hash calculation.
+
+USE_LOGIN(NEWTOY(login, ">1fph:", TOYFLAG_BIN))
+
+config LOGIN
+ bool "login"
+ default y
+ help
+ usage: login [-p] [-h host] [[-f] username]
+
+ Establish a new session with the system.
+ -p Preserve environment
+ -h The name of the remote host for this login
+ -f Do not perform authentication
+*/
+
+#include "toys.h"
+
+#define LOGIN_TIMEOUT 60
+#define LOGIN_FAIL_TIMEOUT 3
+#define USER_NAME_MAX_SIZE 32
+#define HOSTNAME_SIZE 32
+
+DEFINE_GLOBALS(
+ char *hostname;
+)
+#define TT this.login
+
+static void login_timeout_handler(int sig __attribute__((unused)))
+{
+ printf("\nLogin timed out after %d seconds.\n", LOGIN_TIMEOUT);
+ exit(0);
+}
+
+static char *forbid[] = {
+ "BASH_ENV",
+ "ENV",
+ "HOME",
+ "IFS",
+ "LD_LIBRARY_PATH",
+ "LD_PRELOAD",
+ "LD_TRACE_LOADED_OBJECTS",
+ "LD_BIND_NOW",
+ "LD_AOUT_LIBRARY_PATH",
+ "LD_AOUT_PRELOAD",
+ "LD_NOWARN",
+ "LD_KEEPDIR",
+ "SHELL",
+ NULL
+};
+
+int verify_password(char * pwd)
+{
+ char *pass;
+
+ if (read_password(toybuf, sizeof(toybuf), "Password: ")) return 1;
+ if (!pwd) return 1;
+ if (pwd[0] == '!' || pwd[0] == '*') return 1;
+
+ pass = crypt(toybuf, pwd);
+ if (pass && !strcmp(pass, pwd)) return 0;
+
+ return 1;
+}
+
+void read_user(char * buff, int size)
+{
+ char hostname[HOSTNAME_SIZE+1];
+ int i = 0;
+ hostname[HOSTNAME_SIZE] = 0;
+ if(!gethostname(hostname, HOSTNAME_SIZE)) fputs(hostname, stdout);
+
+ fputs(" login: ", stdout);
+ fflush(stdout);
+
+ do {
+ buff[0] = getchar();
+ if (buff[0] == EOF)
+ exit(EXIT_FAILURE);
+ } while (isblank(buff[0]));
+
+ if (buff[0] != '\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(EXIT_FAILURE);
+}
+
+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);
+}
+
+int change_identity(const struct passwd *pwd)
+{
+ if (initgroups(pwd->pw_name,pwd->pw_gid)) return 1;
+ if (setgid(pwd->pw_uid)) return 1;
+ if (setuid(pwd->pw_uid)) return 1;
+
+ return 0;
+}
+
+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 & 4) >> 2;
+ int p_flag = (toys.optflags & 2) >> 1;
+ int h_flag = toys.optflags & 1;
+ char username[USER_NAME_MAX_SIZE+1], *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");
+
+ if (geteuid()) error_exit("not root");
+
+ if (!isatty(0) || !isatty(1) || !isatty(2)) error_exit("no tty");
+
+ openlog("login", LOG_PID | LOG_CONS, LOG_AUTH);
+ signal(SIGALRM, login_timeout_handler);
+ alarm(LOGIN_TIMEOUT);
+
+ for (ss = forbid; *ss; ss++) unsetenv(*ss);
+
+ while (1) {
+ tcflush(0, TCIFLUSH);
+
+ username[USER_NAME_MAX_SIZE] = 0;
+ if (toys.optargs[0])
+ strncpy(username, toys.optargs[0], USER_NAME_MAX_SIZE);
+ else {
+ read_user(username, USER_NAME_MAX_SIZE+1);
+ if (username[0] == 0) continue;
+ }
+
+ pwd = getpwnam(username);
+ if (!pwd) goto query_pass; // Non-existing user
+
+ if (pwd->pw_passwd[0] == '!' || pwd->pw_passwd[0] == '*')
+ goto query_pass; // Locked account
+
+ if (f_flag) break; // Pre-authenticated
+
+ if (!pwd->pw_passwd[0]) break; // Password-less account
+
+ pass = pwd->pw_passwd;
+ if (pwd->pw_passwd[0] == 'x') {
+ spwd = getspnam (username);
+ if (spwd) pass = spwd->sp_pwdp;
+ }
+
+query_pass:
+ if (!verify_password(pass)) 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:"");
+
+ sleep(LOGIN_FAIL_TIMEOUT);
+ puts("Login incorrect");
+
+ if (++auth_fail_cnt == 3)
+ error_exit("Maximum number of tries exceeded (%d)\n", auth_fail_cnt);
+
+ username[0] = 0;
+ pwd = NULL;
+ spwd = NULL;
+ }
+
+ alarm(0);
+
+ if (pwd->pw_uid) handle_nologin();
+
+ if (change_identity(pwd)) error_exit("Failed to change identity");
+
+ setup_environment(pwd, !p_flag);
+
+ handle_motd();
+
+ syslog(LOG_INFO, "%s logged in on %s %s %s", pwd->pw_name,
+ ttyname(0), (h_flag)?"from":"", (h_flag)?TT.hostname:"");
+
+ spawn_shell(pwd->pw_shell);
+}
diff --git a/toys/other/lsmod.c b/toys/other/lsmod.c
new file mode 100644
index 00000000..82dbb589
--- /dev/null
+++ b/toys/other/lsmod.c
@@ -0,0 +1,42 @@
+/* vi: set sw=4 ts=4:
+ *
+ * lsmod.c - Show the status of modules in the kernel
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_LSMOD(NEWTOY(lsmod, NULL, TOYFLAG_BIN))
+
+config LSMOD
+ bool "lsmod"
+ default y
+ help
+ usage: lsmod
+
+ Display the currently loaded modules, their sizes and their
+ dependencies.
+*/
+
+#include "toys.h"
+
+void lsmod_main(void)
+{
+ char *modfile = "/proc/modules";
+ FILE * file = xfopen(modfile, "r");
+
+ xprintf("%-23s Size Used by\n", "Module");
+
+ while (fgets(toybuf, sizeof(toybuf), file)) {
+ char *name = strtok(toybuf, " "), *size = strtok(NULL, " "),
+ *refcnt = strtok(NULL, " "), *users = strtok(NULL, " ");
+
+ if(users) {
+ int len = strlen(users)-1;
+ if (users[len] == ',' || users[len] == '-')
+ users[len] = 0;
+ xprintf("%-19s %8s %s %s\n", name, size, refcnt, users);
+ } else perror_exit("bad %s", modfile);
+ }
+ fclose(file);
+}
diff --git a/toys/other/mdev.c b/toys/other/mdev.c
new file mode 100644
index 00000000..b769828f
--- /dev/null
+++ b/toys/other/mdev.c
@@ -0,0 +1,213 @@
+/* vi:set ts=4:
+ *
+ * mdev - Mini udev for busybox
+ *
+ * Copyright 2005, 2008 Rob Landley <rob@landley.net>
+ * Copyright 2005 Frank Sorenson <frank@tuxrocks.com>
+ *
+ * Not in SUSv3.
+
+USE_MDEV(NEWTOY(mdev, "s", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_UMASK))
+
+config MDEV
+ bool "mdev"
+ default n
+ help
+ usage: mdev [-s]
+
+ Create devices in /dev using information from /sys.
+
+ -s Scan all entries in /sys to populate /dev.
+
+config MDEV_CONF
+ bool "Configuration file for mdev"
+ default y
+ depends on MDEV
+ help
+ The mdev config file (/etc/mdev.conf) contains lines that look like:
+ hd[a-z][0-9]* 0:3 660
+
+ Each line must contain three whitespace separated fields. The first
+ field is a regular expression matching one or more device names, and
+ the second and third fields are uid:gid and file permissions for
+ matching devies.
+*/
+
+#include "toys.h"
+#include "lib/xregcomp.h"
+
+// todo, open() block devices to trigger partition scanning.
+
+// mknod in /dev based on a path like "/sys/block/hda/hda1"
+static void make_device(char *path)
+{
+ char *device_name, *s, *temp;
+ int major, minor, type, len, fd;
+ int mode = 0660;
+ uid_t uid = 0;
+ gid_t gid = 0;
+
+ // Try to read major/minor string
+
+ temp = strrchr(path, '/');
+ fd = open(path, O_RDONLY);
+ *temp=0;
+ temp = toybuf;
+ len = read(fd, temp, 64);
+ close(fd);
+ if (len<1) return;
+ temp[len] = 0;
+
+ // Determine device name, type, major and minor
+
+ device_name = strrchr(path, '/') + 1;
+ type = path[5]=='c' ? S_IFCHR : S_IFBLK;
+ major = minor = 0;
+ sscanf(temp, "%u:%u", &major, &minor);
+
+ // If we have a config file, look up permissions for this device
+
+ if (CFG_MDEV_CONF) {
+ char *conf, *pos, *end;
+
+ // mmap the config file
+ if (-1!=(fd = open("/etc/mdev.conf", O_RDONLY))) {
+ len = fdlength(fd);
+ conf = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (conf) {
+ int line = 0;
+
+ // Loop through lines in mmaped file
+ for (pos = conf; pos-conf<len;) {
+ int field;
+ char *end2;
+
+ line++;
+ // find end of this line
+ for(end = pos; end-conf<len && *end!='\n'; end++);
+
+ // Three fields: regex, uid:gid, mode
+ for (field = 3; field; field--) {
+ // Skip whitespace
+ while (pos<end && isspace(*pos)) pos++;
+ if (pos==end || *pos=='#') break;
+ for (end2 = pos;
+ end2<end && !isspace(*end2) && *end2!='#'; end2++);
+ switch(field) {
+ // Regex to match this device
+ case 3:
+ {
+ char *regex = strndup(pos, end2-pos);
+ regex_t match;
+ regmatch_t off;
+ int result;
+
+ // Is this it?
+ xregcomp(&match, regex, REG_EXTENDED);
+ result=regexec(&match, device_name, 1, &off, 0);
+ regfree(&match);
+ free(regex);
+
+ // If not this device, skip rest of line
+ if (result || off.rm_so
+ || off.rm_eo!=strlen(device_name))
+ goto end_line;
+
+ break;
+ }
+ // uid:gid
+ case 2:
+ {
+ char *s2;
+
+ // Find :
+ for(s = pos; s<end2 && *s!=':'; s++);
+ if (s==end2) goto end_line;
+
+ // Parse UID
+ uid = strtoul(pos,&s2,10);
+ if (s!=s2) {
+ struct passwd *pass;
+ char *str = strndup(pos, s-pos);
+ pass = getpwnam(str);
+ free(str);
+ if (!pass) goto end_line;
+ uid = pass->pw_uid;
+ }
+ s++;
+ // parse GID
+ gid = strtoul(s,&s2,10);
+ if (end2!=s2) {
+ struct group *grp;
+ char *str = strndup(s, end2-s);
+ grp = getgrnam(str);
+ free(str);
+ if (!grp) goto end_line;
+ gid = grp->gr_gid;
+ }
+ break;
+ }
+ // mode
+ case 1:
+ {
+ mode = strtoul(pos, &pos, 8);
+ if (pos!=end2) goto end_line;
+ goto found_device;
+ }
+ }
+ pos=end2;
+ }
+end_line:
+ // Did everything parse happily?
+ if (field && field!=3) error_exit("Bad line %d", line);
+
+ // Next line
+ pos = ++end;
+ }
+found_device:
+ munmap(conf, len);
+ }
+ close(fd);
+ }
+ }
+
+ sprintf(temp, "/dev/%s", device_name);
+ if (mknod(temp, mode | type, makedev(major, minor)) && errno != EEXIST)
+ perror_exit("mknod %s failed", temp);
+
+ if (CFG_MDEV_CONF) mode=chown(temp, uid, gid);
+}
+
+static int callback(struct dirtree *node)
+{
+ // Entries in /sys/class/block aren't char devices, so skip 'em. (We'll
+ // get block devices out of /sys/block.)
+ if(!strcmp(node->name, "block")) return 0;
+
+ // Does this directory have a "dev" entry in it?
+ // This is path based because the hotplug callbacks are
+ if (S_ISDIR(node->st.st_mode) || S_ISLNK(node->st.st_mode)) {
+ int len=4;
+ char *dev = dirtree_path(node, &len);
+ strcpy(dev+len, "/dev");
+ if (!access(dev, R_OK)) make_device(dev);
+ free(dev);
+ }
+
+ // Circa 2.6.25 the entries more than 2 deep are all either redundant
+ // (mouse#, event#) or unnamed (every usb_* entry is called "device").
+
+ return (node->parent && node->parent->parent) ? 0 : DIRTREE_RECURSE;
+}
+
+void mdev_main(void)
+{
+ // Handle -s
+
+ if (toys.optflags) {
+ dirtree_read("/sys/class", callback);
+ dirtree_read("/sys/block", callback);
+ }
+
+ // hotplug support goes here
+}
diff --git a/toys/other/mke2fs.c b/toys/other/mke2fs.c
new file mode 100644
index 00000000..47f31f2d
--- /dev/null
+++ b/toys/other/mke2fs.c
@@ -0,0 +1,662 @@
+/* vi: set ts=4:
+ *
+ * mke2fs.c - Create an ext2 filesystem image.
+ *
+ * Copyright 2006, 2007 Rob Landley <rob@landley.net>
+ *
+ * Not in SUSv3.
+
+// Still to go: "E:jJ:L:m:O:"
+USE_MKE2FS(NEWTOY(mke2fs, "<1>2g:Fnqm#N#i#b#", TOYFLAG_SBIN))
+
+config MKE2FS
+ bool "mke2fs (unfinished and broken by dirtree changes)"
+ default n
+ help
+ usage: mke2fs [-Fnq] [-b ###] [-N|i ###] [-m ###] device
+
+ Create an ext2 filesystem on a block device or filesystem image.
+
+ -F Force to run on a mounted device
+ -n Don't write to device
+ -q Quiet (no output)
+ -b size Block size (1024, 2048, or 4096)
+ -N inodes Allocate this many inodes
+ -i bytes Allocate one inode for every XXX bytes of device
+ -m percent Reserve this percent of filesystem space for root user
+
+config MKE2FS_JOURNAL
+ bool "Journaling support (ext3)"
+ default n
+ depends on MKE2FS
+ help
+ usage: [-j] [-J size=###,device=XXX]
+
+ -j Create journal (ext3)
+ -J Journal options
+ size: Number of blocks (1024-102400)
+ device: Specify an external journal
+
+config MKE2FS_GEN
+ bool "Generate (gene2fs)"
+ default n
+ depends on MKE2FS
+ help
+ usage: gene2fs [options] device filename
+
+ The [options] are the same as mke2fs.
+
+config MKE2FS_LABEL
+ bool "Label support"
+ default n
+ depends on MKE2FS
+ help
+ usage: mke2fs [-L label] [-M path] [-o string]
+
+ -L Volume label
+ -M Path to mount point
+ -o Created by
+
+config MKE2FS_EXTENDED
+ bool "Extended options"
+ default n
+ depends on MKE2FS
+ help
+ usage: mke2fs [-E stride=###] [-O option[,option]]
+
+ -E stride= Set RAID stripe size (in blocks)
+ -O [opts] Specify fewer ext2 option flags (for old kernels)
+ All of these are on by default (as appropriate)
+ none Clear default options (all but journaling)
+ dir_index Use htree indexes for large directories
+ filetype Store file type info in directory entry
+ has_journal Set by -j
+ journal_dev Set by -J device=XXX
+ sparse_super Don't allocate huge numbers of redundant superblocks
+*/
+
+#include "toys.h"
+
+DEFINE_GLOBALS(
+ // Command line arguments.
+ long blocksize;
+ long bytes_per_inode;
+ long inodes; // Total inodes in filesystem.
+ long reserved_percent; // Integer precent of space to reserve for root.
+ char *gendir; // Where to read dirtree from.
+
+ // Internal data.
+ struct dirtree *dt; // Tree of files to copy into the new filesystem.
+ unsigned treeblocks; // Blocks used by dt
+ unsigned treeinodes; // Inodes used by dt
+
+ unsigned blocks; // Total blocks in the filesystem.
+ unsigned freeblocks; // Free blocks in the filesystem.
+ unsigned inodespg; // Inodes per group
+ unsigned groups; // Total number of block groups.
+ unsigned blockbits; // Bits per block. (Also blocks per group.)
+
+ // For gene2fs
+ unsigned nextblock; // Next data block to allocate
+ unsigned nextgroup; // Next group we'll be allocating from
+ int fsfd; // File descriptor of filesystem (to output to).
+
+ struct ext2_superblock sb;
+)
+
+// Shortcut to our global data structure, since we use it so much.
+#define TT this.mke2fs
+
+#define INODES_RESERVED 10
+
+static uint32_t div_round_up(uint32_t a, uint32_t b)
+{
+ uint32_t c = a/b;
+
+ if (a%b) c++;
+ return c;
+}
+
+// Calculate data blocks plus index blocks needed to hold a file.
+
+static uint32_t file_blocks_used(uint64_t size, uint32_t *blocklist)
+{
+ uint32_t dblocks = (uint32_t)((size+(TT.blocksize-1))/TT.blocksize);
+ uint32_t idx=TT.blocksize/4, iblocks=0, diblocks=0, tiblocks=0;
+
+ // Fill out index blocks in inode.
+
+ if (blocklist) {
+ int i;
+
+ // Direct index blocks
+ for (i=0; i<13 && i<dblocks; i++) blocklist[i] = i;
+ // Singly indirect index blocks
+ if (dblocks > 13+idx) blocklist[13] = 13+idx;
+ // Doubly indirect index blocks
+ idx = 13 + idx + (idx*idx);
+ if (dblocks > idx) blocklist[14] = idx;
+
+ return 0;
+ }
+
+ // Account for direct, singly, doubly, and triply indirect index blocks
+
+ if (dblocks > 12) {
+ iblocks = ((dblocks-13)/idx)+1;
+ if (iblocks > 1) {
+ diblocks = ((iblocks-2)/idx)+1;
+ if (diblocks > 1)
+ tiblocks = ((diblocks-2)/idx)+1;
+ }
+ }
+
+ return dblocks + iblocks + diblocks + tiblocks;
+}
+
+// Use the parent pointer to iterate through the tree non-recursively.
+static struct dirtree *treenext(struct dirtree *this)
+{
+ while (this && !this->next) this = this->parent;
+ if (this) this = this->next;
+
+ return this;
+}
+
+// Recursively calculate the number of blocks used by each inode in the tree.
+// Returns blocks used by this directory, assigns bytes used to *size.
+// Writes total block count to TT.treeblocks and inode count to TT.treeinodes.
+
+static long check_treesize(struct dirtree *that, off_t *size)
+{
+ long blocks;
+
+ while (that) {
+ *size += sizeof(struct ext2_dentry) + strlen(that->name);
+
+ if (that->child)
+ that->st.st_blocks = check_treesize(that->child, &that->st.st_size);
+ else if (S_ISREG(that->st.st_mode)) {
+ that->st.st_blocks = file_blocks_used(that->st.st_size, 0);
+ TT.treeblocks += that->st.st_blocks;
+ }
+ that = that->next;
+ }
+ TT.treeblocks += blocks = file_blocks_used(*size, 0);
+ TT.treeinodes++;
+
+ return blocks;
+}
+
+// Calculate inode numbers and link counts.
+//
+// To do this right I need to copy the tree and sort it, but here's a really
+// ugly n^2 way of dealing with the problem that doesn't scale well to large
+// numbers of files (> 100,000) but can be done in very little code.
+// This rewrites inode numbers to their final values, allocating depth first.
+
+static void check_treelinks(struct dirtree *tree)
+{
+ struct dirtree *current=tree, *that;
+ long inode = INODES_RESERVED;
+
+ while (current) {
+ ++inode;
+ // Since we can't hardlink to directories, we know their link count.
+ if (S_ISDIR(current->st.st_mode)) current->st.st_nlink = 2;
+ else {
+ dev_t new = current->st.st_dev;
+
+ if (!new) continue;
+
+ // Look for other copies of current node
+ current->st.st_nlink = 0;
+ for (that = tree; that; that = treenext(that)) {
+ if (current->st.st_ino == that->st.st_ino &&
+ current->st.st_dev == that->st.st_dev)
+ {
+ current->st.st_nlink++;
+ current->st.st_ino = inode;
+ }
+ }
+ }
+ current->st.st_ino = inode;
+ current = treenext(current);
+ }
+}
+
+// According to http://www.opengroup.org/onlinepubs/9629399/apdxa.htm
+// we should generate a uuid structure by reading a clock with 100 nanosecond
+// precision, normalizing it to the start of the gregorian calendar in 1582,
+// and looking up our eth0 mac address.
+//
+// On the other hand, we have 128 bits to come up with a unique identifier, of
+// which 6 have a defined value. /dev/urandom it is.
+
+static void create_uuid(char *uuid)
+{
+ // Read 128 random bits
+ int fd = xopen("/dev/urandom", O_RDONLY);
+ xreadall(fd, uuid, 16);
+ close(fd);
+
+ // Claim to be a DCE format UUID.
+ uuid[6] = (uuid[6] & 0x0F) | 0x40;
+ uuid[8] = (uuid[8] & 0x3F) | 0x80;
+
+ // rfc2518 section 6.4.1 suggests if we're not using a macaddr, we should
+ // set bit 1 of the node ID, which is the mac multicast bit. This means we
+ // should never collide with anybody actually using a macaddr.
+ uuid[11] = uuid[11] | 128;
+}
+
+// Calculate inodes per group from total inodes.
+static uint32_t get_inodespg(uint32_t inodes)
+{
+ uint32_t temp;
+
+ // Round up to fill complete inode blocks.
+ temp = (inodes + TT.groups - 1) / TT.groups;
+ inodes = TT.blocksize/sizeof(struct ext2_inode);
+ return ((temp + inodes - 1)/inodes)*inodes;
+}
+
+// Fill out superblock and TT structures.
+
+static void init_superblock(struct ext2_superblock *sb)
+{
+ uint32_t temp;
+
+ // Set log_block_size and log_frag_size.
+
+ for (temp = 0; temp < 4; temp++) if (TT.blocksize == 1024<<temp) break;
+ if (temp==4) error_exit("bad blocksize");
+ sb->log_block_size = sb->log_frag_size = SWAP_LE32(temp);
+
+ // Fill out blocks_count, r_blocks_count, first_data_block
+
+ sb->blocks_count = SWAP_LE32(TT.blocks);
+ sb->free_blocks_count = SWAP_LE32(TT.freeblocks);
+ temp = (TT.blocks * (uint64_t)TT.reserved_percent) / 100;
+ sb->r_blocks_count = SWAP_LE32(temp);
+
+ sb->first_data_block = SWAP_LE32(TT.blocksize == 1024 ? 1 : 0);
+
+ // Set blocks_per_group and frags_per_group, which is the size of an
+ // allocation bitmap that fits in one block (I.E. how many bits per block)?
+
+ sb->blocks_per_group = sb->frags_per_group = SWAP_LE32(TT.blockbits);
+
+ // Set inodes_per_group and total inodes_count
+ sb->inodes_per_group = SWAP_LE32(TT.inodespg);
+ sb->inodes_count = SWAP_LE32(TT.inodespg * TT.groups);
+
+ // Determine free inodes.
+ temp = TT.inodespg*TT.groups - INODES_RESERVED;
+ if (temp < TT.treeinodes) error_exit("Not enough inodes.\n");
+ sb->free_inodes_count = SWAP_LE32(temp - TT.treeinodes);
+
+ // Fill out the rest of the superblock.
+ sb->max_mnt_count=0xFFFF;
+ sb->wtime = sb->lastcheck = sb->mkfs_time = SWAP_LE32(time(NULL));
+ sb->magic = SWAP_LE32(0xEF53);
+ sb->state = sb->errors = SWAP_LE16(1);
+
+ sb->rev_level = SWAP_LE32(1);
+ sb->first_ino = SWAP_LE32(INODES_RESERVED+1);
+ sb->inode_size = SWAP_LE16(sizeof(struct ext2_inode));
+ sb->feature_incompat = SWAP_LE32(EXT2_FEATURE_INCOMPAT_FILETYPE);
+ sb->feature_ro_compat = SWAP_LE32(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER);
+
+ create_uuid(sb->uuid);
+
+ // TODO If we're called as mke3fs or mkfs.ext3, do a journal.
+
+ //if (strchr(toys.which->name,'3'))
+ // sb->feature_compat |= SWAP_LE32(EXT3_FEATURE_COMPAT_HAS_JOURNAL);
+}
+
+// Does this group contain a superblock backup (and group descriptor table)?
+static int is_sb_group(uint32_t group)
+{
+ int i;
+
+ // Superblock backups are on groups 0, 1, and powers of 3, 5, and 7.
+ if(!group || group==1) return 1;
+ for (i=3; i<9; i+=2) {
+ int j = i;
+ while (j<group) j*=i;
+ if (j==group) return 1;
+ }
+ return 0;
+}
+
+
+// Number of blocks used in group by optional superblock/group list backup.
+static int group_superblock_overhead(uint32_t group)
+{
+ int used;
+
+ if (!is_sb_group(group)) return 0;
+
+ // How many blocks does the group descriptor table take up?
+ used = TT.groups * sizeof(struct ext2_group);
+ used += TT.blocksize - 1;
+ used /= TT.blocksize;
+ // Plus the superblock itself.
+ used++;
+ // And a corner case.
+ if (!group && TT.blocksize == 1024) used++;
+
+ return used;
+}
+
+// Number of blocks used in group to store superblock/group/inode list
+static int group_overhead(uint32_t group)
+{
+ // Return superblock backup overhead (if any), plus block/inode
+ // allocation bitmaps, plus inode tables.
+ return group_superblock_overhead(group) + 2 + get_inodespg(TT.inodespg)
+ / (TT.blocksize/sizeof(struct ext2_inode));
+}
+
+// In bitmap "array" set "len" bits starting at position "start" (from 0).
+static void bits_set(char *array, int start, int len)
+{
+ while(len) {
+ if ((start&7) || len<8) {
+ array[start/8]|=(1<<(start&7));
+ start++;
+ len--;
+ } else {
+ array[start/8]=255;
+ start+=8;
+ len-=8;
+ }
+ }
+}
+
+// Seek past len bytes (to maintain sparse file), or write zeroes if output
+// not seekable
+static void put_zeroes(int len)
+{
+ if(-1 == lseek(TT.fsfd, len, SEEK_SET)) {
+ memset(toybuf, 0, sizeof(toybuf));
+ while (len) {
+ int out = len > sizeof(toybuf) ? sizeof(toybuf) : len;
+ xwrite(TT.fsfd, toybuf, out);
+ len -= out;
+ }
+ }
+}
+
+// Fill out an inode structure from struct stat info in dirtree.
+static void fill_inode(struct ext2_inode *in, struct dirtree *that)
+{
+ uint32_t fbu[15];
+ int temp;
+
+ file_blocks_used(that->st.st_size, fbu);
+
+ // If that inode needs data blocks allocated to it.
+ if (that->st.st_size) {
+ int i, group = TT.nextblock/TT.blockbits;
+
+ // TODO: teach this about indirect blocks.
+ for (i=0; i<15; i++) {
+ // If we just jumped into a new group, skip group overhead blocks.
+ while (group >= TT.nextgroup)
+ TT.nextblock += group_overhead(TT.nextgroup++);
+ }
+ }
+ // TODO : S_ISREG/DIR/CHR/BLK/FIFO/LNK/SOCK(m)
+ in->mode = SWAP_LE32(that->st.st_mode);
+
+ in->uid = SWAP_LE16(that->st.st_uid & 0xFFFF);
+ in->uid_high = SWAP_LE16(that->st.st_uid >> 16);
+ in->gid = SWAP_LE16(that->st.st_gid & 0xFFFF);
+ in->gid_high = SWAP_LE16(that->st.st_gid >> 16);
+ in->size = SWAP_LE32(that->st.st_size & 0xFFFFFFFF);
+
+ // Contortions to make the compiler not generate a warning for x>>32
+ // when x is 32 bits. The optimizer should clean this up.
+ if (sizeof(that->st.st_size) > 4) temp = 32;
+ else temp = 0;
+ if (temp) in->dir_acl = SWAP_LE32(that->st.st_size >> temp);
+
+ in->atime = SWAP_LE32(that->st.st_atime);
+ in->ctime = SWAP_LE32(that->st.st_ctime);
+ in->mtime = SWAP_LE32(that->st.st_mtime);
+
+ in->links_count = SWAP_LE16(that->st.st_nlink);
+ in->blocks = SWAP_LE32(that->st.st_blocks);
+ // in->faddr
+}
+
+// Works like an archiver.
+// The first argument is the name of the file to create. If it already
+// exists, that size will be used.
+
+void mke2fs_main(void)
+{
+ int i, temp;
+ off_t length;
+ uint32_t usedblocks, usedinodes, dtiblk, dtbblk;
+ struct dirtree *dti, *dtb;
+
+ // Handle command line arguments.
+
+ if (toys.optargs[1]) {
+ sscanf(toys.optargs[1], "%u", &TT.blocks);
+ temp = O_RDWR|O_CREAT;
+ } else temp = O_RDWR;
+ if (!TT.reserved_percent) TT.reserved_percent = 5;
+
+ // TODO: Check if filesystem is mounted here
+
+ // For mke?fs, open file. For gene?fs, create file.
+ TT.fsfd = xcreate(*toys.optargs, temp, 0777);
+
+ // Determine appropriate block size and block count from file length.
+ // (If no length, default to 4k. They can override it on the cmdline.)
+
+ length = fdlength(TT.fsfd);
+ if (!TT.blocksize) TT.blocksize = (length && length < 1<<29) ? 1024 : 4096;
+ TT.blockbits = 8*TT.blocksize;
+ if (!TT.blocks) TT.blocks = length/TT.blocksize;
+
+ // Collect gene2fs list or lost+found, calculate requirements.
+
+ if (TT.gendir) {
+ strncpy(toybuf, TT.gendir, sizeof(toybuf));
+ dti = dirtree_read(toybuf, NULL, NULL);
+ } else {
+ dti = xzalloc(sizeof(struct dirtree)+11);
+ strcpy(dti->name, "lost+found");
+ dti->st.st_mode = S_IFDIR|0755;
+ dti->st.st_ctime = dti->st.st_mtime = time(NULL);
+ }
+
+ // Add root directory inode. This is iterated through for when finding
+ // blocks, but not when finding inodes. The tree's parent pointers don't
+ // point back into this.
+
+ dtb = xzalloc(sizeof(struct dirtree)+1);
+ dtb->st.st_mode = S_IFDIR|0755;
+ dtb->st.st_ctime = dtb->st.st_mtime = time(NULL);
+ dtb->child = dti;
+
+ // Figure out how much space is used by preset files
+ length = check_treesize(dtb, &(dtb->st.st_size));
+ check_treelinks(dtb);
+
+ // Figure out how many total inodes we need.
+
+ if (!TT.inodes) {
+ if (!TT.bytes_per_inode) TT.bytes_per_inode = 8192;
+ TT.inodes = (TT.blocks * (uint64_t)TT.blocksize) / TT.bytes_per_inode;
+ }
+
+ // If we're generating a filesystem and have no idea how many blocks it
+ // needs, start with a minimal guess, find the overhead of that many
+ // groups, and loop until this is enough groups to store this many blocks.
+ if (!TT.blocks) TT.groups = (TT.treeblocks/TT.blockbits)+1;
+ else TT.groups = div_round_up(TT.blocks, TT.blockbits);
+
+ for (;;) {
+ temp = TT.treeblocks;
+
+ for (i = 0; i<TT.groups; i++) temp += group_overhead(i);
+
+ if (TT.blocks) {
+ if (TT.blocks < temp) error_exit("Not enough space.\n");
+ break;
+ }
+ if (temp <= TT.groups * TT.blockbits) {
+ TT.blocks = temp;
+ break;
+ }
+ TT.groups++;
+ }
+ TT.freeblocks = TT.blocks - temp;
+
+ // Now we know all the TT data, initialize superblock structure.
+
+ init_superblock(&TT.sb);
+
+ // Start writing. Skip the first 1k to avoid the boot sector (if any).
+ put_zeroes(1024);
+
+ // Loop through block groups, write out each one.
+ dtiblk = dtbblk = usedblocks = usedinodes = 0;
+ for (i=0; i<TT.groups; i++) {
+ struct ext2_inode *in = (struct ext2_inode *)toybuf;
+ uint32_t start, itable, used, end;
+ int j, slot;
+
+ // Where does this group end?
+ end = TT.blockbits;
+ if ((i+1)*TT.blockbits > TT.blocks) end = TT.blocks & (TT.blockbits-1);
+
+ // Blocks used by inode table
+ itable = (TT.inodespg*sizeof(struct ext2_inode))/TT.blocksize;
+
+ // If a superblock goes here, write it out.
+ start = group_superblock_overhead(i);
+ if (start) {
+ struct ext2_group *bg = (struct ext2_group *)toybuf;
+ int treeblocks = TT.treeblocks, treeinodes = TT.treeinodes;
+
+ TT.sb.block_group_nr = SWAP_LE16(i);
+
+ // Write superblock and pad it up to block size
+ xwrite(TT.fsfd, &TT.sb, sizeof(struct ext2_superblock));
+ temp = TT.blocksize - sizeof(struct ext2_superblock);
+ if (!i && TT.blocksize > 1024) temp -= 1024;
+ memset(toybuf, 0, TT.blocksize);
+ xwrite(TT.fsfd, toybuf, temp);
+
+ // Loop through groups to write group descriptor table.
+ for(j=0; j<TT.groups; j++) {
+
+ // Figure out what sector this group starts in.
+ used = group_superblock_overhead(j);
+
+ // Find next array slot in this block (flush block if full).
+ slot = j % (TT.blocksize/sizeof(struct ext2_group));
+ if (!slot) {
+ if (j) xwrite(TT.fsfd, bg, TT.blocksize);
+ memset(bg, 0, TT.blocksize);
+ }
+
+ // How many free inodes in this group?
+ temp = TT.inodespg;
+ if (!i) temp -= INODES_RESERVED;
+ if (temp > treeinodes) {
+ treeinodes -= temp;
+ temp = 0;
+ } else {
+ temp -= treeinodes;
+ treeinodes = 0;
+ }
+ bg[slot].free_inodes_count = SWAP_LE16(temp);
+
+ // How many free blocks in this group?
+ temp = TT.inodespg/(TT.blocksize/sizeof(struct ext2_inode)) + 2;
+ temp = end-used-temp;
+ if (temp > treeblocks) {
+ treeblocks -= temp;
+ temp = 0;
+ } else {
+ temp -= treeblocks;
+ treeblocks = 0;
+ }
+ bg[slot].free_blocks_count = SWAP_LE32(temp);
+
+ // Fill out rest of group structure
+ used += j*TT.blockbits;
+ bg[slot].block_bitmap = SWAP_LE32(used++);
+ bg[slot].inode_bitmap = SWAP_LE32(used++);
+ bg[slot].inode_table = SWAP_LE32(used);
+ bg[slot].used_dirs_count = 0; // (TODO)
+ }
+ xwrite(TT.fsfd, bg, TT.blocksize);
+ }
+
+ // Now write out stuff that every block group has.
+
+ // Write block usage bitmap
+
+ start += 2 + itable;
+ memset(toybuf, 0, TT.blocksize);
+ bits_set(toybuf, 0, start);
+ bits_set(toybuf, end, TT.blockbits-end);
+ temp = TT.treeblocks - usedblocks;
+ if (temp) {
+ if (end-start > temp) temp = end-start;
+ bits_set(toybuf, start, temp);
+ }
+ xwrite(TT.fsfd, toybuf, TT.blocksize);
+
+ // Write inode bitmap
+ memset(toybuf, 0, TT.blocksize);
+ j = 0;
+ if (!i) bits_set(toybuf, 0, j = INODES_RESERVED);
+ bits_set(toybuf, TT.inodespg, slot = TT.blockbits-TT.inodespg);
+ temp = TT.treeinodes - usedinodes;
+ if (temp) {
+ if (slot-j > temp) temp = slot-j;
+ bits_set(toybuf, j, temp);
+ }
+ xwrite(TT.fsfd, toybuf, TT.blocksize);
+
+ // Write inode table for this group (TODO)
+ for (j = 0; j<TT.inodespg; j++) {
+ slot = j % (TT.blocksize/sizeof(struct ext2_inode));
+ if (!slot) {
+ if (j) xwrite(TT.fsfd, in, TT.blocksize);
+ memset(in, 0, TT.blocksize);
+ }
+ if (!i && j<INODES_RESERVED) {
+ // Write root inode
+ if (j == 2) fill_inode(in+slot, dtb);
+ } else if (dti) {
+ fill_inode(in+slot, dti);
+ dti = treenext(dti);
+ }
+ }
+ xwrite(TT.fsfd, in, TT.blocksize);
+
+ while (dtb) {
+ // TODO write index data block
+ // TODO write root directory data block
+ // TODO write directory data block
+ // TODO write file data block
+ put_zeroes(TT.blocksize);
+ start++;
+ if (start == end) break;
+ }
+ // Write data blocks (TODO)
+ put_zeroes((end-start) * TT.blocksize);
+ }
+}
diff --git a/toys/other/mkswap.c b/toys/other/mkswap.c
new file mode 100644
index 00000000..87c1550f
--- /dev/null
+++ b/toys/other/mkswap.c
@@ -0,0 +1,42 @@
+/* vi: set sw=4 ts=4:
+ *
+ * mkswap.c - Format swap device.
+ *
+ * Copyright 2009 Rob Landley <rob@landley.net>
+ *
+ * Not in SUSv4.
+
+USE_MKSWAP(NEWTOY(mkswap, "<1>1", TOYFLAG_SBIN))
+
+config MKSWAP
+ bool "mkswap"
+ default y
+ help
+ usage: mkswap DEVICE
+
+ Sets up a Linux swap area on a device or file.
+*/
+
+#include "toys.h"
+
+void mkswap_main(void)
+{
+ int fd = xopen(*toys.optargs, O_RDWR), pagesize = sysconf(_SC_PAGE_SIZE);
+ off_t len = fdlength(fd);
+ unsigned int pages = (len/pagesize)-1, *swap = (unsigned int *)toybuf;
+
+ // Write header. Note that older kernel versions checked signature
+ // on disk (not in cache) during swapon, so sync after writing.
+
+ swap[0] = 1;
+ swap[1] = pages;
+ xlseek(fd, 1024, SEEK_SET);
+ xwrite(fd, swap, 129*sizeof(unsigned int));
+ xlseek(fd, pagesize-10, SEEK_SET);
+ xwrite(fd, "SWAPSPACE2", 10);
+ fsync(fd);
+
+ if (CFG_TOYBOX_FREE) close(fd);
+
+ printf("Swapspace size: %luk\n", pages*(unsigned long)(pagesize/1024));
+}
diff --git a/toys/other/modinfo.c b/toys/other/modinfo.c
new file mode 100644
index 00000000..c878fc4e
--- /dev/null
+++ b/toys/other/modinfo.c
@@ -0,0 +1,103 @@
+/* vi: set sw=4 ts=4:
+ *
+ * modinfo.c - Display module info
+ *
+ * Copyright 2012 Andre Renaud <andre@bluewatersys.com>
+ *
+
+USE_MODINFO(NEWTOY(modinfo, "<1F:0", TOYFLAG_BIN))
+
+config MODINFO
+ bool "modinfo"
+ default y
+ help
+ usage: modinfo [-0] [-F field] [modulename...]
+*/
+
+#include "toys.h"
+
+#define FLAG_0 (1 << 0)
+
+DEFINE_GLOBALS(
+ char *field;
+)
+#define TT this.modinfo
+
+static const char *modinfo_tags[] = {
+ "alias", "license", "description", "author", "vermagic",
+ "srcversion", "intree", "parm", "depends",
+};
+
+static void output_field(const char *field, const char *value)
+{
+ int len;
+
+ if (TT.field && strcmp(TT.field, field) != 0)
+ return;
+
+ len = strlen(field);
+
+ if (TT.field)
+ xprintf("%s", value);
+ else
+ xprintf("%s:%*s%s",
+ field, 15 - len, "", value);
+ if (toys.optflags & FLAG_0)
+ xwrite(fileno(stdout), "\0", 1);
+ else
+ xputs("");
+}
+
+
+static void modinfo_file(struct dirtree *dir)
+{
+ int fd, len, i;
+ char *buf, *pos;
+ char *full_name;
+
+ full_name = dirtree_path(dir, NULL);
+
+ output_field("filename", full_name);
+ fd = xopen(full_name, O_RDONLY);
+ len = fdlength(fd);
+ buf = xmalloc(len);
+ xreadall(fd, buf, len);
+
+ for (pos = buf; pos < buf + len + 10; pos++) {
+ if (*pos)
+ continue;
+
+ for (i = 0; i < sizeof(modinfo_tags) / sizeof(modinfo_tags[0]); i++) {
+ const char *str = modinfo_tags[i];
+ int len = strlen(str);
+ if (strncmp(pos + 1, str, len) == 0 && pos[len + 1] == '=')
+ output_field(str, &pos[len + 2]);
+ }
+ }
+
+ free(full_name);
+ free(buf);
+ close(fd);
+}
+
+static int check_module(struct dirtree *new)
+{
+ if (S_ISREG(new->st.st_mode)) {
+ char **s;
+ for (s = toys.optargs; *s; s++) {
+ int len = strlen(*s);
+ if (!strncmp(*s, new->name, len) && !strcmp(new->name+len, ".ko"))
+ modinfo_file(new);
+ }
+ }
+
+ return dirtree_notdotdot(new);
+}
+
+void modinfo_main(void)
+{
+ struct utsname uts;
+ if (uname(&uts) < 0) perror_exit("bad uname");
+ sprintf(toybuf, "/lib/modules/%s", uts.release);
+ dirtree_read(toybuf, check_module);
+}
diff --git a/toys/other/mountpoint.c b/toys/other/mountpoint.c
new file mode 100644
index 00000000..405bb3a5
--- /dev/null
+++ b/toys/other/mountpoint.c
@@ -0,0 +1,56 @@
+/* vi: set sw=4 ts=4:
+ *
+ * mountpoint.c - Check if a directory is a mountpoint.
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_MOUNTPOINT(NEWTOY(mountpoint, "<1qdx", TOYFLAG_BIN))
+
+config MOUNTPOINT
+ bool "mountpoint"
+ default y
+ help
+ usage: mountpoint [-q] [-d] directory
+ mountpoint [-q] [-x] device
+ -q Be quiet, return zero if directory is a mountpoint
+ -d Print major/minor device number of the directory
+ -x Print major/minor device number of the block device
+*/
+
+#include "toys.h"
+
+void mountpoint_main(void)
+{
+ struct stat st1, st2;
+ int res = 0;
+ int quiet = toys.optflags & 0x4;
+ toys.exitval = 1; // be pessimistic
+ strncpy(toybuf, toys.optargs[0], sizeof(toybuf));
+ if (((toys.optflags & 0x1) && lstat(toybuf, &st1)) || stat(toybuf, &st1))
+ perror_exit("%s", toybuf);
+
+ if (toys.optflags & 0x1){
+ if (S_ISBLK(st1.st_mode)) {
+ if (!quiet) printf("%u:%u\n", major(st1.st_rdev), minor(st1.st_rdev));
+ toys.exitval = 0;
+ return;
+ }
+ if (!quiet) printf("%s: not a block device\n", toybuf);
+ return;
+ }
+
+ if(!S_ISDIR(st1.st_mode)){
+ if (!quiet) printf("%s: not a directory\n", toybuf);
+ return;
+ }
+ strncat(toybuf, "/..", sizeof(toybuf));
+ stat(toybuf, &st2);
+ res = (st1.st_dev != st2.st_dev) ||
+ (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
+ if (!quiet) printf("%s is %sa mountpoint\n", toys.optargs[0], res ? "" : "not ");
+ if (toys.optflags & 0x2)
+ printf("%u:%u\n", major(st1.st_dev), minor(st1.st_dev));
+ toys.exitval = res ? 0 : 1;
+}
diff --git a/toys/other/netcat.c b/toys/other/netcat.c
new file mode 100644
index 00000000..3da19127
--- /dev/null
+++ b/toys/other/netcat.c
@@ -0,0 +1,232 @@
+/* vi: set sw=4 ts=4:
+ *
+ * nc: mini-netcat - Forward stdin/stdout to a file or network connection.
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+ *
+ * Not in SUSv3.
+
+USE_NETCAT(OLDTOY(nc, netcat, USE_NETCAT_LISTEN("tl^L^")"w#p#s:q#f:", TOYFLAG_BIN))
+USE_NETCAT(NEWTOY(netcat, USE_NETCAT_LISTEN("tl^L^")"w#p#s:q#f:", TOYFLAG_BIN))
+
+config NETCAT
+ bool "netcat"
+ default y
+ help
+ usage: netcat [-wpq #] [-s addr] {IPADDR PORTNUM|-f FILENAME|-let} [-e COMMAND]
+
+ -w SECONDS timeout for connection
+ -p local port number
+ -s local ipv4 address
+ -q SECONDS quit this many seconds after EOF on stdin.
+ -f use FILENAME (ala /dev/ttyS0) instead of network
+
+ Use "stty 115200 -F /dev/ttyS0 && stty raw -echo -ctlecho" with
+ netcat -f to connect to a serial port.
+
+
+config NETCAT_LISTEN
+ bool "netcat sever options (-let)"
+ default y
+ depends on NETCAT
+ help
+ -t allocate tty (must come before -l or -L)
+ -l listen for one incoming connection.
+ -L listen for multiple incoming connections (server mode).
+
+ Any additional command line arguments after -l or -L are executed
+ to handle each incoming connection. If none, the connection is
+ forwarded to stdin/stdout.
+
+ For a quick-and-dirty server, try something like:
+ netcat -s 127.0.0.1 -p 1234 -tL /bin/bash -l
+*/
+
+#include "toys.h"
+#include "toynet.h"
+
+DEFINE_GLOBALS(
+ char *filename; // -f read from filename instead of network
+ long quit_delay; // -q Exit after EOF from stdin after # seconds.
+ char *source_address; // -s Bind to a specific source address.
+ long port; // -p Bind to a specific source port.
+ long wait; // -w Wait # seconds for a connection.
+)
+
+#define TT this.netcat
+
+#define FLAG_f 1
+#define FLAG_L 32
+#define FLAG_l 64
+#define FLAG_t 128
+
+static void timeout(int signum)
+{
+ if (TT.wait) error_exit("Timeout");
+ exit(0);
+}
+
+static void set_alarm(int seconds)
+{
+ signal(SIGALRM, seconds ? timeout : SIG_DFL);
+ alarm(seconds);
+}
+
+// Translate x.x.x.x numeric IPv4 address, or else DNS lookup an IPv4 name.
+static void lookup_name(char *name, uint32_t *result)
+{
+ struct hostent *hostbyname;
+
+ hostbyname = gethostbyname(name);
+ if (!hostbyname) error_exit("no host '%s'", name);
+ *result = *(uint32_t *)*hostbyname->h_addr_list;
+}
+
+// Worry about a fancy lookup later.
+static void lookup_port(char *str, uint16_t *port)
+{
+ *port = SWAP_BE16(atoi(str));
+}
+
+void netcat_main(void)
+{
+ int sockfd=-1, pollcount=2;
+ struct pollfd pollfds[2];
+
+ memset(pollfds, 0, 2*sizeof(struct pollfd));
+ pollfds[0].events = pollfds[1].events = POLLIN;
+ set_alarm(TT.wait);
+
+ // The argument parsing logic can't make "<2" conditional on other
+ // arguments like -f and -l, so we do it by hand here.
+ if (toys.optflags&FLAG_f) {
+ if (toys.optc) toys.exithelp++;
+ } else if (!(toys.optflags&(FLAG_l|FLAG_L)) && toys.optc!=2) toys.exithelp++;
+
+ if (toys.exithelp) error_exit("Argument count wrong");
+
+ if (TT.filename) pollfds[0].fd = xopen(TT.filename, O_RDWR);
+ else {
+ int temp;
+ struct sockaddr_in address;
+
+ // Setup socket
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (-1 == sockfd) perror_exit("socket");
+ fcntl(sockfd, F_SETFD, FD_CLOEXEC);
+ temp = 1;
+ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &temp, sizeof(temp));
+ memset(&address, 0, sizeof(address));
+ address.sin_family = AF_INET;
+ if (TT.source_address || TT.port) {
+ address.sin_port = SWAP_BE16(TT.port);
+ if (TT.source_address)
+ lookup_name(TT.source_address, (uint32_t *)&address.sin_addr);
+ if (bind(sockfd, (struct sockaddr *)&address, sizeof(address)))
+ perror_exit("bind");
+ }
+
+ // Dial out
+
+ if (!CFG_NETCAT_LISTEN || !(toys.optflags&(FLAG_L|FLAG_l))) {
+ // Figure out where to dial out to.
+ lookup_name(*toys.optargs, (uint32_t *)&address.sin_addr);
+ lookup_port(toys.optargs[1], &address.sin_port);
+ temp = connect(sockfd, (struct sockaddr *)&address, sizeof(address));
+ if (temp<0) perror_exit("connect");
+ pollfds[0].fd = sockfd;
+
+ // Listen for incoming connections
+
+ } else {
+ socklen_t len = sizeof(address);
+
+ if (listen(sockfd, 5)) error_exit("listen");
+ if (!TT.port) {
+ getsockname(sockfd, (struct sockaddr *)&address, &len);
+ printf("%d\n", SWAP_BE16(address.sin_port));
+ fflush(stdout);
+ }
+ // Do we need to return immediately because -l has arguments?
+
+ if ((toys.optflags&FLAG_l) && toys.optc) {
+ if (fork()) goto cleanup;
+ close(0);
+ close(1);
+ close(2);
+ }
+
+ for (;;) {
+ pid_t child = 0;
+
+ // For -l, call accept from the _new_ thread.
+
+ pollfds[0].fd = accept(sockfd, (struct sockaddr *)&address,
+ &len);
+ if (pollfds[0].fd<0) perror_exit("accept");
+
+ // Do we need a tty?
+
+ if (toys.optflags&FLAG_t)
+ child = forkpty(&(pollfds[1].fd), NULL, NULL, NULL);
+
+ // Do we need to fork and/or redirect for exec?
+
+ else {
+ if (toys.optflags&FLAG_L) child = fork();
+ if (!child && toys.optc) {
+ int fd = pollfds[0].fd;
+
+ if (!temp) close(sockfd);
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ if (fd>2) close(fd);
+ }
+ }
+
+ if (child<0) error_msg("Fork failed\n");
+ if (child<1) break;
+ close(pollfds[0].fd);
+ }
+ }
+ }
+
+ // We have a connection. Disarm timeout.
+ // (Does not play well with -L, but what _should_ that do?)
+ set_alarm(0);
+
+ if (CFG_NETCAT_LISTEN && (toys.optflags&(FLAG_L|FLAG_l) && toys.optc)) {
+ execvp(*toys.optargs, toys.optargs);
+ error_exit("Exec failed");
+ }
+
+ // Poll loop copying stdin->socket and socket->stdout.
+ for (;;) {
+ int i;
+
+ if (0>poll(pollfds, pollcount, -1)) perror_exit("poll");
+
+ for (i=0; i<pollcount; i++) {
+ if (pollfds[i].revents & POLLIN) {
+ int len = read(pollfds[i].fd, toybuf, sizeof(toybuf));
+ if (len<1) goto dohupnow;
+ xwrite(i ? pollfds[0].fd : 1, toybuf, len);
+ } else if (pollfds[i].revents & POLLHUP) {
+dohupnow:
+ // Close half-connection. This is needed for things like
+ // "echo GET / | netcat landley.net 80"
+ if (i) {
+ shutdown(pollfds[0].fd, SHUT_WR);
+ pollcount--;
+ set_alarm(TT.quit_delay);
+ } else goto cleanup;
+ }
+ }
+ }
+cleanup:
+ if (CFG_TOYBOX_FREE) {
+ close(pollfds[0].fd);
+ close(sockfd);
+ }
+}
diff --git a/toys/other/oneit.c b/toys/other/oneit.c
new file mode 100644
index 00000000..45935918
--- /dev/null
+++ b/toys/other/oneit.c
@@ -0,0 +1,80 @@
+/* vi: set sw=4 ts=4:
+ *
+ * oneit.c, tiny one-process init replacement.
+ *
+ * Copyright 2005, 2007 by Rob Landley <rob@landley.net>.
+ *
+ * Not in SUSv3.
+
+USE_ONEIT(NEWTOY(oneit, "^<1c:p", TOYFLAG_SBIN))
+
+config ONEIT
+ bool "oneit"
+ default y
+ help
+ usage: oneit [-p] [-c /dev/tty0] command [...]
+
+ A simple init program that runs a single supplied command line with a
+ controlling tty (so CTRL-C can kill it).
+
+ -p Power off instead of rebooting when command exits.
+ -c Which console device to use.
+
+ The oneit command runs the supplied command line as a child process
+ (because PID 1 has signals blocked), attached to /dev/tty0, in its
+ own session. Then oneit reaps zombies until the child exits, at
+ which point it reboots (or with -p, powers off) the system.
+*/
+
+#include "toys.h"
+#include <sys/reboot.h>
+
+DEFINE_GLOBALS(
+ char *console;
+)
+
+#define TT this.oneit
+
+// The minimum amount of work necessary to get ctrl-c and such to work is:
+//
+// - Fork a child (PID 1 is special: can't exit, has various signals blocked).
+// - Do a setsid() (so we have our own session).
+// - In the child, attach stdio to /dev/tty0 (/dev/console is special)
+// - Exec the rest of the command line.
+//
+// PID 1 then reaps zombies until the child process it spawned exits, at which
+// point it calls sync() and reboot(). I could stick a kill -1 in there.
+
+
+void oneit_main(void)
+{
+ int i;
+ pid_t pid;
+
+ // Create a new child process.
+ pid = vfork();
+ if (pid) {
+
+ // pid 1 just reaps zombies until it gets its child, then halts the system.
+ while (pid!=wait(&i));
+ sync();
+
+ // PID 1 can't call reboot() because it kills the task that calls it,
+ // which causes the kernel to panic before the actual reboot happens.
+ if (!vfork()) reboot((toys.optflags&1) ? RB_POWER_OFF : RB_AUTOBOOT);
+ sleep(5);
+ _exit(1);
+ }
+
+ // Redirect stdio to /dev/tty0, with new session ID, so ctrl-c works.
+ setsid();
+ for (i=0; i<3; i++) {
+ close(i);
+ xopen(TT.console ? TT.console : "/dev/tty0",O_RDWR);
+ }
+
+ // Can't xexec() here, because we vforked so we don't want to error_exit().
+ toy_exec(toys.optargs);
+ execvp(*toys.optargs, toys.optargs);
+ _exit(127);
+}
diff --git a/toys/other/printenv.c b/toys/other/printenv.c
new file mode 100644
index 00000000..305b3e7d
--- /dev/null
+++ b/toys/other/printenv.c
@@ -0,0 +1,47 @@
+/* vi: set sw=4 ts=4:
+ *
+ * printenv.c - Print environment variables.
+ *
+ * Copyright 2012 Georgi Chorbadzhiyski <georgi@unixsol.org>
+ *
+
+USE_PRINTENV(NEWTOY(printenv, "0(null)", TOYFLAG_USR|TOYFLAG_BIN))
+
+config PRINTENV
+ bool "printenv"
+ default y
+ help
+ usage: printenv [-0] [env_var...]
+
+ Print environment variables.
+
+ -0 Use \0 as delimiter instead of \n
+*/
+
+#include "toys.h"
+
+extern char **environ;
+
+void printenv_main(void)
+{
+ char **env, **var = toys.optargs;
+ char delim = '\n';
+
+ if (toys.optflags) delim = 0;
+
+ do {
+ int catch = 0, len = *var ? strlen(*var) : 0;
+
+ for (env = environ; *env; env++) {
+ char *out = *env;
+ if (*var) {
+ if (!strncmp(out, *var, len) && out[len] == '=')
+ out += len +1;
+ else continue;
+ }
+ xprintf("%s%c", out, delim);
+ catch++;
+ }
+ if (*var && !catch) toys.exitval = 1;
+ } while (*var && *(++var));
+}
diff --git a/toys/other/readlink.c b/toys/other/readlink.c
new file mode 100644
index 00000000..51dbf02f
--- /dev/null
+++ b/toys/other/readlink.c
@@ -0,0 +1,45 @@
+/* vi: set sw=4 ts=4:
+ *
+ * readlink.c - Return string representation of a symbolic link.
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+ *
+ * Not in SUSv3.
+
+USE_READLINK(NEWTOY(readlink, "<1f", TOYFLAG_BIN))
+
+config READLINK
+ bool "readlink"
+ default n
+ help
+ usage: readlink
+
+ Show what a symbolic link points to.
+
+config READLINK_F
+ bool "readlink -f"
+ default n
+ depends on READLINK
+ help
+ usage: readlink [-f]
+
+ -f Show full cannonical path, with no symlinks in it. Returns
+ nonzero if nothing could currently exist at this location.
+*/
+
+#include "toys.h"
+
+void readlink_main(void)
+{
+ char *s;
+
+ // Calculating full cannonical path?
+
+ if (CFG_READLINK_F && toys.optflags) s = xrealpath(*toys.optargs);
+ else s = xreadlink(*toys.optargs);
+
+ if (s) {
+ xputs(s);
+ if (CFG_TOYBOX_FREE) free(s);
+ } else toys.exitval = 1;
+}
diff --git a/toys/other/realpath.c b/toys/other/realpath.c
new file mode 100644
index 00000000..10457cf6
--- /dev/null
+++ b/toys/other/realpath.c
@@ -0,0 +1,31 @@
+/* vi: set sw=4 ts=4:
+ *
+ * realpath.c - Return the canonical version of a pathname
+ *
+ * Copyright 2012 Andre Renaud <andre@bluewatersys.com>
+ *
+ * Not in SUSv4.
+
+USE_REALPATH(NEWTOY(realpath, "<1", TOYFLAG_USR|TOYFLAG_BIN))
+
+config REALPATH
+ bool "realpath"
+ default y
+ help
+ usage: realpath FILE...
+
+ Display the canonical absolute pathname
+*/
+
+#include "toys.h"
+
+void realpath_main(void)
+{
+ char **s = toys.optargs;
+ for (s = toys.optargs; *s; s++) {
+ if (!realpath(*s, toybuf)) {
+ perror_msg("cannot access '%s'", *s);
+ toys.exitval = 1;
+ } else xputs(toybuf);
+ }
+}
diff --git a/toys/other/rmmod.c b/toys/other/rmmod.c
new file mode 100644
index 00000000..d730b45d
--- /dev/null
+++ b/toys/other/rmmod.c
@@ -0,0 +1,51 @@
+/* vi: set sw=4 ts=4:
+ *
+ * rmmod.c - Remove a module from the Linux kernel.
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_RMMOD(NEWTOY(rmmod, "<1wf", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
+
+config RMMOD
+ bool "rmmod"
+ default y
+ help
+ usage: rmmod [-wf] [MODULE]
+
+ Unload the module named MODULE from the Linux kernel.
+ -f Force unload of a module
+ -w Wait until the module is no longer used.
+
+*/
+
+#include "toys.h"
+
+#include <sys/syscall.h>
+#define delete_module(mod, flags) syscall(__NR_delete_module, mod, flags)
+
+void rmmod_main(void)
+{
+ unsigned int flags = O_NONBLOCK|O_EXCL;
+ char * mod_name;
+ int len;
+
+ // Basename
+ mod_name = strrchr(toys.optargs[0],'/');
+ if (mod_name)
+ mod_name++;
+ else
+ mod_name = toys.optargs[0];
+
+ // Remove .ko if present
+ len = strlen(mod_name);
+ if (len > 3 && !strcmp(&mod_name[len-3], ".ko" ))
+ mod_name[len-3] = 0;
+
+ if (toys.optflags & 1) flags |= O_TRUNC;
+ if (toys.optflags & 2) flags &= ~O_NONBLOCK;
+
+ if (delete_module(mod_name, flags))
+ perror_exit("failed to unload %s", mod_name);
+}
diff --git a/toys/other/setsid.c b/toys/other/setsid.c
new file mode 100644
index 00000000..1b98315c
--- /dev/null
+++ b/toys/other/setsid.c
@@ -0,0 +1,33 @@
+/* vi: set sw=4 ts=4:
+ *
+ * setsid.c - Run program in a new session ID.
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ *
+ * Not in SUSv4.
+
+USE_SETSID(NEWTOY(setsid, "^<1t", TOYFLAG_USR|TOYFLAG_BIN))
+
+config SETSID
+ bool "setsid"
+ default y
+ help
+ usage: setsid [-t] command [args...]
+
+ Run process in a new session.
+
+ -t Grab tty (become foreground process, receiving keyboard signals)
+*/
+
+#include "toys.h"
+
+void setsid_main(void)
+{
+ while (setsid()<0)
+ if (vfork()) _exit(0);
+ if (toys.optflags) {
+ setpgid(0,0);
+ tcsetpgrp(0, getpid());
+ }
+ xexec(toys.optargs);
+}
diff --git a/toys/other/sha1sum.c b/toys/other/sha1sum.c
new file mode 100644
index 00000000..3229cd12
--- /dev/null
+++ b/toys/other/sha1sum.c
@@ -0,0 +1,185 @@
+/* vi: set sw=4 ts=4:
+ *
+ * sha1sum.c - Calculate sha1 cryptographic hash for input.
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+ *
+ * Based on the public domain SHA-1 in C by Steve Reid <steve@edmweb.com>
+ * from http://www.mirrors.wiretapped.net/security/cryptography/hashes/sha1/
+ *
+ * Not in SUSv3.
+
+USE_SHA1SUM(NEWTOY(sha1sum, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config SHA1SUM
+ bool "sha1sum"
+ default y
+ help
+ usage: sha1sum [file...]
+
+ Calculate sha1 hash of files (or stdin).
+*/
+
+#include <toys.h>
+
+struct sha1 {
+ uint32_t state[5];
+ uint32_t oldstate[5];
+ uint64_t count;
+ union {
+ unsigned char c[64];
+ uint32_t i[16];
+ } buffer;
+};
+
+static void sha1_init(struct sha1 *this);
+static void sha1_transform(struct sha1 *this);
+static void sha1_update(struct sha1 *this, char *data, unsigned int len);
+static void sha1_final(struct sha1 *this, char digest[20]);
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+// blk0() and blk() perform the initial expand.
+// The idea of expanding during the round function comes from SSLeay
+#if 1
+#define blk0(i) (block[i] = (rol(block[i],24)&0xFF00FF00) \
+ |(rol(block[i],8)&0x00FF00FF))
+#else // big endian?
+#define blk0(i) block[i]
+#endif
+#define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \
+ ^block[(i+2)&15]^block[i&15],1))
+
+static const uint32_t rconsts[]={0x5A827999,0x6ED9EBA1,0x8F1BBCDC,0xCA62C1D6};
+
+// Hash a single 512-bit block. This is the core of the algorithm.
+
+static void sha1_transform(struct sha1 *this)
+{
+ int i, j, k, count;
+ uint32_t *block = this->buffer.i;
+ uint32_t *rot[5], *temp;
+
+ // Copy context->state[] to working vars
+ for (i=0; i<5; i++) {
+ this->oldstate[i] = this->state[i];
+ rot[i] = this->state + i;
+ }
+ // 4 rounds of 20 operations each.
+ for (i=count=0; i<4; i++) {
+ for (j=0; j<20; j++) {
+ uint32_t work;
+
+ work = *rot[2] ^ *rot[3];
+ if (!i) work = (work & *rot[1]) ^ *rot[3];
+ else {
+ if (i==2)
+ work = ((*rot[1]|*rot[2])&*rot[3])|(*rot[1]&*rot[2]);
+ else work ^= *rot[1];
+ }
+ if (!i && j<16) work += blk0(count);
+ else work += blk(count);
+ *rot[4] += work + rol(*rot[0],5) + rconsts[i];
+ *rot[1] = rol(*rot[1],30);
+
+ // Rotate by one for next time.
+ temp = rot[4];
+ for (k=4; k; k--) rot[k] = rot[k-1];
+ *rot = temp;
+ count++;
+ }
+ }
+ // Add the previous values of state[]
+ for (i=0; i<5; i++) this->state[i] += this->oldstate[i];
+}
+
+
+// Initialize a struct sha1.
+
+static void sha1_init(struct sha1 *this)
+{
+ /* SHA1 initialization constants */
+ this->state[0] = 0x67452301;
+ this->state[1] = 0xEFCDAB89;
+ this->state[2] = 0x98BADCFE;
+ this->state[3] = 0x10325476;
+ this->state[4] = 0xC3D2E1F0;
+ this->count = 0;
+}
+
+// Fill the 64-byte working buffer and call sha1_transform() when full.
+
+void sha1_update(struct sha1 *this, char *data, unsigned int len)
+{
+ unsigned int i, j;
+
+ j = this->count & 63;
+ this->count += len;
+
+ // Enough data to process a frame?
+ if ((j + len) > 63) {
+ i = 64-j;
+ memcpy(this->buffer.c + j, data, i);
+ sha1_transform(this);
+ for ( ; i + 63 < len; i += 64) {
+ memcpy(this->buffer.c, data + i, 64);
+ sha1_transform(this);
+ }
+ j = 0;
+ } else i = 0;
+ // Grab remaining chunk
+ memcpy(this->buffer.c + j, data + i, len - i);
+}
+
+// Add padding and return the message digest.
+
+void sha1_final(struct sha1 *this, char digest[20])
+{
+ uint64_t count = this->count << 3;
+ unsigned int i;
+ char buf;
+
+ // End the message by appending a "1" bit to the data, ending with the
+ // message size (in bits, big endian), and adding enough zero bits in
+ // between to pad to the end of the next 64-byte frame.
+ //
+ // Since our input up to now has been in whole bytes, we can deal with
+ // bytes here too.
+
+ buf = 0x80;
+ do {
+ sha1_update(this, &buf, 1);
+ buf = 0;
+ } while ((this->count & 63) != 56);
+ for (i = 0; i < 8; i++)
+ this->buffer.c[56+i] = count >> (8*(7-i));
+ sha1_transform(this);
+
+ for (i = 0; i < 20; i++)
+ digest[i] = this->state[i>>2] >> ((3-(i & 3)) * 8);
+ // Wipe variables. Cryptogropher paranoia.
+ memset(this, 0, sizeof(struct sha1));
+}
+
+// Callback for loopfiles()
+
+static void do_sha1(int fd, char *name)
+{
+ struct sha1 this;
+ int len;
+
+ sha1_init(&this);
+ for (;;) {
+ len = read(fd, toybuf, sizeof(toybuf));
+ if (len<1) break;
+ sha1_update(&this, toybuf, len);
+ }
+ sha1_final(&this, toybuf);
+ for (len = 0; len < 20; len++) printf("%02x", toybuf[len]);
+ printf(" %s\n", name);
+}
+
+void sha1sum_main(void)
+{
+ loopfiles(toys.optargs, do_sha1);
+}
diff --git a/toys/other/swapoff.c b/toys/other/swapoff.c
new file mode 100644
index 00000000..fba0de83
--- /dev/null
+++ b/toys/other/swapoff.c
@@ -0,0 +1,26 @@
+/* vi: set sw=4 ts=4:
+ *
+ * swapoff.c - Disable region for swapping
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_SWAPOFF(NEWTOY(swapoff, "<1>1", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
+
+config SWAPOFF
+ bool "swapoff"
+ default y
+ help
+ usage: swapoff swapregion
+
+ Disable swapping on a given swapregion.
+*/
+
+#include "toys.h"
+
+void swapoff_main(void)
+{
+ if (swapoff(toys.optargs[0]))
+ perror_exit("failed to remove swaparea");
+}
diff --git a/toys/other/swapon.c b/toys/other/swapon.c
new file mode 100644
index 00000000..16ce8d1f
--- /dev/null
+++ b/toys/other/swapon.c
@@ -0,0 +1,37 @@
+/* vi: set sw=4 ts=4:
+ *
+ * swapon.c - Enable region for swapping
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_SWAPON(NEWTOY(swapon, "<1>1p#<0>32767", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
+
+config SWAPON
+ bool "swapon"
+ default y
+ help
+ usage: swapon [-p priority] filename
+
+ Enable swapping on a given device/file.
+*/
+
+#include "toys.h"
+
+DEFINE_GLOBALS(
+ long priority;
+)
+
+#define TT this.swapon
+
+void swapon_main(void)
+{
+ int flags = 0;
+
+ if (toys.optflags & 1)
+ flags = SWAP_FLAG_PREFER | (TT.priority << SWAP_FLAG_PRIO_SHIFT);
+
+ if (swapon(*toys.optargs, flags))
+ perror_exit("Couldn't swapon '%s'", *toys.optargs);
+}
diff --git a/toys/other/tac.c b/toys/other/tac.c
new file mode 100644
index 00000000..cd5f83f6
--- /dev/null
+++ b/toys/other/tac.c
@@ -0,0 +1,55 @@
+/* vi: set sw=4 ts=4:
+ *
+ * tac.c - output lines in reverse order
+ *
+ * Copyright 2012 Rob Landley <rob@landley.net>
+ *
+ * Not in SUSv4.
+
+USE_TAC(NEWTOY(tac, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config TAC
+ bool "tac"
+ default y
+ help
+ usage: tac [FILE...]
+
+ Output lines in reverse order.
+*/
+
+#include "toys.h"
+
+void do_tac(int fd, char *name)
+{
+ struct arg_list *list = NULL;
+ char *c;
+
+ // Read in lines
+ for (;;) {
+ struct arg_list *temp;
+ int len;
+
+ if (!(c = get_line(fd))) break;
+
+ len = strlen(c);
+ if (len && c[len-1]=='\n') c[len-1] = 0;
+ temp = xmalloc(sizeof(struct arg_list));
+ temp->next = list;
+ temp->arg = c;
+ list = temp;
+ }
+
+ // Play them back.
+ while (list) {
+ struct arg_list *temp = list->next;
+ xputs(list->arg);
+ free(list->arg);
+ free(list);
+ list = temp;
+ }
+}
+
+void tac_main(void)
+{
+ loopfiles(toys.optargs, do_tac);
+}
diff --git a/toys/other/taskset.c b/toys/other/taskset.c
new file mode 100644
index 00000000..7ac3b572
--- /dev/null
+++ b/toys/other/taskset.c
@@ -0,0 +1,110 @@
+/* vi: set sw=4 ts=4:
+ *
+ * taskset.c - Retrieve or set the CPU affinity of a process.
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+ *
+ * No standard.
+
+USE_TASKSET(NEWTOY(taskset, "<1^pa", TOYFLAG_BIN|TOYFLAG_STAYROOT))
+
+config TASKSET
+ bool "taskset"
+ default y
+ help
+ usage: taskset [-ap] [mask] [PID | cmd [args...]]
+
+ Launch a new task which may only run on certain processors, or change
+ the processor affinity of an exisitng PID.
+
+ Mask is a hex string where each bit represents a processor the process
+ is allowed to run on. PID without a mask displays existing affinity.
+
+ -p Set/get the affinity of given PID instead of a new command.
+ -a Set/get the affinity of all threads of the PID.
+*/
+
+#include "toys.h"
+
+#define FLAG_a 0x1
+#define FLAG_p 0x2
+
+// Prototype for syscall wrappers sched.h refuses to give us
+int sched_setaffinity(pid_t pid, size_t size, void *cpuset);
+int sched_getaffinity(pid_t pid, size_t size, void *cpuset);
+
+// mask is an array of long, which makes the layout a bit weird on big
+// endian systems but as long as it's consistent...
+
+static void do_taskset(pid_t pid, int quiet)
+{
+ unsigned long *mask = (unsigned long *)toybuf;
+ char *s = *toys.optargs, *failed = "failed to %s %d's affinity";
+ int i, j, k;
+
+ for (i=0; ; i++) {
+ if (!quiet) {
+ int j = sizeof(toybuf), flag = 0;
+
+ if (sched_getaffinity(pid, sizeof(toybuf), (void *)mask))
+ perror_exit(failed, "get", pid);
+
+ printf("pid %d's %s affinity mask: ", pid, i ? "new" : "current");
+
+ while (j--) {
+ int x = 255 & (mask[j/sizeof(long)] >> (8*(j&(sizeof(long)-1))));
+
+ if (flag) printf("%02x", x);
+ else if (x) {
+ flag++;
+ printf("%x", x);
+ }
+ }
+ putchar('\n');
+ }
+
+ if (i || toys.optc < 2) return;
+
+ memset(toybuf, 0, sizeof(toybuf));
+ k = strlen(s = *toys.optargs);
+ s += k;
+ for (j = 0; j<k; j++) {
+ unsigned long digit = *(--s) - '0';
+
+ if (digit > 9) digit = 10 + tolower(*s)-'a';
+ if (digit > 15) error_exit("bad mask '%s'", *toys.optargs);
+ mask[j/(2*sizeof(long))] |= digit << 4*(j&((2*sizeof(long))-1));
+ }
+
+ if (sched_setaffinity(pid, sizeof(toybuf), (void *)mask))
+ perror_exit(failed, "set", pid);
+ }
+}
+
+static int task_callback(struct dirtree *new)
+{
+ if (!new->parent) return DIRTREE_RECURSE;
+ if (isdigit(*new->name)) do_taskset(atoi(new->name), 0);
+
+ return 0;
+}
+
+void taskset_main(void)
+{
+ if (!(toys.optflags & FLAG_p)) {
+ if (toys.optc < 2) error_exit("Needs 2 args");
+ do_taskset(getpid(), 1);
+ xexec(toys.optargs+1);
+ } else {
+ char *c;
+ pid_t pid = strtol(toys.optargs[toys.optc-1], &c, 10);
+
+ if (*c) error_exit("Not int %s", toys.optargs[1]);
+
+ if (toys.optflags & FLAG_a) {
+ char buf[33];
+ sprintf(buf, "/proc/%ld/task/", (long)pid);
+ dirtree_read(buf, task_callback);
+ } else do_taskset(pid, 0);
+ }
+}
diff --git a/toys/other/toysh.c b/toys/other/toysh.c
new file mode 100644
index 00000000..c2f494d3
--- /dev/null
+++ b/toys/other/toysh.c
@@ -0,0 +1,382 @@
+/* vi: set sw=4 ts=4:
+ *
+ * toysh - toybox shell
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ *
+ * The spec for this is at:
+ * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html
+ * and http://www.opengroup.org/onlinepubs/009695399/utilities/sh.html
+ *
+ * There are also specs for:
+ * http://www.opengroup.org/onlinepubs/009695399/utilities/cd.html
+ * http://www.opengroup.org/onlinepubs/009695399/utilities/exit.html
+ *
+ * Things like the bash man page are good to read too.
+ *
+ * TODO: // Handle embedded NUL bytes in the command line.
+
+USE_TOYSH(NEWTOY(cd, NULL, TOYFLAG_NOFORK))
+USE_TOYSH(NEWTOY(exit, NULL, TOYFLAG_NOFORK))
+USE_TOYSH(OLDTOY(sh, toysh, "c:i", TOYFLAG_BIN))
+USE_TOYSH(NEWTOY(toysh, "c:i", TOYFLAG_BIN))
+
+config TOYSH
+ bool "sh (toysh)"
+ default n
+ help
+ usage: sh [-c command] [script]
+
+ The toybox command shell. Runs a shell script, or else reads input
+ interactively and responds to it.
+
+ -c command line to execute
+
+config TOYSH_TTY
+ bool "Interactive shell (terminal control)"
+ default n
+ depends on TOYSH
+ help
+ Add terminal control to toysh. This is necessary for interactive use,
+ so the shell isn't killed by CTRL-C.
+
+config TOYSH_PROFILE
+ bool "Profile support"
+ default n
+ depends on TOYSH_TTY
+ help
+ Read /etc/profile and ~/.profile when running interactively.
+
+ Also enables the built-in command "source".
+
+config TOYSH_JOBCTL
+ bool "Job Control (fg, bg, jobs)"
+ default n
+ depends on TOYSH_TTY
+ help
+ Add job control to toysh. This lets toysh handle CTRL-Z, and enables
+ the built-in commands "fg", "bg", and "jobs".
+
+ With pipe support, enable use of "&" to run background processes.
+
+config TOYSH_FLOWCTL
+ bool "Flow control (if, while, for, functions)"
+ default n
+ depends on TOYSH
+ help
+ Add flow control to toysh. This enables the if/then/else/fi,
+ while/do/done, and for/do/done constructs.
+
+ With pipe support, this enables the ability to define functions
+ using the "function name" or "name()" syntax, plus curly brackets
+ "{ }" to group commands.
+
+config TOYSH_QUOTES
+ bool "Smarter argument parsing (quotes)"
+ default n
+ depends on TOYSH
+ help
+ Add support for parsing "" and '' style quotes to the toysh command
+ parser, with lets arguments have spaces in them.
+
+config TOYSH_WILDCARDS
+ bool "Wildcards ( ?*{,} )"
+ default n
+ depends on TOYSH_QUOTES
+ help
+ Expand wildcards in argument names, ala "ls -l *.t?z" and
+ "rm subdir/{one,two,three}.txt".
+
+config TOYSH_PROCARGS
+ bool "Executable arguments ( `` and $() )"
+ default n
+ depends on TOYSH_QUOTES
+ help
+ Add support for executing arguments contianing $() and ``, using
+ the output of the command as the new argument value(s).
+
+ (Bash calls this "command substitution".)
+
+config TOYSH_ENVVARS
+ bool "Environment variable support"
+ default n
+ depends on TOYSH_QUOTES
+ help
+ Substitute environment variable values for $VARNAME or ${VARNAME},
+ and enable the built-in command "export".
+
+config TOYSH_LOCALS
+ bool "Local variables"
+ default n
+ depends on TOYSH_ENVVARS
+ help
+ Support for local variables, fancy prompts ($PS1), the "set" command,
+ and $?.
+
+config TOYSH_ARRAYS
+ bool "Array variables"
+ default n
+ depends on TOYSH_LOCALS
+ help
+ Support for ${blah[blah]} style array variables.
+
+config TOYSH_PIPES
+ bool "Pipes and redirects ( | > >> < << & && | || () ; )"
+ default n
+ depends on TOYSH
+ help
+ Support multiple commands on the same command line. This includes
+ | pipes, > >> < redirects, << here documents, || && conditional
+ execution, () subshells, ; sequential execution, and (with job
+ control) & background processes.
+
+config TOYSH_BUILTINS
+ bool "Builtin commands"
+ default n
+ depends on TOYSH
+ help
+ Adds the commands exec, fg, bg, help, jobs, pwd, export, source, set,
+ unset, read, alias.
+
+config EXIT
+ bool
+ default n
+ depends on TOYSH
+ help
+ usage: exit [status]
+
+ Exit shell. If no return value supplied on command line, use value
+ of most recent command, or 0 if none.
+
+config CD
+ bool
+ default n
+ depends on TOYSH
+ help
+ usage: cd [path]
+
+ Change current directory. With no arguments, go to $HOME.
+
+config CD_P
+ bool # "-P support for cd"
+ default n
+ depends on TOYSH
+ help
+ usage: cd [-PL]
+
+ -P Physical path: resolve symlinks in path.
+ -L Cancel previous -P and restore default behavior.
+*/
+
+#include "toys.h"
+
+DEFINE_GLOBALS(
+ char *command;
+)
+
+#define TT this.toysh
+
+// A single executable, its arguments, and other information we know about it.
+#define TOYSH_FLAG_EXIT 1
+#define TOYSH_FLAG_SUSPEND 2
+#define TOYSH_FLAG_PIPE 4
+#define TOYSH_FLAG_AND 8
+#define TOYSH_FLAG_OR 16
+#define TOYSH_FLAG_AMP 32
+#define TOYSH_FLAG_SEMI 64
+#define TOYSH_FLAG_PAREN 128
+
+// What we know about a single process.
+struct command {
+ struct command *next;
+ int flags; // exit, suspend, && ||
+ int pid; // pid (or exit code)
+ int argc;
+ char *argv[0];
+};
+
+// A collection of processes piped into/waiting on each other.
+struct pipeline {
+ struct pipeline *next;
+ int job_id;
+ struct command *cmd;
+ char *cmdline; // Unparsed line for display purposes
+ int cmdlinelen; // How long is cmdline?
+};
+
+// Parse one word from the command line, appending one or more argv[] entries
+// to struct command. Handles environment variable substitution and
+// substrings. Returns pointer to next used byte, or NULL if it
+// hit an ending token.
+static char *parse_word(char *start, struct command **cmd)
+{
+ char *end;
+
+ // Detect end of line (and truncate line at comment)
+ if (CFG_TOYSH_PIPES && strchr("><&|(;", *start)) return 0;
+
+ // Grab next word. (Add dequote and envvar logic here)
+ end = start;
+ while (*end && !isspace(*end)) end++;
+ (*cmd)->argv[(*cmd)->argc++] = xstrndup(start, end-start);
+
+ // Allocate more space if there's no room for NULL terminator.
+
+ if (!((*cmd)->argc & 7))
+ *cmd=xrealloc(*cmd,
+ sizeof(struct command) + ((*cmd)->argc+8)*sizeof(char *));
+ (*cmd)->argv[(*cmd)->argc] = 0;
+ return end;
+}
+
+// Parse a line of text into a pipeline.
+// Returns a pointer to the next line.
+
+static char *parse_pipeline(char *cmdline, struct pipeline *line)
+{
+ struct command **cmd = &(line->cmd);
+ char *start = line->cmdline = cmdline;
+
+ if (!cmdline) return 0;
+
+ if (CFG_TOYSH_JOBCTL) line->cmdline = cmdline;
+
+ // Parse command into argv[]
+ for (;;) {
+ char *end;
+
+ // Skip leading whitespace and detect end of line.
+ while (isspace(*start)) start++;
+ if (!*start || *start=='#') {
+ if (CFG_TOYSH_JOBCTL) line->cmdlinelen = start-cmdline;
+ return 0;
+ }
+
+ // Allocate next command structure if necessary
+ if (!*cmd) *cmd = xzalloc(sizeof(struct command)+8*sizeof(char *));
+
+ // Parse next argument and add the results to argv[]
+ end = parse_word(start, cmd);
+
+ // If we hit the end of this command, how did it end?
+ if (!end) {
+ if (CFG_TOYSH_PIPES && *start) {
+ if (*start==';') {
+ start++;
+ break;
+ }
+ // handle | & < > >> << || &&
+ }
+ break;
+ }
+ start = end;
+ }
+
+ if (CFG_TOYSH_JOBCTL) line->cmdlinelen = start-cmdline;
+
+ return start;
+}
+
+// Execute the commands in a pipeline
+static void run_pipeline(struct pipeline *line)
+{
+ struct toy_list *tl;
+ struct command *cmd = line->cmd;
+ if (!cmd || !cmd->argc) return;
+
+ tl = toy_find(cmd->argv[0]);
+ // Is this command a builtin that should run in this process?
+ if (tl && (tl->flags & TOYFLAG_NOFORK)) {
+ struct toy_context temp;
+
+ // This fakes lots of what toybox_main() does.
+ memcpy(&temp, &toys, sizeof(struct toy_context));
+ memset(&toys, 0, sizeof(struct toy_context));
+ toy_init(tl, cmd->argv);
+ tl->toy_main();
+ cmd->pid = toys.exitval;
+ free(toys.optargs);
+ if (toys.old_umask) umask(toys.old_umask);
+ memcpy(&toys, &temp, sizeof(struct toy_context));
+ } else {
+ int status;
+
+ cmd->pid = vfork();
+ if (!cmd->pid) xexec(cmd->argv);
+ else waitpid(cmd->pid, &status, 0);
+
+ if (CFG_TOYSH_FLOWCTL || CFG_TOYSH_PIPES) {
+ if (WIFEXITED(status)) cmd->pid = WEXITSTATUS(status);
+ if (WIFSIGNALED(status)) cmd->pid = WTERMSIG(status);
+ }
+ }
+
+ return;
+}
+
+// Free the contents of a command structure
+static void free_cmd(void *data)
+{
+ struct command *cmd=(struct command *)data;
+
+ while(cmd->argc) free(cmd->argv[--cmd->argc]);
+}
+
+
+// Parse a command line and do what it says to do.
+static void handle(char *command)
+{
+ struct pipeline line;
+ char *start = command;
+
+ // Loop through commands in this line
+
+ for (;;) {
+
+ // Parse a group of connected commands
+
+ memset(&line,0,sizeof(struct pipeline));
+ start = parse_pipeline(start, &line);
+ if (!line.cmd) break;
+
+ // Run those commands
+
+ run_pipeline(&line);
+ llist_traverse(line.cmd, free_cmd);
+ }
+}
+
+void cd_main(void)
+{
+ char *dest = *toys.optargs ? *toys.optargs : getenv("HOME");
+ xchdir(dest);
+}
+
+void exit_main(void)
+{
+ exit(*toys.optargs ? atoi(*toys.optargs) : 0);
+}
+
+void toysh_main(void)
+{
+ FILE *f;
+
+ // Set up signal handlers and grab control of this tty.
+ if (CFG_TOYSH_TTY) {
+ if (isatty(0)) toys.optflags |= 1;
+ }
+ f = *toys.optargs ? xfopen(*toys.optargs, "r") : NULL;
+ if (TT.command) handle(TT.command);
+ else {
+ size_t cmdlen = 0;
+ for (;;) {
+ char *command = 0;
+ if (!f) xputc('$');
+ if (1 > getline(&command, &cmdlen, f ? f : stdin)) break;
+ handle(command);
+ free(command);
+ }
+ }
+
+ toys.exitval = 1;
+}
diff --git a/toys/other/truncate.c b/toys/other/truncate.c
new file mode 100644
index 00000000..dccd3215
--- /dev/null
+++ b/toys/other/truncate.c
@@ -0,0 +1,47 @@
+/* vi: set sw=4 ts=4:
+ *
+ * truncate.c - set file length, extending sparsely if necessary
+ *
+ * Copyright 2011 Rob Landley <rob@landley.net>
+ *
+ * Not in SUSv4
+
+USE_TRUNCATE(NEWTOY(truncate, "<1s#|c", TOYFLAG_BIN))
+
+config TRUNCATE
+ bool "truncate"
+ default y
+ help
+ usage: truncate [-c] -s file...
+ Set length of file(s), extending sparsely if necessary.
+
+ -c Don't create file if it doesn't exist.
+ -s New size
+*/
+
+#include "toys.h"
+
+DEFINE_GLOBALS(
+ long size;
+)
+
+#define TT this.truncate
+
+static void do_truncate(int fd, char *name)
+{
+ if (fd<0) return;
+ if (ftruncate(fd, TT.size)) {
+ perror_msg("failed to set '%s' to '%ld'", name, TT.size);
+ toys.exitval = EXIT_FAILURE;
+ }
+}
+
+void truncate_main(void)
+{
+ int cr = !(toys.optflags&1);
+
+ // Create files with mask rwrwrw.
+ // Nonexistent files are only an error if we're supposed to create them.
+ loopfiles_rw(toys.optargs, O_WRONLY|(cr ? O_CREAT : 0), 0666, cr,
+ do_truncate);
+}
diff --git a/toys/other/unshare.c b/toys/other/unshare.c
new file mode 100644
index 00000000..1df9b758
--- /dev/null
+++ b/toys/other/unshare.c
@@ -0,0 +1,45 @@
+/* vi: set sw=4 ts=4:
+ *
+ * unshare.c - run command in new context
+ *
+ * Copyright 2011 Rob Landley <rob@landley.net>
+ *
+ * Not in SUSv4.
+
+USE_UNSHARE(NEWTOY(unshare, "<1^nium", TOYFLAG_USR|TOYFLAG_BIN))
+
+config UNSHARE
+ bool "unshare"
+ default y
+ depends on TOYBOX_CONTAINER
+ help
+ usage: unshare [-muin] COMMAND...
+
+ Create new namespace(s) for this process and its children, so some
+ attribute is not shared with the parent process. This is part of
+ Linux Containers. Each process can have its own:
+
+ -m Mount/unmount tree
+ -u Host and domain names
+ -i SysV IPC (message queues, semaphores, shared memory)
+ -n Network address, sockets, routing, iptables
+*/
+
+#include "toys.h"
+#include <linux/sched.h>
+extern int unshare (int __flags);
+
+void unshare_main(void)
+{
+ unsigned flags[]={CLONE_NEWNS, CLONE_NEWUTS, CLONE_NEWIPC, CLONE_NEWNET,0};
+ unsigned f=0;
+ int i;
+
+ for (i=0; flags[i]; i++)
+ if (toys.optflags & (1<<i))
+ f |= flags[i];
+
+ if(unshare(f)) perror_exit("failed");
+
+ xexec(toys.optargs);
+}
diff --git a/toys/other/uptime.c b/toys/other/uptime.c
new file mode 100644
index 00000000..83fb6b1e
--- /dev/null
+++ b/toys/other/uptime.c
@@ -0,0 +1,52 @@
+/* vi: set sw=4 ts=4:
+ *
+ * uptime.c - Tell how long the system has been running.
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_UPTIME(NEWTOY(uptime, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config UPTIME
+ bool "uptime"
+ default y
+ help
+ usage: uptime
+
+ Tell how long the system has been running and the system load
+ averages for the past 1, 5 and 15 minutes.
+*/
+
+#include "toys.h"
+
+void uptime_main(void)
+{
+ struct sysinfo info;
+ time_t tmptime;
+ struct tm * now;
+ unsigned int days, hours, minutes;
+
+ // Obtain the data we need.
+ sysinfo(&info);
+ time(&tmptime);
+ now = localtime(&tmptime);
+
+ // Time
+ xprintf(" %02d:%02d:%02d up ", now->tm_hour, now->tm_min, now->tm_sec);
+ // Uptime
+ info.uptime /= 60;
+ minutes = info.uptime%60;
+ info.uptime /= 60;
+ hours = info.uptime%24;
+ days = info.uptime/24;
+ if (days) xprintf("%d day%s, ", days, (days!=1)?"s":"");
+ if (hours)
+ xprintf("%2d:%02d, ", hours, minutes);
+ else
+ printf("%d min, ", minutes);
+
+ printf(" load average: %.02f %.02f %.02f\n", info.loads[0]/65536.0,
+ info.loads[1]/65536.0, info.loads[2]/65536.0);
+
+}
diff --git a/toys/other/usleep.c b/toys/other/usleep.c
new file mode 100644
index 00000000..6efc0c16
--- /dev/null
+++ b/toys/other/usleep.c
@@ -0,0 +1,32 @@
+/* vi: set sw=4 ts=4:
+ *
+ * usleep.c - Wait for a number of microseconds.
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+ *
+ * No standard.
+
+USE_USLEEP(NEWTOY(usleep, "<1", TOYFLAG_BIN))
+
+config USLEEP
+ bool "usleep"
+ default y
+ help
+ usage: usleep MICROSECONDS
+
+ Pause for MICROSECONDS microseconds.
+
+*/
+
+#include "toys.h"
+
+void usleep_main(void)
+{
+ struct timespec tv;
+ long delay = atol(*toys.optargs);
+
+ tv.tv_sec = delay/1000000;
+ tv.tv_nsec = (delay%1000000) * 1000;
+ toys.exitval = !!nanosleep(&tv, NULL);
+
+}
diff --git a/toys/other/vmstat.c b/toys/other/vmstat.c
new file mode 100644
index 00000000..2826ced5
--- /dev/null
+++ b/toys/other/vmstat.c
@@ -0,0 +1,191 @@
+/* vi: set sw=4 ts=4:
+ *
+ * vmstat.c - Report virtual memory statistics.
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_VMSTAT(NEWTOY(vmstat, ">2n", TOYFLAG_BIN))
+
+config VMSTAT
+ bool "vmstat"
+ default y
+ help
+ usage: vmstat [-n] [delay [count]]
+ -n Display the header only once
+ delay The delay between updates in seconds, when not specified
+ the average since boot is displayed.
+ count Number of updates to display, the default is inifinite.
+*/
+
+#include "toys.h"
+
+void read_proc_stat(unsigned int * proc_running, unsigned int * proc_blocked,
+ uint64_t * sys_irq, uint64_t * sys_ctxt,
+ uint64_t * cpu_user, uint64_t * cpu_sys, uint64_t * cpu_idle, uint64_t * cpu_wait)
+{
+ char * off;
+ uint64_t c_user, c_nice, c_sys, c_irq, c_sirq;
+ int fd = xopen("/proc/stat", O_RDONLY);
+ size_t s = xread(fd, toybuf, sizeof(toybuf)-1);
+ toybuf[s] = 0;
+ if ( s == sizeof(toybuf)-1)
+ error_exit("/proc/stat is too large");
+
+ off = strstr(toybuf, "cpu ");
+ // Ignoring steal and guest fields for now.
+ if (off) sscanf(off, "cpu %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64 \
+ " %"PRIu64" %"PRIu64" %"PRIu64, &c_user, &c_nice, &c_sys, cpu_idle,
+ cpu_wait, &c_irq, &c_sirq);
+ *cpu_user = c_user + c_nice;
+ *cpu_sys = c_sys + c_irq + c_sirq;
+ off = strstr(toybuf, "intr");
+ if (off) sscanf(off, "intr %"PRIu64, sys_irq);
+
+ off = strstr(toybuf, "ctxt");
+ if (off) sscanf(off, "ctxt %"PRIu64, sys_ctxt);
+
+ off = strstr(toybuf, "procs_running");
+ if (off) sscanf(off, "procs_running %u", proc_running);
+ (*proc_running)--; // look, i'm invisible.
+
+ off = strstr(toybuf, "procs_blocked");
+ if (off) sscanf(off, "procs_blocked %u", proc_blocked);
+
+ close(fd);
+}
+
+void read_proc_meminfo(unsigned long * mem_swapped, unsigned long * mem_free,
+ unsigned long * mem_buff, unsigned long * mem_cache)
+{
+ char * off;
+ unsigned long swap_total, swap_free;
+ int fd = xopen("/proc/meminfo", O_RDONLY);
+ size_t s = xread(fd, toybuf, sizeof(toybuf)-1);
+ toybuf[s] = 0;
+ if ( s == sizeof(toybuf)-1)
+ error_exit("/proc/meminfo is too large");
+
+ off = strstr(toybuf, "MemFree");
+ if (off) sscanf(off, "MemFree: %lu kB", mem_free);
+
+ off = strstr(toybuf, "Buffers");
+ if (off) sscanf(off, "Buffers: %lu kB", mem_buff);
+
+ off = strstr(toybuf, "Cached");
+ if (off) sscanf(off, "Cached: %lu kB", mem_cache);
+
+ off = strstr(toybuf, "SwapFree");
+ if (off) sscanf(off, "SwapFree: %lu kB", &swap_free);
+
+ off = strstr(toybuf, "SwapTotal");
+ if (off) sscanf(off, "SwapTotal: %lu kB", &swap_total);
+ *mem_swapped = swap_total - swap_free;
+
+ close(fd);
+}
+
+void read_proc_vmstat(unsigned long * io_pages_in, unsigned long * io_pages_out,
+ unsigned long * swap_bytes_in, unsigned long * swap_bytes_out)
+{
+ char * off;
+ unsigned long s_pages_in, s_pages_out;
+ unsigned long pagesize_kb = sysconf(_SC_PAGESIZE) / 1024L;
+ int fd = xopen("/proc/vmstat", O_RDONLY);
+ size_t s = xread(fd, toybuf, sizeof(toybuf)-1);
+ toybuf[s] = 0;
+ if ( s == sizeof(toybuf)-1)
+ error_exit("/proc/vmstat is too large");
+
+ off = strstr(toybuf, "pgpgin");
+ if (off) sscanf(off, "pgpgin %lu", io_pages_in);
+
+ off = strstr(toybuf, "pgpgout");
+ if (off) sscanf(off, "pgpgout %lu", io_pages_out);
+
+ off = strstr(toybuf, "pswpin");
+ if (off) sscanf(off, "pswpin %lu", &s_pages_in);
+ *swap_bytes_in = s_pages_in * pagesize_kb;
+
+ off = strstr(toybuf, "pswpout");
+ if (off) sscanf(off, "pswpout %lu", &s_pages_out);
+ *swap_bytes_out = s_pages_out * pagesize_kb;
+
+ close(fd);
+}
+
+void vmstat_main(void)
+{
+ const char fmt[] = "%2u %2u %6lu %6lu %6lu %6lu %4u %4u %5u %5u %4u %4u %2u %2u %2u %2u\n";
+ unsigned int loop_num = 0, loop_max_num = 0, loop_delay = 0;
+ unsigned int running = 0, blocked = 0;
+ unsigned long mem_swap = 0, mem_free = 0, mem_buff = 0, mem_cache = 0;
+ unsigned long io_pages_in[2], io_pages_out[2], swap_bytes_in[2], swap_bytes_out[2];
+ uint64_t sys_irq[2], sys_ctxt[2], cpu_user[2], cpu_sys[2], cpu_idle[2], cpu_wait[2];
+ int first_run = 1;
+ int no_header = toys.optflags & 0x1;
+ unsigned num_rows = 22;
+
+ if (toys.optc >= 1)
+ loop_delay = atoi(toys.optargs[0]);
+ if (toys.optc >= 2)
+ loop_max_num = atoi(toys.optargs[1]);
+
+ if (loop_max_num < 0 || loop_delay < 0)
+ error_exit("Invalid arguments");
+
+ while(1) {
+ uint64_t total_jif;
+ int idx = loop_num%2;
+
+ if(first_run || (!(loop_num % num_rows) && !no_header)) {
+ unsigned rows = 0, cols = 0;
+ terminal_size(&cols, &rows);
+ num_rows = (rows > 3)? rows - 3 : 22;
+ printf("procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----\n");
+ printf(" r b swpd free buff cache si so bi bo in cs us sy id wa\n");
+ }
+
+ read_proc_stat(&running, &blocked, &sys_irq[idx], &sys_ctxt[idx], &cpu_user[idx],
+ &cpu_sys[idx], &cpu_idle[idx], &cpu_wait[idx]);
+ read_proc_meminfo(&mem_swap, &mem_free, &mem_buff, &mem_cache);
+ read_proc_vmstat(&io_pages_in[idx], &io_pages_out[idx], &swap_bytes_in[idx], &swap_bytes_out[idx]);
+
+ if (first_run) {
+ struct sysinfo inf;
+ sysinfo(&inf);
+ first_run = 0;
+ total_jif = cpu_user[idx] + cpu_idle[idx] + cpu_wait[idx];
+ printf(fmt, running, blocked, mem_swap, mem_free, mem_buff, mem_cache,
+ (unsigned) (swap_bytes_in[idx]/inf.uptime),
+ (unsigned) (swap_bytes_out[idx]/inf.uptime),
+ (unsigned) (io_pages_in[idx]/inf.uptime),
+ (unsigned) (io_pages_out[idx]/inf.uptime),
+ (unsigned) (sys_irq[idx]/inf.uptime),
+ (unsigned) (sys_ctxt[idx]/inf.uptime),
+ (unsigned) (100*cpu_user[idx]/total_jif),
+ (unsigned) (100*cpu_sys[idx]/total_jif),
+ (unsigned) (100*cpu_idle[idx]/total_jif),
+ (unsigned) (100*cpu_wait[idx]/total_jif));
+ }else{
+ total_jif = cpu_user[idx] - cpu_user[!idx] + cpu_idle[idx] - cpu_idle[!idx] + cpu_wait[idx] - cpu_wait[!idx];
+ printf(fmt, running, blocked, mem_swap, mem_free, mem_buff, mem_cache,
+ (unsigned) ((swap_bytes_in[idx] - swap_bytes_in[!idx])/loop_delay),
+ (unsigned) ((swap_bytes_out[idx] - swap_bytes_out[!idx])/loop_delay),
+ (unsigned) ((io_pages_in[idx] - io_pages_in[!idx])/loop_delay),
+ (unsigned) ((io_pages_out[idx] - io_pages_out[!idx])/loop_delay),
+ (unsigned) ((sys_irq[idx] - sys_irq[!idx])/loop_delay),
+ (unsigned) ((sys_ctxt[idx] - sys_ctxt[!idx])/loop_delay),
+ (unsigned) (100*(cpu_user[idx] - cpu_user[!idx])/total_jif),
+ (unsigned) (100*(cpu_sys[idx] - cpu_sys[!idx]) /total_jif),
+ (unsigned) (100*(cpu_idle[idx] - cpu_idle[!idx])/total_jif),
+ (unsigned) (100*(cpu_wait[idx] - cpu_wait[!idx])/total_jif));
+ }
+
+ loop_num++;
+ if (loop_delay == 0 || (loop_max_num != 0 && loop_num >= loop_max_num))
+ break;
+ sleep(loop_delay);
+ }
+}
diff --git a/toys/other/w.c b/toys/other/w.c
new file mode 100644
index 00000000..50f2283b
--- /dev/null
+++ b/toys/other/w.c
@@ -0,0 +1,36 @@
+/* vi: set sw=4 ts=4:
+ *
+ * w.c - shows logged in users
+ *
+ * Copyright 2012 Gaurang Shastri <gmshastri@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_W(NEWTOY(w, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config W
+ bool "w"
+ default y
+ help
+ usage: w
+
+ Show who is logged on and since how long they logged in.
+*/
+
+#include "toys.h"
+
+void w_main(void)
+{
+ struct utmpx *x;
+
+ xprintf("USER TTY LOGIN@ FROM");
+ setutxent();
+ while ((x=getutxent()) != NULL)
+ if (x->ut_type==7) {
+ time_t tt = x->ut_tv.tv_sec;
+
+ xprintf("\n%-9.8s%-9.8s %-4.24s (%-1.12s)", x->ut_user, x->ut_line,
+ ctime(&tt), x->ut_host);
+ }
+ xputc('\n');
+}
diff --git a/toys/other/which.c b/toys/other/which.c
new file mode 100644
index 00000000..4923859c
--- /dev/null
+++ b/toys/other/which.c
@@ -0,0 +1,71 @@
+/* vi: set sw=4 ts=4:
+ *
+ * which.c - Find executable files in $PATH.
+ *
+ * Copyright 2006 Rob landley <rob@landley.net>
+ *
+ * Not in SUSv3.
+
+USE_WHICH(NEWTOY(which, "<1a", TOYFLAG_USR|TOYFLAG_BIN))
+
+config WHICH
+ bool "which"
+ default y
+ help
+ usage: which [-a] filename ...
+
+ Search $PATH for executable files matching filename(s).
+
+ -a Show all matches
+*/
+#include "toys.h"
+
+// Find an exectuable file either at a path with a slash in it (absolute or
+// relative to current directory), or in $PATH. Returns absolute path to file,
+// or NULL if not found.
+
+static int which_in_path(char *filename)
+{
+ struct string_list *list;
+
+ // If they gave us a path, don't worry about $PATH or -a
+
+ if (strchr(filename, '/')) {
+ // Confirm it has the executable bit set, and it's not a directory.
+ if (!access(filename, X_OK)) {
+ struct stat st;
+
+ if (!stat(filename, &st) && S_ISREG(st.st_mode)) {
+ puts(filename);
+ return 0;
+ }
+ return 1;
+ }
+ }
+
+ // Search $PATH for matches.
+ list = find_in_path(getenv("PATH"), filename);
+ if (!list) return 1;
+
+ // Print out matches
+ while (list) {
+ if (!access(list->str, X_OK)) {
+ puts(list->str);
+ // If we should stop at one match, do so
+ if (!toys.optflags) {
+ llist_traverse(list, free);
+ break;
+ }
+ }
+ free(llist_pop(&list));
+ }
+
+ return 0;
+}
+
+void which_main(void)
+{
+ int i;
+ for (i=0; toys.optargs[i]; i++)
+ toys.exitval |= which_in_path(toys.optargs[i]);
+}
diff --git a/toys/other/whoami.c b/toys/other/whoami.c
new file mode 100644
index 00000000..6d9233d1
--- /dev/null
+++ b/toys/other/whoami.c
@@ -0,0 +1,32 @@
+/* vi: set sw=4 ts=4:
+ *
+ * whoami.c - Print effective user name
+ *
+ * Copyright 2012 Georgi Chorbadzhiyski <georgi@unixsol.org>
+ *
+
+USE_WHOAMI(NEWTOY(whoami, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config WHOAMI
+ bool "whoami"
+ default y
+ help
+ usage: whoami
+
+ Print effective user name.
+*/
+
+#include "toys.h"
+
+void whoami_main(void)
+{
+ struct passwd *pw = getpwuid(geteuid());
+
+ if (!pw) {
+ perror("getpwuid");
+ toys.exitval = 1;
+ return;
+ }
+
+ xputs(pw->pw_name);
+}
diff --git a/toys/other/yes.c b/toys/other/yes.c
new file mode 100644
index 00000000..a44937b3
--- /dev/null
+++ b/toys/other/yes.c
@@ -0,0 +1,33 @@
+/* vi: set sw=4 ts=4:
+ *
+ * yes.c - Repeatedly output a string.
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+ *
+ * Not in SUSv3.
+
+USE_YES(NEWTOY(yes, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config YES
+ bool "yes"
+ default y
+ help
+ usage: yes [args...]
+
+ Repeatedly output line until killed. If no args, output 'y'.
+*/
+
+#include "toys.h"
+
+void yes_main(void)
+{
+ for (;;) {
+ int i;
+ for (i=0; toys.optargs[i]; i++) {
+ if (i) xputc(' ');
+ xprintf("%s", toys.optargs[i]);
+ }
+ if (!i) xputc('y');
+ xputc('\n');
+ }
+}