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