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