blob: dcf24f5a9f83ff9efa8573b4822b1945945d27b8 [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 Vlasenko1aa7e472007-11-28 06:49:03 +000037/* Include generated applet names, pointers to <apllet>_main, etc */
38#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 Vlasenko1aa7e472007-11-28 06:49:03 +0000104static int applet_name_compare(const void *name, const void *v)
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000105{
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000106 int i = (const char *)v - applet_names;
107 return strcmp(name, APPLET_NAME(i));
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000108}
109
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000110int find_applet_by_name(const char *name)
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000111{
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000112 const char *p;
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000113 /* Do a binary search to find the applet entry given the name. */
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000114
115 /* NB: any char pointer will work as well, not necessarily applet_names */
116 p = bsearch(name, applet_names, ARRAY_SIZE(applet_mains), 1, applet_name_compare);
117 if (!p)
118 return -1;
119 return p - applet_names;
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000120}
121
122
123#ifdef __GLIBC__
124/* Make it reside in R/W memory: */
125int *const bb_errno __attribute__ ((section (".data")));
126#endif
127
Denis Vlasenko15cb4a42007-10-11 10:06:26 +0000128void lbb_prepare(const char *applet, char **argv)
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000129{
130#ifdef __GLIBC__
131 (*(int **)&bb_errno) = __errno_location();
132#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? */
267 if (ferror(f)) { /* Make sure it wasn't a read error. */
268 parse_error("reading");
269 }
270 fclose(f);
271 suid_config = sct_head; /* Success, so set the pointer. */
272 return;
273 }
274
275 lc++; /* Got a (partial) line. */
276
277 /* If a line is too long for our buffer, we consider it an error.
278 * The following test does mistreat one corner case though.
279 * If the final line of the file does not end with a newline and
280 * yet exactly fills the buffer, it will be treated as too long
281 * even though there isn't really a problem. But it isn't really
282 * worth adding code to deal with such an unlikely situation, and
283 * we do err on the side of caution. Besides, the line would be
284 * too long if it did end with a newline. */
285 if (!strchr(s, '\n') && !feof(f)) {
286 parse_error("line too long");
287 }
288
289 /* Trim leading and trailing whitespace, ignoring comments, and
290 * check if the resulting string is empty. */
291 s = get_trimmed_slice(s, strchrnul(s, '#'));
292 if (!*s) {
293 continue;
294 }
295
296 /* Check for a section header. */
297
298 if (*s == '[') {
299 /* Unlike the old code, we ignore leading and trailing
300 * whitespace for the section name. We also require that
301 * there are no stray characters after the closing bracket. */
302 e = strchr(s, ']');
303 if (!e /* Missing right bracket? */
304 || e[1] /* Trailing characters? */
305 || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */
306 ) {
307 parse_error("section header");
308 }
309 /* Right now we only have one section so just check it.
310 * If more sections are added in the future, please don't
311 * resort to cascading ifs with multiple strcasecmp calls.
312 * That kind of bloated code is all too common. A loop
313 * and a string table would be a better choice unless the
314 * number of sections is very small. */
315 if (strcasecmp(s, "SUID") == 0) {
316 section = 1;
317 continue;
318 }
319 section = -1; /* Unknown section so set to skip. */
320 continue;
321 }
322
323 /* Process sections. */
324
325 if (section == 1) { /* SUID */
326 /* Since we trimmed leading and trailing space above, we're
327 * now looking for strings of the form
328 * <key>[::space::]*=[::space::]*<value>
329 * where both key and value could contain inner whitespace. */
330
331 /* First get the key (an applet name in our case). */
332 e = strchr(s, '=');
333 if (e) {
334 s = get_trimmed_slice(s, e);
335 }
336 if (!e || !*s) { /* Missing '=' or empty key. */
337 parse_error("keyword");
338 }
339
340 /* Ok, we have an applet name. Process the rhs if this
341 * applet is currently built in and ignore it otherwise.
342 * Note: this can hide config file bugs which only pop
343 * up when the busybox configuration is changed. */
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000344 applet_no = find_applet_by_name(s);
345 if (applet_no >= 0) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000346 /* Note: We currently don't check for duplicates!
347 * The last config line for each applet will be the
348 * one used since we insert at the head of the list.
349 * I suppose this could be considered a feature. */
350 sct = xmalloc(sizeof(struct BB_suid_config));
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000351 sct->m_applet = applet_no;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000352 sct->m_mode = 0;
353 sct->m_next = sct_head;
354 sct_head = sct;
355
356 /* Get the specified mode. */
357
358 e = skip_whitespace(e+1);
359
360 for (i = 0; i < 3; i++) {
361 /* There are 4 chars + 1 nul for each of user/group/other. */
362 static const char mode_chars[] ALIGN1 = "Ssx-\0" "Ssx-\0" "Ttx-";
363
364 const char *q;
365 q = strchrnul(mode_chars + 5*i, *e++);
366 if (!*q) {
367 parse_error("mode");
368 }
369 /* Adjust by -i to account for nul. */
370 sct->m_mode |= mode_mask[(q - mode_chars) - i];
371 }
372
373 /* Now get the the user/group info. */
374
375 s = skip_whitespace(e);
376
377 /* Note: we require whitespace between the mode and the
378 * user/group info. */
379 if ((s == e) || !(e = strchr(s, '.'))) {
380 parse_error("<uid>.<gid>");
381 }
382 *e++ = '\0';
383
384 /* We can't use get_ug_id here since it would exit()
385 * if a uid or gid was not found. Oh well... */
386 sct->m_uid = bb_strtoul(s, NULL, 10);
387 if (errno) {
388 struct passwd *pwd = getpwnam(s);
389 if (!pwd) {
390 parse_error("user");
391 }
392 sct->m_uid = pwd->pw_uid;
393 }
394
395 sct->m_gid = bb_strtoul(e, NULL, 10);
396 if (errno) {
397 struct group *grp;
398 grp = getgrnam(e);
399 if (!grp) {
400 parse_error("group");
401 }
402 sct->m_gid = grp->gr_gid;
403 }
404 }
405 continue;
406 }
407
408 /* Unknown sections are ignored. */
409
410 /* Encountering configuration lines prior to seeing a
411 * section header is treated as an error. This is how
412 * the old code worked, but it may not be desirable.
413 * We may want to simply ignore such lines in case they
414 * are used in some future version of busybox. */
415 if (!section) {
416 parse_error("keyword outside section");
417 }
418
419 } /* while (1) */
420
421 pe_label:
422 fprintf(stderr, "Parse error in %s, line %d: %s\n",
423 config_file, lc, errmsg);
424
425 fclose(f);
426 /* Release any allocated memory before returning. */
427 while (sct_head) {
428 sct = sct_head->m_next;
429 free(sct_head);
430 sct_head = sct;
431 }
432}
433#else
434static inline void parse_config_file(void)
435{
436 USE_FEATURE_SUID(ruid = getuid();)
437}
438#endif /* FEATURE_SUID_CONFIG */
439
440
441#if ENABLE_FEATURE_SUID
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000442static void check_suid(int applet_no)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000443{
444 gid_t rgid; /* real gid */
445
446 if (ruid == 0) /* set by parse_config_file() */
447 return; /* run by root - no need to check more */
448 rgid = getgid();
449
450#if ENABLE_FEATURE_SUID_CONFIG
451 if (suid_cfg_readable) {
452 uid_t uid;
453 struct BB_suid_config *sct;
454 mode_t m;
455
456 for (sct = suid_config; sct; sct = sct->m_next) {
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000457 if (sct->m_applet == applet_no)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000458 goto found;
459 }
Denis Vlasenko15ca51e2007-10-29 19:25:45 +0000460 goto check_need_suid;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000461 found:
462 m = sct->m_mode;
463 if (sct->m_uid == ruid)
464 /* same uid */
465 m >>= 6;
466 else if ((sct->m_gid == rgid) || ingroup(ruid, sct->m_gid))
467 /* same group / in group */
468 m >>= 3;
469
470 if (!(m & S_IXOTH)) /* is x bit not set ? */
471 bb_error_msg_and_die("you have no permission to run this applet!");
472
473 /* _both_ sgid and group_exec have to be set for setegid */
474 if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))
475 rgid = sct->m_gid;
476 /* else (no setegid) we will set egid = rgid */
477
478 /* We set effective AND saved ids. If saved-id is not set
479 * like we do below, seteiud(0) can still later succeed! */
480 if (setresgid(-1, rgid, rgid))
481 bb_perror_msg_and_die("setresgid");
482
483 /* do we have to set effective uid? */
484 uid = ruid;
485 if (sct->m_mode & S_ISUID)
486 uid = sct->m_uid;
487 /* else (no seteuid) we will set euid = ruid */
488
489 if (setresuid(-1, uid, uid))
490 bb_perror_msg_and_die("setresuid");
491 return;
492 }
493#if !ENABLE_FEATURE_SUID_CONFIG_QUIET
494 {
495 static bool onetime = 0;
496
497 if (!onetime) {
498 onetime = 1;
499 fprintf(stderr, "Using fallback suid method\n");
500 }
501 }
502#endif
Denis Vlasenko15ca51e2007-10-29 19:25:45 +0000503 check_need_suid:
Denis Vlasenko724d1962007-10-10 14:41:07 +0000504#endif
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000505 if (APPLET_SUID(applet_no) == _BB_SUID_ALWAYS) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000506 /* Real uid is not 0. If euid isn't 0 too, suid bit
507 * is most probably not set on our executable */
508 if (geteuid())
Denis Vlasenko15ca51e2007-10-29 19:25:45 +0000509 bb_error_msg_and_die("must be suid to work properly");
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000510 } else if (APPLET_SUID(applet_no) == _BB_SUID_NEVER) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000511 xsetgid(rgid); /* drop all privileges */
512 xsetuid(ruid);
513 }
514}
515#else
516#define check_suid(x) ((void)0)
517#endif /* FEATURE_SUID */
518
519
520#if ENABLE_FEATURE_INSTALLER
521/* create (sym)links for each applet */
522static void install_links(const char *busybox, int use_symbolic_links)
523{
524 /* directory table
525 * this should be consistent w/ the enum,
526 * busybox.h::bb_install_loc_t, or else... */
527 static const char usr_bin [] ALIGN1 = "/usr/bin";
528 static const char usr_sbin[] ALIGN1 = "/usr/sbin";
529 static const char *const install_dir[] = {
530 &usr_bin [8], /* "", equivalent to "/" for concat_path_file() */
531 &usr_bin [4], /* "/bin" */
532 &usr_sbin[4], /* "/sbin" */
533 usr_bin,
534 usr_sbin
535 };
536
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000537 int (*lf)(const char *, const char *);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000538 char *fpc;
539 int i;
540 int rc;
541
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000542 lf = link;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000543 if (use_symbolic_links)
544 lf = symlink;
545
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000546 for (i = 0; i < ARRAY_SIZE(applet_mains); i++) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000547 fpc = concat_path_file(
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000548 install_dir[APPLET_INSTALL_LOC(i)],
549 APPLET_NAME(i));
Denis Vlasenko724d1962007-10-10 14:41:07 +0000550 rc = lf(busybox, fpc);
551 if (rc != 0 && errno != EEXIST) {
552 bb_simple_perror_msg(fpc);
553 }
554 free(fpc);
555 }
556}
557#else
558#define install_links(x,y) ((void)0)
559#endif /* FEATURE_INSTALLER */
560
561/* If we were called as "busybox..." */
562static int busybox_main(char **argv)
563{
564 if (!argv[1]) {
565 /* Called without arguments */
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000566 const char *a;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000567 int col, output_width;
568 help:
569 output_width = 80;
570 if (ENABLE_FEATURE_AUTOWIDTH) {
571 /* Obtain the terminal width */
572 get_terminal_width_height(0, &output_width, NULL);
573 }
574 /* leading tab and room to wrap */
575 output_width -= sizeof("start-stop-daemon, ") + 8;
576
577 printf("%s multi-call binary\n", bb_banner); /* reuse const string... */
578 printf("Copyright (C) 1998-2006 Erik Andersen, Rob Landley, and others.\n"
579 "Licensed under GPLv2. See source distribution for full notice.\n"
580 "\n"
581 "Usage: busybox [function] [arguments]...\n"
582 " or: [function] [arguments]...\n"
583 "\n"
584 "\tBusyBox is a multi-call binary that combines many common Unix\n"
585 "\tutilities into a single executable. Most people will create a\n"
586 "\tlink to busybox for each function they wish to use and BusyBox\n"
587 "\twill act like whatever it was invoked as!\n"
588 "\nCurrently defined functions:\n");
589 col = 0;
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000590 a = applet_names;
591 while (*a) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000592 if (col > output_width) {
593 puts(",");
594 col = 0;
595 }
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000596 col += printf("%s%s", (col ? ", " : "\t"), a);
597 a += strlen(a) + 1;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000598 }
599 puts("\n");
600 return 0;
601 }
602
603 if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) {
604 const char *busybox;
605 busybox = xmalloc_readlink(bb_busybox_exec_path);
606 if (!busybox)
607 busybox = bb_busybox_exec_path;
608 /* -s makes symlinks */
609 install_links(busybox, argv[2] && strcmp(argv[2], "-s") == 0);
610 return 0;
611 }
612
613 if (strcmp(argv[1], "--help") == 0) {
614 /* "busybox --help [<applet>]" */
615 if (!argv[2])
616 goto help;
617 /* convert to "<applet> --help" */
618 argv[0] = argv[2];
619 argv[2] = NULL;
620 } else {
621 /* "busybox <applet> arg1 arg2 ..." */
622 argv++;
623 }
624 /* We support "busybox /a/path/to/applet args..." too. Allows for
625 * "#!/bin/busybox"-style wrappers */
626 applet_name = bb_get_last_path_component_nostrip(argv[0]);
627 run_applet_and_exit(applet_name, argv);
628 bb_error_msg_and_die("applet not found");
629}
630
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000631void run_applet_no_and_exit(int applet_no, char **argv)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000632{
633 int argc = 1;
634
635 while (argv[argc])
636 argc++;
637
638 /* Reinit some shared global data */
639 optind = 1;
640 xfunc_error_retval = EXIT_FAILURE;
641
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000642 applet_name = APPLET_NAME(applet_no);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000643 if (argc == 2 && !strcmp(argv[1], "--help"))
644 bb_show_usage();
645 if (ENABLE_FEATURE_SUID)
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000646 check_suid(applet_no);
647 exit(applet_mains[applet_no](argc, argv));
Denis Vlasenko724d1962007-10-10 14:41:07 +0000648}
649
650void run_applet_and_exit(const char *name, char **argv)
651{
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000652 int applet = find_applet_by_name(name);
653 if (applet >= 0)
654 run_applet_no_and_exit(applet, argv);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000655 if (!strncmp(name, "busybox", 7))
656 exit(busybox_main(argv));
657}
658
659
660#if ENABLE_BUILD_LIBBUSYBOX
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000661int lbb_main(int argc, char **argv)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000662#else
663int main(int argc, char **argv)
664#endif
665{
Denis Vlasenko15cb4a42007-10-11 10:06:26 +0000666 lbb_prepare("busybox", argv);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000667
668#if !BB_MMU
669 /* NOMMU re-exec trick sets high-order bit in first byte of name */
670 if (argv[0][0] & 0x80) {
671 re_execed = 1;
672 argv[0][0] &= 0x7f;
673 }
674#endif
675 applet_name = argv[0];
676 if (applet_name[0] == '-')
677 applet_name++;
678 applet_name = bb_basename(applet_name);
679
680 parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */
681
682 run_applet_and_exit(applet_name, argv);
683 bb_error_msg_and_die("applet not found");
684}