If you want a more clear explanation of what toybox is than "busybox, done right", please visit the homepage that I linked. This post is about the how.
Tree layout:
generated/: Files that the build system creates; all can be removed.
generated/help.h needs Python to build, so it's included in the tarball; if you add a new toy you need to disable help or have python 2.
lib/: all the toybox common code. This provides functions that are shared by a number of toys; do not add functions to it unless they are used by at least two toys.
kconfig/: Build system (toybox uses kconfig, like the Linux kernel and busybox)
scripts/: Build and convenience scripts, including test.sh.
scripts/test/: where test scripts live.
toys/: Individual commands. The available commands are picked up from the four subdirectories:
toys/posix/*.c: Implementations of the SUSv4/POSIX{2008,2013} command line tools.
toys/lsb/*.c: Implementations of LSB command line tools (md5sum, sha1sum, mknod, dmesg,...)
toys/other/*.c: Implementations of other command line tools. While there is no standard for these, they are widely used on Linux systems and may be needed for a regular system to boot or for many packages to build.
toys/pending/*.c: Not quite finished code. Generally speaking, this may be (a) placeholder skeletons, (b) toys that work but need cleanup, or (c) toys that do part of what they should do.
General features of a new toy:
The official example is http://landley.net/hg/toybox/file/tip/t ... er/hello.c; I'm going to try to explain that.
A toy must start with a multi-line comment in this general fashion:
Code: Select all
/* hello.c - A hello world program. (Template for new commands.)
*
* Copyright 2012 Rob Landley <rob@landley.net>
*
* See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/
* See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/cmdbehav.html
// Accept many different kinds of command line argument:
USE_HELLO(NEWTOY(hello, "(walrus)(blubber):;(also):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".
*/
The "See http://... should link to a standard for the utility you are implementing.
If there is no standard in POSIX 2008/2013 nor in LSB 4.1, write "No standard." Links to a manpage or text description of a file format are sometimes provided after that.
The build system looks at everything from the first line not starting with " *" to the first line to begin with "*/".
Note that the usual comment close with a space before it won't work.
(You will get truly spectacular build failures that way...)
The name in USE_... must match the name following "config", and the first argument to NEWTOY is the name of the command.
The second argument to NEWTOY() is the option string-it tells what options/arguments to parse and how.
"(string)" means that the long option "--string" is supported.
"b:" means that -b requires a string:
-b blah
"c#" requires a number, in decimal, octal (leading 0), or hexadecimal (leading 0x):
-c1 or -c=07 or -c 0xDEADBEEF
"e@" means that the number of occurences of -e are counted.
"d*" stores multiple arguments (as a linked list):
hello -d abc -d def
saves both "abc" and "def"
Everything after "help" is used as both the kconfig help and the command help (toybox command --help / toybox help command)
Code: Select all
#define FOR_hello
#include "toys.h"
// Hello doesn't use these globals, they're here for example/skeleton purposes.
GLOBALS(
char *b_string;
long c_number;
struct arg_list *d_list;
long e_count;
char *also_string;
char *blubber_string;
int more_globals;
)
However, you don't need to worry about the name of the struct:
it is #define'd to be "TT." so here you can use "TT.blubber_string" to access the string passed with --blubber.
The count from "e@" is stored in TT.e_count, and so on.
Now, perhaps you're wondering about the order.
Whenever you have a modifier that saves some information (:@#*), it gets saved in the GLOBALS().
The order in the "option string" is the reverse of the order in GLOBALS();
the first variable in GLOBALS() is always the last modifier in the option string.
: always maps to char *; @ and # are of type long. (option)* maps to a pointer to a linked list (struct arg_list).
After accounting for everything in the option string in GLOBALS(), you can add pointers or non-array types to the end of it. If you need global storage, this is what you are expected to use.
The rest of the arguments are broken up as follows:
toys.optargs: a char ** (basically, the part of argv that isn't flags, options, and similar).
toys.optc: the number of arguments in toys.optargs
toys.optflags: for every option or long option in the option string, there is a bit set in this.
You can check what options were passed by doing a binary AND of this and the automatically generated FLAG_ macros. In the example these are the values.
Code: Select all
FLAG_walrus = 1
FLAG_blubber = 2
FLAG_also = 4
FLAG_e = 8
FLAG_d = 16
FLAG_c = 32
FLAG_b = 64
FLAG_a = 128
That's the argument parsing part of toybox. Ask questions if you find it unclear.
After this I'd like to start covering the convenience and wrapper functions.
Convenience funtions fall into several categories; the main ones are loopfiles, dirtree, and llist/dlist. I'll start with dirtree first (probably using lspci or acpi as an example?).