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