warning removal
[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                                 /* Default is 0.0, else parse USER.GROUP: */
444                                 if (*s) {
445                                         /* We require whitespace between mode and USER.GROUP */
446                                         if ((s == e) || !(e = strchr(s, '.'))) {
447                                                 errmsg = "uid.gid";
448                                                 goto pe_label;
449                                         }
450                                         *e = ':'; /* get_uidgid needs USER:GROUP syntax */
451                                         if (get_uidgid(&sct->m_ugid, s, /*allow_numeric:*/ 1) == 0) {
452                                                 errmsg = "unknown user/group";
453                                                 goto pe_label;
454                                         }
455                                 }
456                         }
457                         continue;
458                 }
459
460                 /* Unknown sections are ignored. */
461
462                 /* Encountering configuration lines prior to seeing a
463                  * section header is treated as an error.  This is how
464                  * the old code worked, but it may not be desirable.
465                  * We may want to simply ignore such lines in case they
466                  * are used in some future version of busybox. */
467                 if (!section) {
468                         errmsg = "keyword outside section";
469                         goto pe_label;
470                 }
471
472         } /* while (1) */
473
474  pe_label:
475         fclose(f);
476         bb_error_msg("parse error in %s, line %u: %s", config_file, lc, errmsg);
477
478         /* Release any allocated memory before returning. */
479         llist_free((llist_t*)sct_head, NULL);
480 }
481 # else
482 static inline void parse_config_file(void)
483 {
484         IF_FEATURE_SUID(ruid = getuid();)
485 }
486 # endif /* FEATURE_SUID_CONFIG */
487
488
489 # if ENABLE_FEATURE_SUID
490 static void check_suid(int applet_no)
491 {
492         gid_t rgid;  /* real gid */
493
494         if (ruid == 0) /* set by parse_config_file() */
495                 return; /* run by root - no need to check more */
496         rgid = getgid();
497
498 #  if ENABLE_FEATURE_SUID_CONFIG
499         if (suid_cfg_readable) {
500                 uid_t uid;
501                 struct suid_config_t *sct;
502                 mode_t m;
503
504                 for (sct = suid_config; sct; sct = sct->m_next) {
505                         if (sct->m_applet == applet_no)
506                                 goto found;
507                 }
508                 goto check_need_suid;
509  found:
510                 /* Is this user allowed to run this applet? */
511                 m = sct->m_mode;
512                 if (sct->m_ugid.uid == ruid)
513                         /* same uid */
514                         m >>= 6;
515                 else if ((sct->m_ugid.gid == rgid) || ingroup(ruid, sct->m_ugid.gid))
516                         /* same group / in group */
517                         m >>= 3;
518                 if (!(m & S_IXOTH)) /* is x bit not set? */
519                         bb_error_msg_and_die("you have no permission to run this applet");
520
521                 /* We set effective AND saved ids. If saved-id is not set
522                  * like we do below, seteuid(0) can still later succeed! */
523
524                 /* Are we directed to change gid
525                  * (APPLET = *s* USER.GROUP or APPLET = *S* USER.GROUP)?
526                  */
527                 if (sct->m_mode & S_ISGID)
528                         rgid = sct->m_ugid.gid;
529                 /* else: we will set egid = rgid, thus dropping sgid effect */
530                 if (setresgid(-1, rgid, rgid))
531                         bb_perror_msg_and_die("setresgid");
532
533                 /* Are we directed to change uid
534                  * (APPLET = s** USER.GROUP or APPLET = S** USER.GROUP)?
535                  */
536                 uid = ruid;
537                 if (sct->m_mode & S_ISUID)
538                         uid = sct->m_ugid.uid;
539                 /* else: we will set euid = ruid, thus dropping suid effect */
540                 if (setresuid(-1, uid, uid))
541                         bb_perror_msg_and_die("setresuid");
542
543                 goto ret;
544         }
545 #   if !ENABLE_FEATURE_SUID_CONFIG_QUIET
546         {
547                 static bool onetime = 0;
548
549                 if (!onetime) {
550                         onetime = 1;
551                         bb_error_msg("using fallback suid method");
552                 }
553         }
554 #   endif
555  check_need_suid:
556 #  endif
557         if (APPLET_SUID(applet_no) == BB_SUID_REQUIRE) {
558                 /* Real uid is not 0. If euid isn't 0 too, suid bit
559                  * is most probably not set on our executable */
560                 if (geteuid())
561                         bb_error_msg_and_die("must be suid to work properly");
562         } else if (APPLET_SUID(applet_no) == BB_SUID_DROP) {
563                 xsetgid(rgid);  /* drop all privileges */
564                 xsetuid(ruid);
565         }
566 #  if ENABLE_FEATURE_SUID_CONFIG
567  ret: ;
568         llist_free((llist_t*)suid_config, NULL);
569 #  endif
570 }
571 # else
572 #  define check_suid(x) ((void)0)
573 # endif /* FEATURE_SUID */
574
575
576 # if ENABLE_FEATURE_INSTALLER
577 static const char usr_bin [] ALIGN1 = "/usr/bin/";
578 static const char usr_sbin[] ALIGN1 = "/usr/sbin/";
579 static const char *const install_dir[] = {
580         &usr_bin [8], /* "/" */
581         &usr_bin [4], /* "/bin/" */
582         &usr_sbin[4]  /* "/sbin/" */
583 #  if !ENABLE_INSTALL_NO_USR
584         ,usr_bin
585         ,usr_sbin
586 #  endif
587 };
588
589 /* create (sym)links for each applet */
590 static void install_links(const char *busybox, int use_symbolic_links,
591                 char *custom_install_dir)
592 {
593         /* directory table
594          * this should be consistent w/ the enum,
595          * busybox.h::bb_install_loc_t, or else... */
596         int (*lf)(const char *, const char *);
597         char *fpc;
598         unsigned i;
599         int rc;
600
601         lf = link;
602         if (use_symbolic_links)
603                 lf = symlink;
604
605         for (i = 0; i < ARRAY_SIZE(applet_main); i++) {
606                 fpc = concat_path_file(
607                                 custom_install_dir ? custom_install_dir : install_dir[APPLET_INSTALL_LOC(i)],
608                                 APPLET_NAME(i));
609                 // debug: bb_error_msg("%slinking %s to busybox",
610                 //              use_symbolic_links ? "sym" : "", fpc);
611                 rc = lf(busybox, fpc);
612                 if (rc != 0 && errno != EEXIST) {
613                         bb_simple_perror_msg(fpc);
614                 }
615                 free(fpc);
616         }
617 }
618 # else
619 #  define install_links(x,y,z) ((void)0)
620 # endif
621
622 /* If we were called as "busybox..." */
623 static int busybox_main(char **argv)
624 {
625         if (!argv[1]) {
626                 /* Called without arguments */
627                 const char *a;
628                 int col;
629                 unsigned output_width;
630  help:
631                 output_width = 80;
632                 if (ENABLE_FEATURE_AUTOWIDTH) {
633                         /* Obtain the terminal width */
634                         get_terminal_width_height(0, &output_width, NULL);
635                 }
636
637                 dup2(1, 2);
638                 full_write2_str(bb_banner); /* reuse const string */
639                 full_write2_str(" multi-call binary.\n"); /* reuse */
640                 full_write2_str(
641                         "Copyright (C) 1998-2009 Erik Andersen, Rob Landley, Denys Vlasenko\n"
642                         "and others. Licensed under GPLv2.\n"
643                         "See source distribution for full notice.\n"
644                         "\n"
645                         "Usage: busybox [function] [arguments]...\n"
646                         "   or: busybox --list[-full]\n"
647                         "   or: function [arguments]...\n"
648                         "\n"
649                         "\tBusyBox is a multi-call binary that combines many common Unix\n"
650                         "\tutilities into a single executable.  Most people will create a\n"
651                         "\tlink to busybox for each function they wish to use and BusyBox\n"
652                         "\twill act like whatever it was invoked as.\n"
653                         "\n"
654                         "Currently defined functions:\n"
655                 );
656                 col = 0;
657                 a = applet_names;
658                 /* prevent last comma to be in the very last pos */
659                 output_width--;
660                 while (*a) {
661                         int len2 = strlen(a) + 2;
662                         if (col >= (int)output_width - len2) {
663                                 full_write2_str(",\n");
664                                 col = 0;
665                         }
666                         if (col == 0) {
667                                 col = 6;
668                                 full_write2_str("\t");
669                         } else {
670                                 full_write2_str(", ");
671                         }
672                         full_write2_str(a);
673                         col += len2;
674                         a += len2 - 1;
675                 }
676                 full_write2_str("\n\n");
677                 return 0;
678         }
679
680         if (strncmp(argv[1], "--list", 6) == 0) {
681                 unsigned i = 0;
682                 const char *a = applet_names;
683                 dup2(1, 2);
684                 while (*a) {
685 # if ENABLE_FEATURE_INSTALLER
686                         if (argv[1][6]) /* --list-path? */
687                                 full_write2_str(install_dir[APPLET_INSTALL_LOC(i)] + 1);
688 # endif
689                         full_write2_str(a);
690                         full_write2_str("\n");
691                         i++;
692                         a += strlen(a) + 1;
693                 }
694                 return 0;
695         }
696
697         if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) {
698                 int use_symbolic_links;
699                 const char *busybox;
700
701                 busybox = xmalloc_readlink(bb_busybox_exec_path);
702                 if (!busybox) {
703                         /* bb_busybox_exec_path is usually "/proc/self/exe".
704                          * In chroot, readlink("/proc/self/exe") usually fails.
705                          * In such case, better use argv[0] as symlink target
706                          * if it is a full path name.
707                          */
708                         if (argv[0][0] != '/')
709                                 bb_error_msg_and_die("'%s' is not an absolute path", argv[0]);
710                         busybox = argv[0];
711                 }
712                 /* busybox --install [-s] [DIR]:
713                  * -s: make symlinks
714                  * DIR: directory to install links to
715                  */
716                 use_symbolic_links = (argv[2] && strcmp(argv[2], "-s") == 0 && argv++);
717                 install_links(busybox, use_symbolic_links, argv[2]);
718                 return 0;
719         }
720
721         if (strcmp(argv[1], "--help") == 0) {
722                 /* "busybox --help [<applet>]" */
723                 if (!argv[2])
724                         goto help;
725                 /* convert to "<applet> --help" */
726                 argv[0] = argv[2];
727                 argv[2] = NULL;
728         } else {
729                 /* "busybox <applet> arg1 arg2 ..." */
730                 argv++;
731         }
732         /* We support "busybox /a/path/to/applet args..." too. Allows for
733          * "#!/bin/busybox"-style wrappers */
734         applet_name = bb_get_last_path_component_nostrip(argv[0]);
735         run_applet_and_exit(applet_name, argv);
736
737         /*bb_error_msg_and_die("applet not found"); - sucks in printf */
738         full_write2_str(applet_name);
739         full_write2_str(": applet not found\n");
740         xfunc_die();
741 }
742
743 void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv)
744 {
745         int argc = 1;
746
747         while (argv[argc])
748                 argc++;
749
750         /* Reinit some shared global data */
751         xfunc_error_retval = EXIT_FAILURE;
752
753         applet_name = APPLET_NAME(applet_no);
754         if (argc == 2 && strcmp(argv[1], "--help") == 0) {
755                 /* Special case. POSIX says "test --help"
756                  * should be no different from e.g. "test --foo".  */
757 //TODO: just compare applet_no with APPLET_NO_test
758                 if (!ENABLE_TEST || strcmp(applet_name, "test") != 0)
759                         bb_show_usage();
760         }
761         if (ENABLE_FEATURE_SUID)
762                 check_suid(applet_no);
763         exit(applet_main[applet_no](argc, argv));
764 }
765
766 void FAST_FUNC run_applet_and_exit(const char *name, char **argv)
767 {
768         int applet = find_applet_by_name(name);
769         if (applet >= 0)
770                 run_applet_no_and_exit(applet, argv);
771         if (strncmp(name, "busybox", 7) == 0)
772                 exit(busybox_main(argv));
773 }
774
775 #endif /* !defined(SINGLE_APPLET_MAIN) */
776
777
778
779 #if ENABLE_BUILD_LIBBUSYBOX
780 int lbb_main(char **argv)
781 #else
782 int main(int argc UNUSED_PARAM, char **argv)
783 #endif
784 {
785         /* Tweak malloc for reduced memory consumption */
786 #ifdef M_TRIM_THRESHOLD
787         /* M_TRIM_THRESHOLD is the maximum amount of freed top-most memory
788          * to keep before releasing to the OS
789          * Default is way too big: 256k
790          */
791         mallopt(M_TRIM_THRESHOLD, 2 * PAGE_SIZE);
792 #endif
793 #ifdef M_MMAP_THRESHOLD
794         /* M_MMAP_THRESHOLD is the request size threshold for using mmap()
795          * Default is too big: 256k
796          */
797         mallopt(M_MMAP_THRESHOLD, 8 * PAGE_SIZE - 256);
798 #endif
799
800 #if !BB_MMU
801         /* NOMMU re-exec trick sets high-order bit in first byte of name */
802         if (argv[0][0] & 0x80) {
803                 re_execed = 1;
804                 argv[0][0] &= 0x7f;
805         }
806 #endif
807
808 #if defined(SINGLE_APPLET_MAIN)
809         /* Only one applet is selected in .config */
810         if (argv[1] && strncmp(argv[0], "busybox", 7) == 0) {
811                 /* "busybox <applet> <params>" should still work as expected */
812                 argv++;
813         }
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         applet_name = argv[0];
821         if (applet_name[0] == '-')
822                 applet_name++;
823         applet_name = bb_basename(applet_name);
824
825         parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */
826
827         run_applet_and_exit(applet_name, argv);
828
829         /*bb_error_msg_and_die("applet not found"); - sucks in printf */
830         full_write2_str(applet_name);
831         full_write2_str(": applet not found\n");
832         xfunc_die();
833 #endif
834 }