Magellan Linux

Annotation of /trunk/mkinitrd-magellan/busybox/libbb/appletlib.c

Parent Directory Parent Directory | Revision Log Revision Log


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