blob: e2bb37868f2bfa7c5b3f08577d81b00d3abe7280 [file] [log] [blame]
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +00001/* vi: set sw=4 ts=4: */
2/*
Denis Vlasenko724d1962007-10-10 14:41:07 +00003 * Utility routines.
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +00004 *
Denis Vlasenko724d1962007-10-10 14:41:07 +00005 * 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.
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +00008 *
Denis Vlasenko724d1962007-10-10 14:41:07 +00009 * 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.
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +000013 */
14
15#include <assert.h>
16#include "busybox.h"
17
18
19/* Declare <applet>_main() */
20#define PROTOTYPES
21#include "applets.h"
22#undef PROTOTYPES
23
24#if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE
25/* Define usage_messages[] */
26static const char usage_messages[] ALIGN1 = ""
27#define MAKE_USAGE
28#include "usage.h"
29#include "applets.h"
30;
31#undef MAKE_USAGE
32#else
33#define usage_messages 0
34#endif /* SHOW_USAGE */
35
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +000036
Denis Vlasenko32b2a9f2008-02-22 22:43:22 +000037/* Include generated applet names, pointers to <applet>_main, etc */
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000038#include "applet_tables.h"
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +000039
40
41#if ENABLE_FEATURE_COMPRESS_USAGE
42
43#include "usage_compressed.h"
44#include "unarchive.h"
45
46static const char *unpack_usage_messages(void)
47{
48 char *outbuf = NULL;
49 bunzip_data *bd;
50 int i;
51
52 i = start_bunzip(&bd,
53 /* src_fd: */ -1,
54 /* inbuf: */ packed_usage,
55 /* len: */ sizeof(packed_usage));
56 /* read_bunzip can longjmp to start_bunzip, and ultimately
57 * end up here with i != 0 on read data errors! Not trivial */
58 if (!i) {
59 /* Cannot use xmalloc: will leak bd in NOFORK case! */
60 outbuf = malloc_or_warn(SIZEOF_usage_messages);
61 if (outbuf)
62 read_bunzip(bd, outbuf, SIZEOF_usage_messages);
63 }
64 dealloc_bunzip(bd);
65 return outbuf;
66}
67#define dealloc_usage_messages(s) free(s)
68
69#else
70
71#define unpack_usage_messages() usage_messages
72#define dealloc_usage_messages(s) ((void)(s))
73
74#endif /* FEATURE_COMPRESS_USAGE */
75
76
77void bb_show_usage(void)
78{
79 if (ENABLE_SHOW_USAGE) {
80 const char *format_string;
81 const char *p;
82 const char *usage_string = p = unpack_usage_messages();
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000083 int ap = find_applet_by_name(applet_name);
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +000084
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000085 if (ap < 0) /* never happens, paranoia */
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +000086 xfunc_die();
87
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000088 while (ap) {
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +000089 while (*p++) continue;
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000090 ap--;
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +000091 }
92
93 fprintf(stderr, "%s multi-call binary\n", bb_banner);
94 format_string = "\nUsage: %s %s\n\n";
95 if (*p == '\b')
96 format_string = "\nNo help available.\n\n";
97 fprintf(stderr, format_string, applet_name, p);
98 dealloc_usage_messages((char*)usage_string);
99 }
100 xfunc_die();
101}
102
103
Denis Vlasenko745cd172007-11-29 03:31:20 +0000104/* NB: any char pointer will work as well, not necessarily applet_names */
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000105static int applet_name_compare(const void *name, const void *v)
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000106{
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000107 int i = (const char *)v - applet_names;
108 return strcmp(name, APPLET_NAME(i));
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000109}
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000110int find_applet_by_name(const char *name)
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000111{
112 /* Do a binary search to find the applet entry given the name. */
Denis Vlasenko745cd172007-11-29 03:31:20 +0000113 const char *p;
114 p = bsearch(name, applet_names, ARRAY_SIZE(applet_main), 1, applet_name_compare);
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000115 if (!p)
116 return -1;
117 return p - applet_names;
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000118}
119
120
121#ifdef __GLIBC__
122/* Make it reside in R/W memory: */
123int *const bb_errno __attribute__ ((section (".data")));
124#endif
125
Denis Vlasenko68404f12008-03-17 09:00:54 +0000126void lbb_prepare(const char *applet
127 USE_FEATURE_INDIVIDUAL(, char **argv))
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000128{
129#ifdef __GLIBC__
130 (*(int **)&bb_errno) = __errno_location();
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000131 barrier();
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000132#endif
Denis Vlasenko15cb4a42007-10-11 10:06:26 +0000133 applet_name = applet;
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000134
135 /* Set locale for everybody except 'init' */
136 if (ENABLE_LOCALE_SUPPORT && getpid() != 1)
137 setlocale(LC_ALL, "");
138
Denis Vlasenko82d38da2007-10-10 14:38:47 +0000139#if ENABLE_FEATURE_INDIVIDUAL
140 /* Redundant for busybox (run_applet_and_exit covers that case)
141 * but needed for "individual applet" mode */
Denis Vlasenkod419a9f2007-10-08 20:45:42 +0000142 if (argv[1] && strcmp(argv[1], "--help") == 0)
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000143 bb_show_usage();
Denis Vlasenko82d38da2007-10-10 14:38:47 +0000144#endif
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000145}
Denis Vlasenko724d1962007-10-10 14:41:07 +0000146
147/* The code below can well be in applets/applets.c, as it is used only
148 * for busybox binary, not "individual" binaries.
149 * However, keeping it here and linking it into libbusybox.so
150 * (together with remaining tiny applets/applets.o)
151 * makes it possible to avoid --whole-archive at link time.
152 * This makes (shared busybox) + libbusybox smaller.
153 * (--gc-sections would be even better....)
154 */
155
156const char *applet_name;
157#if !BB_MMU
158bool re_execed;
159#endif
160
161USE_FEATURE_SUID(static uid_t ruid;) /* real uid */
162
163#if ENABLE_FEATURE_SUID_CONFIG
164
165/* applets[] is const, so we have to define this "override" structure */
166static struct BB_suid_config {
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000167 int m_applet;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000168 uid_t m_uid;
169 gid_t m_gid;
170 mode_t m_mode;
171 struct BB_suid_config *m_next;
172} *suid_config;
173
174static bool suid_cfg_readable;
175
176/* check if u is member of group g */
177static int ingroup(uid_t u, gid_t g)
178{
179 struct group *grp = getgrgid(g);
180
181 if (grp) {
182 char **mem;
183
184 for (mem = grp->gr_mem; *mem; mem++) {
185 struct passwd *pwd = getpwnam(*mem);
186
187 if (pwd && (pwd->pw_uid == u))
188 return 1;
189 }
190 }
191 return 0;
192}
193
194/* This should probably be a libbb routine. In that case,
195 * I'd probably rename it to something like bb_trimmed_slice.
196 */
197static char *get_trimmed_slice(char *s, char *e)
198{
199 /* First, consider the value at e to be nul and back up until we
200 * reach a non-space char. Set the char after that (possibly at
201 * the original e) to nul. */
202 while (e-- > s) {
203 if (!isspace(*e)) {
204 break;
205 }
206 }
207 e[1] = '\0';
208
209 /* Next, advance past all leading space and return a ptr to the
210 * first non-space char; possibly the terminating nul. */
211 return skip_whitespace(s);
212}
213
214/* Don't depend on the tools to combine strings. */
215static const char config_file[] ALIGN1 = "/etc/busybox.conf";
216
217/* We don't supply a value for the nul, so an index adjustment is
218 * necessary below. Also, we use unsigned short here to save some
219 * space even though these are really mode_t values. */
220static const unsigned short mode_mask[] ALIGN2 = {
221 /* SST sst xxx --- */
222 S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* user */
223 S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* group */
224 0, S_IXOTH, S_IXOTH, 0 /* other */
225};
226
227#define parse_error(x) do { errmsg = x; goto pe_label; } while (0)
228
229static void parse_config_file(void)
230{
231 struct BB_suid_config *sct_head;
232 struct BB_suid_config *sct;
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000233 int applet_no;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000234 FILE *f;
235 const char *errmsg;
236 char *s;
237 char *e;
238 int i;
239 unsigned lc;
240 smallint section;
241 char buffer[256];
242 struct stat st;
243
244 assert(!suid_config); /* Should be set to NULL by bss init. */
245
246 ruid = getuid();
247 if (ruid == 0) /* run by root - don't need to even read config file */
248 return;
249
250 if ((stat(config_file, &st) != 0) /* No config file? */
251 || !S_ISREG(st.st_mode) /* Not a regular file? */
252 || (st.st_uid != 0) /* Not owned by root? */
253 || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */
254 || !(f = fopen(config_file, "r")) /* Cannot open? */
255 ) {
256 return;
257 }
258
259 suid_cfg_readable = 1;
260 sct_head = NULL;
261 section = lc = 0;
262
263 while (1) {
264 s = buffer;
265
266 if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */
Denis Vlasenkod02db892008-03-17 09:05:21 +0000267// why?
Denis Vlasenko724d1962007-10-10 14:41:07 +0000268 if (ferror(f)) { /* Make sure it wasn't a read error. */
269 parse_error("reading");
270 }
271 fclose(f);
272 suid_config = sct_head; /* Success, so set the pointer. */
273 return;
274 }
275
276 lc++; /* Got a (partial) line. */
277
278 /* If a line is too long for our buffer, we consider it an error.
279 * The following test does mistreat one corner case though.
280 * If the final line of the file does not end with a newline and
281 * yet exactly fills the buffer, it will be treated as too long
282 * even though there isn't really a problem. But it isn't really
283 * worth adding code to deal with such an unlikely situation, and
284 * we do err on the side of caution. Besides, the line would be
285 * too long if it did end with a newline. */
286 if (!strchr(s, '\n') && !feof(f)) {
287 parse_error("line too long");
288 }
289
290 /* Trim leading and trailing whitespace, ignoring comments, and
291 * check if the resulting string is empty. */
292 s = get_trimmed_slice(s, strchrnul(s, '#'));
293 if (!*s) {
294 continue;
295 }
296
297 /* Check for a section header. */
298
299 if (*s == '[') {
300 /* Unlike the old code, we ignore leading and trailing
301 * whitespace for the section name. We also require that
302 * there are no stray characters after the closing bracket. */
303 e = strchr(s, ']');
304 if (!e /* Missing right bracket? */
305 || e[1] /* Trailing characters? */
306 || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */
307 ) {
308 parse_error("section header");
309 }
310 /* Right now we only have one section so just check it.
311 * If more sections are added in the future, please don't
312 * resort to cascading ifs with multiple strcasecmp calls.
313 * That kind of bloated code is all too common. A loop
314 * and a string table would be a better choice unless the
315 * number of sections is very small. */
316 if (strcasecmp(s, "SUID") == 0) {
317 section = 1;
318 continue;
319 }
320 section = -1; /* Unknown section so set to skip. */
321 continue;
322 }
323
324 /* Process sections. */
325
326 if (section == 1) { /* SUID */
327 /* Since we trimmed leading and trailing space above, we're
328 * now looking for strings of the form
329 * <key>[::space::]*=[::space::]*<value>
330 * where both key and value could contain inner whitespace. */
331
332 /* First get the key (an applet name in our case). */
333 e = strchr(s, '=');
334 if (e) {
335 s = get_trimmed_slice(s, e);
336 }
337 if (!e || !*s) { /* Missing '=' or empty key. */
338 parse_error("keyword");
339 }
340
341 /* Ok, we have an applet name. Process the rhs if this
342 * applet is currently built in and ignore it otherwise.
343 * Note: this can hide config file bugs which only pop
344 * up when the busybox configuration is changed. */
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000345 applet_no = find_applet_by_name(s);
346 if (applet_no >= 0) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000347 /* Note: We currently don't check for duplicates!
348 * The last config line for each applet will be the
349 * one used since we insert at the head of the list.
350 * I suppose this could be considered a feature. */
351 sct = xmalloc(sizeof(struct BB_suid_config));
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000352 sct->m_applet = applet_no;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000353 sct->m_mode = 0;
354 sct->m_next = sct_head;
355 sct_head = sct;
356
357 /* Get the specified mode. */
358
359 e = skip_whitespace(e+1);
360
361 for (i = 0; i < 3; i++) {
362 /* There are 4 chars + 1 nul for each of user/group/other. */
363 static const char mode_chars[] ALIGN1 = "Ssx-\0" "Ssx-\0" "Ttx-";
364
365 const char *q;
366 q = strchrnul(mode_chars + 5*i, *e++);
367 if (!*q) {
368 parse_error("mode");
369 }
370 /* Adjust by -i to account for nul. */
371 sct->m_mode |= mode_mask[(q - mode_chars) - i];
372 }
373
374 /* Now get the the user/group info. */
375
376 s = skip_whitespace(e);
377
378 /* Note: we require whitespace between the mode and the
379 * user/group info. */
380 if ((s == e) || !(e = strchr(s, '.'))) {
381 parse_error("<uid>.<gid>");
382 }
383 *e++ = '\0';
384
385 /* We can't use get_ug_id here since it would exit()
386 * if a uid or gid was not found. Oh well... */
387 sct->m_uid = bb_strtoul(s, NULL, 10);
388 if (errno) {
389 struct passwd *pwd = getpwnam(s);
390 if (!pwd) {
391 parse_error("user");
392 }
393 sct->m_uid = pwd->pw_uid;
394 }
395
396 sct->m_gid = bb_strtoul(e, NULL, 10);
397 if (errno) {
398 struct group *grp;
399 grp = getgrnam(e);
400 if (!grp) {
401 parse_error("group");
402 }
403 sct->m_gid = grp->gr_gid;
404 }
405 }
406 continue;
407 }
408
409 /* Unknown sections are ignored. */
410
411 /* Encountering configuration lines prior to seeing a
412 * section header is treated as an error. This is how
413 * the old code worked, but it may not be desirable.
414 * We may want to simply ignore such lines in case they
415 * are used in some future version of busybox. */
416 if (!section) {
417 parse_error("keyword outside section");
418 }
419
420 } /* while (1) */
421
422 pe_label:
423 fprintf(stderr, "Parse error in %s, line %d: %s\n",
424 config_file, lc, errmsg);
425
426 fclose(f);
427 /* Release any allocated memory before returning. */
428 while (sct_head) {
429 sct = sct_head->m_next;
430 free(sct_head);
431 sct_head = sct;
432 }
433}
434#else
435static inline void parse_config_file(void)
436{
437 USE_FEATURE_SUID(ruid = getuid();)
438}
439#endif /* FEATURE_SUID_CONFIG */
440
441
442#if ENABLE_FEATURE_SUID
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000443static void check_suid(int applet_no)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000444{
445 gid_t rgid; /* real gid */
446
447 if (ruid == 0) /* set by parse_config_file() */
448 return; /* run by root - no need to check more */
449 rgid = getgid();
450
451#if ENABLE_FEATURE_SUID_CONFIG
452 if (suid_cfg_readable) {
453 uid_t uid;
454 struct BB_suid_config *sct;
455 mode_t m;
456
457 for (sct = suid_config; sct; sct = sct->m_next) {
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000458 if (sct->m_applet == applet_no)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000459 goto found;
460 }
Denis Vlasenko15ca51e2007-10-29 19:25:45 +0000461 goto check_need_suid;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000462 found:
463 m = sct->m_mode;
464 if (sct->m_uid == ruid)
465 /* same uid */
466 m >>= 6;
467 else if ((sct->m_gid == rgid) || ingroup(ruid, sct->m_gid))
468 /* same group / in group */
469 m >>= 3;
470
471 if (!(m & S_IXOTH)) /* is x bit not set ? */
472 bb_error_msg_and_die("you have no permission to run this applet!");
473
474 /* _both_ sgid and group_exec have to be set for setegid */
475 if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))
476 rgid = sct->m_gid;
477 /* else (no setegid) we will set egid = rgid */
478
479 /* We set effective AND saved ids. If saved-id is not set
480 * like we do below, seteiud(0) can still later succeed! */
481 if (setresgid(-1, rgid, rgid))
482 bb_perror_msg_and_die("setresgid");
483
484 /* do we have to set effective uid? */
485 uid = ruid;
486 if (sct->m_mode & S_ISUID)
487 uid = sct->m_uid;
488 /* else (no seteuid) we will set euid = ruid */
489
490 if (setresuid(-1, uid, uid))
491 bb_perror_msg_and_die("setresuid");
492 return;
493 }
494#if !ENABLE_FEATURE_SUID_CONFIG_QUIET
495 {
496 static bool onetime = 0;
497
498 if (!onetime) {
499 onetime = 1;
500 fprintf(stderr, "Using fallback suid method\n");
501 }
502 }
503#endif
Denis Vlasenko15ca51e2007-10-29 19:25:45 +0000504 check_need_suid:
Denis Vlasenko724d1962007-10-10 14:41:07 +0000505#endif
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000506 if (APPLET_SUID(applet_no) == _BB_SUID_ALWAYS) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000507 /* Real uid is not 0. If euid isn't 0 too, suid bit
508 * is most probably not set on our executable */
509 if (geteuid())
Denis Vlasenko15ca51e2007-10-29 19:25:45 +0000510 bb_error_msg_and_die("must be suid to work properly");
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000511 } else if (APPLET_SUID(applet_no) == _BB_SUID_NEVER) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000512 xsetgid(rgid); /* drop all privileges */
513 xsetuid(ruid);
514 }
515}
516#else
517#define check_suid(x) ((void)0)
518#endif /* FEATURE_SUID */
519
520
521#if ENABLE_FEATURE_INSTALLER
522/* create (sym)links for each applet */
523static void install_links(const char *busybox, int use_symbolic_links)
524{
525 /* directory table
526 * this should be consistent w/ the enum,
527 * busybox.h::bb_install_loc_t, or else... */
528 static const char usr_bin [] ALIGN1 = "/usr/bin";
529 static const char usr_sbin[] ALIGN1 = "/usr/sbin";
530 static const char *const install_dir[] = {
531 &usr_bin [8], /* "", equivalent to "/" for concat_path_file() */
532 &usr_bin [4], /* "/bin" */
533 &usr_sbin[4], /* "/sbin" */
534 usr_bin,
535 usr_sbin
536 };
537
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000538 int (*lf)(const char *, const char *);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000539 char *fpc;
540 int i;
541 int rc;
542
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000543 lf = link;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000544 if (use_symbolic_links)
545 lf = symlink;
546
Denis Vlasenko745cd172007-11-29 03:31:20 +0000547 for (i = 0; i < ARRAY_SIZE(applet_main); i++) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000548 fpc = concat_path_file(
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000549 install_dir[APPLET_INSTALL_LOC(i)],
550 APPLET_NAME(i));
Denis Vlasenko745cd172007-11-29 03:31:20 +0000551 // debug: bb_error_msg("%slinking %s to busybox",
552 // use_symbolic_links ? "sym" : "", fpc);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000553 rc = lf(busybox, fpc);
554 if (rc != 0 && errno != EEXIST) {
555 bb_simple_perror_msg(fpc);
556 }
557 free(fpc);
558 }
559}
560#else
561#define install_links(x,y) ((void)0)
562#endif /* FEATURE_INSTALLER */
563
564/* If we were called as "busybox..." */
565static int busybox_main(char **argv)
566{
567 if (!argv[1]) {
568 /* Called without arguments */
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000569 const char *a;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000570 int col, output_width;
571 help:
572 output_width = 80;
573 if (ENABLE_FEATURE_AUTOWIDTH) {
574 /* Obtain the terminal width */
575 get_terminal_width_height(0, &output_width, NULL);
576 }
577 /* leading tab and room to wrap */
578 output_width -= sizeof("start-stop-daemon, ") + 8;
579
580 printf("%s multi-call binary\n", bb_banner); /* reuse const string... */
Denis Vlasenkofcfb5c02007-12-24 12:16:24 +0000581 printf("Copyright (C) 1998-2007 Erik Andersen, Rob Landley, Denys Vlasenko\n"
582 "and others. Licensed under GPLv2.\n"
583 "See source distribution for full notice.\n"
Denis Vlasenko724d1962007-10-10 14:41:07 +0000584 "\n"
585 "Usage: busybox [function] [arguments]...\n"
Denis Vlasenkofcfb5c02007-12-24 12:16:24 +0000586 " or: function [arguments]...\n"
Denis Vlasenko724d1962007-10-10 14:41:07 +0000587 "\n"
588 "\tBusyBox is a multi-call binary that combines many common Unix\n"
589 "\tutilities into a single executable. Most people will create a\n"
590 "\tlink to busybox for each function they wish to use and BusyBox\n"
591 "\twill act like whatever it was invoked as!\n"
Denis Vlasenkofcfb5c02007-12-24 12:16:24 +0000592 "\n"
593 "Currently defined functions:\n");
Denis Vlasenko724d1962007-10-10 14:41:07 +0000594 col = 0;
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000595 a = applet_names;
596 while (*a) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000597 if (col > output_width) {
598 puts(",");
599 col = 0;
600 }
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000601 col += printf("%s%s", (col ? ", " : "\t"), a);
602 a += strlen(a) + 1;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000603 }
604 puts("\n");
605 return 0;
606 }
607
608 if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) {
609 const char *busybox;
610 busybox = xmalloc_readlink(bb_busybox_exec_path);
611 if (!busybox)
612 busybox = bb_busybox_exec_path;
613 /* -s makes symlinks */
614 install_links(busybox, argv[2] && strcmp(argv[2], "-s") == 0);
615 return 0;
616 }
617
618 if (strcmp(argv[1], "--help") == 0) {
619 /* "busybox --help [<applet>]" */
620 if (!argv[2])
621 goto help;
622 /* convert to "<applet> --help" */
623 argv[0] = argv[2];
624 argv[2] = NULL;
625 } else {
626 /* "busybox <applet> arg1 arg2 ..." */
627 argv++;
628 }
629 /* We support "busybox /a/path/to/applet args..." too. Allows for
630 * "#!/bin/busybox"-style wrappers */
631 applet_name = bb_get_last_path_component_nostrip(argv[0]);
632 run_applet_and_exit(applet_name, argv);
633 bb_error_msg_and_die("applet not found");
634}
635
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000636void run_applet_no_and_exit(int applet_no, char **argv)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000637{
638 int argc = 1;
639
640 while (argv[argc])
641 argc++;
642
643 /* Reinit some shared global data */
Denis Vlasenko724d1962007-10-10 14:41:07 +0000644 xfunc_error_retval = EXIT_FAILURE;
645
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000646 applet_name = APPLET_NAME(applet_no);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000647 if (argc == 2 && !strcmp(argv[1], "--help"))
648 bb_show_usage();
649 if (ENABLE_FEATURE_SUID)
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000650 check_suid(applet_no);
Denis Vlasenko745cd172007-11-29 03:31:20 +0000651 exit(applet_main[applet_no](argc, argv));
Denis Vlasenko724d1962007-10-10 14:41:07 +0000652}
653
654void run_applet_and_exit(const char *name, char **argv)
655{
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000656 int applet = find_applet_by_name(name);
657 if (applet >= 0)
658 run_applet_no_and_exit(applet, argv);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000659 if (!strncmp(name, "busybox", 7))
660 exit(busybox_main(argv));
661}
662
663
664#if ENABLE_BUILD_LIBBUSYBOX
Denis Vlasenko85c24712008-03-17 09:04:04 +0000665int lbb_main(char **argv)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000666#else
Denis Vlasenko68404f12008-03-17 09:00:54 +0000667int main(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000668#endif
669{
Denis Vlasenko68404f12008-03-17 09:00:54 +0000670 lbb_prepare("busybox" USE_FEATURE_INDIVIDUAL(, argv));
Denis Vlasenko724d1962007-10-10 14:41:07 +0000671
672#if !BB_MMU
673 /* NOMMU re-exec trick sets high-order bit in first byte of name */
674 if (argv[0][0] & 0x80) {
675 re_execed = 1;
676 argv[0][0] &= 0x7f;
677 }
678#endif
679 applet_name = argv[0];
680 if (applet_name[0] == '-')
681 applet_name++;
682 applet_name = bb_basename(applet_name);
683
684 parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */
685
686 run_applet_and_exit(applet_name, argv);
687 bb_error_msg_and_die("applet not found");
688}