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