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