Ignore:
Timestamp:
Feb 25, 2011, 9:26:54 PM (13 years ago)
Author:
Bruno Cornec
Message:
  • Update mindi-busybox to 1.18.3 to avoid problems with the tar command which is now failing on recent versions with busybox 1.7.3
File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/2.2.9/mindi-busybox/applets/applets.c

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