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