1 /* vi: set sw=4 ts=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.
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.
12 * Licensed under GPLv2 or later, see file License in this tarball for details.
15 /* We are trying to not use printf, this benefits the case when selected
16 * applets are really simple. Example:
20 * Currently defined functions:
21 * basename, false, true
24 * text data bss dec hex filename
25 * 4473 52 72 4597 11f5 busybox
27 * FEATURE_INSTALLER or FEATURE_SUID will still link printf routines in. :(
34 /* Declare <applet>_main() */
39 #if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE
40 /* Define usage_messages[] */
41 static const char usage_messages[] ALIGN1 = ""
48 #define usage_messages 0
49 #endif /* SHOW_USAGE */
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__
63 #if ENABLE_FEATURE_COMPRESS_USAGE
65 #include "usage_compressed.h"
66 #include "unarchive.h"
68 static const char *unpack_usage_messages(void)
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 */
82 /* Cannot use xmalloc: will leak bd in NOFORK case! */
83 outbuf = malloc_or_warn(SIZEOF_usage_messages);
85 read_bunzip(bd, outbuf, SIZEOF_usage_messages);
90 #define dealloc_usage_messages(s) free(s)
94 #define unpack_usage_messages() usage_messages
95 #define dealloc_usage_messages(s) ((void)(s))
97 #endif /* FEATURE_COMPRESS_USAGE */
100 static void full_write2_str(const char *str)
102 xwrite_str(STDERR_FILENO, str);
105 void FAST_FUNC bb_show_usage(void)
107 if (ENABLE_SHOW_USAGE) {
108 #ifdef SINGLE_APPLET_STR
109 /* Imagine that this applet is "true". Dont suck in printf! */
111 const char *usage_string = p = unpack_usage_messages();
114 full_write2_str("No help available.\n\n");
116 full_write2_str("Usage: "SINGLE_APPLET_STR" ");
118 full_write2_str("\n\n");
120 dealloc_usage_messages((char*)usage_string);
123 const char *usage_string = p = unpack_usage_messages();
124 int ap = find_applet_by_name(applet_name);
126 if (ap < 0) /* never happens, paranoia */
129 while (*p++) continue;
132 full_write2_str(bb_banner);
133 full_write2_str(" multi-call binary\n");
135 full_write2_str("\nNo help available.\n\n");
137 full_write2_str("\nUsage: ");
138 full_write2_str(applet_name);
139 full_write2_str(" ");
141 full_write2_str("\n\n");
143 dealloc_usage_messages((char*)usage_string);
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)
153 int i = (const char *)v - applet_names;
154 return strcmp(name, APPLET_NAME(i));
157 int FAST_FUNC find_applet_by_name(const char *name)
160 /* Do a binary search to find the applet entry given the name. */
162 p = bsearch(name, applet_names, ARRAY_SIZE(applet_main), 1, applet_name_compare);
165 return p - applet_names;
167 /* A version which does not pull in bsearch */
169 const char *p = applet_names;
170 while (i < NUM_APPLETS) {
171 if (strcmp(name, p) == 0)
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))
188 (*(int **)&bb_errno) = __errno_location();
191 applet_name = applet;
193 /* Set locale for everybody except 'init' */
194 if (ENABLE_LOCALE_SUPPORT && getpid() != 1)
195 setlocale(LC_ALL, "");
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)
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....)
218 const char *applet_name;
224 /* If not built as a single-applet executable... */
225 #if !defined(SINGLE_APPLET_MAIN)
227 USE_FEATURE_SUID(static uid_t ruid;) /* real uid */
229 #if ENABLE_FEATURE_SUID_CONFIG
231 /* applets[] is const, so we have to define this "override" structure */
232 static struct BB_suid_config {
237 struct BB_suid_config *m_next;
240 static bool suid_cfg_readable;
242 /* check if u is member of group g */
243 static int ingroup(uid_t u, gid_t g)
245 struct group *grp = getgrgid(g);
250 for (mem = grp->gr_mem; *mem; mem++) {
251 struct passwd *pwd = getpwnam(*mem);
253 if (pwd && (pwd->pw_uid == u))
260 /* This should probably be a libbb routine. In that case,
261 * I'd probably rename it to something like bb_trimmed_slice.
263 static char *get_trimmed_slice(char *s, char *e)
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. */
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);
280 /* Don't depend on the tools to combine strings. */
281 static const char config_file[] ALIGN1 = "/etc/busybox.conf";
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 */
293 #define parse_error(x) do { errmsg = x; goto pe_label; } while (0)
295 static void parse_config_file(void)
297 struct BB_suid_config *sct_head;
298 struct BB_suid_config *sct;
310 assert(!suid_config); /* Should be set to NULL by bss init. */
313 if (ruid == 0) /* run by root - don't need to even read config file */
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? */
325 suid_cfg_readable = 1;
332 if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */
334 if (ferror(f)) { /* Make sure it wasn't a read error. */
335 parse_error("reading");
338 suid_config = sct_head; /* Success, so set the pointer. */
342 lc++; /* Got a (partial) line. */
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");
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, '#'));
363 /* Check for a section header. */
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. */
370 if (!e /* Missing right bracket? */
371 || e[1] /* Trailing characters? */
372 || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */
374 parse_error("section header");
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) {
386 section = -1; /* Unknown section so set to skip. */
390 /* Process sections. */
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. */
398 /* First get the key (an applet name in our case). */
401 s = get_trimmed_slice(s, e);
403 if (!e || !*s) { /* Missing '=' or empty key. */
404 parse_error("keyword");
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;
420 sct->m_next = sct_head;
423 /* Get the specified mode. */
425 e = skip_whitespace(e+1);
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-";
432 q = strchrnul(mode_chars + 5*i, *e++);
436 /* Adjust by -i to account for nul. */
437 sct->m_mode |= mode_mask[(q - mode_chars) - i];
440 /* Now get the the user/group info. */
442 s = skip_whitespace(e);
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>");
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);
455 struct passwd *pwd = getpwnam(s);
459 sct->m_uid = pwd->pw_uid;
462 sct->m_gid = bb_strtoul(e, NULL, 10);
467 parse_error("group");
469 sct->m_gid = grp->gr_gid;
475 /* Unknown sections are ignored. */
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. */
483 parse_error("keyword outside section");
489 fprintf(stderr, "Parse error in %s, line %d: %s\n",
490 config_file, lc, errmsg);
493 /* Release any allocated memory before returning. */
495 sct = sct_head->m_next;
501 static inline void parse_config_file(void)
503 USE_FEATURE_SUID(ruid = getuid();)
505 #endif /* FEATURE_SUID_CONFIG */
508 #if ENABLE_FEATURE_SUID
509 static void check_suid(int applet_no)
511 gid_t rgid; /* real gid */
513 if (ruid == 0) /* set by parse_config_file() */
514 return; /* run by root - no need to check more */
517 #if ENABLE_FEATURE_SUID_CONFIG
518 if (suid_cfg_readable) {
520 struct BB_suid_config *sct;
523 for (sct = suid_config; sct; sct = sct->m_next) {
524 if (sct->m_applet == applet_no)
527 goto check_need_suid;
530 if (sct->m_uid == ruid)
533 else if ((sct->m_gid == rgid) || ingroup(ruid, sct->m_gid))
534 /* same group / in group */
537 if (!(m & S_IXOTH)) /* is x bit not set ? */
538 bb_error_msg_and_die("you have no permission to run this applet!");
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))
543 /* else (no setegid) we will set egid = rgid */
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");
550 /* do we have to set effective uid? */
552 if (sct->m_mode & S_ISUID)
554 /* else (no seteuid) we will set euid = ruid */
556 if (setresuid(-1, uid, uid))
557 bb_perror_msg_and_die("setresuid");
560 #if !ENABLE_FEATURE_SUID_CONFIG_QUIET
562 static bool onetime = 0;
566 fprintf(stderr, "Using fallback suid method\n");
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 */
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 */
583 #define check_suid(x) ((void)0)
584 #endif /* FEATURE_SUID */
587 #if ENABLE_FEATURE_INSTALLER
588 /* create (sym)links for each applet */
589 static void install_links(const char *busybox, int use_symbolic_links)
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" */
604 int (*lf)(const char *, const char *);
610 if (use_symbolic_links)
613 for (i = 0; i < ARRAY_SIZE(applet_main); i++) {
614 fpc = concat_path_file(
615 install_dir[APPLET_INSTALL_LOC(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);
627 #define install_links(x,y) ((void)0)
628 #endif /* FEATURE_INSTALLER */
630 /* If we were called as "busybox..." */
631 static int busybox_main(char **argv)
634 /* Called without arguments */
636 unsigned col, output_width;
639 if (ENABLE_FEATURE_AUTOWIDTH) {
640 /* Obtain the terminal width */
641 get_terminal_width_height(0, &output_width, NULL);
643 /* leading tab and room to wrap */
644 output_width -= MAX_APPLET_NAME_LEN + 8;
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"
653 "Usage: busybox [function] [arguments]...\n"
654 " or: function [arguments]...\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"
661 "Currently defined functions:\n");
666 if (col > output_width) {
667 full_write2_str(",\n");
670 full_write2_str(col ? ", " : "\t");
676 full_write2_str("\n\n");
680 if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) {
682 busybox = xmalloc_readlink(bb_busybox_exec_path);
684 busybox = bb_busybox_exec_path;
685 /* -s makes symlinks */
686 install_links(busybox, argv[2] && strcmp(argv[2], "-s") == 0);
690 if (strcmp(argv[1], "--help") == 0) {
691 /* "busybox --help [<applet>]" */
694 /* convert to "<applet> --help" */
698 /* "busybox <applet> arg1 arg2 ..." */
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);
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");
712 void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv)
719 /* Reinit some shared global data */
720 xfunc_error_retval = EXIT_FAILURE;
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)
730 if (ENABLE_FEATURE_SUID)
731 check_suid(applet_no);
732 exit(applet_main[applet_no](argc, argv));
735 void FAST_FUNC run_applet_and_exit(const char *name, char **argv)
737 int applet = find_applet_by_name(name);
739 run_applet_no_and_exit(applet, argv);
740 if (!strncmp(name, "busybox", 7))
741 exit(busybox_main(argv));
744 #endif /* !defined(SINGLE_APPLET_MAIN) */
748 #if ENABLE_BUILD_LIBBUSYBOX
749 int lbb_main(char **argv)
751 int main(int argc UNUSED_PARAM, char **argv)
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);
760 lbb_prepare("busybox" USE_FEATURE_INDIVIDUAL(, argv));
763 /* NOMMU re-exec trick sets high-order bit in first byte of name */
764 if (argv[0][0] & 0x80) {
769 applet_name = argv[0];
770 if (applet_name[0] == '-')
772 applet_name = bb_basename(applet_name);
774 parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */
776 run_applet_and_exit(applet_name, argv);
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");