General cleanup of command line parsing to allow "busybox" to work as a prefix.

(I.E. any argv[0] that starts with "busybox" winds up in busybox_main().)

Added testing/busybox.tests which tests the following permutations:

./busybox
./busybox-suffix
./busybox cat
./busybox-suffix cat
./busybox --help
./busybox-suffix --help
./busybox --help cat
./busybox-suffix --help cat
./busybox --help unknown
./busybox-suffix --help unknown
./unknown

Also repair the test suite so ./runtest calls the ".tests" scripts properly.

Note: you can now go "busybox busybox busbox ls -l" and it'll take it.  The
new code is pretty generic.  I can block that if anybody can come up with a
good reason to...
This commit is contained in:
Rob Landley
2005-09-04 11:10:37 +00:00
parent 9754b91c16
commit b766c39456
5 changed files with 181 additions and 123 deletions

View File

@ -90,8 +90,7 @@ static int suid_cfg_readable;
extern void extern void bb_show_usage (void)
bb_show_usage (void)
{ {
const char *format_string; const char *format_string;
const char *usage_string = usage_messages; const char *usage_string = usage_messages;
@ -112,8 +111,7 @@ bb_show_usage (void)
exit (EXIT_FAILURE); exit (EXIT_FAILURE);
} }
static int static int applet_name_compare (const void *x, const void *y)
applet_name_compare (const void *x, const void *y)
{ {
const char *name = x; const char *name = x;
const struct BB_applet *applet = y; const struct BB_applet *applet = y;
@ -123,47 +121,25 @@ applet_name_compare (const void *x, const void *y)
extern const size_t NUM_APPLETS; extern const size_t NUM_APPLETS;
struct BB_applet * struct BB_applet *find_applet_by_name (const char *name)
find_applet_by_name (const char *name)
{ {
return bsearch (name, applets, NUM_APPLETS, sizeof (struct BB_applet), return bsearch (name, applets, NUM_APPLETS, sizeof (struct BB_applet),
applet_name_compare); applet_name_compare);
} }
void void run_applet_by_name (const char *name, int argc, char **argv)
run_applet_by_name (const char *name, int argc, char **argv)
{ {
static int recurse_level = 0; if(ENABLE_FEATURE_SUID_CONFIG) parse_config_file ();
extern int been_there_done_that; /* From busybox.c */
#ifdef CONFIG_FEATURE_SUID_CONFIG if(!strncmp(name, "busybox", 7)) busybox_main(argc, argv);
if (recurse_level == 0) /* Do a binary search to find the applet entry given the name. */
parse_config_file (); applet_using = find_applet_by_name(name);
#endif if(applet_using) {
bb_applet_name = applet_using->name;
recurse_level++; if(argc==2 && !strcmp(argv[1], "--help")) bb_show_usage ();
/* Do a binary search to find the applet entry given the name. */ if(ENABLE_FEATURE_SUID) check_suid (applet_using);
if ((applet_using = find_applet_by_name (name)) != NULL) { exit ((*(applet_using->main)) (argc, argv));
bb_applet_name = applet_using->name;
if (argv[1] && strcmp (argv[1], "--help") == 0) {
if (strcmp (applet_using->name, "busybox") == 0) {
if (argv[2])
applet_using = find_applet_by_name (argv[2]);
else
applet_using = NULL;
}
if (applet_using)
bb_show_usage ();
been_there_done_that = 1;
busybox_main (0, NULL);
} }
#ifdef CONFIG_FEATURE_SUID
check_suid (applet_using);
#endif
exit ((*(applet_using->main)) (argc, argv));
}
recurse_level--;
} }

View File

@ -9,7 +9,6 @@
#include <locale.h> #include <locale.h>
#endif #endif
int been_there_done_that = 0; /* Also used in applets.c */
const char *bb_applet_name; const char *bb_applet_name;
#ifdef CONFIG_FEATURE_INSTALLER #ifdef CONFIG_FEATURE_INSTALLER
@ -32,17 +31,6 @@ static const char* const install_dir[] = {
/* abstract link() */ /* abstract link() */
typedef int (*__link_f)(const char *, const char *); typedef int (*__link_f)(const char *, const char *);
/*
* Where in the filesystem is this busybox?
* [return]
* malloc'd string w/ full pathname of busybox's location
* NULL on failure
*/
static inline char *busybox_fullpath(void)
{
return xreadlink("/proc/self/exe");
}
/* create (sym)links for each applet */ /* create (sym)links for each applet */
static void install_links(const char *busybox, int use_symbolic_links) static void install_links(const char *busybox, int use_symbolic_links)
{ {
@ -72,41 +60,27 @@ int main(int argc, char **argv)
{ {
const char *s; const char *s;
bb_applet_name = argv[0]; bb_applet_name=argv[0];
if (*bb_applet_name == '-') bb_applet_name++;
for (s = bb_applet_name; *s ;)
if (*(s++) == '/') bb_applet_name = s;
if (bb_applet_name[0] == '-') /* Set locale for everybody except `init' */
bb_applet_name++; if(ENABLE_LOCALE_SUPPORT && (!ENABLE_INIT || getpid()==1))
for (s = bb_applet_name; *s != '\0';) {
if (*s++ == '/')
bb_applet_name = s;
}
#ifdef CONFIG_LOCALE_SUPPORT
#ifdef CONFIG_INIT
if(getpid()!=1) /* Do not set locale for `init' */
#endif
{
setlocale(LC_ALL, ""); setlocale(LC_ALL, "");
}
#endif
run_applet_by_name(bb_applet_name, argc, argv); run_applet_by_name(bb_applet_name, argc, argv);
bb_error_msg_and_die("applet not found"); bb_error_msg_and_die("applet not found");
} }
int busybox_main(int argc, char **argv) int busybox_main(int argc, char **argv)
{ {
int col = 0, len, i;
#ifdef CONFIG_FEATURE_INSTALLER
/* /*
* This style of argument parsing doesn't scale well * This style of argument parsing doesn't scale well
* in the event that busybox starts wanting more --options. * in the event that busybox starts wanting more --options.
* If someone has a cleaner approach, by all means implement it. * If someone has a cleaner approach, by all means implement it.
*/ */
if (argc > 1 && (strcmp(argv[1], "--install") == 0)) { if (ENABLE_FEATURE_INSTALLER && argc > 1 && !strcmp(argv[1], "--install")) {
int use_symbolic_links = 0; int use_symbolic_links = 0;
int rc = 0; int rc = 0;
char *busybox; char *busybox;
@ -119,7 +93,7 @@ int busybox_main(int argc, char **argv)
} }
/* link */ /* link */
busybox = busybox_fullpath(); busybox = xreadlink("/proc/self/exe");
if (busybox) { if (busybox) {
install_links(busybox, use_symbolic_links); install_links(busybox, use_symbolic_links);
free(busybox); free(busybox);
@ -128,60 +102,45 @@ int busybox_main(int argc, char **argv)
} }
return rc; return rc;
} }
#endif /* CONFIG_FEATURE_INSTALLER */
argc--; /* Deal with --help. (Also print help when called with no arguments) */
/* If we've already been here once, exit now */ if (argc==1 || !strcmp(argv[1],"--help") ) {
if (been_there_done_that == 1 || argc < 1) { if (argc>2) run_applet_by_name(bb_applet_name=argv[2], argc, argv);
const struct BB_applet *a = applets; else {
int output_width = 60; const struct BB_applet *a;
int col, output_width;
#ifdef CONFIG_FEATURE_AUTOWIDTH if (ENABLE_FEATURE_AUTOWIDTH) {
/* Obtain the terminal width. */ /* Obtain the terminal width. */
get_terminal_width_height(0, &output_width, NULL); get_terminal_width_height(0, &output_width, NULL);
/* leading tab and room to wrap */ /* leading tab and room to wrap */
output_width -= 20; output_width -= 20;
#endif } else output_width = 60;
printf("%s\n\n" printf("%s\n\n"
"Usage: busybox [function] [arguments]...\n" "Usage: busybox [function] [arguments]...\n"
" or: [function] [arguments]...\n\n" " or: [function] [arguments]...\n\n"
"\tBusyBox is a multi-call binary that combines many common Unix\n" "\tBusyBox is a multi-call binary that combines many common Unix\n"
"\tutilities into a single executable. Most people will create a\n" "\tutilities into a single executable. Most people will create a\n"
"\tlink to busybox for each function they wish to use and BusyBox\n" "\tlink to busybox for each function they wish to use and BusyBox\n"
"\twill act like whatever it was invoked as!\n" "\twill act like whatever it was invoked as!\n"
"\nCurrently defined functions:\n", bb_msg_full_version); "\nCurrently defined functions:\n", bb_msg_full_version);
while (a->name != 0) { col=0;
col += for(a = applets; a->name;) {
printf("%s%s", ((col == 0) ? "\t" : ", "), col += printf("%s%s", (col ? ", " : "\t"), (a++)->name);
(a++)->name); if (col > output_width && a->name) {
if (col > output_width && a->name != 0) { printf(",\n");
printf(",\n"); col = 0;
col = 0; }
} }
printf("\n\n");
exit(0);
} }
printf("\n\n"); } else run_applet_by_name(bb_applet_name=argv[1], argc-1, argv+1);
exit(0);
}
/* Flag that we've been here already */ bb_error_msg_and_die("applet not found");
been_there_done_that = 1;
/* Move the command line down a notch */
/* Preserve pointers so setproctitle() works consistently */
len = argv[argc] + strlen(argv[argc]) - argv[1];
memmove(argv[0], argv[1], len);
memset(argv[0] + len, 0, argv[1] - argv[0]);
/* Fix up the argv pointers */
len = argv[1] - argv[0];
memmove(argv, argv + 1, sizeof(char *) * (argc + 1));
for (i = 0; i < argc; i++)
argv[i] -= len;
return (main(argc, argv));
} }
/* /*

123
testsuite/busybox.tests Executable file
View File

@ -0,0 +1,123 @@
#!/bin/sh
# Tests for busybox applet itself.
# Copyright 2005 by Rob Landley <rob@landley.net>
# Licensed under GPL v2, see file LICENSE for details.
if [ ${#COMMAND} -eq 0 ]; then COMMAND=busybox; fi
. testing.sh
# We'll assume "cat" is built in, because we need some other command to test.
HELPDUMP=`$COMMAND`
# The gratuitous "\n"s are due to a shell idiosyncrasy: environment variables
# seem to strip trailing whitespace, which makes cmp and diff unhappy.
ln -s `which "$COMMAND"` busybox-suffix
ln -s `which "$COMMAND"` unknown
for i in busybox busybox-suffix
do
# The gratuitous "\n"s are due to a shell idiosyncrasy:
# environment variables seem to strip trailing whitespace.
testing "$i" "" "$HELPDUMP\n\n" "" ""
testing "$i cat" "cat" "moo" "" "moo"
testing "$i unknown" "unknown 2>&1" \
"unknown: applet not found\n" "" ""
testing "$i --help" "--help 2>&1" "$HELPDUMP\n\n" "" ""
testing "$i --help cat" "--help cat 2>&1 | grep prints" \
"Concatenates FILE(s) and prints them to stdout.\n" "" ""
testing "$i --help unknown" "--help unknown 2>&1" \
"unknown: applet not found\n" "" ""
COMMAND=./busybox-suffix
done
COMMAND="./unknown"
testing "busybox as unknown name" "2>&1" "unknown: applet not found\n" "" ""
rm -f busybox-suffix unknown
exit
General cleanup of command line parsing to allow "busybox" to work as a prefix.
(I.E. any argv[0] that starts with "busybox" winds up in busybox_main().)
Tests:
./busybox
./busybox-walrus
./busybox ls
./busybox-walrus ls
./busybox --help
./busybox-walrus --help
./busybox --help ls
./busybox-walrus --help ls
./busybox --help walrus
./busybox-walrus --help walrus
# These tests require the full option set.
# Longish chunk of data re-used by the next few tests
data="42 1 3 woot
42 1 010 zoology
egg 1 2 papyrus
7 3 42 soup
999 3 0 algebra
"
# Sorting with keys
testing "sort one key" "-k4,4 input" \
"999 3 0 algebra
egg 1 2 papyrus
7 3 42 soup
42 1 3 woot
42 1 010 zoology
" "$data" ""
testing "sort key range with numeric option" "-k2,3n input" \
"42 1 010 zoology
42 1 3 woot
egg 1 2 papyrus
7 3 42 soup
999 3 0 algebra
" "$data" ""
# Busybox is definitely doing this one wrong just now...
testing "sort key range with numeric option and global reverse" \
"-k2,3n -r input" \
"egg 1 2 papyrus
42 1 3 woot
42 1 010 zoology
999 3 0 algebra
7 3 42 soup
" "$data" ""
#
testing "sort key range with multiple options" "-k2,3rn input" \
"7 3 42 soup
999 3 0 algebra
42 1 010 zoology
42 1 3 woot
egg 1 2 papyrus
" "$data" ""
exit $FAILCOUNT

View File

@ -97,8 +97,8 @@ for applet in $applets; do
status=1 status=1
fi fi
fi fi
applet=`echo "$applet" | sed -n 's/\.tests$//p'`
if [ -f "$applet".tests ] if [ ${#applet} != 0 ]
then then
rm -f links/"$applet" rm -f links/"$applet"
ln -s ../../busybox links/"$applet" ln -s ../../busybox links/"$applet"

View File

@ -48,13 +48,13 @@ function testing()
if [ $? -ne 0 ] if [ $? -ne 0 ]
then then
FAILCOUNT=$[$FAILCOUNT+1] FAILCOUNT=$[$FAILCOUNT+1]
echo FAIL:"$1" echo "FAIL: $1"
if [ $verbose ] if [ $verbose ]
then then
diff -u expected actual diff -u expected actual
fi fi
else else
echo PASS:"$1" echo "PASS: $1"
fi fi
rm -f input expected actual rm -f input expected actual