1303 files changed, 32187 insertions, 57138 deletions
diff --git a/libbb/appletlib.c b/libbb/appletlib.c index 5e4f4a9..966d3a8 100644 --- a/libbb/appletlib.c +++ b/libbb/appletlib.c @@ -52,7 +52,6 @@ #include "usage_compressed.h" - #if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE static const char usage_messages[] ALIGN1 = UNPACKED_USAGE; #else @@ -130,7 +129,7 @@ void FAST_FUNC bb_show_usage(void) full_write2_str(applet_name); full_write2_str(" "); full_write2_str(p); - full_write2_str("\n\n"); + full_write2_str("\n"); } if (ENABLE_FEATURE_CLEAN_UP) dealloc_usage_messages((char*)usage_string); @@ -139,32 +138,127 @@ void FAST_FUNC bb_show_usage(void) xfunc_die(); } -#if NUM_APPLETS > 8 -static int applet_name_compare(const void *name, const void *idx) -{ - int i = (int)(ptrdiff_t)idx - 1; - return strcmp(name, APPLET_NAME(i)); -} -#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. */ + unsigned i, max; + int j; const char *p; - p = bsearch(name, (void*)(ptrdiff_t)1, ARRAY_SIZE(applet_main), 1, applet_name_compare); - /* - * if (!p) return -1; - * ^^^^^^^^^^^^^^^^^^ the code below will do this if p == NULL :) - */ - return (int)(ptrdiff_t)p - 1; + +/* The commented-out word-at-a-time code is ~40% faster, but +160 bytes. + * "Faster" here saves ~0.5 microsecond of real time - not worth it. + */ +#if 0 /*BB_UNALIGNED_MEMACCESS_OK && BB_LITTLE_ENDIAN*/ + uint32_t n32; + + /* Handle all names < 2 chars long early */ + if (name[0] == '\0') + return -1; /* "" is not a valid applet name */ + if (name[1] == '\0') { + if (!ENABLE_TEST) + return -1; /* 1-char name is not valid */ + if (name[0] != ']') + return -1; /* 1-char name which isn't "[" is not valid */ + /* applet "[" is always applet #0: */ + return 0; + } +#endif + + p = applet_names; + i = 0; +#if KNOWN_APPNAME_OFFSETS <= 0 + max = NUM_APPLETS; #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) + max = NUM_APPLETS * KNOWN_APPNAME_OFFSETS; + for (j = ARRAY_SIZE(applet_nameofs)-1; j >= 0; j--) { + const char *pp = applet_names + applet_nameofs[j]; + if (strcmp(name, pp) >= 0) { + //bb_error_msg("name:'%s' >= pp:'%s'", name, pp); + p = pp; + i = max - NUM_APPLETS; + break; + } + max -= NUM_APPLETS; + } + max /= (unsigned)KNOWN_APPNAME_OFFSETS; + i /= (unsigned)KNOWN_APPNAME_OFFSETS; + //bb_error_msg("name:'%s' starting from:'%s' i:%u max:%u", name, p, i, max); +#endif + + /* Open-coded linear search without strcmp/strlen calls for speed */ + +#if 0 /*BB_UNALIGNED_MEMACCESS_OK && BB_LITTLE_ENDIAN*/ + /* skip "[\0" name, it's surely not it */ + if (ENABLE_TEST && LONE_CHAR(p, '[')) + i++, p += 2; + /* All remaining applet names in p[] are at least 2 chars long */ + /* name[] is also at least 2 chars long */ + + n32 = (name[0] << 0) | (name[1] << 8) | (name[2] << 16); + while (i < max) { + uint32_t p32; + char ch; + + /* Quickly check match of the first 3 bytes */ + move_from_unaligned32(p32, p); + p += 3; + if ((p32 & 0x00ffffff) != n32) { + /* Most likely case: 3 first bytes do not match */ + i++; + if ((p32 & 0x00ff0000) == '\0') + continue; // p[2] was NUL + p++; + if ((p32 & 0xff000000) == '\0') + continue; // p[3] was NUL + /* p[0..3] aren't matching and none is NUL, check the rest */ + while (*p++ != '\0') + continue; + continue; + } + + /* Unlikely branch: first 3 bytes ([0..2]) match */ + if ((p32 & 0x00ff0000) == '\0') { + /* name is 2-byte long, it is full match */ + //bb_error_msg("found:'%s' i:%u", name, i); return i; - p += strlen(p) + 1; + } + /* Check remaining bytes [3..NUL] */ + ch = (p32 >> 24); + j = 3; + while (ch == name[j]) { + if (ch == '\0') { + //bb_error_msg("found:'%s' i:%u", name, i); + return i; + } + ch = *++p; + j++; + } + /* Not a match. Skip it, including NUL */ + while (ch != '\0') + ch = *++p; + p++; + i++; + } + return -1; +#else + while (i < max) { + char ch; + j = 0; + /* Do we see "name\0" in applet_names[p] position? */ + while ((ch = *p) == name[j]) { + if (ch == '\0') { + //bb_error_msg("found:'%s' i:%u", name, i); + return i; /* yes */ + } + p++; + j++; + } + /* No. + * p => 1st non-matching char in applet_names[], + * skip to and including NUL. + */ + while (ch != '\0') + ch = *++p; + p++; i++; } return -1; @@ -184,8 +278,7 @@ void lbb_prepare(const char *applet #endif applet_name = applet; - /* Set locale for everybody except 'init' */ - if (ENABLE_LOCALE_SUPPORT && getpid() != 1) + if (ENABLE_LOCALE_SUPPORT) setlocale(LC_ALL, ""); #if ENABLE_FEATURE_INDIVIDUAL @@ -194,7 +287,7 @@ void lbb_prepare(const char *applet if (argv[1] && !argv[2] && strcmp(argv[1], "--help") == 0 - && strncmp(applet, "busybox", 7) != 0 + && !is_prefixed_with(applet, "busybox") ) { /* Special case. POSIX says "test --help" * should be no different from e.g. "test --foo". */ @@ -236,21 +329,6 @@ static struct suid_config_t { static bool suid_cfg_readable; -/* check if u is member of group g */ -static int ingroup(uid_t u, gid_t g) -{ - struct group *grp = getgrgid(g); - if (grp) { - char **mem; - for (mem = grp->gr_mem; *mem; mem++) { - struct passwd *pwd = getpwnam(*mem); - if (pwd && (pwd->pw_uid == u)) - return 1; - } - } - return 0; -} - /* libbb candidate */ static char *get_trimmed_slice(char *s, char *e) { @@ -438,7 +516,7 @@ static void parse_config_file(void) goto pe_label; } *e = ':'; /* get_uidgid needs USER:GROUP syntax */ - if (get_uidgid(&sct->m_ugid, s, /*allow_numeric:*/ 1) == 0) { + if (get_uidgid(&sct->m_ugid, s) == 0) { errmsg = "unknown user/group"; goto pe_label; } @@ -458,7 +536,6 @@ static void parse_config_file(void) errmsg = "keyword outside section"; goto pe_label; } - } /* while (1) */ pe_label: @@ -476,7 +553,22 @@ static inline void parse_config_file(void) # endif /* FEATURE_SUID_CONFIG */ -# if ENABLE_FEATURE_SUID +# if ENABLE_FEATURE_SUID && NUM_APPLETS > 0 +/* check if u is member of group g */ +static int ingroup(uid_t u, gid_t g) +{ + struct group *grp = getgrgid(g); + if (grp) { + char **mem; + for (mem = grp->gr_mem; *mem; mem++) { + struct passwd *pwd = getpwnam(*mem); + if (pwd && (pwd->pw_uid == u)) + return 1; + } + } + return 0; +} + static void check_suid(int applet_no) { gid_t rgid; /* real gid */ @@ -585,6 +677,7 @@ static void install_links(const char *busybox, int use_symbolic_links, * busybox.h::bb_install_loc_t, or else... */ int (*lf)(const char *, const char *); char *fpc; + const char *appname = applet_names; unsigned i; int rc; @@ -595,7 +688,7 @@ static void install_links(const char *busybox, int use_symbolic_links, for (i = 0; i < ARRAY_SIZE(applet_main); i++) { fpc = concat_path_file( custom_install_dir ? custom_install_dir : install_dir[APPLET_INSTALL_LOC(i)], - APPLET_NAME(i)); + appname); // debug: bb_error_msg("%slinking %s to busybox", // use_symbolic_links ? "sym" : "", fpc); rc = lf(busybox, fpc); @@ -603,12 +696,21 @@ static void install_links(const char *busybox, int use_symbolic_links, bb_simple_perror_msg(fpc); } free(fpc); + while (*appname++ != '\0') + continue; } } -# else -# define install_links(x,y,z) ((void)0) +# elif ENABLE_BUSYBOX +static void install_links(const char *busybox UNUSED_PARAM, + int use_symbolic_links UNUSED_PARAM, + char *custom_install_dir UNUSED_PARAM) +{ +} # endif +# if ENABLE_BUSYBOX +static void run_applet_and_exit(const char *name, char **argv) NORETURN; + /* If we were called as "busybox..." */ static int busybox_main(char **argv) { @@ -621,14 +723,14 @@ static int busybox_main(char **argv) output_width = 80; if (ENABLE_FEATURE_AUTOWIDTH) { /* Obtain the terminal width */ - get_terminal_width_height(0, &output_width, NULL); + output_width = get_terminal_width(2); } dup2(1, 2); full_write2_str(bb_banner); /* reuse const string */ full_write2_str(" multi-call binary.\n"); /* reuse */ full_write2_str( - "BusyBox is copyrighted by many authors between 1998-2012.\n" + "BusyBox is copyrighted by many authors between 1998-2015.\n" "Licensed under GPLv2. See source distribution for detailed\n" "copyright notices. Merged for bionic by tpruvot@github\n" "\n" @@ -639,10 +741,19 @@ static int busybox_main(char **argv) ) " or: function [arguments]...\n" "\n" + IF_NOT_FEATURE_SH_STANDALONE( "\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" + ) + IF_FEATURE_SH_STANDALONE( + "\tBusyBox is a multi-call binary that combines many common Unix\n" + "\tutilities into a single executable. The shell in this build\n" + "\tis configured to run built-in utilities without $PATH search.\n" + "\tYou don't need to install a link to busybox for each utility.\n" + "\tTo run external program, use full path (/sbin/ip instead of ip).\n" + ) "\n" "Currently defined functions:\n" ); @@ -666,23 +777,24 @@ static int busybox_main(char **argv) col += len2; a += len2 - 1; } - full_write2_str("\n\n"); + full_write2_str("\n"); return 0; } - if (strncmp(argv[1], "--list", 6) == 0) { + if (is_prefixed_with(argv[1], "--list")) { unsigned i = 0; const char *a = applet_names; dup2(1, 2); while (*a) { -# if ENABLE_FEATURE_INSTALLER +# if ENABLE_FEATURE_INSTALLER if (argv[1][6]) /* --list-full? */ full_write2_str(install_dir[APPLET_INSTALL_LOC(i)] + 1); -# endif +# endif full_write2_str(a); full_write2_str("\n"); i++; - a += strlen(a) + 1; + while (*a++ != '\0') + continue; } return 0; } @@ -726,13 +838,10 @@ static int busybox_main(char **argv) * "#!/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"); - sucks in printf */ - full_write2_str(applet_name); - full_write2_str(": applet not found\n"); - xfunc_die(); } +# endif +# if NUM_APPLETS > 0 void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv) { int argc = 1; @@ -742,42 +851,84 @@ void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv) /* Reinit some shared global data */ xfunc_error_retval = EXIT_FAILURE; + applet_name = bb_get_last_path_component_nostrip(argv[0]); - 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) { - /* If you want "foo --help" to return 0: */ + /* Special case. POSIX says "test --help" + * should be no different from e.g. "test --foo". + * Thus for "test", we skip --help check. + * "true" and "false" are also special. + */ + if (1 +# if defined APPLET_NO_test + && applet_no != APPLET_NO_test +# endif +# if defined APPLET_NO_true + && applet_no != APPLET_NO_true +# endif +# if defined APPLET_NO_false + && applet_no != APPLET_NO_false +# endif + ) { + if (argc == 2 && strcmp(argv[1], "--help") == 0) { + /* Make "foo --help" exit with 0: */ xfunc_error_retval = 0; bb_show_usage(); } } if (ENABLE_FEATURE_SUID) check_suid(applet_no); - exit(applet_main[applet_no](argc, argv)); + xfunc_error_retval = applet_main[applet_no](argc, argv); + /* Note: applet_main() may also not return (die on a xfunc or such) */ + xfunc_die(); } +# endif /* NUM_APPLETS > 0 */ -void FAST_FUNC run_applet_and_exit(const char *name, char **argv) +# if ENABLE_BUSYBOX || NUM_APPLETS > 0 +static NORETURN void run_applet_and_exit(const char *name, char **argv) { - int applet = find_applet_by_name(name); - if (applet >= 0) - run_applet_no_and_exit(applet, argv); - if (strncmp(name, "busybox", 7) == 0) +# if ENABLE_BUSYBOX + if (is_prefixed_with(name, "busybox")) exit(busybox_main(argv)); +# endif +# if NUM_APPLETS > 0 + /* find_applet_by_name() search is more expensive, so goes second */ + { + int applet = find_applet_by_name(name); + if (applet >= 0) + run_applet_no_and_exit(applet, argv); + } +# endif + + /*bb_error_msg_and_die("applet not found"); - links in printf */ + full_write2_str(applet_name); + full_write2_str(": applet not found\n"); + /* POSIX: "If a command is not found, the exit status shall be 127" */ + exit(127); } +# endif #endif /* !defined(SINGLE_APPLET_MAIN) */ - #if ENABLE_BUILD_LIBBUSYBOX int lbb_main(char **argv) #else int main(int argc UNUSED_PARAM, char **argv) #endif { +#if 0 + /* TODO: find a use for a block of memory between end of .bss + * and end of page. For example, I'm getting "_end:0x812e698 2408 bytes" + * - more than 2k of wasted memory (in this particular build) + * *per each running process*! + * (If your linker does not generate "_end" name, weak attribute + * makes &_end == NULL, end_len == 0 here.) + */ + extern char _end[] __attribute__((weak)); + unsigned end_len = (-(int)_end) & 0xfff; + printf("_end:%p %u bytes\n", &_end, end_len); +#endif + /* Tweak malloc for reduced memory consumption */ #ifdef M_TRIM_THRESHOLD /* M_TRIM_THRESHOLD is the maximum amount of freed top-most memory @@ -792,6 +943,14 @@ int main(int argc UNUSED_PARAM, char **argv) */ mallopt(M_MMAP_THRESHOLD, 32 * 1024 - 256); #endif +#if 0 /*def M_TOP_PAD*/ + /* When the program break is increased, then M_TOP_PAD bytes are added + * to the sbrk(2) request. When the heap is trimmed because of free(3), + * this much free space is preserved at the top of the heap. + * glibc default seems to be way too big: 128k, but need to verify. + */ + mallopt(M_TOP_PAD, 8 * 1024); +#endif #if !BB_MMU /* NOMMU re-exec trick sets high-order bit in first byte of name */ @@ -802,29 +961,35 @@ int main(int argc UNUSED_PARAM, char **argv) #endif #if defined(SINGLE_APPLET_MAIN) + /* Only one applet is selected in .config */ - if (argv[1] && strncmp(argv[0], "busybox", 7) == 0) { + if (argv[1] && is_prefixed_with(argv[0], "busybox")) { /* "busybox <applet> <params>" should still work as expected */ argv++; } /* applet_names in this case is just "applet\0\0" */ lbb_prepare(applet_names IF_FEATURE_INDIVIDUAL(, argv)); return SINGLE_APPLET_MAIN(argc, argv); + +#elif !ENABLE_BUSYBOX && NUM_APPLETS == 0 + + full_write2_str(bb_basename(argv[0])); + full_write2_str(": no applets enabled\n"); + exit(127); + #else - lbb_prepare("busybox" IF_FEATURE_INDIVIDUAL(, argv)); + lbb_prepare("busybox" IF_FEATURE_INDIVIDUAL(, argv)); +# if !ENABLE_BUSYBOX + if (argv[1] && is_prefixed_with(bb_basename(argv[0]), "busybox")) + argv++; +# endif applet_name = argv[0]; if (applet_name[0] == '-') applet_name++; applet_name = bb_basename(applet_name); - parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */ - run_applet_and_exit(applet_name, argv); - /*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 } |