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