blob: fec99c5cef2e1d11744222b33183363ebf88f999 [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 Vlasenko15cb4a42007-10-11 10:06:26 +0000126void lbb_prepare(const char *applet, char **argv)
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000127{
128#ifdef __GLIBC__
129 (*(int **)&bb_errno) = __errno_location();
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000130 barrier();
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000131#endif
Denis Vlasenko15cb4a42007-10-11 10:06:26 +0000132 applet_name = applet;
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000133
134 /* Set locale for everybody except 'init' */
135 if (ENABLE_LOCALE_SUPPORT && getpid() != 1)
136 setlocale(LC_ALL, "");
137
Denis Vlasenko82d38da2007-10-10 14:38:47 +0000138#if ENABLE_FEATURE_INDIVIDUAL
139 /* Redundant for busybox (run_applet_and_exit covers that case)
140 * but needed for "individual applet" mode */
Denis Vlasenkod419a9f2007-10-08 20:45:42 +0000141 if (argv[1] && strcmp(argv[1], "--help") == 0)
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000142 bb_show_usage();
Denis Vlasenko82d38da2007-10-10 14:38:47 +0000143#endif
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000144}
Denis Vlasenko724d1962007-10-10 14:41:07 +0000145
146/* The code below can well be in applets/applets.c, as it is used only
147 * for busybox binary, not "individual" binaries.
148 * However, keeping it here and linking it into libbusybox.so
149 * (together with remaining tiny applets/applets.o)
150 * makes it possible to avoid --whole-archive at link time.
151 * This makes (shared busybox) + libbusybox smaller.
152 * (--gc-sections would be even better....)
153 */
154
155const char *applet_name;
156#if !BB_MMU
157bool re_execed;
158#endif
159
160USE_FEATURE_SUID(static uid_t ruid;) /* real uid */
161
162#if ENABLE_FEATURE_SUID_CONFIG
163
164/* applets[] is const, so we have to define this "override" structure */
165static struct BB_suid_config {
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000166 int m_applet;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000167 uid_t m_uid;
168 gid_t m_gid;
169 mode_t m_mode;
170 struct BB_suid_config *m_next;
171} *suid_config;
172
173static bool suid_cfg_readable;
174
175/* check if u is member of group g */
176static int ingroup(uid_t u, gid_t g)
177{
178 struct group *grp = getgrgid(g);
179
180 if (grp) {
181 char **mem;
182
183 for (mem = grp->gr_mem; *mem; mem++) {
184 struct passwd *pwd = getpwnam(*mem);
185
186 if (pwd && (pwd->pw_uid == u))
187 return 1;
188 }
189 }
190 return 0;
191}
192
193/* This should probably be a libbb routine. In that case,
194 * I'd probably rename it to something like bb_trimmed_slice.
195 */
196static char *get_trimmed_slice(char *s, char *e)
197{
198 /* First, consider the value at e to be nul and back up until we
199 * reach a non-space char. Set the char after that (possibly at
200 * the original e) to nul. */
201 while (e-- > s) {
202 if (!isspace(*e)) {
203 break;
204 }
205 }
206 e[1] = '\0';
207
208 /* Next, advance past all leading space and return a ptr to the
209 * first non-space char; possibly the terminating nul. */
210 return skip_whitespace(s);
211}
212
213/* Don't depend on the tools to combine strings. */
214static const char config_file[] ALIGN1 = "/etc/busybox.conf";
215
216/* We don't supply a value for the nul, so an index adjustment is
217 * necessary below. Also, we use unsigned short here to save some
218 * space even though these are really mode_t values. */
219static const unsigned short mode_mask[] ALIGN2 = {
220 /* SST sst xxx --- */
221 S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* user */
222 S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* group */
223 0, S_IXOTH, S_IXOTH, 0 /* other */
224};
225
226#define parse_error(x) do { errmsg = x; goto pe_label; } while (0)
227
228static void parse_config_file(void)
229{
230 struct BB_suid_config *sct_head;
231 struct BB_suid_config *sct;
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000232 int applet_no;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000233 FILE *f;
234 const char *errmsg;
235 char *s;
236 char *e;
237 int i;
238 unsigned lc;
239 smallint section;
240 char buffer[256];
241 struct stat st;
242
243 assert(!suid_config); /* Should be set to NULL by bss init. */
244
245 ruid = getuid();
246 if (ruid == 0) /* run by root - don't need to even read config file */
247 return;
248
249 if ((stat(config_file, &st) != 0) /* No config file? */
250 || !S_ISREG(st.st_mode) /* Not a regular file? */
251 || (st.st_uid != 0) /* Not owned by root? */
252 || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */
253 || !(f = fopen(config_file, "r")) /* Cannot open? */
254 ) {
255 return;
256 }
257
258 suid_cfg_readable = 1;
259 sct_head = NULL;
260 section = lc = 0;
261
262 while (1) {
263 s = buffer;
264
265 if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */
266 if (ferror(f)) { /* Make sure it wasn't a read error. */
267 parse_error("reading");
268 }
269 fclose(f);
270 suid_config = sct_head; /* Success, so set the pointer. */
271 return;
272 }
273
274 lc++; /* Got a (partial) line. */
275
276 /* If a line is too long for our buffer, we consider it an error.
277 * The following test does mistreat one corner case though.
278 * If the final line of the file does not end with a newline and
279 * yet exactly fills the buffer, it will be treated as too long
280 * even though there isn't really a problem. But it isn't really
281 * worth adding code to deal with such an unlikely situation, and
282 * we do err on the side of caution. Besides, the line would be
283 * too long if it did end with a newline. */
284 if (!strchr(s, '\n') && !feof(f)) {
285 parse_error("line too long");
286 }
287
288 /* Trim leading and trailing whitespace, ignoring comments, and
289 * check if the resulting string is empty. */
290 s = get_trimmed_slice(s, strchrnul(s, '#'));
291 if (!*s) {
292 continue;
293 }
294
295 /* Check for a section header. */
296
297 if (*s == '[') {
298 /* Unlike the old code, we ignore leading and trailing
299 * whitespace for the section name. We also require that
300 * there are no stray characters after the closing bracket. */
301 e = strchr(s, ']');
302 if (!e /* Missing right bracket? */
303 || e[1] /* Trailing characters? */
304 || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */
305 ) {
306 parse_error("section header");
307 }
308 /* Right now we only have one section so just check it.
309 * If more sections are added in the future, please don't
310 * resort to cascading ifs with multiple strcasecmp calls.
311 * That kind of bloated code is all too common. A loop
312 * and a string table would be a better choice unless the
313 * number of sections is very small. */
314 if (strcasecmp(s, "SUID") == 0) {
315 section = 1;
316 continue;
317 }
318 section = -1; /* Unknown section so set to skip. */
319 continue;
320 }
321
322 /* Process sections. */
323
324 if (section == 1) { /* SUID */
325 /* Since we trimmed leading and trailing space above, we're
326 * now looking for strings of the form
327 * <key>[::space::]*=[::space::]*<value>
328 * where both key and value could contain inner whitespace. */
329
330 /* First get the key (an applet name in our case). */
331 e = strchr(s, '=');
332 if (e) {
333 s = get_trimmed_slice(s, e);
334 }
335 if (!e || !*s) { /* Missing '=' or empty key. */
336 parse_error("keyword");
337 }
338
339 /* Ok, we have an applet name. Process the rhs if this
340 * applet is currently built in and ignore it otherwise.
341 * Note: this can hide config file bugs which only pop
342 * up when the busybox configuration is changed. */
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000343 applet_no = find_applet_by_name(s);
344 if (applet_no >= 0) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000345 /* Note: We currently don't check for duplicates!
346 * The last config line for each applet will be the
347 * one used since we insert at the head of the list.
348 * I suppose this could be considered a feature. */
349 sct = xmalloc(sizeof(struct BB_suid_config));
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000350 sct->m_applet = applet_no;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000351 sct->m_mode = 0;
352 sct->m_next = sct_head;
353 sct_head = sct;
354
355 /* Get the specified mode. */
356
357 e = skip_whitespace(e+1);
358
359 for (i = 0; i < 3; i++) {
360 /* There are 4 chars + 1 nul for each of user/group/other. */
361 static const char mode_chars[] ALIGN1 = "Ssx-\0" "Ssx-\0" "Ttx-";
362
363 const char *q;
364 q = strchrnul(mode_chars + 5*i, *e++);
365 if (!*q) {
366 parse_error("mode");
367 }
368 /* Adjust by -i to account for nul. */
369 sct->m_mode |= mode_mask[(q - mode_chars) - i];
370 }
371
372 /* Now get the the user/group info. */
373
374 s = skip_whitespace(e);
375
376 /* Note: we require whitespace between the mode and the
377 * user/group info. */
378 if ((s == e) || !(e = strchr(s, '.'))) {
379 parse_error("<uid>.<gid>");
380 }
381 *e++ = '\0';
382
383 /* We can't use get_ug_id here since it would exit()
384 * if a uid or gid was not found. Oh well... */
385 sct->m_uid = bb_strtoul(s, NULL, 10);
386 if (errno) {
387 struct passwd *pwd = getpwnam(s);
388 if (!pwd) {
389 parse_error("user");
390 }
391 sct->m_uid = pwd->pw_uid;
392 }
393
394 sct->m_gid = bb_strtoul(e, NULL, 10);
395 if (errno) {
396 struct group *grp;
397 grp = getgrnam(e);
398 if (!grp) {
399 parse_error("group");
400 }
401 sct->m_gid = grp->gr_gid;
402 }
403 }
404 continue;
405 }
406
407 /* Unknown sections are ignored. */
408
409 /* Encountering configuration lines prior to seeing a
410 * section header is treated as an error. This is how
411 * the old code worked, but it may not be desirable.
412 * We may want to simply ignore such lines in case they
413 * are used in some future version of busybox. */
414 if (!section) {
415 parse_error("keyword outside section");
416 }
417
418 } /* while (1) */
419
420 pe_label:
421 fprintf(stderr, "Parse error in %s, line %d: %s\n",
422 config_file, lc, errmsg);
423
424 fclose(f);
425 /* Release any allocated memory before returning. */
426 while (sct_head) {
427 sct = sct_head->m_next;
428 free(sct_head);
429 sct_head = sct;
430 }
431}
432#else
433static inline void parse_config_file(void)
434{
435 USE_FEATURE_SUID(ruid = getuid();)
436}
437#endif /* FEATURE_SUID_CONFIG */
438
439
440#if ENABLE_FEATURE_SUID
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000441static void check_suid(int applet_no)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000442{
443 gid_t rgid; /* real gid */
444
445 if (ruid == 0) /* set by parse_config_file() */
446 return; /* run by root - no need to check more */
447 rgid = getgid();
448
449#if ENABLE_FEATURE_SUID_CONFIG
450 if (suid_cfg_readable) {
451 uid_t uid;
452 struct BB_suid_config *sct;
453 mode_t m;
454
455 for (sct = suid_config; sct; sct = sct->m_next) {
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000456 if (sct->m_applet == applet_no)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000457 goto found;
458 }
Denis Vlasenko15ca51e2007-10-29 19:25:45 +0000459 goto check_need_suid;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000460 found:
461 m = sct->m_mode;
462 if (sct->m_uid == ruid)
463 /* same uid */
464 m >>= 6;
465 else if ((sct->m_gid == rgid) || ingroup(ruid, sct->m_gid))
466 /* same group / in group */
467 m >>= 3;
468
469 if (!(m & S_IXOTH)) /* is x bit not set ? */
470 bb_error_msg_and_die("you have no permission to run this applet!");
471
472 /* _both_ sgid and group_exec have to be set for setegid */
473 if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))
474 rgid = sct->m_gid;
475 /* else (no setegid) we will set egid = rgid */
476
477 /* We set effective AND saved ids. If saved-id is not set
478 * like we do below, seteiud(0) can still later succeed! */
479 if (setresgid(-1, rgid, rgid))
480 bb_perror_msg_and_die("setresgid");
481
482 /* do we have to set effective uid? */
483 uid = ruid;
484 if (sct->m_mode & S_ISUID)
485 uid = sct->m_uid;
486 /* else (no seteuid) we will set euid = ruid */
487
488 if (setresuid(-1, uid, uid))
489 bb_perror_msg_and_die("setresuid");
490 return;
491 }
492#if !ENABLE_FEATURE_SUID_CONFIG_QUIET
493 {
494 static bool onetime = 0;
495
496 if (!onetime) {
497 onetime = 1;
498 fprintf(stderr, "Using fallback suid method\n");
499 }
500 }
501#endif
Denis Vlasenko15ca51e2007-10-29 19:25:45 +0000502 check_need_suid:
Denis Vlasenko724d1962007-10-10 14:41:07 +0000503#endif
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000504 if (APPLET_SUID(applet_no) == _BB_SUID_ALWAYS) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000505 /* Real uid is not 0. If euid isn't 0 too, suid bit
506 * is most probably not set on our executable */
507 if (geteuid())
Denis Vlasenko15ca51e2007-10-29 19:25:45 +0000508 bb_error_msg_and_die("must be suid to work properly");
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000509 } else if (APPLET_SUID(applet_no) == _BB_SUID_NEVER) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000510 xsetgid(rgid); /* drop all privileges */
511 xsetuid(ruid);
512 }
513}
514#else
515#define check_suid(x) ((void)0)
516#endif /* FEATURE_SUID */
517
518
519#if ENABLE_FEATURE_INSTALLER
520/* create (sym)links for each applet */
521static void install_links(const char *busybox, int use_symbolic_links)
522{
523 /* directory table
524 * this should be consistent w/ the enum,
525 * busybox.h::bb_install_loc_t, or else... */
526 static const char usr_bin [] ALIGN1 = "/usr/bin";
527 static const char usr_sbin[] ALIGN1 = "/usr/sbin";
528 static const char *const install_dir[] = {
529 &usr_bin [8], /* "", equivalent to "/" for concat_path_file() */
530 &usr_bin [4], /* "/bin" */
531 &usr_sbin[4], /* "/sbin" */
532 usr_bin,
533 usr_sbin
534 };
535
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000536 int (*lf)(const char *, const char *);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000537 char *fpc;
538 int i;
539 int rc;
540
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000541 lf = link;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000542 if (use_symbolic_links)
543 lf = symlink;
544
Denis Vlasenko745cd172007-11-29 03:31:20 +0000545 for (i = 0; i < ARRAY_SIZE(applet_main); i++) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000546 fpc = concat_path_file(
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000547 install_dir[APPLET_INSTALL_LOC(i)],
548 APPLET_NAME(i));
Denis Vlasenko745cd172007-11-29 03:31:20 +0000549 // debug: bb_error_msg("%slinking %s to busybox",
550 // use_symbolic_links ? "sym" : "", fpc);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000551 rc = lf(busybox, fpc);
552 if (rc != 0 && errno != EEXIST) {
553 bb_simple_perror_msg(fpc);
554 }
555 free(fpc);
556 }
557}
558#else
559#define install_links(x,y) ((void)0)
560#endif /* FEATURE_INSTALLER */
561
562/* If we were called as "busybox..." */
563static int busybox_main(char **argv)
564{
565 if (!argv[1]) {
566 /* Called without arguments */
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000567 const char *a;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000568 int col, output_width;
569 help:
570 output_width = 80;
571 if (ENABLE_FEATURE_AUTOWIDTH) {
572 /* Obtain the terminal width */
573 get_terminal_width_height(0, &output_width, NULL);
574 }
575 /* leading tab and room to wrap */
576 output_width -= sizeof("start-stop-daemon, ") + 8;
577
578 printf("%s multi-call binary\n", bb_banner); /* reuse const string... */
Denis Vlasenkofcfb5c02007-12-24 12:16:24 +0000579 printf("Copyright (C) 1998-2007 Erik Andersen, Rob Landley, Denys Vlasenko\n"
580 "and others. Licensed under GPLv2.\n"
581 "See source distribution for full notice.\n"
Denis Vlasenko724d1962007-10-10 14:41:07 +0000582 "\n"
583 "Usage: busybox [function] [arguments]...\n"
Denis Vlasenkofcfb5c02007-12-24 12:16:24 +0000584 " or: function [arguments]...\n"
Denis Vlasenko724d1962007-10-10 14:41:07 +0000585 "\n"
586 "\tBusyBox is a multi-call binary that combines many common Unix\n"
587 "\tutilities into a single executable. Most people will create a\n"
588 "\tlink to busybox for each function they wish to use and BusyBox\n"
589 "\twill act like whatever it was invoked as!\n"
Denis Vlasenkofcfb5c02007-12-24 12:16:24 +0000590 "\n"
591 "Currently defined functions:\n");
Denis Vlasenko724d1962007-10-10 14:41:07 +0000592 col = 0;
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000593 a = applet_names;
594 while (*a) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000595 if (col > output_width) {
596 puts(",");
597 col = 0;
598 }
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000599 col += printf("%s%s", (col ? ", " : "\t"), a);
600 a += strlen(a) + 1;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000601 }
602 puts("\n");
603 return 0;
604 }
605
606 if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) {
607 const char *busybox;
608 busybox = xmalloc_readlink(bb_busybox_exec_path);
609 if (!busybox)
610 busybox = bb_busybox_exec_path;
611 /* -s makes symlinks */
612 install_links(busybox, argv[2] && strcmp(argv[2], "-s") == 0);
613 return 0;
614 }
615
616 if (strcmp(argv[1], "--help") == 0) {
617 /* "busybox --help [<applet>]" */
618 if (!argv[2])
619 goto help;
620 /* convert to "<applet> --help" */
621 argv[0] = argv[2];
622 argv[2] = NULL;
623 } else {
624 /* "busybox <applet> arg1 arg2 ..." */
625 argv++;
626 }
627 /* We support "busybox /a/path/to/applet args..." too. Allows for
628 * "#!/bin/busybox"-style wrappers */
629 applet_name = bb_get_last_path_component_nostrip(argv[0]);
630 run_applet_and_exit(applet_name, argv);
631 bb_error_msg_and_die("applet not found");
632}
633
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000634void run_applet_no_and_exit(int applet_no, char **argv)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000635{
636 int argc = 1;
637
638 while (argv[argc])
639 argc++;
640
641 /* Reinit some shared global data */
Denis Vlasenko724d1962007-10-10 14:41:07 +0000642 xfunc_error_retval = EXIT_FAILURE;
643
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000644 applet_name = APPLET_NAME(applet_no);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000645 if (argc == 2 && !strcmp(argv[1], "--help"))
646 bb_show_usage();
647 if (ENABLE_FEATURE_SUID)
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000648 check_suid(applet_no);
Denis Vlasenko745cd172007-11-29 03:31:20 +0000649 exit(applet_main[applet_no](argc, argv));
Denis Vlasenko724d1962007-10-10 14:41:07 +0000650}
651
652void run_applet_and_exit(const char *name, char **argv)
653{
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000654 int applet = find_applet_by_name(name);
655 if (applet >= 0)
656 run_applet_no_and_exit(applet, argv);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000657 if (!strncmp(name, "busybox", 7))
658 exit(busybox_main(argv));
659}
660
661
662#if ENABLE_BUILD_LIBBUSYBOX
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000663int lbb_main(int argc, char **argv)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000664#else
665int main(int argc, char **argv)
666#endif
667{
Denis Vlasenko15cb4a42007-10-11 10:06:26 +0000668 lbb_prepare("busybox", argv);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000669
670#if !BB_MMU
671 /* NOMMU re-exec trick sets high-order bit in first byte of name */
672 if (argv[0][0] & 0x80) {
673 re_execed = 1;
674 argv[0][0] &= 0x7f;
675 }
676#endif
677 applet_name = argv[0];
678 if (applet_name[0] == '-')
679 applet_name++;
680 applet_name = bb_basename(applet_name);
681
682 parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */
683
684 run_applet_and_exit(applet_name, argv);
685 bb_error_msg_and_die("applet not found");
686}