busybox/libbb/appletlib.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Utility routines.
   4 *
   5 * Copyright (C) tons of folks.  Tracking down who wrote what
   6 * isn't something I'm going to worry about...  If you wrote something
   7 * here, please feel free to acknowledge your work.
   8 *
   9 * Based in part on code from sash, Copyright (c) 1999 by David I. Bell
  10 * Permission has been granted to redistribute this code under GPL.
  11 *
  12 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  13 */
  14
  15/* We are trying to not use printf, this benefits the case when selected
  16 * applets are really simple. Example:
  17 *
  18 * $ ./busybox
  19 * ...
  20 * Currently defined functions:
  21 *         basename, false, true
  22 *
  23 * $ size busybox
  24 *    text    data     bss     dec     hex filename
  25 *    4473      52      72    4597    11f5 busybox
  26 *
  27 * FEATURE_INSTALLER or FEATURE_SUID will still link printf routines in. :(
  28 */
  29#include "busybox.h"
  30#include <assert.h>
  31#include <malloc.h>
  32/* Try to pull in PAGE_SIZE */
  33#ifdef __linux__
  34# include <sys/user.h>
  35#endif
  36#ifdef __GNU__ /* Hurd */
  37# include <mach/vm_param.h>
  38#endif
  39
  40
  41/* Declare <applet>_main() */
  42#define PROTOTYPES
  43#include "applets.h"
  44#undef PROTOTYPES
  45
  46
  47/* Include generated applet names, pointers to <applet>_main, etc */
  48#include "applet_tables.h"
  49/* ...and if applet_tables generator says we have only one applet... */
  50#ifdef SINGLE_APPLET_MAIN
  51# undef ENABLE_FEATURE_INDIVIDUAL
  52# define ENABLE_FEATURE_INDIVIDUAL 1
  53# undef IF_FEATURE_INDIVIDUAL
  54# define IF_FEATURE_INDIVIDUAL(...) __VA_ARGS__
  55#endif
  56
  57
  58#include "usage_compressed.h"
  59
  60#if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE
  61static const char usage_messages[] ALIGN1 = UNPACKED_USAGE;
  62#else
  63# define usage_messages 0
  64#endif
  65
  66#if ENABLE_FEATURE_COMPRESS_USAGE
  67
  68static const char packed_usage[] ALIGN1 = { PACKED_USAGE };
  69# include "archive.h"
  70static const char *unpack_usage_messages(void)
  71{
  72        char *outbuf = NULL;
  73        bunzip_data *bd;
  74        int i;
  75
  76        i = start_bunzip(&bd,
  77                        /* src_fd: */ -1,
  78                        /* inbuf:  */ packed_usage,
  79                        /* len:    */ sizeof(packed_usage));
  80        /* read_bunzip can longjmp to start_bunzip, and ultimately
  81         * end up here with i != 0 on read data errors! Not trivial */
  82        if (!i) {
  83                /* Cannot use xmalloc: will leak bd in NOFORK case! */
  84                outbuf = malloc_or_warn(sizeof(UNPACKED_USAGE));
  85                if (outbuf)
  86                        read_bunzip(bd, outbuf, sizeof(UNPACKED_USAGE));
  87        }
  88        dealloc_bunzip(bd);
  89        return outbuf;
  90}
  91# define dealloc_usage_messages(s) free(s)
  92
  93#else
  94
  95# define unpack_usage_messages() usage_messages
  96# define dealloc_usage_messages(s) ((void)(s))
  97
  98#endif /* FEATURE_COMPRESS_USAGE */
  99
 100
 101void FAST_FUNC bb_show_usage(void)
 102{
 103        if (ENABLE_SHOW_USAGE) {
 104#ifdef SINGLE_APPLET_STR
 105                /* Imagine that this applet is "true". Dont suck in printf! */
 106                const char *usage_string = unpack_usage_messages();
 107
 108                if (*usage_string == '\b') {
 109                        full_write2_str("No help available.\n\n");
 110                } else {
 111                        full_write2_str("Usage: "SINGLE_APPLET_STR" ");
 112                        full_write2_str(usage_string);
 113                        full_write2_str("\n\n");
 114                }
 115                if (ENABLE_FEATURE_CLEAN_UP)
 116                        dealloc_usage_messages((char*)usage_string);
 117#else
 118                const char *p;
 119                const char *usage_string = p = unpack_usage_messages();
 120                int ap = find_applet_by_name(applet_name);
 121
 122                if (ap < 0) /* never happens, paranoia */
 123                        xfunc_die();
 124                while (ap) {
 125                        while (*p++) continue;
 126                        ap--;
 127                }
 128                full_write2_str(bb_banner);
 129                full_write2_str(" multi-call binary.\n");
 130                if (*p == '\b')
 131                        full_write2_str("\nNo help available.\n\n");
 132                else {
 133                        full_write2_str("\nUsage: ");
 134                        full_write2_str(applet_name);
 135                        full_write2_str(" ");
 136                        full_write2_str(p);
 137                        full_write2_str("\n\n");
 138                }
 139                if (ENABLE_FEATURE_CLEAN_UP)
 140                        dealloc_usage_messages((char*)usage_string);
 141#endif
 142        }
 143        xfunc_die();
 144}
 145
 146#if NUM_APPLETS > 8
 147/* NB: any char pointer will work as well, not necessarily applet_names */
 148static int applet_name_compare(const void *name, const void *v)
 149{
 150        int i = (const char *)v - applet_names;
 151        return strcmp(name, APPLET_NAME(i));
 152}
 153#endif
 154int FAST_FUNC find_applet_by_name(const char *name)
 155{
 156#if NUM_APPLETS > 8
 157        /* Do a binary search to find the applet entry given the name. */
 158        const char *p;
 159        p = bsearch(name, applet_names, ARRAY_SIZE(applet_main), 1, applet_name_compare);
 160        if (!p)
 161                return -1;
 162        return p - applet_names;
 163#else
 164        /* A version which does not pull in bsearch */
 165        int i = 0;
 166        const char *p = applet_names;
 167        while (i < NUM_APPLETS) {
 168                if (strcmp(name, p) == 0)
 169                        return i;
 170                p += strlen(p) + 1;
 171                i++;
 172        }
 173        return -1;
 174#endif
 175}
 176
 177
 178void lbb_prepare(const char *applet
 179                IF_FEATURE_INDIVIDUAL(, char **argv))
 180                                MAIN_EXTERNALLY_VISIBLE;
 181void lbb_prepare(const char *applet
 182                IF_FEATURE_INDIVIDUAL(, char **argv))
 183{
 184#ifdef __GLIBC__
 185        (*(int **)&bb_errno) = __errno_location();
 186        barrier();
 187#endif
 188        applet_name = applet;
 189
 190        /* Set locale for everybody except 'init' */
 191        if (ENABLE_LOCALE_SUPPORT && getpid() != 1)
 192                setlocale(LC_ALL, "");
 193
 194#if ENABLE_FEATURE_INDIVIDUAL
 195        /* Redundant for busybox (run_applet_and_exit covers that case)
 196         * but needed for "individual applet" mode */
 197        if (argv[1]
 198         && !argv[2]
 199         && strcmp(argv[1], "--help") == 0
 200         && strncmp(applet, "busybox", 7) != 0
 201        ) {
 202                /* Special case. POSIX says "test --help"
 203                 * should be no different from e.g. "test --foo".  */
 204                if (!ENABLE_TEST || strcmp(applet_name, "test") != 0)
 205                        bb_show_usage();
 206        }
 207#endif
 208}
 209
 210/* The code below can well be in applets/applets.c, as it is used only
 211 * for busybox binary, not "individual" binaries.
 212 * However, keeping it here and linking it into libbusybox.so
 213 * (together with remaining tiny applets/applets.o)
 214 * makes it possible to avoid --whole-archive at link time.
 215 * This makes (shared busybox) + libbusybox smaller.
 216 * (--gc-sections would be even better....)
 217 */
 218
 219const char *applet_name;
 220#if !BB_MMU
 221bool re_execed;
 222#endif
 223
 224
 225/* If not built as a single-applet executable... */
 226#if !defined(SINGLE_APPLET_MAIN)
 227
 228IF_FEATURE_SUID(static uid_t ruid;)  /* real uid */
 229
 230#if ENABLE_FEATURE_SUID_CONFIG
 231
 232/* applets[] is const, so we have to define this "override" structure */
 233static struct BB_suid_config {
 234        int m_applet;
 235        uid_t m_uid;
 236        gid_t m_gid;
 237        mode_t m_mode;
 238        struct BB_suid_config *m_next;
 239} *suid_config;
 240
 241static bool suid_cfg_readable;
 242
 243/* check if u is member of group g */
 244static int ingroup(uid_t u, gid_t g)
 245{
 246        struct group *grp = getgrgid(g);
 247
 248        if (grp) {
 249                char **mem;
 250
 251                for (mem = grp->gr_mem; *mem; mem++) {
 252                        struct passwd *pwd = getpwnam(*mem);
 253
 254                        if (pwd && (pwd->pw_uid == u))
 255                                return 1;
 256                }
 257        }
 258        return 0;
 259}
 260
 261/* This should probably be a libbb routine.  In that case,
 262 * I'd probably rename it to something like bb_trimmed_slice.
 263 */
 264static char *get_trimmed_slice(char *s, char *e)
 265{
 266        /* First, consider the value at e to be nul and back up until we
 267         * reach a non-space char.  Set the char after that (possibly at
 268         * the original e) to nul. */
 269        while (e-- > s) {
 270                if (!isspace(*e)) {
 271                        break;
 272                }
 273        }
 274        e[1] = '\0';
 275
 276        /* Next, advance past all leading space and return a ptr to the
 277         * first non-space char; possibly the terminating nul. */
 278        return skip_whitespace(s);
 279}
 280
 281/* Don't depend on the tools to combine strings. */
 282static const char config_file[] ALIGN1 = "/etc/busybox.conf";
 283
 284/* We don't supply a value for the nul, so an index adjustment is
 285 * necessary below.  Also, we use unsigned short here to save some
 286 * space even though these are really mode_t values. */
 287static const unsigned short mode_mask[] ALIGN2 = {
 288        /*  SST     sst                 xxx         --- */
 289        S_ISUID,    S_ISUID|S_IXUSR,    S_IXUSR,    0,  /* user */
 290        S_ISGID,    S_ISGID|S_IXGRP,    S_IXGRP,    0,  /* group */
 291        0,          S_IXOTH,            S_IXOTH,    0   /* other */
 292};
 293
 294#define parse_error(x)  do { errmsg = x; goto pe_label; } while (0)
 295
 296static void parse_config_file(void)
 297{
 298        struct BB_suid_config *sct_head;
 299        struct BB_suid_config *sct;
 300        int applet_no;
 301        FILE *f;
 302        const char *errmsg;
 303        char *s;
 304        char *e;
 305        int i;
 306        unsigned lc;
 307        smallint section;
 308        char buffer[256];
 309        struct stat st;
 310
 311        assert(!suid_config); /* Should be set to NULL by bss init. */
 312
 313        ruid = getuid();
 314        if (ruid == 0) /* run by root - don't need to even read config file */
 315                return;
 316
 317        if ((stat(config_file, &st) != 0)       /* No config file? */
 318         || !S_ISREG(st.st_mode)                /* Not a regular file? */
 319         || (st.st_uid != 0)                    /* Not owned by root? */
 320         || (st.st_mode & (S_IWGRP | S_IWOTH))  /* Writable by non-root? */
 321         || !(f = fopen_for_read(config_file))      /* Cannot open? */
 322        ) {
 323                return;
 324        }
 325
 326        suid_cfg_readable = 1;
 327        sct_head = NULL;
 328        section = lc = 0;
 329
 330        while (1) {
 331                s = buffer;
 332
 333                if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */
 334// why?
 335                        if (ferror(f)) {   /* Make sure it wasn't a read error. */
 336                                parse_error("reading");
 337                        }
 338                        fclose(f);
 339                        suid_config = sct_head; /* Success, so set the pointer. */
 340                        return;
 341                }
 342
 343                lc++;                                   /* Got a (partial) line. */
 344
 345                /* If a line is too long for our buffer, we consider it an error.
 346                 * The following test does mistreat one corner case though.
 347                 * If the final line of the file does not end with a newline and
 348                 * yet exactly fills the buffer, it will be treated as too long
 349                 * even though there isn't really a problem.  But it isn't really
 350                 * worth adding code to deal with such an unlikely situation, and
 351                 * we do err on the side of caution.  Besides, the line would be
 352                 * too long if it did end with a newline. */
 353                if (!strchr(s, '\n') && !feof(f)) {
 354                        parse_error("line too long");
 355                }
 356
 357                /* Trim leading and trailing whitespace, ignoring comments, and
 358                 * check if the resulting string is empty. */
 359                s = get_trimmed_slice(s, strchrnul(s, '#'));
 360                if (!*s) {
 361                        continue;
 362                }
 363
 364                /* Check for a section header. */
 365
 366                if (*s == '[') {
 367                        /* Unlike the old code, we ignore leading and trailing
 368                         * whitespace for the section name.  We also require that
 369                         * there are no stray characters after the closing bracket. */
 370                        e = strchr(s, ']');
 371                        if (!e   /* Missing right bracket? */
 372                         || e[1] /* Trailing characters? */
 373                         || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */
 374                        ) {
 375                                parse_error("section header");
 376                        }
 377                        /* Right now we only have one section so just check it.
 378                         * If more sections are added in the future, please don't
 379                         * resort to cascading ifs with multiple strcasecmp calls.
 380                         * That kind of bloated code is all too common.  A loop
 381                         * and a string table would be a better choice unless the
 382                         * number of sections is very small. */
 383                        if (strcasecmp(s, "SUID") == 0) {
 384                                section = 1;
 385                                continue;
 386                        }
 387                        section = -1;   /* Unknown section so set to skip. */
 388                        continue;
 389                }
 390
 391                /* Process sections. */
 392
 393                if (section == 1) {             /* SUID */
 394                        /* Since we trimmed leading and trailing space above, we're
 395                         * now looking for strings of the form
 396                         *    <key>[::space::]*=[::space::]*<value>
 397                         * where both key and value could contain inner whitespace. */
 398
 399                        /* First get the key (an applet name in our case). */
 400                        e = strchr(s, '=');
 401                        if (e) {
 402                                s = get_trimmed_slice(s, e);
 403                        }
 404                        if (!e || !*s) {        /* Missing '=' or empty key. */
 405                                parse_error("keyword");
 406                        }
 407
 408                        /* Ok, we have an applet name.  Process the rhs if this
 409                         * applet is currently built in and ignore it otherwise.
 410                         * Note: this can hide config file bugs which only pop
 411                         * up when the busybox configuration is changed. */
 412                        applet_no = find_applet_by_name(s);
 413                        if (applet_no >= 0) {
 414                                /* Note: We currently don't check for duplicates!
 415                                 * The last config line for each applet will be the
 416                                 * one used since we insert at the head of the list.
 417                                 * I suppose this could be considered a feature. */
 418                                sct = xmalloc(sizeof(struct BB_suid_config));
 419                                sct->m_applet = applet_no;
 420                                sct->m_mode = 0;
 421                                sct->m_next = sct_head;
 422                                sct_head = sct;
 423
 424                                /* Get the specified mode. */
 425
 426                                e = skip_whitespace(e+1);
 427
 428                                for (i = 0; i < 3; i++) {
 429                                        /* There are 4 chars + 1 nul for each of user/group/other. */
 430                                        static const char mode_chars[] ALIGN1 = "Ssx-\0" "Ssx-\0" "Ttx-";
 431
 432                                        const char *q;
 433                                        q = strchrnul(mode_chars + 5*i, *e++);
 434                                        if (!*q) {
 435                                                parse_error("mode");
 436                                        }
 437                                        /* Adjust by -i to account for nul. */
 438                                        sct->m_mode |= mode_mask[(q - mode_chars) - i];
 439                                }
 440
 441                                /* Now get the the user/group info. */
 442
 443                                s = skip_whitespace(e);
 444
 445                                /* Note: we require whitespace between the mode and the
 446                                 * user/group info. */
 447                                if ((s == e) || !(e = strchr(s, '.'))) {
 448                                        parse_error("<uid>.<gid>");
 449                                }
 450                                *e++ = '\0';
 451
 452                                /* We can't use get_ug_id here since it would exit()
 453                                 * if a uid or gid was not found.  Oh well... */
 454                                sct->m_uid = bb_strtoul(s, NULL, 10);
 455                                if (errno) {
 456                                        struct passwd *pwd = getpwnam(s);
 457                                        if (!pwd) {
 458                                                parse_error("user");
 459                                        }
 460                                        sct->m_uid = pwd->pw_uid;
 461                                }
 462
 463                                sct->m_gid = bb_strtoul(e, NULL, 10);
 464                                if (errno) {
 465                                        struct group *grp;
 466                                        grp = getgrnam(e);
 467                                        if (!grp) {
 468                                                parse_error("group");
 469                                        }
 470                                        sct->m_gid = grp->gr_gid;
 471                                }
 472                        }
 473                        continue;
 474                }
 475
 476                /* Unknown sections are ignored. */
 477
 478                /* Encountering configuration lines prior to seeing a
 479                 * section header is treated as an error.  This is how
 480                 * the old code worked, but it may not be desirable.
 481                 * We may want to simply ignore such lines in case they
 482                 * are used in some future version of busybox. */
 483                if (!section) {
 484                        parse_error("keyword outside section");
 485                }
 486
 487        } /* while (1) */
 488
 489 pe_label:
 490        fprintf(stderr, "Parse error in %s, line %d: %s\n",
 491                        config_file, lc, errmsg);
 492
 493        fclose(f);
 494        /* Release any allocated memory before returning. */
 495        while (sct_head) {
 496                sct = sct_head->m_next;
 497                free(sct_head);
 498                sct_head = sct;
 499        }
 500}
 501#else
 502static inline void parse_config_file(void)
 503{
 504        IF_FEATURE_SUID(ruid = getuid();)
 505}
 506#endif /* FEATURE_SUID_CONFIG */
 507
 508
 509#if ENABLE_FEATURE_SUID
 510static void check_suid(int applet_no)
 511{
 512        gid_t rgid;  /* real gid */
 513
 514        if (ruid == 0) /* set by parse_config_file() */
 515                return; /* run by root - no need to check more */
 516        rgid = getgid();
 517
 518#if ENABLE_FEATURE_SUID_CONFIG
 519        if (suid_cfg_readable) {
 520                uid_t uid;
 521                struct BB_suid_config *sct;
 522                mode_t m;
 523
 524                for (sct = suid_config; sct; sct = sct->m_next) {
 525                        if (sct->m_applet == applet_no)
 526                                goto found;
 527                }
 528                goto check_need_suid;
 529 found:
 530                m = sct->m_mode;
 531                if (sct->m_uid == ruid)
 532                        /* same uid */
 533                        m >>= 6;
 534                else if ((sct->m_gid == rgid) || ingroup(ruid, sct->m_gid))
 535                        /* same group / in group */
 536                        m >>= 3;
 537
 538                if (!(m & S_IXOTH))           /* is x bit not set ? */
 539                        bb_error_msg_and_die("you have no permission to run this applet!");
 540
 541                /* _both_ sgid and group_exec have to be set for setegid */
 542                if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))
 543                        rgid = sct->m_gid;
 544                /* else (no setegid) we will set egid = rgid */
 545
 546                /* We set effective AND saved ids. If saved-id is not set
 547                 * like we do below, seteiud(0) can still later succeed! */
 548                if (setresgid(-1, rgid, rgid))
 549                        bb_perror_msg_and_die("setresgid");
 550
 551                /* do we have to set effective uid? */
 552                uid = ruid;
 553                if (sct->m_mode & S_ISUID)
 554                        uid = sct->m_uid;
 555                /* else (no seteuid) we will set euid = ruid */
 556
 557                if (setresuid(-1, uid, uid))
 558                        bb_perror_msg_and_die("setresuid");
 559                return;
 560        }
 561#if !ENABLE_FEATURE_SUID_CONFIG_QUIET
 562        {
 563                static bool onetime = 0;
 564
 565                if (!onetime) {
 566                        onetime = 1;
 567                        fprintf(stderr, "Using fallback suid method\n");
 568                }
 569        }
 570#endif
 571 check_need_suid:
 572#endif
 573        if (APPLET_SUID(applet_no) == _BB_SUID_REQUIRE) {
 574                /* Real uid is not 0. If euid isn't 0 too, suid bit
 575                 * is most probably not set on our executable */
 576                if (geteuid())
 577                        bb_error_msg_and_die("must be suid to work properly");
 578        } else if (APPLET_SUID(applet_no) == _BB_SUID_DROP) {
 579                xsetgid(rgid);  /* drop all privileges */
 580                xsetuid(ruid);
 581        }
 582}
 583#else
 584#define check_suid(x) ((void)0)
 585#endif /* FEATURE_SUID */
 586
 587
 588#if ENABLE_FEATURE_INSTALLER
 589static const char usr_bin [] ALIGN1 = "/usr/bin/";
 590static const char usr_sbin[] ALIGN1 = "/usr/sbin/";
 591static const char *const install_dir[] = {
 592        &usr_bin [8], /* "/" */
 593        &usr_bin [4], /* "/bin/" */
 594        &usr_sbin[4]  /* "/sbin/" */
 595# if !ENABLE_INSTALL_NO_USR
 596        ,usr_bin
 597        ,usr_sbin
 598# endif
 599};
 600
 601
 602/* create (sym)links for each applet */
 603static void install_links(const char *busybox, int use_symbolic_links,
 604                char *custom_install_dir)
 605{
 606        /* directory table
 607         * this should be consistent w/ the enum,
 608         * busybox.h::bb_install_loc_t, or else... */
 609        int (*lf)(const char *, const char *);
 610        char *fpc;
 611        unsigned i;
 612        int rc;
 613
 614        lf = link;
 615        if (use_symbolic_links)
 616                lf = symlink;
 617
 618        for (i = 0; i < ARRAY_SIZE(applet_main); i++) {
 619                fpc = concat_path_file(
 620                                custom_install_dir ? custom_install_dir : install_dir[APPLET_INSTALL_LOC(i)],
 621                                APPLET_NAME(i));
 622                // debug: bb_error_msg("%slinking %s to busybox",
 623                //              use_symbolic_links ? "sym" : "", fpc);
 624                rc = lf(busybox, fpc);
 625                if (rc != 0 && errno != EEXIST) {
 626                        bb_simple_perror_msg(fpc);
 627                }
 628                free(fpc);
 629        }
 630}
 631#else
 632# define install_links(x,y,z) ((void)0)
 633#endif
 634
 635/* If we were called as "busybox..." */
 636static int busybox_main(char **argv)
 637{
 638        if (!argv[1]) {
 639                /* Called without arguments */
 640                const char *a;
 641                int col;
 642                unsigned output_width;
 643 help:
 644                output_width = 80;
 645                if (ENABLE_FEATURE_AUTOWIDTH) {
 646                        /* Obtain the terminal width */
 647                        get_terminal_width_height(0, &output_width, NULL);
 648                }
 649
 650                dup2(1, 2);
 651                full_write2_str(bb_banner); /* reuse const string */
 652                full_write2_str(" multi-call binary.\n"); /* reuse */
 653                full_write2_str(
 654                        "Copyright (C) 1998-2009 Erik Andersen, Rob Landley, Denys Vlasenko\n"
 655                        "and others. Licensed under GPLv2.\n"
 656                        "See source distribution for full notice.\n"
 657                        "\n"
 658                        "Usage: busybox [function] [arguments]...\n"
 659                        "   or: busybox --list[-full]\n"
 660                        "   or: function [arguments]...\n"
 661                        "\n"
 662                        "\tBusyBox is a multi-call binary that combines many common Unix\n"
 663                        "\tutilities into a single executable.  Most people will create a\n"
 664                        "\tlink to busybox for each function they wish to use and BusyBox\n"
 665                        "\twill act like whatever it was invoked as.\n"
 666                        "\n"
 667                        "Currently defined functions:\n"
 668                );
 669                col = 0;
 670                a = applet_names;
 671                /* prevent last comma to be in the very last pos */
 672                output_width--;
 673                while (*a) {
 674                        int len2 = strlen(a) + 2;
 675                        if (col >= (int)output_width - len2) {
 676                                full_write2_str(",\n");
 677                                col = 0;
 678                        }
 679                        if (col == 0) {
 680                                col = 6;
 681                                full_write2_str("\t");
 682                        } else {
 683                                full_write2_str(", ");
 684                        }
 685                        full_write2_str(a);
 686                        col += len2;
 687                        a += len2 - 1;
 688                }
 689                full_write2_str("\n\n");
 690                return 0;
 691        }
 692
 693        if (strncmp(argv[1], "--list", 6) == 0) {
 694                unsigned i = 0;
 695                const char *a = applet_names;
 696                dup2(1, 2);
 697                while (*a) {
 698#if ENABLE_FEATURE_INSTALLER
 699                        if (argv[1][6]) /* --list-path? */
 700                                full_write2_str(install_dir[APPLET_INSTALL_LOC(i)] + 1);
 701#endif
 702                        full_write2_str(a);
 703                        full_write2_str("\n");
 704                        i++;
 705                        a += strlen(a) + 1;
 706                }
 707                return 0;
 708        }
 709
 710        if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) {
 711                int use_symbolic_links;
 712                const char *busybox;
 713                busybox = xmalloc_readlink(bb_busybox_exec_path);
 714                if (!busybox)
 715                        busybox = bb_busybox_exec_path;
 716                /* busybox --install [-s] [DIR]: */
 717                /* -s: make symlinks */
 718                /* DIR: directory to install links to */
 719                use_symbolic_links = (argv[2] && strcmp(argv[2], "-s") == 0 && argv++);
 720                install_links(busybox, use_symbolic_links, argv[2]);
 721                return 0;
 722        }
 723
 724        if (strcmp(argv[1], "--help") == 0) {
 725                /* "busybox --help [<applet>]" */
 726                if (!argv[2])
 727                        goto help;
 728                /* convert to "<applet> --help" */
 729                argv[0] = argv[2];
 730                argv[2] = NULL;
 731        } else {
 732                /* "busybox <applet> arg1 arg2 ..." */
 733                argv++;
 734        }
 735        /* We support "busybox /a/path/to/applet args..." too. Allows for
 736         * "#!/bin/busybox"-style wrappers */
 737        applet_name = bb_get_last_path_component_nostrip(argv[0]);
 738        run_applet_and_exit(applet_name, argv);
 739
 740        /*bb_error_msg_and_die("applet not found"); - sucks in printf */
 741        full_write2_str(applet_name);
 742        full_write2_str(": applet not found\n");
 743        xfunc_die();
 744}
 745
 746void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv)
 747{
 748        int argc = 1;
 749
 750        while (argv[argc])
 751                argc++;
 752
 753        /* Reinit some shared global data */
 754        xfunc_error_retval = EXIT_FAILURE;
 755
 756        applet_name = APPLET_NAME(applet_no);
 757        if (argc == 2 && strcmp(argv[1], "--help") == 0) {
 758                /* Special case. POSIX says "test --help"
 759                 * should be no different from e.g. "test --foo".  */
 760//TODO: just compare applet_no with APPLET_NO_test
 761                if (!ENABLE_TEST || strcmp(applet_name, "test") != 0)
 762                        bb_show_usage();
 763        }
 764        if (ENABLE_FEATURE_SUID)
 765                check_suid(applet_no);
 766        exit(applet_main[applet_no](argc, argv));
 767}
 768
 769void FAST_FUNC run_applet_and_exit(const char *name, char **argv)
 770{
 771        int applet = find_applet_by_name(name);
 772        if (applet >= 0)
 773                run_applet_no_and_exit(applet, argv);
 774        if (!strncmp(name, "busybox", 7))
 775                exit(busybox_main(argv));
 776}
 777
 778#endif /* !defined(SINGLE_APPLET_MAIN) */
 779
 780
 781
 782#if ENABLE_BUILD_LIBBUSYBOX
 783int lbb_main(char **argv)
 784#else
 785int main(int argc UNUSED_PARAM, char **argv)
 786#endif
 787{
 788        /* Tweak malloc for reduced memory consumption */
 789#ifndef PAGE_SIZE
 790# define PAGE_SIZE (4*1024) /* guess */
 791#endif
 792#ifdef M_TRIM_THRESHOLD
 793        /* M_TRIM_THRESHOLD is the maximum amount of freed top-most memory
 794         * to keep before releasing to the OS
 795         * Default is way too big: 256k
 796         */
 797        mallopt(M_TRIM_THRESHOLD, 2 * PAGE_SIZE);
 798#endif
 799#ifdef M_MMAP_THRESHOLD
 800        /* M_MMAP_THRESHOLD is the request size threshold for using mmap()
 801         * Default is too big: 256k
 802         */
 803        mallopt(M_MMAP_THRESHOLD, 8 * PAGE_SIZE - 256);
 804#endif
 805
 806#if defined(SINGLE_APPLET_MAIN)
 807        /* Only one applet is selected by the user! */
 808        /* applet_names in this case is just "applet\0\0" */
 809        lbb_prepare(applet_names IF_FEATURE_INDIVIDUAL(, argv));
 810        return SINGLE_APPLET_MAIN(argc, argv);
 811#else
 812        lbb_prepare("busybox" IF_FEATURE_INDIVIDUAL(, argv));
 813
 814#if !BB_MMU
 815        /* NOMMU re-exec trick sets high-order bit in first byte of name */
 816        if (argv[0][0] & 0x80) {
 817                re_execed = 1;
 818                argv[0][0] &= 0x7f;
 819        }
 820#endif
 821        applet_name = argv[0];
 822        if (applet_name[0] == '-')
 823                applet_name++;
 824        applet_name = bb_basename(applet_name);
 825
 826        parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */
 827
 828        run_applet_and_exit(applet_name, argv);
 829
 830        /*bb_error_msg_and_die("applet not found"); - sucks in printf */
 831        full_write2_str(applet_name);
 832        full_write2_str(": applet not found\n");
 833        xfunc_die();
 834#endif
 835}
 836