blob: b19680808f8a00815d53cfa02c8a3009d0220df9 [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
Denis Vlasenko79cedcb2008-04-08 21:13:28 +000015/* We are trying to not use printf, this benefits the case when selected
16 * applets are really simple. Example:
17 *
18 * $ ./busybox
19 * ...
20 * Currently defined functions:
21 * basename, false, true
22 *
23 * $ size busybox
24 * text data bss dec hex filename
25 * 4473 52 72 4597 11f5 busybox
26 *
27 * FEATURE_INSTALLER or FEATURE_SUID will still link printf routines in. :(
28 */
29
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +000030#include <assert.h>
31#include "busybox.h"
32
33
34/* Declare <applet>_main() */
35#define PROTOTYPES
36#include "applets.h"
37#undef PROTOTYPES
38
39#if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE
40/* Define usage_messages[] */
41static const char usage_messages[] ALIGN1 = ""
42#define MAKE_USAGE
43#include "usage.h"
44#include "applets.h"
45;
46#undef MAKE_USAGE
47#else
48#define usage_messages 0
49#endif /* SHOW_USAGE */
50
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +000051
Denis Vlasenko32b2a9f2008-02-22 22:43:22 +000052/* Include generated applet names, pointers to <applet>_main, etc */
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000053#include "applet_tables.h"
Denis Vlasenko468aea22008-04-01 14:47:57 +000054/* ...and if applet_tables generator says we have only one applet... */
55#ifdef SINGLE_APPLET_MAIN
56#undef ENABLE_FEATURE_INDIVIDUAL
57#define ENABLE_FEATURE_INDIVIDUAL 1
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000058#undef IF_FEATURE_INDIVIDUAL
59#define IF_FEATURE_INDIVIDUAL(...) __VA_ARGS__
Denis Vlasenko468aea22008-04-01 14:47:57 +000060#endif
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +000061
62
63#if ENABLE_FEATURE_COMPRESS_USAGE
64
65#include "usage_compressed.h"
66#include "unarchive.h"
67
68static const char *unpack_usage_messages(void)
69{
70 char *outbuf = NULL;
71 bunzip_data *bd;
72 int i;
73
74 i = start_bunzip(&bd,
75 /* src_fd: */ -1,
Denis Vlasenkoe9ad84d2008-08-05 13:10:34 +000076//FIXME: can avoid storing these 2 bytes!
77 /* inbuf: */ (void *)packed_usage + 2,
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +000078 /* len: */ sizeof(packed_usage));
79 /* read_bunzip can longjmp to start_bunzip, and ultimately
80 * end up here with i != 0 on read data errors! Not trivial */
81 if (!i) {
82 /* Cannot use xmalloc: will leak bd in NOFORK case! */
83 outbuf = malloc_or_warn(SIZEOF_usage_messages);
84 if (outbuf)
85 read_bunzip(bd, outbuf, SIZEOF_usage_messages);
86 }
87 dealloc_bunzip(bd);
88 return outbuf;
89}
90#define dealloc_usage_messages(s) free(s)
91
92#else
93
94#define unpack_usage_messages() usage_messages
95#define dealloc_usage_messages(s) ((void)(s))
96
97#endif /* FEATURE_COMPRESS_USAGE */
98
99
Denis Vlasenko79cedcb2008-04-08 21:13:28 +0000100static void full_write2_str(const char *str)
101{
Denis Vlasenko73c571a2009-03-09 00:12:37 +0000102 xwrite_str(STDERR_FILENO, str);
Denis Vlasenko79cedcb2008-04-08 21:13:28 +0000103}
104
Denis Vlasenkodefc1ea2008-06-27 02:52:20 +0000105void FAST_FUNC bb_show_usage(void)
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000106{
107 if (ENABLE_SHOW_USAGE) {
Denis Vlasenko468aea22008-04-01 14:47:57 +0000108#ifdef SINGLE_APPLET_STR
109 /* Imagine that this applet is "true". Dont suck in printf! */
110 const char *p;
111 const char *usage_string = p = unpack_usage_messages();
112
113 if (*p == '\b') {
Denis Vlasenko42404082008-11-24 13:42:24 +0000114 full_write2_str("No help available.\n\n");
Denis Vlasenko468aea22008-04-01 14:47:57 +0000115 } else {
Denis Vlasenko42404082008-11-24 13:42:24 +0000116 full_write2_str("Usage: "SINGLE_APPLET_STR" ");
Denis Vlasenko79cedcb2008-04-08 21:13:28 +0000117 full_write2_str(p);
118 full_write2_str("\n\n");
Denis Vlasenko468aea22008-04-01 14:47:57 +0000119 }
120 dealloc_usage_messages((char*)usage_string);
121#else
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000122 const char *p;
123 const char *usage_string = p = unpack_usage_messages();
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000124 int ap = find_applet_by_name(applet_name);
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000125
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000126 if (ap < 0) /* never happens, paranoia */
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000127 xfunc_die();
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000128 while (ap) {
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000129 while (*p++) continue;
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000130 ap--;
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000131 }
Denis Vlasenko79cedcb2008-04-08 21:13:28 +0000132 full_write2_str(bb_banner);
133 full_write2_str(" multi-call binary\n");
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000134 if (*p == '\b')
Denis Vlasenko79cedcb2008-04-08 21:13:28 +0000135 full_write2_str("\nNo help available.\n\n");
136 else {
137 full_write2_str("\nUsage: ");
138 full_write2_str(applet_name);
139 full_write2_str(" ");
140 full_write2_str(p);
141 full_write2_str("\n\n");
142 }
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000143 dealloc_usage_messages((char*)usage_string);
Denis Vlasenko468aea22008-04-01 14:47:57 +0000144#endif
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000145 }
146 xfunc_die();
147}
148
Denis Vlasenko79cedcb2008-04-08 21:13:28 +0000149#if NUM_APPLETS > 8
Denis Vlasenko745cd172007-11-29 03:31:20 +0000150/* NB: any char pointer will work as well, not necessarily applet_names */
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000151static int applet_name_compare(const void *name, const void *v)
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000152{
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000153 int i = (const char *)v - applet_names;
154 return strcmp(name, APPLET_NAME(i));
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000155}
Denis Vlasenko79cedcb2008-04-08 21:13:28 +0000156#endif
Denis Vlasenkodefc1ea2008-06-27 02:52:20 +0000157int FAST_FUNC find_applet_by_name(const char *name)
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000158{
Denis Vlasenko79cedcb2008-04-08 21:13:28 +0000159#if NUM_APPLETS > 8
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000160 /* Do a binary search to find the applet entry given the name. */
Denis Vlasenko745cd172007-11-29 03:31:20 +0000161 const char *p;
162 p = bsearch(name, applet_names, ARRAY_SIZE(applet_main), 1, applet_name_compare);
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000163 if (!p)
164 return -1;
165 return p - applet_names;
Denis Vlasenko79cedcb2008-04-08 21:13:28 +0000166#else
167 /* A version which does not pull in bsearch */
168 int i = 0;
169 const char *p = applet_names;
170 while (i < NUM_APPLETS) {
171 if (strcmp(name, p) == 0)
172 return i;
173 p += strlen(p) + 1;
174 i++;
175 }
176 return -1;
177#endif
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000178}
179
180
Denis Vlasenko68404f12008-03-17 09:00:54 +0000181void lbb_prepare(const char *applet
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000182 IF_FEATURE_INDIVIDUAL(, char **argv))
Denis Vlasenko468aea22008-04-01 14:47:57 +0000183 MAIN_EXTERNALLY_VISIBLE;
184void lbb_prepare(const char *applet
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000185 IF_FEATURE_INDIVIDUAL(, char **argv))
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000186{
187#ifdef __GLIBC__
188 (*(int **)&bb_errno) = __errno_location();
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000189 barrier();
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000190#endif
Denis Vlasenko15cb4a42007-10-11 10:06:26 +0000191 applet_name = applet;
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000192
193 /* Set locale for everybody except 'init' */
194 if (ENABLE_LOCALE_SUPPORT && getpid() != 1)
195 setlocale(LC_ALL, "");
196
Denis Vlasenko82d38da2007-10-10 14:38:47 +0000197#if ENABLE_FEATURE_INDIVIDUAL
198 /* Redundant for busybox (run_applet_and_exit covers that case)
199 * but needed for "individual applet" mode */
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000200 if (argv[1] && !argv[2] && strcmp(argv[1], "--help") == 0) {
201 /* Special case. POSIX says "test --help"
202 * should be no different from e.g. "test --foo". */
203 if (!ENABLE_TEST || strcmp(applet_name, "test") != 0)
204 bb_show_usage();
205 }
Denis Vlasenko82d38da2007-10-10 14:38:47 +0000206#endif
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000207}
Denis Vlasenko724d1962007-10-10 14:41:07 +0000208
209/* The code below can well be in applets/applets.c, as it is used only
210 * for busybox binary, not "individual" binaries.
211 * However, keeping it here and linking it into libbusybox.so
212 * (together with remaining tiny applets/applets.o)
213 * makes it possible to avoid --whole-archive at link time.
214 * This makes (shared busybox) + libbusybox smaller.
215 * (--gc-sections would be even better....)
216 */
217
218const char *applet_name;
219#if !BB_MMU
220bool re_execed;
221#endif
222
Denis Vlasenko468aea22008-04-01 14:47:57 +0000223
Denis Vlasenko10f6fb12008-04-29 00:10:27 +0000224/* If not built as a single-applet executable... */
225#if !defined(SINGLE_APPLET_MAIN)
Denis Vlasenko468aea22008-04-01 14:47:57 +0000226
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000227IF_FEATURE_SUID(static uid_t ruid;) /* real uid */
Denis Vlasenko724d1962007-10-10 14:41:07 +0000228
229#if ENABLE_FEATURE_SUID_CONFIG
230
231/* applets[] is const, so we have to define this "override" structure */
232static struct BB_suid_config {
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000233 int m_applet;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000234 uid_t m_uid;
235 gid_t m_gid;
236 mode_t m_mode;
237 struct BB_suid_config *m_next;
238} *suid_config;
239
240static bool suid_cfg_readable;
241
242/* check if u is member of group g */
243static int ingroup(uid_t u, gid_t g)
244{
245 struct group *grp = getgrgid(g);
246
247 if (grp) {
248 char **mem;
249
250 for (mem = grp->gr_mem; *mem; mem++) {
251 struct passwd *pwd = getpwnam(*mem);
252
253 if (pwd && (pwd->pw_uid == u))
254 return 1;
255 }
256 }
257 return 0;
258}
259
260/* This should probably be a libbb routine. In that case,
261 * I'd probably rename it to something like bb_trimmed_slice.
262 */
263static char *get_trimmed_slice(char *s, char *e)
264{
265 /* First, consider the value at e to be nul and back up until we
266 * reach a non-space char. Set the char after that (possibly at
267 * the original e) to nul. */
268 while (e-- > s) {
269 if (!isspace(*e)) {
270 break;
271 }
272 }
273 e[1] = '\0';
274
275 /* Next, advance past all leading space and return a ptr to the
276 * first non-space char; possibly the terminating nul. */
277 return skip_whitespace(s);
278}
279
280/* Don't depend on the tools to combine strings. */
281static const char config_file[] ALIGN1 = "/etc/busybox.conf";
282
283/* We don't supply a value for the nul, so an index adjustment is
284 * necessary below. Also, we use unsigned short here to save some
285 * space even though these are really mode_t values. */
286static const unsigned short mode_mask[] ALIGN2 = {
287 /* SST sst xxx --- */
288 S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* user */
289 S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* group */
290 0, S_IXOTH, S_IXOTH, 0 /* other */
291};
292
293#define parse_error(x) do { errmsg = x; goto pe_label; } while (0)
294
295static void parse_config_file(void)
296{
297 struct BB_suid_config *sct_head;
298 struct BB_suid_config *sct;
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000299 int applet_no;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000300 FILE *f;
301 const char *errmsg;
302 char *s;
303 char *e;
304 int i;
305 unsigned lc;
306 smallint section;
307 char buffer[256];
308 struct stat st;
309
310 assert(!suid_config); /* Should be set to NULL by bss init. */
311
312 ruid = getuid();
313 if (ruid == 0) /* run by root - don't need to even read config file */
314 return;
315
316 if ((stat(config_file, &st) != 0) /* No config file? */
317 || !S_ISREG(st.st_mode) /* Not a regular file? */
318 || (st.st_uid != 0) /* Not owned by root? */
319 || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */
Denis Vlasenko5415c852008-07-21 23:05:26 +0000320 || !(f = fopen_for_read(config_file)) /* Cannot open? */
Denis Vlasenko724d1962007-10-10 14:41:07 +0000321 ) {
322 return;
323 }
324
325 suid_cfg_readable = 1;
326 sct_head = NULL;
327 section = lc = 0;
328
329 while (1) {
330 s = buffer;
331
332 if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */
Denis Vlasenkod02db892008-03-17 09:05:21 +0000333// why?
Denis Vlasenko724d1962007-10-10 14:41:07 +0000334 if (ferror(f)) { /* Make sure it wasn't a read error. */
335 parse_error("reading");
336 }
337 fclose(f);
338 suid_config = sct_head; /* Success, so set the pointer. */
339 return;
340 }
341
342 lc++; /* Got a (partial) line. */
343
344 /* If a line is too long for our buffer, we consider it an error.
345 * The following test does mistreat one corner case though.
346 * If the final line of the file does not end with a newline and
347 * yet exactly fills the buffer, it will be treated as too long
348 * even though there isn't really a problem. But it isn't really
349 * worth adding code to deal with such an unlikely situation, and
350 * we do err on the side of caution. Besides, the line would be
351 * too long if it did end with a newline. */
352 if (!strchr(s, '\n') && !feof(f)) {
353 parse_error("line too long");
354 }
355
356 /* Trim leading and trailing whitespace, ignoring comments, and
357 * check if the resulting string is empty. */
358 s = get_trimmed_slice(s, strchrnul(s, '#'));
359 if (!*s) {
360 continue;
361 }
362
363 /* Check for a section header. */
364
365 if (*s == '[') {
366 /* Unlike the old code, we ignore leading and trailing
367 * whitespace for the section name. We also require that
368 * there are no stray characters after the closing bracket. */
369 e = strchr(s, ']');
370 if (!e /* Missing right bracket? */
371 || e[1] /* Trailing characters? */
372 || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */
373 ) {
374 parse_error("section header");
375 }
376 /* Right now we only have one section so just check it.
377 * If more sections are added in the future, please don't
378 * resort to cascading ifs with multiple strcasecmp calls.
379 * That kind of bloated code is all too common. A loop
380 * and a string table would be a better choice unless the
381 * number of sections is very small. */
382 if (strcasecmp(s, "SUID") == 0) {
383 section = 1;
384 continue;
385 }
386 section = -1; /* Unknown section so set to skip. */
387 continue;
388 }
389
390 /* Process sections. */
391
392 if (section == 1) { /* SUID */
393 /* Since we trimmed leading and trailing space above, we're
394 * now looking for strings of the form
395 * <key>[::space::]*=[::space::]*<value>
396 * where both key and value could contain inner whitespace. */
397
398 /* First get the key (an applet name in our case). */
399 e = strchr(s, '=');
400 if (e) {
401 s = get_trimmed_slice(s, e);
402 }
403 if (!e || !*s) { /* Missing '=' or empty key. */
404 parse_error("keyword");
405 }
406
407 /* Ok, we have an applet name. Process the rhs if this
408 * applet is currently built in and ignore it otherwise.
409 * Note: this can hide config file bugs which only pop
410 * up when the busybox configuration is changed. */
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000411 applet_no = find_applet_by_name(s);
412 if (applet_no >= 0) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000413 /* Note: We currently don't check for duplicates!
414 * The last config line for each applet will be the
415 * one used since we insert at the head of the list.
416 * I suppose this could be considered a feature. */
417 sct = xmalloc(sizeof(struct BB_suid_config));
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000418 sct->m_applet = applet_no;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000419 sct->m_mode = 0;
420 sct->m_next = sct_head;
421 sct_head = sct;
422
423 /* Get the specified mode. */
424
425 e = skip_whitespace(e+1);
426
427 for (i = 0; i < 3; i++) {
428 /* There are 4 chars + 1 nul for each of user/group/other. */
429 static const char mode_chars[] ALIGN1 = "Ssx-\0" "Ssx-\0" "Ttx-";
430
431 const char *q;
432 q = strchrnul(mode_chars + 5*i, *e++);
433 if (!*q) {
434 parse_error("mode");
435 }
436 /* Adjust by -i to account for nul. */
437 sct->m_mode |= mode_mask[(q - mode_chars) - i];
438 }
439
440 /* Now get the the user/group info. */
441
442 s = skip_whitespace(e);
443
444 /* Note: we require whitespace between the mode and the
445 * user/group info. */
446 if ((s == e) || !(e = strchr(s, '.'))) {
447 parse_error("<uid>.<gid>");
448 }
449 *e++ = '\0';
450
451 /* We can't use get_ug_id here since it would exit()
452 * if a uid or gid was not found. Oh well... */
453 sct->m_uid = bb_strtoul(s, NULL, 10);
454 if (errno) {
455 struct passwd *pwd = getpwnam(s);
456 if (!pwd) {
457 parse_error("user");
458 }
459 sct->m_uid = pwd->pw_uid;
460 }
461
462 sct->m_gid = bb_strtoul(e, NULL, 10);
463 if (errno) {
464 struct group *grp;
465 grp = getgrnam(e);
466 if (!grp) {
467 parse_error("group");
468 }
469 sct->m_gid = grp->gr_gid;
470 }
471 }
472 continue;
473 }
474
475 /* Unknown sections are ignored. */
476
477 /* Encountering configuration lines prior to seeing a
478 * section header is treated as an error. This is how
479 * the old code worked, but it may not be desirable.
480 * We may want to simply ignore such lines in case they
481 * are used in some future version of busybox. */
482 if (!section) {
483 parse_error("keyword outside section");
484 }
485
486 } /* while (1) */
487
488 pe_label:
489 fprintf(stderr, "Parse error in %s, line %d: %s\n",
490 config_file, lc, errmsg);
491
492 fclose(f);
493 /* Release any allocated memory before returning. */
494 while (sct_head) {
495 sct = sct_head->m_next;
496 free(sct_head);
497 sct_head = sct;
498 }
499}
500#else
501static inline void parse_config_file(void)
502{
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000503 IF_FEATURE_SUID(ruid = getuid();)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000504}
505#endif /* FEATURE_SUID_CONFIG */
506
507
508#if ENABLE_FEATURE_SUID
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000509static void check_suid(int applet_no)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000510{
511 gid_t rgid; /* real gid */
512
513 if (ruid == 0) /* set by parse_config_file() */
514 return; /* run by root - no need to check more */
515 rgid = getgid();
516
517#if ENABLE_FEATURE_SUID_CONFIG
518 if (suid_cfg_readable) {
519 uid_t uid;
520 struct BB_suid_config *sct;
521 mode_t m;
522
523 for (sct = suid_config; sct; sct = sct->m_next) {
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000524 if (sct->m_applet == applet_no)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000525 goto found;
526 }
Denis Vlasenko15ca51e2007-10-29 19:25:45 +0000527 goto check_need_suid;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000528 found:
529 m = sct->m_mode;
530 if (sct->m_uid == ruid)
531 /* same uid */
532 m >>= 6;
533 else if ((sct->m_gid == rgid) || ingroup(ruid, sct->m_gid))
534 /* same group / in group */
535 m >>= 3;
536
537 if (!(m & S_IXOTH)) /* is x bit not set ? */
538 bb_error_msg_and_die("you have no permission to run this applet!");
539
540 /* _both_ sgid and group_exec have to be set for setegid */
541 if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))
542 rgid = sct->m_gid;
543 /* else (no setegid) we will set egid = rgid */
544
545 /* We set effective AND saved ids. If saved-id is not set
546 * like we do below, seteiud(0) can still later succeed! */
547 if (setresgid(-1, rgid, rgid))
548 bb_perror_msg_and_die("setresgid");
549
550 /* do we have to set effective uid? */
551 uid = ruid;
552 if (sct->m_mode & S_ISUID)
553 uid = sct->m_uid;
554 /* else (no seteuid) we will set euid = ruid */
555
556 if (setresuid(-1, uid, uid))
557 bb_perror_msg_and_die("setresuid");
558 return;
559 }
560#if !ENABLE_FEATURE_SUID_CONFIG_QUIET
561 {
562 static bool onetime = 0;
563
564 if (!onetime) {
565 onetime = 1;
566 fprintf(stderr, "Using fallback suid method\n");
567 }
568 }
569#endif
Denis Vlasenko15ca51e2007-10-29 19:25:45 +0000570 check_need_suid:
Denis Vlasenko724d1962007-10-10 14:41:07 +0000571#endif
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000572 if (APPLET_SUID(applet_no) == _BB_SUID_ALWAYS) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000573 /* Real uid is not 0. If euid isn't 0 too, suid bit
574 * is most probably not set on our executable */
575 if (geteuid())
Denis Vlasenko15ca51e2007-10-29 19:25:45 +0000576 bb_error_msg_and_die("must be suid to work properly");
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000577 } else if (APPLET_SUID(applet_no) == _BB_SUID_NEVER) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000578 xsetgid(rgid); /* drop all privileges */
579 xsetuid(ruid);
580 }
581}
582#else
583#define check_suid(x) ((void)0)
584#endif /* FEATURE_SUID */
585
586
587#if ENABLE_FEATURE_INSTALLER
588/* create (sym)links for each applet */
Denys Vlasenko3b405432009-07-15 00:35:34 +0200589static void install_links(const char *busybox, int use_symbolic_links,
590 char *custom_install_dir)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000591{
592 /* directory table
593 * this should be consistent w/ the enum,
594 * busybox.h::bb_install_loc_t, or else... */
595 static const char usr_bin [] ALIGN1 = "/usr/bin";
596 static const char usr_sbin[] ALIGN1 = "/usr/sbin";
597 static const char *const install_dir[] = {
598 &usr_bin [8], /* "", equivalent to "/" for concat_path_file() */
599 &usr_bin [4], /* "/bin" */
600 &usr_sbin[4], /* "/sbin" */
601 usr_bin,
602 usr_sbin
603 };
604
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000605 int (*lf)(const char *, const char *);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000606 char *fpc;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000607 unsigned i;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000608 int rc;
609
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000610 lf = link;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000611 if (use_symbolic_links)
612 lf = symlink;
613
Denis Vlasenko745cd172007-11-29 03:31:20 +0000614 for (i = 0; i < ARRAY_SIZE(applet_main); i++) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000615 fpc = concat_path_file(
Denys Vlasenko3b405432009-07-15 00:35:34 +0200616 custom_install_dir ? custom_install_dir : install_dir[APPLET_INSTALL_LOC(i)],
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000617 APPLET_NAME(i));
Denis Vlasenko745cd172007-11-29 03:31:20 +0000618 // debug: bb_error_msg("%slinking %s to busybox",
619 // use_symbolic_links ? "sym" : "", fpc);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000620 rc = lf(busybox, fpc);
621 if (rc != 0 && errno != EEXIST) {
622 bb_simple_perror_msg(fpc);
623 }
624 free(fpc);
625 }
626}
627#else
Denys Vlasenko3b405432009-07-15 00:35:34 +0200628#define install_links(x,y,z) ((void)0)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000629#endif /* FEATURE_INSTALLER */
630
631/* If we were called as "busybox..." */
632static int busybox_main(char **argv)
633{
634 if (!argv[1]) {
635 /* Called without arguments */
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000636 const char *a;
Denys Vlasenko0149f022009-05-19 18:01:42 +0200637 int col;
638 unsigned output_width;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000639 help:
640 output_width = 80;
641 if (ENABLE_FEATURE_AUTOWIDTH) {
642 /* Obtain the terminal width */
643 get_terminal_width_height(0, &output_width, NULL);
644 }
Denis Vlasenko724d1962007-10-10 14:41:07 +0000645
Denis Vlasenko21278df2008-06-25 12:15:46 +0000646 dup2(1, 2);
Denis Vlasenko79cedcb2008-04-08 21:13:28 +0000647 full_write2_str(bb_banner); /* reuse const string... */
648 full_write2_str(" multi-call binary\n"
Bernhard Reutner-Fischer15ce7f52008-04-24 10:35:50 +0000649 "Copyright (C) 1998-2008 Erik Andersen, Rob Landley, Denys Vlasenko\n"
Denis Vlasenkofcfb5c02007-12-24 12:16:24 +0000650 "and others. Licensed under GPLv2.\n"
651 "See source distribution for full notice.\n"
Denis Vlasenko724d1962007-10-10 14:41:07 +0000652 "\n"
653 "Usage: busybox [function] [arguments]...\n"
Denis Vlasenkofcfb5c02007-12-24 12:16:24 +0000654 " or: function [arguments]...\n"
Denis Vlasenko724d1962007-10-10 14:41:07 +0000655 "\n"
656 "\tBusyBox is a multi-call binary that combines many common Unix\n"
657 "\tutilities into a single executable. Most people will create a\n"
658 "\tlink to busybox for each function they wish to use and BusyBox\n"
659 "\twill act like whatever it was invoked as!\n"
Denis Vlasenkofcfb5c02007-12-24 12:16:24 +0000660 "\n"
661 "Currently defined functions:\n");
Denis Vlasenko724d1962007-10-10 14:41:07 +0000662 col = 0;
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000663 a = applet_names;
Denys Vlasenko0149f022009-05-19 18:01:42 +0200664 /* prevent last comma to be in the very last pos */
665 output_width--;
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000666 while (*a) {
Denys Vlasenko0149f022009-05-19 18:01:42 +0200667 int len2 = strlen(a) + 2;
668 if (col >= (int)output_width - len2) {
Denis Vlasenko79cedcb2008-04-08 21:13:28 +0000669 full_write2_str(",\n");
Denis Vlasenko724d1962007-10-10 14:41:07 +0000670 col = 0;
671 }
Denys Vlasenko0149f022009-05-19 18:01:42 +0200672 if (col == 0) {
673 col = 6;
674 full_write2_str("\t");
675 } else {
676 full_write2_str(", ");
677 }
Denis Vlasenko79cedcb2008-04-08 21:13:28 +0000678 full_write2_str(a);
Denys Vlasenko0149f022009-05-19 18:01:42 +0200679 col += len2;
680 a += len2 - 1;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000681 }
Denis Vlasenko79cedcb2008-04-08 21:13:28 +0000682 full_write2_str("\n\n");
Denis Vlasenko724d1962007-10-10 14:41:07 +0000683 return 0;
684 }
685
686 if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) {
Denys Vlasenko3b405432009-07-15 00:35:34 +0200687 int use_symbolic_links;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000688 const char *busybox;
689 busybox = xmalloc_readlink(bb_busybox_exec_path);
690 if (!busybox)
691 busybox = bb_busybox_exec_path;
Denys Vlasenko3b405432009-07-15 00:35:34 +0200692 /* -s makes symlinks, argv[3] is a custom defined */
693 /* install directory or NULL to use the hardcoded defaults */
694 use_symbolic_links = (argv[2] && strcmp(argv[2], "-s") == 0 && argv++);
695 install_links(busybox, use_symbolic_links, argv[2]);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000696 return 0;
697 }
698
699 if (strcmp(argv[1], "--help") == 0) {
700 /* "busybox --help [<applet>]" */
701 if (!argv[2])
702 goto help;
703 /* convert to "<applet> --help" */
704 argv[0] = argv[2];
705 argv[2] = NULL;
706 } else {
707 /* "busybox <applet> arg1 arg2 ..." */
708 argv++;
709 }
710 /* We support "busybox /a/path/to/applet args..." too. Allows for
711 * "#!/bin/busybox"-style wrappers */
712 applet_name = bb_get_last_path_component_nostrip(argv[0]);
713 run_applet_and_exit(applet_name, argv);
Denis Vlasenko79cedcb2008-04-08 21:13:28 +0000714
715 /*bb_error_msg_and_die("applet not found"); - sucks in printf */
716 full_write2_str(applet_name);
717 full_write2_str(": applet not found\n");
718 xfunc_die();
Denis Vlasenko724d1962007-10-10 14:41:07 +0000719}
720
Denis Vlasenkodefc1ea2008-06-27 02:52:20 +0000721void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000722{
723 int argc = 1;
724
725 while (argv[argc])
726 argc++;
727
728 /* Reinit some shared global data */
Denis Vlasenko724d1962007-10-10 14:41:07 +0000729 xfunc_error_retval = EXIT_FAILURE;
730
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000731 applet_name = APPLET_NAME(applet_no);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000732 if (argc == 2 && strcmp(argv[1], "--help") == 0) {
733 /* Special case. POSIX says "test --help"
734 * should be no different from e.g. "test --foo". */
735//TODO: just compare applet_no with APPLET_NO_test
736 if (!ENABLE_TEST || strcmp(applet_name, "test") != 0)
737 bb_show_usage();
738 }
Denis Vlasenko724d1962007-10-10 14:41:07 +0000739 if (ENABLE_FEATURE_SUID)
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000740 check_suid(applet_no);
Denis Vlasenko745cd172007-11-29 03:31:20 +0000741 exit(applet_main[applet_no](argc, argv));
Denis Vlasenko724d1962007-10-10 14:41:07 +0000742}
743
Denis Vlasenkodefc1ea2008-06-27 02:52:20 +0000744void FAST_FUNC run_applet_and_exit(const char *name, char **argv)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000745{
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000746 int applet = find_applet_by_name(name);
747 if (applet >= 0)
748 run_applet_no_and_exit(applet, argv);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000749 if (!strncmp(name, "busybox", 7))
750 exit(busybox_main(argv));
751}
752
Denis Vlasenko10f6fb12008-04-29 00:10:27 +0000753#endif /* !defined(SINGLE_APPLET_MAIN) */
Denis Vlasenko468aea22008-04-01 14:47:57 +0000754
755
Denis Vlasenko724d1962007-10-10 14:41:07 +0000756
757#if ENABLE_BUILD_LIBBUSYBOX
Denis Vlasenko85c24712008-03-17 09:04:04 +0000758int lbb_main(char **argv)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000759#else
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000760int main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000761#endif
762{
Denis Vlasenko10f6fb12008-04-29 00:10:27 +0000763#if defined(SINGLE_APPLET_MAIN)
Denis Vlasenko468aea22008-04-01 14:47:57 +0000764 /* Only one applet is selected by the user! */
Denis Vlasenko79cedcb2008-04-08 21:13:28 +0000765 /* applet_names in this case is just "applet\0\0" */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000766 lbb_prepare(applet_names IF_FEATURE_INDIVIDUAL(, argv));
Denis Vlasenko468aea22008-04-01 14:47:57 +0000767 return SINGLE_APPLET_MAIN(argc, argv);
768#else
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000769 lbb_prepare("busybox" IF_FEATURE_INDIVIDUAL(, argv));
Denis Vlasenko724d1962007-10-10 14:41:07 +0000770
771#if !BB_MMU
772 /* NOMMU re-exec trick sets high-order bit in first byte of name */
773 if (argv[0][0] & 0x80) {
774 re_execed = 1;
775 argv[0][0] &= 0x7f;
776 }
777#endif
778 applet_name = argv[0];
779 if (applet_name[0] == '-')
780 applet_name++;
781 applet_name = bb_basename(applet_name);
782
783 parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */
784
785 run_applet_and_exit(applet_name, argv);
Denis Vlasenko79cedcb2008-04-08 21:13:28 +0000786
787 /*bb_error_msg_and_die("applet not found"); - sucks in printf */
788 full_write2_str(applet_name);
789 full_write2_str(": applet not found\n");
790 xfunc_die();
Denis Vlasenko468aea22008-04-01 14:47:57 +0000791#endif
Denis Vlasenko724d1962007-10-10 14:41:07 +0000792}