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