whitespace and comment format fixes, 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 /* We are trying to not use printf, this benefits the case when selected
15  * applets are really simple. Example:
16  *
17  * $ ./busybox
18  * ...
19  * Currently defined functions:
20  *         basename, false, true
21  *
22  * $ size busybox
23  *    text    data     bss     dec     hex filename
24  *    4473      52      72    4597    11f5 busybox
25  *
26  * FEATURE_INSTALLER or FEATURE_SUID will still link printf routines in. :(
27  */
28 #include "busybox.h"
29
30 #if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \
31     || defined(__APPLE__) \
32     )
33 # include <malloc.h> /* for mallopt */
34 #endif
35
36 /* Declare <applet>_main() */
37 #define PROTOTYPES
38 #include "applets.h"
39 #undef PROTOTYPES
40
41 /* Include generated applet names, pointers to <applet>_main, etc */
42 #include "applet_tables.h"
43 /* ...and if applet_tables generator says we have only one applet... */
44 #ifdef SINGLE_APPLET_MAIN
45 # undef ENABLE_FEATURE_INDIVIDUAL
46 # define ENABLE_FEATURE_INDIVIDUAL 1
47 # undef IF_FEATURE_INDIVIDUAL
48 # define IF_FEATURE_INDIVIDUAL(...) __VA_ARGS__
49 #endif
50
51 #include "usage_compressed.h"
52
53
54 /* "Do not compress usage text if uncompressed text is small
55  *  and we don't include bunzip2 code for other reasons"
56  *
57  * Useful for mass one-applet rebuild (bunzip2 code is ~2.7k).
58  *
59  * Unlike BUNZIP2, if FEATURE_SEAMLESS_BZ2 is on, bunzip2 code is built but
60  * still may be unused if none of the selected applets calls open_zipped()
61  * or its friends; we test for (FEATURE_SEAMLESS_BZ2 && <APPLET>) instead.
62  * For example, only if TAR and FEATURE_SEAMLESS_BZ2 are both selected,
63  * then bunzip2 code will be linked in anyway, and disabling help compression
64  * would be not optimal:
65  */
66 #if UNPACKED_USAGE_LENGTH < 4*1024 \
67  && !(ENABLE_FEATURE_SEAMLESS_BZ2 && ENABLE_TAR) \
68  && !(ENABLE_FEATURE_SEAMLESS_BZ2 && ENABLE_MODPROBE) \
69  && !(ENABLE_FEATURE_SEAMLESS_BZ2 && ENABLE_INSMOD) \
70  && !(ENABLE_FEATURE_SEAMLESS_BZ2 && ENABLE_DEPMOD) \
71  && !(ENABLE_FEATURE_SEAMLESS_BZ2 && ENABLE_MAN) \
72  && !ENABLE_BUNZIP2 \
73  && !ENABLE_BZCAT
74 # undef  ENABLE_FEATURE_COMPRESS_USAGE
75 # define ENABLE_FEATURE_COMPRESS_USAGE 0
76 #endif
77
78
79 unsigned FAST_FUNC string_array_len(char **argv)
80 {
81         char **start = argv;
82
83         while (*argv)
84                 argv++;
85
86         return argv - start;
87 }
88
89
90 #if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE
91 static const char usage_messages[] ALIGN1 = UNPACKED_USAGE;
92 #else
93 # define usage_messages 0
94 #endif
95
96 #if ENABLE_FEATURE_COMPRESS_USAGE
97
98 static const char packed_usage[] ALIGN1 = { PACKED_USAGE };
99 # include "bb_archive.h"
100 static const char *unpack_usage_messages(void)
101 {
102         char *outbuf = NULL;
103         bunzip_data *bd;
104         int i;
105
106         i = start_bunzip(&bd,
107                         /* src_fd: */ -1,
108                         /* inbuf:  */ packed_usage,
109                         /* len:    */ sizeof(packed_usage));
110         /* read_bunzip can longjmp to start_bunzip, and ultimately
111          * end up here with i != 0 on read data errors! Not trivial */
112         if (!i) {
113                 /* Cannot use xmalloc: will leak bd in NOFORK case! */
114                 outbuf = malloc_or_warn(sizeof(UNPACKED_USAGE));
115                 if (outbuf)
116                         read_bunzip(bd, outbuf, sizeof(UNPACKED_USAGE));
117         }
118         dealloc_bunzip(bd);
119         return outbuf;
120 }
121 # define dealloc_usage_messages(s) free(s)
122
123 #else
124
125 # define unpack_usage_messages() usage_messages
126 # define dealloc_usage_messages(s) ((void)(s))
127
128 #endif /* FEATURE_COMPRESS_USAGE */
129
130
131 void FAST_FUNC bb_show_usage(void)
132 {
133         if (ENABLE_SHOW_USAGE) {
134 #ifdef SINGLE_APPLET_STR
135                 /* Imagine that this applet is "true". Dont suck in printf! */
136                 const char *usage_string = unpack_usage_messages();
137
138                 if (*usage_string == '\b') {
139                         full_write2_str("No help available.\n\n");
140                 } else {
141                         full_write2_str("Usage: "SINGLE_APPLET_STR" ");
142                         full_write2_str(usage_string);
143                         full_write2_str("\n\n");
144                 }
145                 if (ENABLE_FEATURE_CLEAN_UP)
146                         dealloc_usage_messages((char*)usage_string);
147 #else
148                 const char *p;
149                 const char *usage_string = p = unpack_usage_messages();
150                 int ap = find_applet_by_name(applet_name);
151
152                 if (ap < 0) /* never happens, paranoia */
153                         xfunc_die();
154                 while (ap) {
155                         while (*p++) continue;
156                         ap--;
157                 }
158                 full_write2_str(bb_banner);
159                 full_write2_str(" multi-call binary.\n");
160                 if (*p == '\b')
161                         full_write2_str("\nNo help available.\n\n");
162                 else {
163                         full_write2_str("\nUsage: ");
164                         full_write2_str(applet_name);
165                         full_write2_str(" ");
166                         full_write2_str(p);
167                         full_write2_str("\n");
168                 }
169                 if (ENABLE_FEATURE_CLEAN_UP)
170                         dealloc_usage_messages((char*)usage_string);
171 #endif
172         }
173         xfunc_die();
174 }
175
176 int FAST_FUNC find_applet_by_name(const char *name)
177 {
178         unsigned i, max;
179         int j;
180         const char *p;
181
182 /* The commented-out word-at-a-time code is ~40% faster, but +160 bytes.
183  * "Faster" here saves ~0.5 microsecond of real time - not worth it.
184  */
185 #if 0 /*BB_UNALIGNED_MEMACCESS_OK && BB_LITTLE_ENDIAN*/
186         uint32_t n32;
187
188         /* Handle all names < 2 chars long early */
189         if (name[0] == '\0')
190                 return -1; /* "" is not a valid applet name */
191         if (name[1] == '\0') {
192                 if (!ENABLE_TEST)
193                         return -1; /* 1-char name is not valid */
194                 if (name[0] != ']')
195                         return -1; /* 1-char name which isn't "[" is not valid */
196                 /* applet "[" is always applet #0: */
197                 return 0;
198         }
199 #endif
200
201         p = applet_names;
202         i = 0;
203 #if KNOWN_APPNAME_OFFSETS <= 0
204         max = NUM_APPLETS;
205 #else
206         max = NUM_APPLETS * KNOWN_APPNAME_OFFSETS;
207         for (j = ARRAY_SIZE(applet_nameofs)-1; j >= 0; j--) {
208                 const char *pp = applet_names + applet_nameofs[j];
209                 if (strcmp(name, pp) >= 0) {
210                         //bb_error_msg("name:'%s' >= pp:'%s'", name, pp);
211                         p = pp;
212                         i = max - NUM_APPLETS;
213                         break;
214                 }
215                 max -= NUM_APPLETS;
216         }
217         max /= (unsigned)KNOWN_APPNAME_OFFSETS;
218         i /= (unsigned)KNOWN_APPNAME_OFFSETS;
219         //bb_error_msg("name:'%s' starting from:'%s' i:%u max:%u", name, p, i, max);
220 #endif
221
222         /* Open-coded linear search without strcmp/strlen calls for speed */
223
224 #if 0 /*BB_UNALIGNED_MEMACCESS_OK && BB_LITTLE_ENDIAN*/
225         /* skip "[\0" name, it's surely not it */
226         if (ENABLE_TEST && LONE_CHAR(p, '['))
227                 i++, p += 2;
228         /* All remaining applet names in p[] are at least 2 chars long */
229         /* name[] is also at least 2 chars long */
230
231         n32 = (name[0] << 0) | (name[1] << 8) | (name[2] << 16);
232         while (i < max) {
233                 uint32_t p32;
234                 char ch;
235
236                 /* Quickly check match of the first 3 bytes */
237                 move_from_unaligned32(p32, p);
238                 p += 3;
239                 if ((p32 & 0x00ffffff) != n32) {
240                         /* Most likely case: 3 first bytes do not match */
241                         i++;
242                         if ((p32 & 0x00ff0000) == '\0')
243                                 continue; // p[2] was NUL
244                         p++;
245                         if ((p32 & 0xff000000) == '\0')
246                                 continue; // p[3] was NUL
247                         /* p[0..3] aren't matching and none is NUL, check the rest */
248                         while (*p++ != '\0')
249                                 continue;
250                         continue;
251                 }
252
253                 /* Unlikely branch: first 3 bytes ([0..2]) match */
254                 if ((p32 & 0x00ff0000) == '\0') {
255                         /* name is 2-byte long, it is full match */
256                         //bb_error_msg("found:'%s' i:%u", name, i);
257                         return i;
258                 }
259                 /* Check remaining bytes [3..NUL] */
260                 ch = (p32 >> 24);
261                 j = 3;
262                 while (ch == name[j]) {
263                         if (ch == '\0') {
264                                 //bb_error_msg("found:'%s' i:%u", name, i);
265                                 return i;
266                         }
267                         ch = *++p;
268                         j++;
269                 }
270                 /* Not a match. Skip it, including NUL */
271                 while (ch != '\0')
272                         ch = *++p;
273                 p++;
274                 i++;
275         }
276         return -1;
277 #else
278         while (i < max) {
279                 char ch;
280                 j = 0;
281                 /* Do we see "name\0" in applet_names[p] position? */
282                 while ((ch = *p) == name[j]) {
283                         if (ch == '\0') {
284                                 //bb_error_msg("found:'%s' i:%u", name, i);
285                                 return i; /* yes */
286                         }
287                         p++;
288                         j++;
289                 }
290                 /* No.
291                  * p => 1st non-matching char in applet_names[],
292                  * skip to and including NUL.
293                  */
294                 while (ch != '\0')
295                         ch = *++p;
296                 p++;
297                 i++;
298         }
299         return -1;
300 #endif
301 }
302
303
304 void lbb_prepare(const char *applet
305                 IF_FEATURE_INDIVIDUAL(, char **argv))
306                                 MAIN_EXTERNALLY_VISIBLE;
307 void lbb_prepare(const char *applet
308                 IF_FEATURE_INDIVIDUAL(, char **argv))
309 {
310 #ifdef __GLIBC__
311         (*(int **)&bb_errno) = __errno_location();
312         barrier();
313 #endif
314         applet_name = applet;
315
316         if (ENABLE_LOCALE_SUPPORT)
317                 setlocale(LC_ALL, "");
318
319 #if ENABLE_FEATURE_INDIVIDUAL
320         /* Redundant for busybox (run_applet_and_exit covers that case)
321          * but needed for "individual applet" mode */
322         if (argv[1]
323          && !argv[2]
324          && strcmp(argv[1], "--help") == 0
325          && !is_prefixed_with(applet, "busybox")
326         ) {
327                 /* Special case. POSIX says "test --help"
328                  * should be no different from e.g. "test --foo".  */
329                 if (!ENABLE_TEST || strcmp(applet_name, "test") != 0)
330                         bb_show_usage();
331         }
332 #endif
333 }
334
335 /* The code below can well be in applets/applets.c, as it is used only
336  * for busybox binary, not "individual" binaries.
337  * However, keeping it here and linking it into libbusybox.so
338  * (together with remaining tiny applets/applets.o)
339  * makes it possible to avoid --whole-archive at link time.
340  * This makes (shared busybox) + libbusybox smaller.
341  * (--gc-sections would be even better....)
342  */
343
344 const char *applet_name;
345 #if !BB_MMU
346 bool re_execed;
347 #endif
348
349
350 /* If not built as a single-applet executable... */
351 #if !defined(SINGLE_APPLET_MAIN)
352
353 IF_FEATURE_SUID(static uid_t ruid;)  /* real uid */
354
355 # if ENABLE_FEATURE_SUID_CONFIG
356
357 static struct suid_config_t {
358         /* next ptr must be first: this struct needs to be llist-compatible */
359         struct suid_config_t *m_next;
360         struct bb_uidgid_t m_ugid;
361         int m_applet;
362         mode_t m_mode;
363 } *suid_config;
364
365 static bool suid_cfg_readable;
366
367 /* libbb candidate */
368 static char *get_trimmed_slice(char *s, char *e)
369 {
370         /* First, consider the value at e to be nul and back up until we
371          * reach a non-space char.  Set the char after that (possibly at
372          * the original e) to nul. */
373         while (e-- > s) {
374                 if (!isspace(*e)) {
375                         break;
376                 }
377         }
378         e[1] = '\0';
379
380         /* Next, advance past all leading space and return a ptr to the
381          * first non-space char; possibly the terminating nul. */
382         return skip_whitespace(s);
383 }
384
385 static void parse_config_file(void)
386 {
387         /* Don't depend on the tools to combine strings. */
388         static const char config_file[] ALIGN1 = "/etc/busybox.conf";
389
390         struct suid_config_t *sct_head;
391         int applet_no;
392         FILE *f;
393         const char *errmsg;
394         unsigned lc;
395         smallint section;
396         struct stat st;
397
398         ruid = getuid();
399         if (ruid == 0) /* run by root - don't need to even read config file */
400                 return;
401
402         if ((stat(config_file, &st) != 0)       /* No config file? */
403          || !S_ISREG(st.st_mode)                /* Not a regular file? */
404          || (st.st_uid != 0)                    /* Not owned by root? */
405          || (st.st_mode & (S_IWGRP | S_IWOTH))  /* Writable by non-root? */
406          || !(f = fopen_for_read(config_file))  /* Cannot open? */
407         ) {
408                 return;
409         }
410
411         suid_cfg_readable = 1;
412         sct_head = NULL;
413         section = lc = 0;
414
415         while (1) {
416                 char buffer[256];
417                 char *s;
418
419                 if (!fgets(buffer, sizeof(buffer), f)) { /* Are we done? */
420                         // Looks like bloat
421                         //if (ferror(f)) {   /* Make sure it wasn't a read error. */
422                         //      errmsg = "reading";
423                         //      goto pe_label;
424                         //}
425                         fclose(f);
426                         suid_config = sct_head; /* Success, so set the pointer. */
427                         return;
428                 }
429
430                 s = buffer;
431                 lc++;                                   /* Got a (partial) line. */
432
433                 /* If a line is too long for our buffer, we consider it an error.
434                  * The following test does mistreat one corner case though.
435                  * If the final line of the file does not end with a newline and
436                  * yet exactly fills the buffer, it will be treated as too long
437                  * even though there isn't really a problem.  But it isn't really
438                  * worth adding code to deal with such an unlikely situation, and
439                  * we do err on the side of caution.  Besides, the line would be
440                  * too long if it did end with a newline. */
441                 if (!strchr(s, '\n') && !feof(f)) {
442                         errmsg = "line too long";
443                         goto pe_label;
444                 }
445
446                 /* Trim leading and trailing whitespace, ignoring comments, and
447                  * check if the resulting string is empty. */
448                 s = get_trimmed_slice(s, strchrnul(s, '#'));
449                 if (!*s) {
450                         continue;
451                 }
452
453                 /* Check for a section header. */
454
455                 if (*s == '[') {
456                         /* Unlike the old code, we ignore leading and trailing
457                          * whitespace for the section name.  We also require that
458                          * there are no stray characters after the closing bracket. */
459                         char *e = strchr(s, ']');
460                         if (!e   /* Missing right bracket? */
461                          || e[1] /* Trailing characters? */
462                          || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */
463                         ) {
464                                 errmsg = "section header";
465                                 goto pe_label;
466                         }
467                         /* Right now we only have one section so just check it.
468                          * If more sections are added in the future, please don't
469                          * resort to cascading ifs with multiple strcasecmp calls.
470                          * That kind of bloated code is all too common.  A loop
471                          * and a string table would be a better choice unless the
472                          * number of sections is very small. */
473                         if (strcasecmp(s, "SUID") == 0) {
474                                 section = 1;
475                                 continue;
476                         }
477                         section = -1;   /* Unknown section so set to skip. */
478                         continue;
479                 }
480
481                 /* Process sections. */
482
483                 if (section == 1) {             /* SUID */
484                         /* Since we trimmed leading and trailing space above, we're
485                          * now looking for strings of the form
486                          *    <key>[::space::]*=[::space::]*<value>
487                          * where both key and value could contain inner whitespace. */
488
489                         /* First get the key (an applet name in our case). */
490                         char *e = strchr(s, '=');
491                         if (e) {
492                                 s = get_trimmed_slice(s, e);
493                         }
494                         if (!e || !*s) {        /* Missing '=' or empty key. */
495                                 errmsg = "keyword";
496                                 goto pe_label;
497                         }
498
499                         /* Ok, we have an applet name.  Process the rhs if this
500                          * applet is currently built in and ignore it otherwise.
501                          * Note: this can hide config file bugs which only pop
502                          * up when the busybox configuration is changed. */
503                         applet_no = find_applet_by_name(s);
504                         if (applet_no >= 0) {
505                                 unsigned i;
506                                 struct suid_config_t *sct;
507
508                                 /* Note: We currently don't check for duplicates!
509                                  * The last config line for each applet will be the
510                                  * one used since we insert at the head of the list.
511                                  * I suppose this could be considered a feature. */
512                                 sct = xzalloc(sizeof(*sct));
513                                 sct->m_applet = applet_no;
514                                 /*sct->m_mode = 0;*/
515                                 sct->m_next = sct_head;
516                                 sct_head = sct;
517
518                                 /* Get the specified mode. */
519
520                                 e = skip_whitespace(e+1);
521
522                                 for (i = 0; i < 3; i++) {
523                                         /* There are 4 chars for each of user/group/other.
524                                          * "x-xx" instead of "x-" are to make
525                                          * "idx > 3" check catch invalid chars.
526                                          */
527                                         static const char mode_chars[] ALIGN1 = "Ssx-" "Ssx-" "x-xx";
528                                         static const unsigned short mode_mask[] ALIGN2 = {
529                                                 S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* Ssx- */
530                                                 S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* Ssx- */
531                                                                           S_IXOTH, 0  /*   x- */
532                                         };
533                                         const char *q = strchrnul(mode_chars + 4*i, *e);
534                                         unsigned idx = q - (mode_chars + 4*i);
535                                         if (idx > 3) {
536                                                 errmsg = "mode";
537                                                 goto pe_label;
538                                         }
539                                         sct->m_mode |= mode_mask[q - mode_chars];
540                                         e++;
541                                 }
542
543                                 /* Now get the user/group info. */
544
545                                 s = skip_whitespace(e);
546                                 /* Default is 0.0, else parse USER.GROUP: */
547                                 if (*s) {
548                                         /* We require whitespace between mode and USER.GROUP */
549                                         if ((s == e) || !(e = strchr(s, '.'))) {
550                                                 errmsg = "uid.gid";
551                                                 goto pe_label;
552                                         }
553                                         *e = ':'; /* get_uidgid needs USER:GROUP syntax */
554                                         if (get_uidgid(&sct->m_ugid, s) == 0) {
555                                                 errmsg = "unknown user/group";
556                                                 goto pe_label;
557                                         }
558                                 }
559                         }
560                         continue;
561                 }
562
563                 /* Unknown sections are ignored. */
564
565                 /* Encountering configuration lines prior to seeing a
566                  * section header is treated as an error.  This is how
567                  * the old code worked, but it may not be desirable.
568                  * We may want to simply ignore such lines in case they
569                  * are used in some future version of busybox. */
570                 if (!section) {
571                         errmsg = "keyword outside section";
572                         goto pe_label;
573                 }
574         } /* while (1) */
575
576  pe_label:
577         fclose(f);
578         bb_error_msg("parse error in %s, line %u: %s", config_file, lc, errmsg);
579
580         /* Release any allocated memory before returning. */
581         llist_free((llist_t*)sct_head, NULL);
582 }
583 # else
584 static inline void parse_config_file(void)
585 {
586         IF_FEATURE_SUID(ruid = getuid();)
587 }
588 # endif /* FEATURE_SUID_CONFIG */
589
590
591 # if ENABLE_FEATURE_SUID && NUM_APPLETS > 0
592 #  if ENABLE_FEATURE_SUID_CONFIG
593 /* check if u is member of group g */
594 static int ingroup(uid_t u, gid_t g)
595 {
596         struct group *grp = getgrgid(g);
597         if (grp) {
598                 char **mem;
599                 for (mem = grp->gr_mem; *mem; mem++) {
600                         struct passwd *pwd = getpwnam(*mem);
601                         if (pwd && (pwd->pw_uid == u))
602                                 return 1;
603                 }
604         }
605         return 0;
606 }
607 #  endif
608
609 static void check_suid(int applet_no)
610 {
611         gid_t rgid;  /* real gid */
612
613         if (ruid == 0) /* set by parse_config_file() */
614                 return; /* run by root - no need to check more */
615         rgid = getgid();
616
617 #  if ENABLE_FEATURE_SUID_CONFIG
618         if (suid_cfg_readable) {
619                 uid_t uid;
620                 struct suid_config_t *sct;
621                 mode_t m;
622
623                 for (sct = suid_config; sct; sct = sct->m_next) {
624                         if (sct->m_applet == applet_no)
625                                 goto found;
626                 }
627                 goto check_need_suid;
628  found:
629                 /* Is this user allowed to run this applet? */
630                 m = sct->m_mode;
631                 if (sct->m_ugid.uid == ruid)
632                         /* same uid */
633                         m >>= 6;
634                 else if ((sct->m_ugid.gid == rgid) || ingroup(ruid, sct->m_ugid.gid))
635                         /* same group / in group */
636                         m >>= 3;
637                 if (!(m & S_IXOTH)) /* is x bit not set? */
638                         bb_error_msg_and_die("you have no permission to run this applet");
639
640                 /* We set effective AND saved ids. If saved-id is not set
641                  * like we do below, seteuid(0) can still later succeed! */
642
643                 /* Are we directed to change gid
644                  * (APPLET = *s* USER.GROUP or APPLET = *S* USER.GROUP)?
645                  */
646                 if (sct->m_mode & S_ISGID)
647                         rgid = sct->m_ugid.gid;
648                 /* else: we will set egid = rgid, thus dropping sgid effect */
649                 if (setresgid(-1, rgid, rgid))
650                         bb_perror_msg_and_die("setresgid");
651
652                 /* Are we directed to change uid
653                  * (APPLET = s** USER.GROUP or APPLET = S** USER.GROUP)?
654                  */
655                 uid = ruid;
656                 if (sct->m_mode & S_ISUID)
657                         uid = sct->m_ugid.uid;
658                 /* else: we will set euid = ruid, thus dropping suid effect */
659                 if (setresuid(-1, uid, uid))
660                         bb_perror_msg_and_die("setresuid");
661
662                 goto ret;
663         }
664 #   if !ENABLE_FEATURE_SUID_CONFIG_QUIET
665         {
666                 static bool onetime = 0;
667
668                 if (!onetime) {
669                         onetime = 1;
670                         bb_error_msg("using fallback suid method");
671                 }
672         }
673 #   endif
674  check_need_suid:
675 #  endif
676         if (APPLET_SUID(applet_no) == BB_SUID_REQUIRE) {
677                 /* Real uid is not 0. If euid isn't 0 too, suid bit
678                  * is most probably not set on our executable */
679                 if (geteuid())
680                         bb_error_msg_and_die("must be suid to work properly");
681         } else if (APPLET_SUID(applet_no) == BB_SUID_DROP) {
682                 /*
683                  * Drop all privileges.
684                  *
685                  * Don't check for errors: in normal use, they are impossible,
686                  * and in special cases, exiting is harmful. Example:
687                  * 'unshare --user' when user's shell is also from busybox.
688                  *
689                  * 'unshare --user' creates a new user namespace without any
690                  * uid mappings. Thus, busybox binary is setuid nobody:nogroup
691                  * within the namespace, as that is the only user. However,
692                  * since no uids are mapped, calls to setgid/setuid
693                  * fail (even though they would do nothing).
694                  */
695                 setgid(rgid);
696                 setuid(ruid);
697         }
698 #  if ENABLE_FEATURE_SUID_CONFIG
699  ret: ;
700         llist_free((llist_t*)suid_config, NULL);
701 #  endif
702 }
703 # else
704 #  define check_suid(x) ((void)0)
705 # endif /* FEATURE_SUID */
706
707
708 # if ENABLE_FEATURE_INSTALLER
709 static const char usr_bin [] ALIGN1 = "/usr/bin/";
710 static const char usr_sbin[] ALIGN1 = "/usr/sbin/";
711 static const char *const install_dir[] = {
712         &usr_bin [8], /* "/" */
713         &usr_bin [4], /* "/bin/" */
714         &usr_sbin[4]  /* "/sbin/" */
715 #  if !ENABLE_INSTALL_NO_USR
716         ,usr_bin
717         ,usr_sbin
718 #  endif
719 };
720
721 /* create (sym)links for each applet */
722 static void install_links(const char *busybox, int use_symbolic_links,
723                 char *custom_install_dir)
724 {
725         /* directory table
726          * this should be consistent w/ the enum,
727          * busybox.h::bb_install_loc_t, or else... */
728         int (*lf)(const char *, const char *);
729         char *fpc;
730         const char *appname = applet_names;
731         unsigned i;
732         int rc;
733
734         lf = link;
735         if (use_symbolic_links)
736                 lf = symlink;
737
738         for (i = 0; i < ARRAY_SIZE(applet_main); i++) {
739                 fpc = concat_path_file(
740                                 custom_install_dir ? custom_install_dir : install_dir[APPLET_INSTALL_LOC(i)],
741                                 appname);
742                 // debug: bb_error_msg("%slinking %s to busybox",
743                 //              use_symbolic_links ? "sym" : "", fpc);
744                 rc = lf(busybox, fpc);
745                 if (rc != 0 && errno != EEXIST) {
746                         bb_simple_perror_msg(fpc);
747                 }
748                 free(fpc);
749                 while (*appname++ != '\0')
750                         continue;
751         }
752 }
753 # elif ENABLE_BUSYBOX
754 static void install_links(const char *busybox UNUSED_PARAM,
755                 int use_symbolic_links UNUSED_PARAM,
756                 char *custom_install_dir UNUSED_PARAM)
757 {
758 }
759 # endif
760
761 static void run_applet_and_exit(const char *name, char **argv) NORETURN;
762
763 # if ENABLE_BUSYBOX
764 #  if ENABLE_FEATURE_SH_STANDALONE && ENABLE_FEATURE_TAB_COMPLETION
765     /*
766      * Insert "busybox" into applet table as well.
767      * This makes standalone shell tab-complete this name too.
768      * (Otherwise having "busybox" in applet table is not necessary,
769      * there is other code which routes "busyboxANY_SUFFIX" name
770      * to busybox_main()).
771      */
772 //usage:#define busybox_trivial_usage NOUSAGE_STR
773 //usage:#define busybox_full_usage ""
774 //applet:IF_BUSYBOX(IF_FEATURE_SH_STANDALONE(IF_FEATURE_TAB_COMPLETION(APPLET(busybox, BB_DIR_BIN, BB_SUID_MAYBE))))
775 int busybox_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE;
776 #  else
777 #   define busybox_main(argc,argv) busybox_main(argv)
778 static
779 #  endif
780 int busybox_main(int argc UNUSED_PARAM, char **argv)
781 {
782         if (!argv[1]) {
783                 /* Called without arguments */
784                 const char *a;
785                 int col;
786                 unsigned output_width;
787  help:
788                 output_width = get_terminal_width(2);
789
790                 dup2(1, 2);
791                 full_write2_str(bb_banner); /* reuse const string */
792                 full_write2_str(" multi-call binary.\n"); /* reuse */
793                 full_write2_str(
794                         "BusyBox is copyrighted by many authors between 1998-2015.\n"
795                         "Licensed under GPLv2. See source distribution for detailed\n"
796                         "copyright notices.\n"
797                         "\n"
798                         "Usage: busybox [function [arguments]...]\n"
799                         "   or: busybox --list"IF_FEATURE_INSTALLER("[-full]")"\n"
800                         IF_FEATURE_INSTALLER(
801                         "   or: busybox --install [-s] [DIR]\n"
802                         )
803                         "   or: function [arguments]...\n"
804                         "\n"
805                         IF_NOT_FEATURE_SH_STANDALONE(
806                         "\tBusyBox is a multi-call binary that combines many common Unix\n"
807                         "\tutilities into a single executable.  Most people will create a\n"
808                         "\tlink to busybox for each function they wish to use and BusyBox\n"
809                         "\twill act like whatever it was invoked as.\n"
810                         )
811                         IF_FEATURE_SH_STANDALONE(
812                         "\tBusyBox is a multi-call binary that combines many common Unix\n"
813                         "\tutilities into a single executable.  The shell in this build\n"
814                         "\tis configured to run built-in utilities without $PATH search.\n"
815                         "\tYou don't need to install a link to busybox for each utility.\n"
816                         "\tTo run external program, use full path (/sbin/ip instead of ip).\n"
817                         )
818                         "\n"
819                         "Currently defined functions:\n"
820                 );
821                 col = 0;
822                 a = applet_names;
823                 /* prevent last comma to be in the very last pos */
824                 output_width--;
825                 while (*a) {
826                         int len2 = strlen(a) + 2;
827                         if (col >= (int)output_width - len2) {
828                                 full_write2_str(",\n");
829                                 col = 0;
830                         }
831                         if (col == 0) {
832                                 col = 6;
833                                 full_write2_str("\t");
834                         } else {
835                                 full_write2_str(", ");
836                         }
837                         full_write2_str(a);
838                         col += len2;
839                         a += len2 - 1;
840                 }
841                 full_write2_str("\n");
842                 return 0;
843         }
844
845         if (is_prefixed_with(argv[1], "--list")) {
846                 unsigned i = 0;
847                 const char *a = applet_names;
848                 dup2(1, 2);
849                 while (*a) {
850 #  if ENABLE_FEATURE_INSTALLER
851                         if (argv[1][6]) /* --list-full? */
852                                 full_write2_str(install_dir[APPLET_INSTALL_LOC(i)] + 1);
853 #  endif
854                         full_write2_str(a);
855                         full_write2_str("\n");
856                         i++;
857                         while (*a++ != '\0')
858                                 continue;
859                 }
860                 return 0;
861         }
862
863         if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) {
864                 int use_symbolic_links;
865                 const char *busybox;
866
867                 busybox = xmalloc_readlink(bb_busybox_exec_path);
868                 if (!busybox) {
869                         /* bb_busybox_exec_path is usually "/proc/self/exe".
870                          * In chroot, readlink("/proc/self/exe") usually fails.
871                          * In such case, better use argv[0] as symlink target
872                          * if it is a full path name.
873                          */
874                         if (argv[0][0] != '/')
875                                 bb_error_msg_and_die("'%s' is not an absolute path", argv[0]);
876                         busybox = argv[0];
877                 }
878                 /* busybox --install [-s] [DIR]:
879                  * -s: make symlinks
880                  * DIR: directory to install links to
881                  */
882                 use_symbolic_links = (argv[2] && strcmp(argv[2], "-s") == 0 && ++argv);
883                 install_links(busybox, use_symbolic_links, argv[2]);
884                 return 0;
885         }
886
887         if (strcmp(argv[1], "--help") == 0) {
888                 /* "busybox --help [<applet>]" */
889                 if (!argv[2])
890                         goto help;
891                 /* convert to "<applet> --help" */
892                 argv[0] = argv[2];
893                 argv[2] = NULL;
894         } else {
895                 /* "busybox <applet> arg1 arg2 ..." */
896                 argv++;
897         }
898         /* We support "busybox /a/path/to/applet args..." too. Allows for
899          * "#!/bin/busybox"-style wrappers */
900         applet_name = bb_get_last_path_component_nostrip(argv[0]);
901         run_applet_and_exit(applet_name, argv);
902 }
903 # endif
904
905 # if NUM_APPLETS > 0
906 void FAST_FUNC run_applet_no_and_exit(int applet_no, const char *name, char **argv)
907 {
908         int argc = string_array_len(argv);
909
910         /*
911          * We do not use argv[0]: do not want to repeat massaging of
912          * "-/sbin/halt" -> "halt", for example.
913          */
914         applet_name = name;
915
916         /* Special case. POSIX says "test --help"
917          * should be no different from e.g. "test --foo".
918          * Thus for "test", we skip --help check.
919          * "true" and "false" are also special.
920          */
921         if (1
922 #  if defined APPLET_NO_test
923          && applet_no != APPLET_NO_test
924 #  endif
925 #  if defined APPLET_NO_true
926          && applet_no != APPLET_NO_true
927 #  endif
928 #  if defined APPLET_NO_false
929          && applet_no != APPLET_NO_false
930 #  endif
931         ) {
932                 if (argc == 2 && strcmp(argv[1], "--help") == 0) {
933                         /* Make "foo --help" exit with 0: */
934                         xfunc_error_retval = 0;
935                         bb_show_usage();
936                 }
937         }
938         if (ENABLE_FEATURE_SUID)
939                 check_suid(applet_no);
940         xfunc_error_retval = applet_main[applet_no](argc, argv);
941         /* Note: applet_main() may also not return (die on a xfunc or such) */
942         xfunc_die();
943 }
944 # endif /* NUM_APPLETS > 0 */
945
946 # if ENABLE_BUSYBOX || NUM_APPLETS > 0
947 static NORETURN void run_applet_and_exit(const char *name, char **argv)
948 {
949 #  if ENABLE_BUSYBOX
950         if (is_prefixed_with(name, "busybox"))
951                 exit(busybox_main(/*unused:*/ 0, argv));
952 #  endif
953 #  if NUM_APPLETS > 0
954         /* find_applet_by_name() search is more expensive, so goes second */
955         {
956                 int applet = find_applet_by_name(name);
957                 if (applet >= 0)
958                         run_applet_no_and_exit(applet, name, argv);
959         }
960 #  endif
961
962         /*bb_error_msg_and_die("applet not found"); - links in printf */
963         full_write2_str(applet_name);
964         full_write2_str(": applet not found\n");
965         /* POSIX: "If a command is not found, the exit status shall be 127" */
966         exit(127);
967 }
968 # endif
969
970 #endif /* !defined(SINGLE_APPLET_MAIN) */
971
972
973 #if ENABLE_BUILD_LIBBUSYBOX
974 int lbb_main(char **argv)
975 #else
976 int main(int argc UNUSED_PARAM, char **argv)
977 #endif
978 {
979 #if 0
980         /* TODO: find a use for a block of memory between end of .bss
981          * and end of page. For example, I'm getting "_end:0x812e698 2408 bytes"
982          * - more than 2k of wasted memory (in this particular build)
983          * *per each running process*!
984          * (If your linker does not generate "_end" name, weak attribute
985          * makes &_end == NULL, end_len == 0 here.)
986          */
987         extern char _end[] __attribute__((weak));
988         unsigned end_len = (-(int)_end) & 0xfff;
989         printf("_end:%p %u bytes\n", &_end, end_len);
990 #endif
991
992         /* Tweak malloc for reduced memory consumption */
993 #ifdef M_TRIM_THRESHOLD
994         /* M_TRIM_THRESHOLD is the maximum amount of freed top-most memory
995          * to keep before releasing to the OS
996          * Default is way too big: 256k
997          */
998         mallopt(M_TRIM_THRESHOLD, 8 * 1024);
999 #endif
1000 #ifdef M_MMAP_THRESHOLD
1001         /* M_MMAP_THRESHOLD is the request size threshold for using mmap()
1002          * Default is too big: 256k
1003          */
1004         mallopt(M_MMAP_THRESHOLD, 32 * 1024 - 256);
1005 #endif
1006 #if 0 /*def M_TOP_PAD*/
1007         /* When the program break is increased, then M_TOP_PAD bytes are added
1008          * to the sbrk(2) request. When the heap is trimmed because of free(3),
1009          * this much free space is preserved at the top of the heap.
1010          * glibc default seems to be way too big: 128k, but need to verify.
1011          */
1012         mallopt(M_TOP_PAD, 8 * 1024);
1013 #endif
1014
1015 #if !BB_MMU
1016         /* NOMMU re-exec trick sets high-order bit in first byte of name */
1017         if (argv[0][0] & 0x80) {
1018                 re_execed = 1;
1019                 argv[0][0] &= 0x7f;
1020         }
1021 #endif
1022
1023 #if defined(SINGLE_APPLET_MAIN)
1024
1025         /* Only one applet is selected in .config */
1026         if (argv[1] && is_prefixed_with(argv[0], "busybox")) {
1027                 /* "busybox <applet> <params>" should still work as expected */
1028                 argv++;
1029         }
1030         /* applet_names in this case is just "applet\0\0" */
1031         lbb_prepare(applet_names IF_FEATURE_INDIVIDUAL(, argv));
1032 # if ENABLE_BUILD_LIBBUSYBOX
1033         return SINGLE_APPLET_MAIN(string_array_len(argv), argv);
1034 # else
1035         return SINGLE_APPLET_MAIN(argc, argv);
1036 # endif
1037
1038 #elif !ENABLE_BUSYBOX && NUM_APPLETS == 0
1039
1040         full_write2_str(bb_basename(argv[0]));
1041         full_write2_str(": no applets enabled\n");
1042         exit(127);
1043
1044 #else
1045
1046         lbb_prepare("busybox" IF_FEATURE_INDIVIDUAL(, argv));
1047 # if !ENABLE_BUSYBOX
1048         if (argv[1] && is_prefixed_with(bb_basename(argv[0]), "busybox"))
1049                 argv++;
1050 # endif
1051         applet_name = argv[0];
1052         if (applet_name[0] == '-')
1053                 applet_name++;
1054         applet_name = bb_basename(applet_name);
1055
1056         /* If we are a result of execv("/proc/self/exe"), fix ugly comm of "exe" */
1057         if (ENABLE_FEATURE_SH_STANDALONE
1058          || ENABLE_FEATURE_PREFER_APPLETS
1059          || !BB_MMU
1060         ) {
1061                 if (NUM_APPLETS > 1)
1062                         set_task_comm(applet_name);
1063         }
1064
1065         parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */
1066         run_applet_and_exit(applet_name, argv);
1067
1068 #endif
1069 }