blob: de27dd80eee520b9171064cbb00a0b18062dd823 [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? */
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 Vlasenko745cd172007-11-29 03:31:20 +0000546 for (i = 0; i < ARRAY_SIZE(applet_main); 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 Vlasenko745cd172007-11-29 03:31:20 +0000550 // debug: bb_error_msg("%slinking %s to busybox",
551 // use_symbolic_links ? "sym" : "", fpc);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000552 rc = lf(busybox, fpc);
553 if (rc != 0 && errno != EEXIST) {
554 bb_simple_perror_msg(fpc);
555 }
556 free(fpc);
557 }
558}
559#else
560#define install_links(x,y) ((void)0)
561#endif /* FEATURE_INSTALLER */
562
563/* If we were called as "busybox..." */
564static int busybox_main(char **argv)
565{
566 if (!argv[1]) {
567 /* Called without arguments */
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000568 const char *a;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000569 int col, output_width;
570 help:
571 output_width = 80;
572 if (ENABLE_FEATURE_AUTOWIDTH) {
573 /* Obtain the terminal width */
574 get_terminal_width_height(0, &output_width, NULL);
575 }
576 /* leading tab and room to wrap */
577 output_width -= sizeof("start-stop-daemon, ") + 8;
578
579 printf("%s multi-call binary\n", bb_banner); /* reuse const string... */
Denis Vlasenkofcfb5c02007-12-24 12:16:24 +0000580 printf("Copyright (C) 1998-2007 Erik Andersen, Rob Landley, Denys Vlasenko\n"
581 "and others. Licensed under GPLv2.\n"
582 "See source distribution for full notice.\n"
Denis Vlasenko724d1962007-10-10 14:41:07 +0000583 "\n"
584 "Usage: busybox [function] [arguments]...\n"
Denis Vlasenkofcfb5c02007-12-24 12:16:24 +0000585 " or: function [arguments]...\n"
Denis Vlasenko724d1962007-10-10 14:41:07 +0000586 "\n"
587 "\tBusyBox is a multi-call binary that combines many common Unix\n"
588 "\tutilities into a single executable. Most people will create a\n"
589 "\tlink to busybox for each function they wish to use and BusyBox\n"
590 "\twill act like whatever it was invoked as!\n"
Denis Vlasenkofcfb5c02007-12-24 12:16:24 +0000591 "\n"
592 "Currently defined functions:\n");
Denis Vlasenko724d1962007-10-10 14:41:07 +0000593 col = 0;
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000594 a = applet_names;
595 while (*a) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000596 if (col > output_width) {
597 puts(",");
598 col = 0;
599 }
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000600 col += printf("%s%s", (col ? ", " : "\t"), a);
601 a += strlen(a) + 1;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000602 }
603 puts("\n");
604 return 0;
605 }
606
607 if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) {
608 const char *busybox;
609 busybox = xmalloc_readlink(bb_busybox_exec_path);
610 if (!busybox)
611 busybox = bb_busybox_exec_path;
612 /* -s makes symlinks */
613 install_links(busybox, argv[2] && strcmp(argv[2], "-s") == 0);
614 return 0;
615 }
616
617 if (strcmp(argv[1], "--help") == 0) {
618 /* "busybox --help [<applet>]" */
619 if (!argv[2])
620 goto help;
621 /* convert to "<applet> --help" */
622 argv[0] = argv[2];
623 argv[2] = NULL;
624 } else {
625 /* "busybox <applet> arg1 arg2 ..." */
626 argv++;
627 }
628 /* We support "busybox /a/path/to/applet args..." too. Allows for
629 * "#!/bin/busybox"-style wrappers */
630 applet_name = bb_get_last_path_component_nostrip(argv[0]);
631 run_applet_and_exit(applet_name, argv);
632 bb_error_msg_and_die("applet not found");
633}
634
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000635void run_applet_no_and_exit(int applet_no, char **argv)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000636{
637 int argc = 1;
638
639 while (argv[argc])
640 argc++;
641
642 /* Reinit some shared global data */
Denis Vlasenko724d1962007-10-10 14:41:07 +0000643 xfunc_error_retval = EXIT_FAILURE;
644
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000645 applet_name = APPLET_NAME(applet_no);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000646 if (argc == 2 && !strcmp(argv[1], "--help"))
647 bb_show_usage();
648 if (ENABLE_FEATURE_SUID)
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000649 check_suid(applet_no);
Denis Vlasenko745cd172007-11-29 03:31:20 +0000650 exit(applet_main[applet_no](argc, argv));
Denis Vlasenko724d1962007-10-10 14:41:07 +0000651}
652
653void run_applet_and_exit(const char *name, char **argv)
654{
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000655 int applet = find_applet_by_name(name);
656 if (applet >= 0)
657 run_applet_no_and_exit(applet, argv);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000658 if (!strncmp(name, "busybox", 7))
659 exit(busybox_main(argv));
660}
661
662
663#if ENABLE_BUILD_LIBBUSYBOX
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000664int lbb_main(int argc, char **argv)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000665#else
Denis Vlasenko68404f12008-03-17 09:00:54 +0000666int main(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000667#endif
668{
Denis Vlasenko68404f12008-03-17 09:00:54 +0000669 lbb_prepare("busybox" USE_FEATURE_INDIVIDUAL(, argv));
Denis Vlasenko724d1962007-10-10 14:41:07 +0000670
671#if !BB_MMU
672 /* NOMMU re-exec trick sets high-order bit in first byte of name */
673 if (argv[0][0] & 0x80) {
674 re_execed = 1;
675 argv[0][0] &= 0x7f;
676 }
677#endif
678 applet_name = argv[0];
679 if (applet_name[0] == '-')
680 applet_name++;
681 applet_name = bb_basename(applet_name);
682
683 parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */
684
685 run_applet_and_exit(applet_name, argv);
686 bb_error_msg_and_die("applet not found");
687}