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