*: move get_sock_lsa and xwrite_str to libbb, use where appropriate
[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 USE_FEATURE_INDIVIDUAL
59 #define USE_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                 USE_FEATURE_INDIVIDUAL(, char **argv))
183                                 MAIN_EXTERNALLY_VISIBLE;
184 void lbb_prepare(const char *applet
185                 USE_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 USE_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         USE_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_ALWAYS) {
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_NEVER) {
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 {
591         /* directory table
592          * this should be consistent w/ the enum,
593          * busybox.h::bb_install_loc_t, or else... */
594         static const char usr_bin [] ALIGN1 = "/usr/bin";
595         static const char usr_sbin[] ALIGN1 = "/usr/sbin";
596         static const char *const install_dir[] = {
597                 &usr_bin [8], /* "", equivalent to "/" for concat_path_file() */
598                 &usr_bin [4], /* "/bin" */
599                 &usr_sbin[4], /* "/sbin" */
600                 usr_bin,
601                 usr_sbin
602         };
603
604         int (*lf)(const char *, const char *);
605         char *fpc;
606         unsigned i;
607         int rc;
608
609         lf = link;
610         if (use_symbolic_links)
611                 lf = symlink;
612
613         for (i = 0; i < ARRAY_SIZE(applet_main); i++) {
614                 fpc = concat_path_file(
615                                 install_dir[APPLET_INSTALL_LOC(i)],
616                                 APPLET_NAME(i));
617                 // debug: bb_error_msg("%slinking %s to busybox",
618                 //              use_symbolic_links ? "sym" : "", fpc);
619                 rc = lf(busybox, fpc);
620                 if (rc != 0 && errno != EEXIST) {
621                         bb_simple_perror_msg(fpc);
622                 }
623                 free(fpc);
624         }
625 }
626 #else
627 #define install_links(x,y) ((void)0)
628 #endif /* FEATURE_INSTALLER */
629
630 /* If we were called as "busybox..." */
631 static int busybox_main(char **argv)
632 {
633         if (!argv[1]) {
634                 /* Called without arguments */
635                 const char *a;
636                 unsigned col, output_width;
637  help:
638                 output_width = 80;
639                 if (ENABLE_FEATURE_AUTOWIDTH) {
640                         /* Obtain the terminal width */
641                         get_terminal_width_height(0, &output_width, NULL);
642                 }
643                 /* leading tab and room to wrap */
644                 output_width -= MAX_APPLET_NAME_LEN + 8;
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                 while (*a) {
665                         int len;
666                         if (col > output_width) {
667                                 full_write2_str(",\n");
668                                 col = 0;
669                         }
670                         full_write2_str(col ? ", " : "\t");
671                         full_write2_str(a);
672                         len = strlen(a);
673                         col += len + 2;
674                         a += len + 1;
675                 }
676                 full_write2_str("\n\n");
677                 return 0;
678         }
679
680         if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) {
681                 const char *busybox;
682                 busybox = xmalloc_readlink(bb_busybox_exec_path);
683                 if (!busybox)
684                         busybox = bb_busybox_exec_path;
685                 /* -s makes symlinks */
686                 install_links(busybox, argv[2] && strcmp(argv[2], "-s") == 0);
687                 return 0;
688         }
689
690         if (strcmp(argv[1], "--help") == 0) {
691                 /* "busybox --help [<applet>]" */
692                 if (!argv[2])
693                         goto help;
694                 /* convert to "<applet> --help" */
695                 argv[0] = argv[2];
696                 argv[2] = NULL;
697         } else {
698                 /* "busybox <applet> arg1 arg2 ..." */
699                 argv++;
700         }
701         /* We support "busybox /a/path/to/applet args..." too. Allows for
702          * "#!/bin/busybox"-style wrappers */
703         applet_name = bb_get_last_path_component_nostrip(argv[0]);
704         run_applet_and_exit(applet_name, argv);
705
706         /*bb_error_msg_and_die("applet not found"); - sucks in printf */
707         full_write2_str(applet_name);
708         full_write2_str(": applet not found\n");
709         xfunc_die();
710 }
711
712 void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv)
713 {
714         int argc = 1;
715
716         while (argv[argc])
717                 argc++;
718
719         /* Reinit some shared global data */
720         xfunc_error_retval = EXIT_FAILURE;
721
722         applet_name = APPLET_NAME(applet_no);
723         if (argc == 2 && strcmp(argv[1], "--help") == 0) {
724                 /* Special case. POSIX says "test --help"
725                  * should be no different from e.g. "test --foo".  */
726 //TODO: just compare applet_no with APPLET_NO_test
727                 if (!ENABLE_TEST || strcmp(applet_name, "test") != 0)
728                         bb_show_usage();
729         }
730         if (ENABLE_FEATURE_SUID)
731                 check_suid(applet_no);
732         exit(applet_main[applet_no](argc, argv));
733 }
734
735 void FAST_FUNC run_applet_and_exit(const char *name, char **argv)
736 {
737         int applet = find_applet_by_name(name);
738         if (applet >= 0)
739                 run_applet_no_and_exit(applet, argv);
740         if (!strncmp(name, "busybox", 7))
741                 exit(busybox_main(argv));
742 }
743
744 #endif /* !defined(SINGLE_APPLET_MAIN) */
745
746
747
748 #if ENABLE_BUILD_LIBBUSYBOX
749 int lbb_main(char **argv)
750 #else
751 int main(int argc UNUSED_PARAM, char **argv)
752 #endif
753 {
754 #if defined(SINGLE_APPLET_MAIN)
755         /* Only one applet is selected by the user! */
756         /* applet_names in this case is just "applet\0\0" */
757         lbb_prepare(applet_names USE_FEATURE_INDIVIDUAL(, argv));
758         return SINGLE_APPLET_MAIN(argc, argv);
759 #else
760         lbb_prepare("busybox" USE_FEATURE_INDIVIDUAL(, argv));
761
762 #if !BB_MMU
763         /* NOMMU re-exec trick sets high-order bit in first byte of name */
764         if (argv[0][0] & 0x80) {
765                 re_execed = 1;
766                 argv[0][0] &= 0x7f;
767         }
768 #endif
769         applet_name = argv[0];
770         if (applet_name[0] == '-')
771                 applet_name++;
772         applet_name = bb_basename(applet_name);
773
774         parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */
775
776         run_applet_and_exit(applet_name, argv);
777
778         /*bb_error_msg_and_die("applet not found"); - sucks in printf */
779         full_write2_str(applet_name);
780         full_write2_str(": applet not found\n");
781         xfunc_die();
782 #endif
783 }