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