blob: a48a2600369241ce82c0f86b4a5e559c84ddd35b [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 Vlasenko468aea22008-04-01 14:47:57 +000039/* ...and if applet_tables generator says we have only one applet... */
40#ifdef SINGLE_APPLET_MAIN
41#undef ENABLE_FEATURE_INDIVIDUAL
42#define ENABLE_FEATURE_INDIVIDUAL 1
43#undef USE_FEATURE_INDIVIDUAL
44#define USE_FEATURE_INDIVIDUAL(...) __VA_ARGS__
45#endif
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +000046
47
48#if ENABLE_FEATURE_COMPRESS_USAGE
49
50#include "usage_compressed.h"
51#include "unarchive.h"
52
53static const char *unpack_usage_messages(void)
54{
55 char *outbuf = NULL;
56 bunzip_data *bd;
57 int i;
58
59 i = start_bunzip(&bd,
60 /* src_fd: */ -1,
61 /* inbuf: */ packed_usage,
62 /* len: */ sizeof(packed_usage));
63 /* read_bunzip can longjmp to start_bunzip, and ultimately
64 * end up here with i != 0 on read data errors! Not trivial */
65 if (!i) {
66 /* Cannot use xmalloc: will leak bd in NOFORK case! */
67 outbuf = malloc_or_warn(SIZEOF_usage_messages);
68 if (outbuf)
69 read_bunzip(bd, outbuf, SIZEOF_usage_messages);
70 }
71 dealloc_bunzip(bd);
72 return outbuf;
73}
74#define dealloc_usage_messages(s) free(s)
75
76#else
77
78#define unpack_usage_messages() usage_messages
79#define dealloc_usage_messages(s) ((void)(s))
80
81#endif /* FEATURE_COMPRESS_USAGE */
82
83
84void bb_show_usage(void)
85{
86 if (ENABLE_SHOW_USAGE) {
Denis Vlasenko468aea22008-04-01 14:47:57 +000087#ifdef SINGLE_APPLET_STR
88 /* Imagine that this applet is "true". Dont suck in printf! */
89 const char *p;
90 const char *usage_string = p = unpack_usage_messages();
91
92 if (*p == '\b') {
93 write(2, "\nNo help available.\n\n",
94 sizeof("\nNo help available.\n\n") - 1);
95 } else {
96 write(2, "\nUsage: "SINGLE_APPLET_STR" ",
97 sizeof("\nUsage: "SINGLE_APPLET_STR" ") - 1);
98 write(2, p, strlen(p));
99 write(2, "\n\n", 2);
100 }
101 dealloc_usage_messages((char*)usage_string);
102#else
103// TODO: in this case, stdio is sucked in by busybox_main() anyway...
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000104 const char *format_string;
105 const char *p;
106 const char *usage_string = p = unpack_usage_messages();
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000107 int ap = find_applet_by_name(applet_name);
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000108
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000109 if (ap < 0) /* never happens, paranoia */
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000110 xfunc_die();
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000111 while (ap) {
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000112 while (*p++) continue;
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000113 ap--;
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000114 }
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000115 fprintf(stderr, "%s multi-call binary\n", bb_banner);
116 format_string = "\nUsage: %s %s\n\n";
117 if (*p == '\b')
118 format_string = "\nNo help available.\n\n";
119 fprintf(stderr, format_string, applet_name, p);
120 dealloc_usage_messages((char*)usage_string);
Denis Vlasenko468aea22008-04-01 14:47:57 +0000121#endif
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000122 }
123 xfunc_die();
124}
125
126
Denis Vlasenko745cd172007-11-29 03:31:20 +0000127/* NB: any char pointer will work as well, not necessarily applet_names */
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000128static int applet_name_compare(const void *name, const void *v)
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000129{
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000130 int i = (const char *)v - applet_names;
131 return strcmp(name, APPLET_NAME(i));
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000132}
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000133int find_applet_by_name(const char *name)
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000134{
135 /* Do a binary search to find the applet entry given the name. */
Denis Vlasenko745cd172007-11-29 03:31:20 +0000136 const char *p;
137 p = bsearch(name, applet_names, ARRAY_SIZE(applet_main), 1, applet_name_compare);
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000138 if (!p)
139 return -1;
140 return p - applet_names;
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000141}
142
143
144#ifdef __GLIBC__
145/* Make it reside in R/W memory: */
146int *const bb_errno __attribute__ ((section (".data")));
147#endif
148
Denis Vlasenko68404f12008-03-17 09:00:54 +0000149void lbb_prepare(const char *applet
150 USE_FEATURE_INDIVIDUAL(, char **argv))
Denis Vlasenko468aea22008-04-01 14:47:57 +0000151 MAIN_EXTERNALLY_VISIBLE;
152void lbb_prepare(const char *applet
153 USE_FEATURE_INDIVIDUAL(, char **argv))
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000154{
155#ifdef __GLIBC__
156 (*(int **)&bb_errno) = __errno_location();
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000157 barrier();
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000158#endif
Denis Vlasenko15cb4a42007-10-11 10:06:26 +0000159 applet_name = applet;
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000160
161 /* Set locale for everybody except 'init' */
162 if (ENABLE_LOCALE_SUPPORT && getpid() != 1)
163 setlocale(LC_ALL, "");
164
Denis Vlasenko82d38da2007-10-10 14:38:47 +0000165#if ENABLE_FEATURE_INDIVIDUAL
166 /* Redundant for busybox (run_applet_and_exit covers that case)
167 * but needed for "individual applet" mode */
Denis Vlasenkod419a9f2007-10-08 20:45:42 +0000168 if (argv[1] && strcmp(argv[1], "--help") == 0)
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000169 bb_show_usage();
Denis Vlasenko82d38da2007-10-10 14:38:47 +0000170#endif
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000171}
Denis Vlasenko724d1962007-10-10 14:41:07 +0000172
173/* The code below can well be in applets/applets.c, as it is used only
174 * for busybox binary, not "individual" binaries.
175 * However, keeping it here and linking it into libbusybox.so
176 * (together with remaining tiny applets/applets.o)
177 * makes it possible to avoid --whole-archive at link time.
178 * This makes (shared busybox) + libbusybox smaller.
179 * (--gc-sections would be even better....)
180 */
181
182const char *applet_name;
183#if !BB_MMU
184bool re_execed;
185#endif
186
Denis Vlasenko468aea22008-04-01 14:47:57 +0000187
188#if !ENABLE_FEATURE_INDIVIDUAL
189
Denis Vlasenko724d1962007-10-10 14:41:07 +0000190USE_FEATURE_SUID(static uid_t ruid;) /* real uid */
191
192#if ENABLE_FEATURE_SUID_CONFIG
193
194/* applets[] is const, so we have to define this "override" structure */
195static struct BB_suid_config {
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000196 int m_applet;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000197 uid_t m_uid;
198 gid_t m_gid;
199 mode_t m_mode;
200 struct BB_suid_config *m_next;
201} *suid_config;
202
203static bool suid_cfg_readable;
204
205/* check if u is member of group g */
206static int ingroup(uid_t u, gid_t g)
207{
208 struct group *grp = getgrgid(g);
209
210 if (grp) {
211 char **mem;
212
213 for (mem = grp->gr_mem; *mem; mem++) {
214 struct passwd *pwd = getpwnam(*mem);
215
216 if (pwd && (pwd->pw_uid == u))
217 return 1;
218 }
219 }
220 return 0;
221}
222
223/* This should probably be a libbb routine. In that case,
224 * I'd probably rename it to something like bb_trimmed_slice.
225 */
226static char *get_trimmed_slice(char *s, char *e)
227{
228 /* First, consider the value at e to be nul and back up until we
229 * reach a non-space char. Set the char after that (possibly at
230 * the original e) to nul. */
231 while (e-- > s) {
232 if (!isspace(*e)) {
233 break;
234 }
235 }
236 e[1] = '\0';
237
238 /* Next, advance past all leading space and return a ptr to the
239 * first non-space char; possibly the terminating nul. */
240 return skip_whitespace(s);
241}
242
243/* Don't depend on the tools to combine strings. */
244static const char config_file[] ALIGN1 = "/etc/busybox.conf";
245
246/* We don't supply a value for the nul, so an index adjustment is
247 * necessary below. Also, we use unsigned short here to save some
248 * space even though these are really mode_t values. */
249static const unsigned short mode_mask[] ALIGN2 = {
250 /* SST sst xxx --- */
251 S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* user */
252 S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* group */
253 0, S_IXOTH, S_IXOTH, 0 /* other */
254};
255
256#define parse_error(x) do { errmsg = x; goto pe_label; } while (0)
257
258static void parse_config_file(void)
259{
260 struct BB_suid_config *sct_head;
261 struct BB_suid_config *sct;
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000262 int applet_no;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000263 FILE *f;
264 const char *errmsg;
265 char *s;
266 char *e;
267 int i;
268 unsigned lc;
269 smallint section;
270 char buffer[256];
271 struct stat st;
272
273 assert(!suid_config); /* Should be set to NULL by bss init. */
274
275 ruid = getuid();
276 if (ruid == 0) /* run by root - don't need to even read config file */
277 return;
278
279 if ((stat(config_file, &st) != 0) /* No config file? */
280 || !S_ISREG(st.st_mode) /* Not a regular file? */
281 || (st.st_uid != 0) /* Not owned by root? */
282 || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */
283 || !(f = fopen(config_file, "r")) /* Cannot open? */
284 ) {
285 return;
286 }
287
288 suid_cfg_readable = 1;
289 sct_head = NULL;
290 section = lc = 0;
291
292 while (1) {
293 s = buffer;
294
295 if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */
Denis Vlasenkod02db892008-03-17 09:05:21 +0000296// why?
Denis Vlasenko724d1962007-10-10 14:41:07 +0000297 if (ferror(f)) { /* Make sure it wasn't a read error. */
298 parse_error("reading");
299 }
300 fclose(f);
301 suid_config = sct_head; /* Success, so set the pointer. */
302 return;
303 }
304
305 lc++; /* Got a (partial) line. */
306
307 /* If a line is too long for our buffer, we consider it an error.
308 * The following test does mistreat one corner case though.
309 * If the final line of the file does not end with a newline and
310 * yet exactly fills the buffer, it will be treated as too long
311 * even though there isn't really a problem. But it isn't really
312 * worth adding code to deal with such an unlikely situation, and
313 * we do err on the side of caution. Besides, the line would be
314 * too long if it did end with a newline. */
315 if (!strchr(s, '\n') && !feof(f)) {
316 parse_error("line too long");
317 }
318
319 /* Trim leading and trailing whitespace, ignoring comments, and
320 * check if the resulting string is empty. */
321 s = get_trimmed_slice(s, strchrnul(s, '#'));
322 if (!*s) {
323 continue;
324 }
325
326 /* Check for a section header. */
327
328 if (*s == '[') {
329 /* Unlike the old code, we ignore leading and trailing
330 * whitespace for the section name. We also require that
331 * there are no stray characters after the closing bracket. */
332 e = strchr(s, ']');
333 if (!e /* Missing right bracket? */
334 || e[1] /* Trailing characters? */
335 || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */
336 ) {
337 parse_error("section header");
338 }
339 /* Right now we only have one section so just check it.
340 * If more sections are added in the future, please don't
341 * resort to cascading ifs with multiple strcasecmp calls.
342 * That kind of bloated code is all too common. A loop
343 * and a string table would be a better choice unless the
344 * number of sections is very small. */
345 if (strcasecmp(s, "SUID") == 0) {
346 section = 1;
347 continue;
348 }
349 section = -1; /* Unknown section so set to skip. */
350 continue;
351 }
352
353 /* Process sections. */
354
355 if (section == 1) { /* SUID */
356 /* Since we trimmed leading and trailing space above, we're
357 * now looking for strings of the form
358 * <key>[::space::]*=[::space::]*<value>
359 * where both key and value could contain inner whitespace. */
360
361 /* First get the key (an applet name in our case). */
362 e = strchr(s, '=');
363 if (e) {
364 s = get_trimmed_slice(s, e);
365 }
366 if (!e || !*s) { /* Missing '=' or empty key. */
367 parse_error("keyword");
368 }
369
370 /* Ok, we have an applet name. Process the rhs if this
371 * applet is currently built in and ignore it otherwise.
372 * Note: this can hide config file bugs which only pop
373 * up when the busybox configuration is changed. */
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000374 applet_no = find_applet_by_name(s);
375 if (applet_no >= 0) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000376 /* Note: We currently don't check for duplicates!
377 * The last config line for each applet will be the
378 * one used since we insert at the head of the list.
379 * I suppose this could be considered a feature. */
380 sct = xmalloc(sizeof(struct BB_suid_config));
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000381 sct->m_applet = applet_no;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000382 sct->m_mode = 0;
383 sct->m_next = sct_head;
384 sct_head = sct;
385
386 /* Get the specified mode. */
387
388 e = skip_whitespace(e+1);
389
390 for (i = 0; i < 3; i++) {
391 /* There are 4 chars + 1 nul for each of user/group/other. */
392 static const char mode_chars[] ALIGN1 = "Ssx-\0" "Ssx-\0" "Ttx-";
393
394 const char *q;
395 q = strchrnul(mode_chars + 5*i, *e++);
396 if (!*q) {
397 parse_error("mode");
398 }
399 /* Adjust by -i to account for nul. */
400 sct->m_mode |= mode_mask[(q - mode_chars) - i];
401 }
402
403 /* Now get the the user/group info. */
404
405 s = skip_whitespace(e);
406
407 /* Note: we require whitespace between the mode and the
408 * user/group info. */
409 if ((s == e) || !(e = strchr(s, '.'))) {
410 parse_error("<uid>.<gid>");
411 }
412 *e++ = '\0';
413
414 /* We can't use get_ug_id here since it would exit()
415 * if a uid or gid was not found. Oh well... */
416 sct->m_uid = bb_strtoul(s, NULL, 10);
417 if (errno) {
418 struct passwd *pwd = getpwnam(s);
419 if (!pwd) {
420 parse_error("user");
421 }
422 sct->m_uid = pwd->pw_uid;
423 }
424
425 sct->m_gid = bb_strtoul(e, NULL, 10);
426 if (errno) {
427 struct group *grp;
428 grp = getgrnam(e);
429 if (!grp) {
430 parse_error("group");
431 }
432 sct->m_gid = grp->gr_gid;
433 }
434 }
435 continue;
436 }
437
438 /* Unknown sections are ignored. */
439
440 /* Encountering configuration lines prior to seeing a
441 * section header is treated as an error. This is how
442 * the old code worked, but it may not be desirable.
443 * We may want to simply ignore such lines in case they
444 * are used in some future version of busybox. */
445 if (!section) {
446 parse_error("keyword outside section");
447 }
448
449 } /* while (1) */
450
451 pe_label:
452 fprintf(stderr, "Parse error in %s, line %d: %s\n",
453 config_file, lc, errmsg);
454
455 fclose(f);
456 /* Release any allocated memory before returning. */
457 while (sct_head) {
458 sct = sct_head->m_next;
459 free(sct_head);
460 sct_head = sct;
461 }
462}
463#else
464static inline void parse_config_file(void)
465{
466 USE_FEATURE_SUID(ruid = getuid();)
467}
468#endif /* FEATURE_SUID_CONFIG */
469
470
471#if ENABLE_FEATURE_SUID
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000472static void check_suid(int applet_no)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000473{
474 gid_t rgid; /* real gid */
475
476 if (ruid == 0) /* set by parse_config_file() */
477 return; /* run by root - no need to check more */
478 rgid = getgid();
479
480#if ENABLE_FEATURE_SUID_CONFIG
481 if (suid_cfg_readable) {
482 uid_t uid;
483 struct BB_suid_config *sct;
484 mode_t m;
485
486 for (sct = suid_config; sct; sct = sct->m_next) {
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000487 if (sct->m_applet == applet_no)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000488 goto found;
489 }
Denis Vlasenko15ca51e2007-10-29 19:25:45 +0000490 goto check_need_suid;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000491 found:
492 m = sct->m_mode;
493 if (sct->m_uid == ruid)
494 /* same uid */
495 m >>= 6;
496 else if ((sct->m_gid == rgid) || ingroup(ruid, sct->m_gid))
497 /* same group / in group */
498 m >>= 3;
499
500 if (!(m & S_IXOTH)) /* is x bit not set ? */
501 bb_error_msg_and_die("you have no permission to run this applet!");
502
503 /* _both_ sgid and group_exec have to be set for setegid */
504 if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))
505 rgid = sct->m_gid;
506 /* else (no setegid) we will set egid = rgid */
507
508 /* We set effective AND saved ids. If saved-id is not set
509 * like we do below, seteiud(0) can still later succeed! */
510 if (setresgid(-1, rgid, rgid))
511 bb_perror_msg_and_die("setresgid");
512
513 /* do we have to set effective uid? */
514 uid = ruid;
515 if (sct->m_mode & S_ISUID)
516 uid = sct->m_uid;
517 /* else (no seteuid) we will set euid = ruid */
518
519 if (setresuid(-1, uid, uid))
520 bb_perror_msg_and_die("setresuid");
521 return;
522 }
523#if !ENABLE_FEATURE_SUID_CONFIG_QUIET
524 {
525 static bool onetime = 0;
526
527 if (!onetime) {
528 onetime = 1;
529 fprintf(stderr, "Using fallback suid method\n");
530 }
531 }
532#endif
Denis Vlasenko15ca51e2007-10-29 19:25:45 +0000533 check_need_suid:
Denis Vlasenko724d1962007-10-10 14:41:07 +0000534#endif
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000535 if (APPLET_SUID(applet_no) == _BB_SUID_ALWAYS) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000536 /* Real uid is not 0. If euid isn't 0 too, suid bit
537 * is most probably not set on our executable */
538 if (geteuid())
Denis Vlasenko15ca51e2007-10-29 19:25:45 +0000539 bb_error_msg_and_die("must be suid to work properly");
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000540 } else if (APPLET_SUID(applet_no) == _BB_SUID_NEVER) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000541 xsetgid(rgid); /* drop all privileges */
542 xsetuid(ruid);
543 }
544}
545#else
546#define check_suid(x) ((void)0)
547#endif /* FEATURE_SUID */
548
549
550#if ENABLE_FEATURE_INSTALLER
551/* create (sym)links for each applet */
552static void install_links(const char *busybox, int use_symbolic_links)
553{
554 /* directory table
555 * this should be consistent w/ the enum,
556 * busybox.h::bb_install_loc_t, or else... */
557 static const char usr_bin [] ALIGN1 = "/usr/bin";
558 static const char usr_sbin[] ALIGN1 = "/usr/sbin";
559 static const char *const install_dir[] = {
560 &usr_bin [8], /* "", equivalent to "/" for concat_path_file() */
561 &usr_bin [4], /* "/bin" */
562 &usr_sbin[4], /* "/sbin" */
563 usr_bin,
564 usr_sbin
565 };
566
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000567 int (*lf)(const char *, const char *);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000568 char *fpc;
569 int i;
570 int rc;
571
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000572 lf = link;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000573 if (use_symbolic_links)
574 lf = symlink;
575
Denis Vlasenko745cd172007-11-29 03:31:20 +0000576 for (i = 0; i < ARRAY_SIZE(applet_main); i++) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000577 fpc = concat_path_file(
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000578 install_dir[APPLET_INSTALL_LOC(i)],
579 APPLET_NAME(i));
Denis Vlasenko745cd172007-11-29 03:31:20 +0000580 // debug: bb_error_msg("%slinking %s to busybox",
581 // use_symbolic_links ? "sym" : "", fpc);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000582 rc = lf(busybox, fpc);
583 if (rc != 0 && errno != EEXIST) {
584 bb_simple_perror_msg(fpc);
585 }
586 free(fpc);
587 }
588}
589#else
590#define install_links(x,y) ((void)0)
591#endif /* FEATURE_INSTALLER */
592
593/* If we were called as "busybox..." */
594static int busybox_main(char **argv)
595{
596 if (!argv[1]) {
597 /* Called without arguments */
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000598 const char *a;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000599 int col, output_width;
600 help:
601 output_width = 80;
602 if (ENABLE_FEATURE_AUTOWIDTH) {
603 /* Obtain the terminal width */
604 get_terminal_width_height(0, &output_width, NULL);
605 }
606 /* leading tab and room to wrap */
607 output_width -= sizeof("start-stop-daemon, ") + 8;
608
609 printf("%s multi-call binary\n", bb_banner); /* reuse const string... */
Denis Vlasenkofcfb5c02007-12-24 12:16:24 +0000610 printf("Copyright (C) 1998-2007 Erik Andersen, Rob Landley, Denys Vlasenko\n"
611 "and others. Licensed under GPLv2.\n"
612 "See source distribution for full notice.\n"
Denis Vlasenko724d1962007-10-10 14:41:07 +0000613 "\n"
614 "Usage: busybox [function] [arguments]...\n"
Denis Vlasenkofcfb5c02007-12-24 12:16:24 +0000615 " or: function [arguments]...\n"
Denis Vlasenko724d1962007-10-10 14:41:07 +0000616 "\n"
617 "\tBusyBox is a multi-call binary that combines many common Unix\n"
618 "\tutilities into a single executable. Most people will create a\n"
619 "\tlink to busybox for each function they wish to use and BusyBox\n"
620 "\twill act like whatever it was invoked as!\n"
Denis Vlasenkofcfb5c02007-12-24 12:16:24 +0000621 "\n"
622 "Currently defined functions:\n");
Denis Vlasenko724d1962007-10-10 14:41:07 +0000623 col = 0;
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000624 a = applet_names;
625 while (*a) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000626 if (col > output_width) {
627 puts(",");
628 col = 0;
629 }
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000630 col += printf("%s%s", (col ? ", " : "\t"), a);
631 a += strlen(a) + 1;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000632 }
633 puts("\n");
634 return 0;
635 }
636
637 if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) {
638 const char *busybox;
639 busybox = xmalloc_readlink(bb_busybox_exec_path);
640 if (!busybox)
641 busybox = bb_busybox_exec_path;
642 /* -s makes symlinks */
643 install_links(busybox, argv[2] && strcmp(argv[2], "-s") == 0);
644 return 0;
645 }
646
647 if (strcmp(argv[1], "--help") == 0) {
648 /* "busybox --help [<applet>]" */
649 if (!argv[2])
650 goto help;
651 /* convert to "<applet> --help" */
652 argv[0] = argv[2];
653 argv[2] = NULL;
654 } else {
655 /* "busybox <applet> arg1 arg2 ..." */
656 argv++;
657 }
658 /* We support "busybox /a/path/to/applet args..." too. Allows for
659 * "#!/bin/busybox"-style wrappers */
660 applet_name = bb_get_last_path_component_nostrip(argv[0]);
661 run_applet_and_exit(applet_name, argv);
662 bb_error_msg_and_die("applet not found");
663}
664
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000665void run_applet_no_and_exit(int applet_no, char **argv)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000666{
667 int argc = 1;
668
669 while (argv[argc])
670 argc++;
671
672 /* Reinit some shared global data */
Denis Vlasenko724d1962007-10-10 14:41:07 +0000673 xfunc_error_retval = EXIT_FAILURE;
674
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000675 applet_name = APPLET_NAME(applet_no);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000676 if (argc == 2 && !strcmp(argv[1], "--help"))
677 bb_show_usage();
678 if (ENABLE_FEATURE_SUID)
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000679 check_suid(applet_no);
Denis Vlasenko745cd172007-11-29 03:31:20 +0000680 exit(applet_main[applet_no](argc, argv));
Denis Vlasenko724d1962007-10-10 14:41:07 +0000681}
682
683void run_applet_and_exit(const char *name, char **argv)
684{
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000685 int applet = find_applet_by_name(name);
686 if (applet >= 0)
687 run_applet_no_and_exit(applet, argv);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000688 if (!strncmp(name, "busybox", 7))
689 exit(busybox_main(argv));
690}
691
Denis Vlasenko468aea22008-04-01 14:47:57 +0000692#endif /* !ENABLE_FEATURE_INDIVIDUAL */
693
694
Denis Vlasenko724d1962007-10-10 14:41:07 +0000695
696#if ENABLE_BUILD_LIBBUSYBOX
Denis Vlasenko85c24712008-03-17 09:04:04 +0000697int lbb_main(char **argv)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000698#else
Denis Vlasenko68404f12008-03-17 09:00:54 +0000699int main(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000700#endif
701{
Denis Vlasenko468aea22008-04-01 14:47:57 +0000702#if ENABLE_FEATURE_INDIVIDUAL
703 /* Only one applet is selected by the user! */
704 lbb_prepare(SINGLE_APPLET_STR USE_FEATURE_INDIVIDUAL(, argv));
705 return SINGLE_APPLET_MAIN(argc, argv);
706#else
Denis Vlasenko68404f12008-03-17 09:00:54 +0000707 lbb_prepare("busybox" USE_FEATURE_INDIVIDUAL(, argv));
Denis Vlasenko724d1962007-10-10 14:41:07 +0000708
709#if !BB_MMU
710 /* NOMMU re-exec trick sets high-order bit in first byte of name */
711 if (argv[0][0] & 0x80) {
712 re_execed = 1;
713 argv[0][0] &= 0x7f;
714 }
715#endif
716 applet_name = argv[0];
717 if (applet_name[0] == '-')
718 applet_name++;
719 applet_name = bb_basename(applet_name);
720
721 parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */
722
723 run_applet_and_exit(applet_name, argv);
724 bb_error_msg_and_die("applet not found");
Denis Vlasenko468aea22008-04-01 14:47:57 +0000725#endif
Denis Vlasenko724d1962007-10-10 14:41:07 +0000726}