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