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