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