* Licensed under GPLv2 or later, see file License in this tarball for details.
*/
-#include <assert.h>
+/* We are trying to not use printf, this benefits the case when selected
+ * applets are really simple. Example:
+ *
+ * $ ./busybox
+ * ...
+ * Currently defined functions:
+ * basename, false, true
+ *
+ * $ size busybox
+ * text data bss dec hex filename
+ * 4473 52 72 4597 11f5 busybox
+ *
+ * FEATURE_INSTALLER or FEATURE_SUID will still link printf routines in. :(
+ */
+
#include "busybox.h"
+#include <assert.h>
+#include <malloc.h>
+#include <sys/user.h> /* PAGE_SIZE */
/* Declare <applet>_main() */
#define usage_messages 0
#endif /* SHOW_USAGE */
-/* Define struct bb_applet applets[] */
-#include "applets.h"
-#if ENABLE_FEATURE_SH_STANDALONE
-/* -1 because last entry is NULL */
-const unsigned short NUM_APPLETS = ARRAY_SIZE(applets) - 1;
+/* Include generated applet names, pointers to <applet>_main, etc */
+#include "applet_tables.h"
+/* ...and if applet_tables generator says we have only one applet... */
+#ifdef SINGLE_APPLET_MAIN
+#undef ENABLE_FEATURE_INDIVIDUAL
+#define ENABLE_FEATURE_INDIVIDUAL 1
+#undef IF_FEATURE_INDIVIDUAL
+#define IF_FEATURE_INDIVIDUAL(...) __VA_ARGS__
#endif
i = start_bunzip(&bd,
/* src_fd: */ -1,
- /* inbuf: */ packed_usage,
+ /* inbuf: */ (void *)packed_usage,
/* len: */ sizeof(packed_usage));
/* read_bunzip can longjmp to start_bunzip, and ultimately
* end up here with i != 0 on read data errors! Not trivial */
#endif /* FEATURE_COMPRESS_USAGE */
-void bb_show_usage(void)
+static void full_write2_str(const char *str)
+{
+ xwrite_str(STDERR_FILENO, str);
+}
+
+void FAST_FUNC bb_show_usage(void)
{
if (ENABLE_SHOW_USAGE) {
- const char *format_string;
+#ifdef SINGLE_APPLET_STR
+ /* Imagine that this applet is "true". Dont suck in printf! */
const char *p;
const char *usage_string = p = unpack_usage_messages();
- const struct bb_applet *ap = find_applet_by_name(applet_name);
- int i;
- if (!ap) /* never happens, paranoia */
- xfunc_die();
+ if (*p == '\b') {
+ full_write2_str("No help available.\n\n");
+ } else {
+ full_write2_str("Usage: "SINGLE_APPLET_STR" ");
+ full_write2_str(p);
+ full_write2_str("\n\n");
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ dealloc_usage_messages((char*)usage_string);
+#else
+ const char *p;
+ const char *usage_string = p = unpack_usage_messages();
+ int ap = find_applet_by_name(applet_name);
- i = ap - applets;
- while (i) {
+ if (ap < 0) /* never happens, paranoia */
+ xfunc_die();
+ while (ap) {
while (*p++) continue;
- i--;
+ ap--;
}
-
- fprintf(stderr, "%s multi-call binary\n", bb_banner);
- format_string = "\nUsage: %s %s\n\n";
+ full_write2_str(bb_banner);
+ full_write2_str(" multi-call binary.\n");
if (*p == '\b')
- format_string = "\nNo help available.\n\n";
- fprintf(stderr, format_string, applet_name, p);
- dealloc_usage_messages((char*)usage_string);
+ full_write2_str("\nNo help available.\n\n");
+ else {
+ full_write2_str("\nUsage: ");
+ full_write2_str(applet_name);
+ full_write2_str(" ");
+ full_write2_str(p);
+ full_write2_str("\n\n");
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ dealloc_usage_messages((char*)usage_string);
+#endif
}
xfunc_die();
}
-
-static int applet_name_compare(const void *name, const void *vapplet)
+#if NUM_APPLETS > 8
+/* NB: any char pointer will work as well, not necessarily applet_names */
+static int applet_name_compare(const void *name, const void *v)
{
- const struct bb_applet *applet = vapplet;
-
- return strcmp(name, applet->name);
+ int i = (const char *)v - applet_names;
+ return strcmp(name, APPLET_NAME(i));
}
-
-const struct bb_applet *find_applet_by_name(const char *name)
+#endif
+int FAST_FUNC find_applet_by_name(const char *name)
{
+#if NUM_APPLETS > 8
/* Do a binary search to find the applet entry given the name. */
- return bsearch(name, applets, ARRAY_SIZE(applets)-1, sizeof(applets[0]),
- applet_name_compare);
+ const char *p;
+ p = bsearch(name, applet_names, ARRAY_SIZE(applet_main), 1, applet_name_compare);
+ if (!p)
+ return -1;
+ return p - applet_names;
+#else
+ /* A version which does not pull in bsearch */
+ int i = 0;
+ const char *p = applet_names;
+ while (i < NUM_APPLETS) {
+ if (strcmp(name, p) == 0)
+ return i;
+ p += strlen(p) + 1;
+ i++;
+ }
+ return -1;
+#endif
}
-#ifdef __GLIBC__
-/* Make it reside in R/W memory: */
-int *const bb_errno __attribute__ ((section (".data")));
-#endif
-
-void lbb_prepare(const char *applet, char **argv)
+void lbb_prepare(const char *applet
+ IF_FEATURE_INDIVIDUAL(, char **argv))
+ MAIN_EXTERNALLY_VISIBLE;
+void lbb_prepare(const char *applet
+ IF_FEATURE_INDIVIDUAL(, char **argv))
{
#ifdef __GLIBC__
(*(int **)&bb_errno) = __errno_location();
+ barrier();
#endif
applet_name = applet;
#if ENABLE_FEATURE_INDIVIDUAL
/* Redundant for busybox (run_applet_and_exit covers that case)
* but needed for "individual applet" mode */
- if (argv[1] && strcmp(argv[1], "--help") == 0)
- bb_show_usage();
+ if (argv[1] && !argv[2] && strcmp(argv[1], "--help") == 0) {
+ /* Special case. POSIX says "test --help"
+ * should be no different from e.g. "test --foo". */
+ if (!ENABLE_TEST || strcmp(applet_name, "test") != 0)
+ bb_show_usage();
+ }
#endif
}
bool re_execed;
#endif
-USE_FEATURE_SUID(static uid_t ruid;) /* real uid */
+
+/* If not built as a single-applet executable... */
+#if !defined(SINGLE_APPLET_MAIN)
+
+IF_FEATURE_SUID(static uid_t ruid;) /* real uid */
#if ENABLE_FEATURE_SUID_CONFIG
/* applets[] is const, so we have to define this "override" structure */
static struct BB_suid_config {
- const struct bb_applet *m_applet;
+ int m_applet;
uid_t m_uid;
gid_t m_gid;
mode_t m_mode;
{
struct BB_suid_config *sct_head;
struct BB_suid_config *sct;
- const struct bb_applet *applet;
+ int applet_no;
FILE *f;
const char *errmsg;
char *s;
|| !S_ISREG(st.st_mode) /* Not a regular file? */
|| (st.st_uid != 0) /* Not owned by root? */
|| (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */
- || !(f = fopen(config_file, "r")) /* Cannot open? */
+ || !(f = fopen_for_read(config_file)) /* Cannot open? */
) {
return;
}
s = buffer;
if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */
+// why?
if (ferror(f)) { /* Make sure it wasn't a read error. */
parse_error("reading");
}
* applet is currently built in and ignore it otherwise.
* Note: this can hide config file bugs which only pop
* up when the busybox configuration is changed. */
- applet = find_applet_by_name(s);
- if (applet) {
+ applet_no = find_applet_by_name(s);
+ if (applet_no >= 0) {
/* Note: We currently don't check for duplicates!
* The last config line for each applet will be the
* one used since we insert at the head of the list.
* I suppose this could be considered a feature. */
sct = xmalloc(sizeof(struct BB_suid_config));
- sct->m_applet = applet;
+ sct->m_applet = applet_no;
sct->m_mode = 0;
sct->m_next = sct_head;
sct_head = sct;
#else
static inline void parse_config_file(void)
{
- USE_FEATURE_SUID(ruid = getuid();)
+ IF_FEATURE_SUID(ruid = getuid();)
}
#endif /* FEATURE_SUID_CONFIG */
#if ENABLE_FEATURE_SUID
-static void check_suid(const struct bb_applet *applet)
+static void check_suid(int applet_no)
{
gid_t rgid; /* real gid */
mode_t m;
for (sct = suid_config; sct; sct = sct->m_next) {
- if (sct->m_applet == applet)
+ if (sct->m_applet == applet_no)
goto found;
}
- /* default: drop all privileges */
- xsetgid(rgid);
- xsetuid(ruid);
- return;
+ goto check_need_suid;
found:
m = sct->m_mode;
if (sct->m_uid == ruid)
}
}
#endif
+ check_need_suid:
#endif
-
- if (applet->need_suid == _BB_SUID_ALWAYS) {
+ if (APPLET_SUID(applet_no) == _BB_SUID_REQUIRE) {
/* Real uid is not 0. If euid isn't 0 too, suid bit
* is most probably not set on our executable */
if (geteuid())
- bb_error_msg_and_die("applet requires root privileges!");
- } else if (applet->need_suid == _BB_SUID_NEVER) {
+ bb_error_msg_and_die("must be suid to work properly");
+ } else if (APPLET_SUID(applet_no) == _BB_SUID_DROP) {
xsetgid(rgid); /* drop all privileges */
xsetuid(ruid);
}
#if ENABLE_FEATURE_INSTALLER
/* 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,
+ char *custom_install_dir)
{
/* directory table
* this should be consistent w/ the enum,
usr_sbin
};
- int (*lf)(const char *, const char *) = link;
+ int (*lf)(const char *, const char *);
char *fpc;
- int i;
+ unsigned i;
int rc;
+ lf = link;
if (use_symbolic_links)
lf = symlink;
- for (i = 0; applets[i].name != NULL; i++) {
+ for (i = 0; i < ARRAY_SIZE(applet_main); i++) {
fpc = concat_path_file(
- install_dir[applets[i].install_loc],
- applets[i].name);
+ custom_install_dir ? custom_install_dir : install_dir[APPLET_INSTALL_LOC(i)],
+ APPLET_NAME(i));
+ // debug: bb_error_msg("%slinking %s to busybox",
+ // use_symbolic_links ? "sym" : "", fpc);
rc = lf(busybox, fpc);
if (rc != 0 && errno != EEXIST) {
bb_simple_perror_msg(fpc);
}
}
#else
-#define install_links(x,y) ((void)0)
+#define install_links(x,y,z) ((void)0)
#endif /* FEATURE_INSTALLER */
/* If we were called as "busybox..." */
{
if (!argv[1]) {
/* Called without arguments */
- const struct bb_applet *a;
- int col, output_width;
+ const char *a;
+ int col;
+ unsigned output_width;
help:
output_width = 80;
if (ENABLE_FEATURE_AUTOWIDTH) {
/* Obtain the terminal width */
get_terminal_width_height(0, &output_width, NULL);
}
- /* leading tab and room to wrap */
- output_width -= sizeof("start-stop-daemon, ") + 8;
- printf("%s multi-call binary\n", bb_banner); /* reuse const string... */
- printf("Copyright (C) 1998-2006 Erik Andersen, Rob Landley, and others.\n"
- "Licensed under GPLv2. See source distribution for full notice.\n"
+ dup2(1, 2);
+ full_write2_str(bb_banner); /* reuse const string */
+ full_write2_str(" multi-call binary.\n"); /* reuse */
+ full_write2_str(
+ "Copyright (C) 1998-2009 Erik Andersen, Rob Landley, Denys Vlasenko\n"
+ "and others. Licensed under GPLv2.\n"
+ "See source distribution for full notice.\n"
"\n"
"Usage: busybox [function] [arguments]...\n"
- " or: [function] [arguments]...\n"
+ " or: function [arguments]...\n"
"\n"
"\tBusyBox is a multi-call binary that combines many common Unix\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"
- "\twill act like whatever it was invoked as!\n"
- "\nCurrently defined functions:\n");
+ "\twill act like whatever it was invoked as.\n"
+ "\n"
+ "Currently defined functions:\n");
col = 0;
- a = applets;
- while (a->name) {
- if (col > output_width) {
- puts(",");
+ a = applet_names;
+ /* prevent last comma to be in the very last pos */
+ output_width--;
+ while (*a) {
+ int len2 = strlen(a) + 2;
+ if (col >= (int)output_width - len2) {
+ full_write2_str(",\n");
col = 0;
}
- col += printf("%s%s", (col ? ", " : "\t"), a->name);
- a++;
+ if (col == 0) {
+ col = 6;
+ full_write2_str("\t");
+ } else {
+ full_write2_str(", ");
+ }
+ full_write2_str(a);
+ col += len2;
+ a += len2 - 1;
}
- puts("\n");
+ full_write2_str("\n\n");
return 0;
}
if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) {
+ int use_symbolic_links;
const char *busybox;
busybox = xmalloc_readlink(bb_busybox_exec_path);
if (!busybox)
busybox = bb_busybox_exec_path;
- /* -s makes symlinks */
- install_links(busybox, argv[2] && strcmp(argv[2], "-s") == 0);
+ /* busybox --install [-s] [DIR]: */
+ /* -s: make symlinks */
+ /* DIR: directory to install links to */
+ use_symbolic_links = (argv[2] && strcmp(argv[2], "-s") == 0 && argv++);
+ install_links(busybox, use_symbolic_links, argv[2]);
return 0;
}
* "#!/bin/busybox"-style wrappers */
applet_name = bb_get_last_path_component_nostrip(argv[0]);
run_applet_and_exit(applet_name, argv);
- bb_error_msg_and_die("applet not found");
+
+ /*bb_error_msg_and_die("applet not found"); - sucks in printf */
+ full_write2_str(applet_name);
+ full_write2_str(": applet not found\n");
+ xfunc_die();
}
-void run_appletstruct_and_exit(const struct bb_applet *applet, char **argv)
+void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv)
{
int argc = 1;
argc++;
/* Reinit some shared global data */
- optind = 1;
xfunc_error_retval = EXIT_FAILURE;
- applet_name = applet->name;
- if (argc == 2 && !strcmp(argv[1], "--help"))
- bb_show_usage();
+ applet_name = APPLET_NAME(applet_no);
+ if (argc == 2 && strcmp(argv[1], "--help") == 0) {
+ /* Special case. POSIX says "test --help"
+ * should be no different from e.g. "test --foo". */
+//TODO: just compare applet_no with APPLET_NO_test
+ if (!ENABLE_TEST || strcmp(applet_name, "test") != 0)
+ bb_show_usage();
+ }
if (ENABLE_FEATURE_SUID)
- check_suid(applet);
- exit(applet->main(argc, argv));
+ check_suid(applet_no);
+ exit(applet_main[applet_no](argc, argv));
}
-void run_applet_and_exit(const char *name, char **argv)
+void FAST_FUNC run_applet_and_exit(const char *name, char **argv)
{
- const struct bb_applet *applet = find_applet_by_name(name);
- if (applet)
- run_appletstruct_and_exit(applet, argv);
+ int applet = find_applet_by_name(name);
+ if (applet >= 0)
+ run_applet_no_and_exit(applet, argv);
if (!strncmp(name, "busybox", 7))
exit(busybox_main(argv));
}
+#endif /* !defined(SINGLE_APPLET_MAIN) */
+
+
#if ENABLE_BUILD_LIBBUSYBOX
-int lbb_main(int argc, char **argv)
+int lbb_main(char **argv)
#else
-int main(int argc, char **argv)
+int main(int argc UNUSED_PARAM, char **argv)
#endif
{
- lbb_prepare("busybox", argv);
+ /* Tweak malloc for reduced memory consumption */
+#ifndef PAGE_SIZE
+# define PAGE_SIZE (4*1024) /* guess */
+#endif
+#ifdef M_TRIM_THRESHOLD
+ /* M_TRIM_THRESHOLD is the maximum amount of freed top-most memory
+ * to keep before releasing to the OS
+ * Default is way too big: 256k
+ */
+ mallopt(M_TRIM_THRESHOLD, 2 * PAGE_SIZE);
+#endif
+#ifdef M_MMAP_THRESHOLD
+ /* M_MMAP_THRESHOLD is the request size threshold for using mmap()
+ * Default is too big: 256k
+ */
+ mallopt(M_MMAP_THRESHOLD, 8 * PAGE_SIZE - 256);
+#endif
+
+#if defined(SINGLE_APPLET_MAIN)
+ /* Only one applet is selected by the user! */
+ /* applet_names in this case is just "applet\0\0" */
+ lbb_prepare(applet_names IF_FEATURE_INDIVIDUAL(, argv));
+ return SINGLE_APPLET_MAIN(argc, argv);
+#else
+ lbb_prepare("busybox" IF_FEATURE_INDIVIDUAL(, argv));
#if !BB_MMU
/* NOMMU re-exec trick sets high-order bit in first byte of name */
parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */
run_applet_and_exit(applet_name, argv);
- bb_error_msg_and_die("applet not found");
+
+ /*bb_error_msg_and_die("applet not found"); - sucks in printf */
+ full_write2_str(applet_name);
+ full_write2_str(": applet not found\n");
+ xfunc_die();
+#endif
}