diff options
Diffstat (limited to 'toys/other')
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'); + } +} |