From 4f344e356d2c36c4b1df46917eaef25f82ca79a9 Mon Sep 17 00:00:00 2001 From: landley Date: Thu, 5 Oct 2006 16:18:03 -0400 Subject: Infrastructure, first drop of toy shell, and a bit of work on df. --- lib/functions.c | 65 +++++++++++++++++++++++++++++ lib/getmountlist.c | 39 +++++++++++++++++ lib/lib.h | 19 +++++++++ main.c | 120 +++++++++++++++++++++++++++++++++-------------------- toys.h | 34 +++++++++++---- toys/df.c | 22 +++++++++- toys/toysh.c | 92 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 334 insertions(+), 57 deletions(-) create mode 100644 lib/functions.c create mode 100644 lib/getmountlist.c create mode 100644 lib/lib.h create mode 100644 toys/toysh.c diff --git a/lib/functions.c b/lib/functions.c new file mode 100644 index 00000000..3d9a8aca --- /dev/null +++ b/lib/functions.c @@ -0,0 +1,65 @@ +/* vi: set ts=4 :*/ +/* functions.c - reusable stuff. + * + * Functions with the x prefix never return failure, they either succeed or + * kill the program with an error message. + */ + +#include "toys.h" + +// Die with an error message. +void error_exit(char *msg, ...) +{ + va_list args; + + va_start(args, msg); + fprintf(stderr, "%s: ", toys.which->name); + vfprintf(stderr, msg, args); + va_end(args); + exit(toys.exitval); +} + +void strlcpy(char *dest, char *src, size_t size) +{ + strncpy(dest,src,size); + dest[size-1] = 0; +} + +// Die unless we can allocate memory. +void *xmalloc(size_t size) +{ + void *ret = malloc(size); + if (!ret) error_exit("xmalloc"); +} + +// Die unless we can copy this string. +void *xstrndup(char *s, size_t n) +{ + void *ret = xmalloc(++n); + strlcpy(ret, s, n); + + return ret; +} + +// Die unless we can exec argv[] +void *xexec(char **argv) +{ + execvp(argv[0], argv); + error_exit("No %s", argv[0]); +} + +// Die unless we can open/create a file, returning file descriptor. +int xopen(char *path, int flags, int mode) +{ + int fd = open(path, flags, mode); + if (fd == -1) error_exit("No file %s\n", path); + return fd; +} + +// Die unless we can open/create a file, returning FILE *. +FILE *xfopen(char *path, char *mode) +{ + FILE *f = fopen(path, mode); + if (!f) error_exit("No file %s\n", path); + return f; +} diff --git a/lib/getmountlist.c b/lib/getmountlist.c new file mode 100644 index 00000000..a2b0698d --- /dev/null +++ b/lib/getmountlist.c @@ -0,0 +1,39 @@ +/* vi: set ts=4 : */ + +#include "toys.h" + +#include + +char *path_mounts = "/proc/mounts"; + +// Get a list of mount points from /etc/mtab or /proc/mounts. This returns +// a reversed list, which is good for finding overmounts and such. + +struct mtab_list *getmountlist(int die) +{ + FILE *fp; + struct mtab_list *mtlist, *mt; + struct mntent me; + char evilbuf[2*PATH_MAX]; + + mtlist = 0; + if (!(fp = setmntent(path_mounts, "r"))) { + if (die) error_exit("cannot open %s", path_mounts); + } else { + while (getmntent_r(fp, &me, evilbuf, sizeof(evilbuf))) { + char *str; + + mt = xmalloc(sizeof(struct mtab_list) + strlen(me.mnt_fsname) + + strlen(me.mnt_dir) + strlen(me.mnt_type) + 3); + mt->next = mtlist; + strcpy(mt->type, me.mnt_type); + mt->dir = mt->type + strlen(mt->type) + 1; + strcpy(mt->dir, me.mnt_dir); + mt->device = mt->dir + strlen(mt->dir) + 1; + mt->device = ++str; + strcpy(str, me.mnt_fsname); + mtlist = mt; + } + } + return mtlist; +} diff --git a/lib/lib.h b/lib/lib.h new file mode 100644 index 00000000..fa24c8d5 --- /dev/null +++ b/lib/lib.h @@ -0,0 +1,19 @@ +/* vi: set ts=4 :*/ + +void error_exit(char *msg, ...); +void strlcpy(char *dest, char *src, size_t size); +void *xmalloc(size_t size); +void *xstrndup(char *s, size_t n); +void *xexec(char **argv); +int xopen(char *path, int flags, int mode); +FILE *xfopen(char *path, char *mode); + +struct mtab_list { + struct mtab_list *next; + char *dir; + char *device; + char type[0]; +}; + +struct mtab_list *getmountlist(int die); + diff --git a/main.c b/main.c index 396a354c..90d80c78 100644 --- a/main.c +++ b/main.c @@ -11,37 +11,22 @@ // The monster fun applet list. struct toy_list toy_list[] = { - {"toybox", toybox_main}, - {"df", df_main}, - {"toysh", toysh_main} + // This one is out of order on purpose. + {"toybox", toybox_main, 0}, + // The rest of these are alphabetical, for binary search. + {"cd", cd_main, TOYFLAG_NOFORK}, + {"df", df_main, TOYFLAG_USR|TOYFLAG_SBIN}, + {"exit", exit_main, TOYFLAG_NOFORK}, + {"toysh", toysh_main, TOYFLAG_BIN} }; +#define TOY_LIST_LEN (sizeof(toy_list)/sizeof(struct toy_list)) + // global context for this applet. struct toy_context toys; - - -/* -name -main() -struct -usage (short long example info) -path (/usr/sbin) -*/ - -int toybox_main(void) -{ - printf("toybox\n"); - return 0; -} - -int toysh_main(void) -{ - printf("toysh\n"); -} - -struct toy_list *find_toy_by_name(char *name) +struct toy_list *toy_find(char *name) { int top, bottom, middle; @@ -49,40 +34,83 @@ struct toy_list *find_toy_by_name(char *name) // skip the first entry, which is out of order. if (!strncmp(name,"toybox",6)) return toy_list; - bottom=1; + bottom = 1; // Binary search to find this applet. - top=(sizeof(toy_list)/sizeof(struct toy_list))-1; - for(;;) { + top = TOY_LIST_LEN-1; + for (;;) { int result; - middle=(top+bottom)/2; - if(middletop) return NULL; + middle = (top+bottom)/2; + if (middletop) return NULL; result = strcmp(name,toy_list[middle].name); - if(!result) return toy_list+middle; - if(result<0) top=--middle; - else bottom=++middle; + if (!result) return toy_list+middle; + if (result<0) top=--middle; + else bottom = ++middle; } } -int main(int argc, char *argv[]) +// Run a toy. +void toy_exec(char *argv[]) { - char *name; + struct toy_list *which; + + which = toy_find(argv[0]); + if (!which) return; + + // Free old toys contents here? - // Record command line arguments. - toys.argc = argc; + toys.which = which; toys.argv = argv; + for (toys.argc = 0; argv[toys.argc]; toys.argc++); + toys.exitval = 1; + + exit(toys.which->toy_main()); +} - // Figure out which applet got called. - name = rindex(argv[0],'/'); - if (!name) name = argv[0]; - else name++; - toys.which = find_toy_by_name(name); +int toybox_main(void) +{ + static char *toy_paths[]={"usr/","bin/","sbin/",0}; + int i, len = 0; + + if (toys.argv[1]) { + if (toys.argv[1][0]!='-') { + toy_exec(toys.argv+1); + error_exit("No behavior for %s\n",toys.argv[1]); + } + } - if (!toys.which) { - dprintf(2,"No behavior for %s\n",name); - return 1; + // Output list of applets. + for (i=1; i65) { + putchar('\n'); + len=0; + } + } } - return toys.which->toy_main(); + putchar('\n'); + return 0; +} + +int main(int argc, char *argv[]) +{ + char *name; + + // Figure out which applet to call. + name = rindex(argv[0], '/'); + if (!name) name=argv[0]; + else name++; + argv[0] = name; + + toys.argv = argv-1; + return toybox_main(); } diff --git a/toys.h b/toys.h index ec1ad762..4bf29b70 100644 --- a/toys.h +++ b/toys.h @@ -6,43 +6,59 @@ * Licensed under GPL version 2, see file LICENSE in this tarball for details. */ +#include +#include #include +#include +#include #include +#include -/* -name -main() -struct -usage (short long example info) -path (/usr/sbin) -*/ +#include "lib/lib.h" +int cd_main(void); +int exit_main(void); int toybox_main(void); int toysh_main(void); int df_main(void); +#define TOYFLAG_USR (1<<0) +#define TOYFLAG_BIN (1<<1) +#define TOYFLAG_SBIN (1<<2) +#define TOYMASK_LOCATION ((1<<4)-1) + +#define TOYFLAG_NOFORK (1<<4) + extern struct toy_list { char *name; int (*toy_main)(void); + int flags; } toy_list[]; -struct toy_list *find_toy_by_name(char *name); +struct toy_list *toy_find(char *name); // Global context for this applet. extern struct toy_context { - struct toy_list *which; + struct toy_list *which; // Which entry in toy_list is this one? + int exitval; // Value error_exit feeds to exit() int argc; char **argv; char buf[4096]; } toys; +struct exit_data {;}; +struct cd_data {;}; struct toybox_data {;}; struct toysh_data {;}; struct df_data {;}; union toy_union { + struct exit_data exit; + struct cd_data cd; struct toybox_data toybox; struct toysh_data toysh; struct df_data df; } toy; +// I need a real config system. +#define CFG_TOYS_FREE 0 diff --git a/toys/df.c b/toys/df.c index 05c4b6c4..8901baa5 100644 --- a/toys/df.c +++ b/toys/df.c @@ -1,7 +1,25 @@ -/* vi: set ts=4 : */ +/* vi: set sw=4 ts=4: */ +/* + * df.c - report free disk space. + * + * Implemented according to SUSv3: + * http://www.opengroup.org/onlinepubs/009695399/utilities/df.html + * + * usage: df [-k] [-P|-t] [file...] + */ + #include "toys.h" int df_main(void) { - printf("toys.which->name=%s\n",toys.which->name); + struct mtab_list *mt, *mtlist; + + //int units = 512; + mtlist = getmountlist(1); + // Zap overmounts + for (mt = mtlist; mt; mt = mt->next) { + printf("type=%s dir=%s device=%s\n",mt->type,mt->dir,mt->device); + } + + return 0; } diff --git a/toys/toysh.c b/toys/toysh.c new file mode 100644 index 00000000..77db82db --- /dev/null +++ b/toys/toysh.c @@ -0,0 +1,92 @@ +/* vi: set sw=4 ts=4: + * + * toysh - toybox shell + * + * Copyright 2006 Rob Landley + * + * The spec for this is at: + * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html + * + * Although things like the bash man page are good to read too. + */ + +// Handle embedded NUL bytes in the command line. + +#include "toys.h" + +static int handle(char *command) +{ + int argc = 0, status; + char *argv[10], *start = command; + pid_t pid; + struct toy_list *tl; + + // Parse command into argv[] + for (;;) { + char *end; + + // Skip leading whitespace and detect EOL. + while(isspace(*start)) start++; + if (!*start || *start=='#') break; + + // Grab next word. (Add dequote and envvar logic here) + end = start; + while (*end && !isspace(*end)) end++; + argv[argc++] = xstrndup(start, end-start); + start=end; + } + argv[argc]=0; + + if (!argc) return 0; + + tl = toy_find(argv[0]); + // This is a bit awkward, next design cycle should clean it up. + // Should vfork(), move to tryspawn()? + pid = 0; + if (tl && (tl->flags & TOYFLAG_NOFORK)) + status = tl->toy_main(); + else { + pid=fork(); + if(!pid) { + toy_exec(argv); + xexec(argv); + } else waitpid(pid, &status, 0); + } + while(argc) free(argv[--argc]); + + return 0; +} + +int cd_main(void) +{ + char *dest = toys.argc>1 ? toys.argv[1] : getenv("HOME"); + if (chdir(dest)) error_exit("chdir %s",dest); + return 0; +} + +int exit_main(void) +{ + exit(toys.argc>1 ? atoi(toys.argv[1]) : 0); +} + +int toysh_main(void) +{ + char *command=NULL; + FILE *f; + + // TODO get_optflags(argv, "c:", &command); + + f = toys.argv[1] ? xfopen(toys.argv[1], "r") : NULL; + if (command) handle(command); + else { + unsigned cmdlen=0; + for (;;) { + if (!f) putchar('$'); + if (1 > getline(&command, &cmdlen, f ? : stdin)) break; + handle(command); + } + if (CFG_TOYS_FREE) free(command); + } + + return 1; +} -- cgit v1.2.3