blob: bf6d4762cb1dbacc4e70504056a887ba222e265b [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
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +020010 * Permission has been granted to redistribute this code under GPL.
Denis Vlasenko724d1962007-10-10 14:41:07 +000011 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +020012 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
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 */
Denys Vlasenkoc1947f12009-10-23 01:30:26 +020029#include "busybox.h"
Denys Vlasenko9be47022011-05-16 12:21:31 +020030
Denys Vlasenko8da415e2010-12-05 01:30:14 +010031#if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \
Denys Vlasenko982e87f2013-07-30 11:52:58 +020032 || defined(__APPLE__) \
Denys Vlasenko8da415e2010-12-05 01:30:14 +010033 )
34# include <malloc.h> /* for mallopt */
35#endif
Denys Vlasenko9be47022011-05-16 12:21:31 +020036
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +000037
38/* Declare <applet>_main() */
39#define PROTOTYPES
40#include "applets.h"
41#undef PROTOTYPES
42
Denis Vlasenko32b2a9f2008-02-22 22:43:22 +000043/* Include generated applet names, pointers to <applet>_main, etc */
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000044#include "applet_tables.h"
Denis Vlasenko468aea22008-04-01 14:47:57 +000045/* ...and if applet_tables generator says we have only one applet... */
46#ifdef SINGLE_APPLET_MAIN
Denys Vlasenko0e5ba082010-06-05 23:11:07 +020047# undef ENABLE_FEATURE_INDIVIDUAL
48# define ENABLE_FEATURE_INDIVIDUAL 1
49# undef IF_FEATURE_INDIVIDUAL
50# define IF_FEATURE_INDIVIDUAL(...) __VA_ARGS__
Denis Vlasenko468aea22008-04-01 14:47:57 +000051#endif
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +000052
Denys Vlasenko0e5ba082010-06-05 23:11:07 +020053#include "usage_compressed.h"
54
Denys Vlasenkofe936242017-01-06 19:16:36 +010055
56/* "Do not compress usage text if uncompressed text is small
57 * and we don't include bunzip2 code for other reasons"
58 *
59 * Useful for mass one-applet rebuild (bunzip2 code is ~2.7k).
60 *
61 * Unlike BUNZIP2, if FEATURE_SEAMLESS_BZ2 is on, bunzip2 code is built but
62 * still may be unused if none of the selected applets calls open_zipped()
63 * or its friends; we test for (FEATURE_SEAMLESS_BZ2 && <APPLET>) instead.
64 * For example, only if TAR and FEATURE_SEAMLESS_BZ2 are both selected,
65 * then bunzip2 code will be linked in anyway, and disabling help compression
66 * would be not optimal:
67 */
68#if UNPACKED_USAGE_LENGTH < 4*1024 \
69 && !(ENABLE_FEATURE_SEAMLESS_BZ2 && ENABLE_TAR) \
70 && !(ENABLE_FEATURE_SEAMLESS_BZ2 && ENABLE_MODPROBE) \
71 && !(ENABLE_FEATURE_SEAMLESS_BZ2 && ENABLE_INSMOD) \
72 && !(ENABLE_FEATURE_SEAMLESS_BZ2 && ENABLE_DEPMOD) \
73 && !(ENABLE_FEATURE_SEAMLESS_BZ2 && ENABLE_MAN) \
74 && !ENABLE_BUNZIP2 \
75 && !ENABLE_BZCAT
76# undef ENABLE_FEATURE_COMPRESS_USAGE
77# define ENABLE_FEATURE_COMPRESS_USAGE 0
78#endif
79
80
Denys Vlasenko0e5ba082010-06-05 23:11:07 +020081#if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE
82static const char usage_messages[] ALIGN1 = UNPACKED_USAGE;
83#else
84# define usage_messages 0
Denys Vlasenko1fcbff22010-06-26 02:40:08 +020085#endif
Denys Vlasenko0e5ba082010-06-05 23:11:07 +020086
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +000087#if ENABLE_FEATURE_COMPRESS_USAGE
88
Denys Vlasenko5c296de2010-07-03 14:28:35 +020089static const char packed_usage[] ALIGN1 = { PACKED_USAGE };
Denys Vlasenkod184a722011-09-22 12:45:14 +020090# include "bb_archive.h"
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +000091static const char *unpack_usage_messages(void)
92{
93 char *outbuf = NULL;
94 bunzip_data *bd;
95 int i;
96
97 i = start_bunzip(&bd,
98 /* src_fd: */ -1,
Denys Vlasenkocaddfc82010-10-28 23:08:53 +020099 /* inbuf: */ packed_usage,
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000100 /* len: */ sizeof(packed_usage));
101 /* read_bunzip can longjmp to start_bunzip, and ultimately
102 * end up here with i != 0 on read data errors! Not trivial */
103 if (!i) {
104 /* Cannot use xmalloc: will leak bd in NOFORK case! */
Denys Vlasenko0e5ba082010-06-05 23:11:07 +0200105 outbuf = malloc_or_warn(sizeof(UNPACKED_USAGE));
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000106 if (outbuf)
Denys Vlasenko0e5ba082010-06-05 23:11:07 +0200107 read_bunzip(bd, outbuf, sizeof(UNPACKED_USAGE));
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000108 }
109 dealloc_bunzip(bd);
110 return outbuf;
111}
Denys Vlasenko0e5ba082010-06-05 23:11:07 +0200112# define dealloc_usage_messages(s) free(s)
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000113
114#else
115
Denys Vlasenko0e5ba082010-06-05 23:11:07 +0200116# define unpack_usage_messages() usage_messages
117# define dealloc_usage_messages(s) ((void)(s))
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000118
119#endif /* FEATURE_COMPRESS_USAGE */
120
121
Denis Vlasenkodefc1ea2008-06-27 02:52:20 +0000122void FAST_FUNC bb_show_usage(void)
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000123{
124 if (ENABLE_SHOW_USAGE) {
Denis Vlasenko468aea22008-04-01 14:47:57 +0000125#ifdef SINGLE_APPLET_STR
126 /* Imagine that this applet is "true". Dont suck in printf! */
Lauri Kasanen2b662c52010-11-13 23:16:05 +0100127 const char *usage_string = unpack_usage_messages();
Denis Vlasenko468aea22008-04-01 14:47:57 +0000128
Lauri Kasanen2b662c52010-11-13 23:16:05 +0100129 if (*usage_string == '\b') {
Denis Vlasenko42404082008-11-24 13:42:24 +0000130 full_write2_str("No help available.\n\n");
Denis Vlasenko468aea22008-04-01 14:47:57 +0000131 } else {
Denis Vlasenko42404082008-11-24 13:42:24 +0000132 full_write2_str("Usage: "SINGLE_APPLET_STR" ");
Lauri Kasanen2b662c52010-11-13 23:16:05 +0100133 full_write2_str(usage_string);
Denis Vlasenko79cedcb2008-04-08 21:13:28 +0000134 full_write2_str("\n\n");
Denis Vlasenko468aea22008-04-01 14:47:57 +0000135 }
Denys Vlasenko630dde12009-08-30 19:57:49 +0200136 if (ENABLE_FEATURE_CLEAN_UP)
137 dealloc_usage_messages((char*)usage_string);
Denis Vlasenko468aea22008-04-01 14:47:57 +0000138#else
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000139 const char *p;
140 const char *usage_string = p = unpack_usage_messages();
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000141 int ap = find_applet_by_name(applet_name);
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000142
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000143 if (ap < 0) /* never happens, paranoia */
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000144 xfunc_die();
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000145 while (ap) {
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000146 while (*p++) continue;
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000147 ap--;
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000148 }
Denis Vlasenko79cedcb2008-04-08 21:13:28 +0000149 full_write2_str(bb_banner);
Denys Vlasenko630dde12009-08-30 19:57:49 +0200150 full_write2_str(" multi-call binary.\n");
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000151 if (*p == '\b')
Denis Vlasenko79cedcb2008-04-08 21:13:28 +0000152 full_write2_str("\nNo help available.\n\n");
153 else {
154 full_write2_str("\nUsage: ");
155 full_write2_str(applet_name);
156 full_write2_str(" ");
157 full_write2_str(p);
Denys Vlasenko7f4a49a2015-05-25 14:30:52 +0200158 full_write2_str("\n");
Denis Vlasenko79cedcb2008-04-08 21:13:28 +0000159 }
Denys Vlasenko630dde12009-08-30 19:57:49 +0200160 if (ENABLE_FEATURE_CLEAN_UP)
161 dealloc_usage_messages((char*)usage_string);
Denis Vlasenko468aea22008-04-01 14:47:57 +0000162#endif
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000163 }
164 xfunc_die();
165}
166
Denis Vlasenkodefc1ea2008-06-27 02:52:20 +0000167int FAST_FUNC find_applet_by_name(const char *name)
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000168{
Ron Yorstonb2206172016-04-03 22:29:35 +0200169 unsigned i, max;
170 int j;
Denis Vlasenko745cd172007-11-29 03:31:20 +0000171 const char *p;
Ron Yorston610c4c32016-03-30 00:42:05 +0200172
Denys Vlasenkoa93e4fd2016-04-02 22:54:23 +0200173/* The commented-out word-at-a-time code is ~40% faster, but +160 bytes.
174 * "Faster" here saves ~0.5 microsecond of real time - not worth it.
175 */
176#if 0 /*BB_UNALIGNED_MEMACCESS_OK && BB_LITTLE_ENDIAN*/
177 uint32_t n32;
178
179 /* Handle all names < 2 chars long early */
180 if (name[0] == '\0')
181 return -1; /* "" is not a valid applet name */
182 if (name[1] == '\0') {
183 if (!ENABLE_TEST)
184 return -1; /* 1-char name is not valid */
185 if (name[0] != ']')
186 return -1; /* 1-char name which isn't "[" is not valid */
187 /* applet "[" is always applet #0: */
188 return 0;
189 }
190#endif
191
Ron Yorston610c4c32016-03-30 00:42:05 +0200192 p = applet_names;
193 i = 0;
194#if KNOWN_APPNAME_OFFSETS <= 0
195 max = NUM_APPLETS;
Denis Vlasenko79cedcb2008-04-08 21:13:28 +0000196#else
Ron Yorston610c4c32016-03-30 00:42:05 +0200197 max = NUM_APPLETS * KNOWN_APPNAME_OFFSETS;
198 for (j = ARRAY_SIZE(applet_nameofs)-1; j >= 0; j--) {
199 const char *pp = applet_names + applet_nameofs[j];
200 if (strcmp(name, pp) >= 0) {
201 //bb_error_msg("name:'%s' >= pp:'%s'", name, pp);
202 p = pp;
203 i = max - NUM_APPLETS;
204 break;
205 }
206 max -= NUM_APPLETS;
207 }
208 max /= (unsigned)KNOWN_APPNAME_OFFSETS;
209 i /= (unsigned)KNOWN_APPNAME_OFFSETS;
210 //bb_error_msg("name:'%s' starting from:'%s' i:%u max:%u", name, p, i, max);
211#endif
212
Denys Vlasenko1cf68e32016-04-02 22:57:17 +0200213 /* Open-coded linear search without strcmp/strlen calls for speed */
Denys Vlasenkoa93e4fd2016-04-02 22:54:23 +0200214
215#if 0 /*BB_UNALIGNED_MEMACCESS_OK && BB_LITTLE_ENDIAN*/
216 /* skip "[\0" name, it's surely not it */
217 if (ENABLE_TEST && LONE_CHAR(p, '['))
218 i++, p += 2;
219 /* All remaining applet names in p[] are at least 2 chars long */
220 /* name[] is also at least 2 chars long */
221
222 n32 = (name[0] << 0) | (name[1] << 8) | (name[2] << 16);
223 while (i < max) {
224 uint32_t p32;
225 char ch;
226
227 /* Quickly check match of the first 3 bytes */
228 move_from_unaligned32(p32, p);
229 p += 3;
230 if ((p32 & 0x00ffffff) != n32) {
231 /* Most likely case: 3 first bytes do not match */
232 i++;
233 if ((p32 & 0x00ff0000) == '\0')
234 continue; // p[2] was NUL
235 p++;
236 if ((p32 & 0xff000000) == '\0')
237 continue; // p[3] was NUL
238 /* p[0..3] aren't matching and none is NUL, check the rest */
239 while (*p++ != '\0')
240 continue;
241 continue;
242 }
243
244 /* Unlikely branch: first 3 bytes ([0..2]) match */
245 if ((p32 & 0x00ff0000) == '\0') {
246 /* name is 2-byte long, it is full match */
247 //bb_error_msg("found:'%s' i:%u", name, i);
248 return i;
249 }
250 /* Check remaining bytes [3..NUL] */
251 ch = (p32 >> 24);
252 j = 3;
253 while (ch == name[j]) {
254 if (ch == '\0') {
255 //bb_error_msg("found:'%s' i:%u", name, i);
256 return i;
257 }
258 ch = *++p;
259 j++;
260 }
261 /* Not a match. Skip it, including NUL */
262 while (ch != '\0')
263 ch = *++p;
264 p++;
265 i++;
266 }
267 return -1;
268#else
Ron Yorston610c4c32016-03-30 00:42:05 +0200269 while (i < max) {
270 char ch;
271 j = 0;
272 /* Do we see "name\0" in applet_names[p] position? */
273 while ((ch = *p) == name[j]) {
274 if (ch == '\0') {
275 //bb_error_msg("found:'%s' i:%u", name, i);
276 return i; /* yes */
277 }
278 p++;
279 j++;
280 }
281 /* No.
282 * p => 1st non-matching char in applet_names[],
283 * skip to and including NUL.
284 */
285 while (ch != '\0')
286 ch = *++p;
287 p++;
Denis Vlasenko79cedcb2008-04-08 21:13:28 +0000288 i++;
289 }
290 return -1;
Denys Vlasenkoa93e4fd2016-04-02 22:54:23 +0200291#endif
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000292}
293
294
Denis Vlasenko68404f12008-03-17 09:00:54 +0000295void lbb_prepare(const char *applet
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000296 IF_FEATURE_INDIVIDUAL(, char **argv))
Denis Vlasenko468aea22008-04-01 14:47:57 +0000297 MAIN_EXTERNALLY_VISIBLE;
298void lbb_prepare(const char *applet
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000299 IF_FEATURE_INDIVIDUAL(, char **argv))
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000300{
301#ifdef __GLIBC__
302 (*(int **)&bb_errno) = __errno_location();
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000303 barrier();
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000304#endif
Denis Vlasenko15cb4a42007-10-11 10:06:26 +0000305 applet_name = applet;
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000306
Denys Vlasenko45b4ecc2014-08-11 20:33:18 +0200307 if (ENABLE_LOCALE_SUPPORT)
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000308 setlocale(LC_ALL, "");
309
Denis Vlasenko82d38da2007-10-10 14:38:47 +0000310#if ENABLE_FEATURE_INDIVIDUAL
311 /* Redundant for busybox (run_applet_and_exit covers that case)
312 * but needed for "individual applet" mode */
Denys Vlasenko9297dbc2010-07-05 21:37:12 +0200313 if (argv[1]
314 && !argv[2]
315 && strcmp(argv[1], "--help") == 0
Denys Vlasenko8dff01d2015-03-12 17:48:34 +0100316 && !is_prefixed_with(applet, "busybox")
Denys Vlasenko9297dbc2010-07-05 21:37:12 +0200317 ) {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000318 /* Special case. POSIX says "test --help"
319 * should be no different from e.g. "test --foo". */
320 if (!ENABLE_TEST || strcmp(applet_name, "test") != 0)
321 bb_show_usage();
322 }
Denis Vlasenko82d38da2007-10-10 14:38:47 +0000323#endif
Denis Vlasenkoac7d0e32007-10-08 19:32:12 +0000324}
Denis Vlasenko724d1962007-10-10 14:41:07 +0000325
326/* The code below can well be in applets/applets.c, as it is used only
327 * for busybox binary, not "individual" binaries.
328 * However, keeping it here and linking it into libbusybox.so
329 * (together with remaining tiny applets/applets.o)
330 * makes it possible to avoid --whole-archive at link time.
331 * This makes (shared busybox) + libbusybox smaller.
332 * (--gc-sections would be even better....)
333 */
334
335const char *applet_name;
336#if !BB_MMU
337bool re_execed;
338#endif
339
Denis Vlasenko468aea22008-04-01 14:47:57 +0000340
Denis Vlasenko10f6fb12008-04-29 00:10:27 +0000341/* If not built as a single-applet executable... */
342#if !defined(SINGLE_APPLET_MAIN)
Denis Vlasenko468aea22008-04-01 14:47:57 +0000343
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000344IF_FEATURE_SUID(static uid_t ruid;) /* real uid */
Denis Vlasenko724d1962007-10-10 14:41:07 +0000345
Denys Vlasenko8f0af3b2010-11-29 02:55:35 +0100346# if ENABLE_FEATURE_SUID_CONFIG
Denis Vlasenko724d1962007-10-10 14:41:07 +0000347
Denys Vlasenko9be47022011-05-16 12:21:31 +0200348static struct suid_config_t {
349 /* next ptr must be first: this struct needs to be llist-compatible */
350 struct suid_config_t *m_next;
Denys Vlasenko4566e172011-05-16 00:01:08 +0200351 struct bb_uidgid_t m_ugid;
Denys Vlasenko9be47022011-05-16 12:21:31 +0200352 int m_applet;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000353 mode_t m_mode;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000354} *suid_config;
355
356static bool suid_cfg_readable;
357
Denys Vlasenkod83aff12011-05-16 13:53:19 +0200358/* libbb candidate */
Denis Vlasenko724d1962007-10-10 14:41:07 +0000359static char *get_trimmed_slice(char *s, char *e)
360{
361 /* First, consider the value at e to be nul and back up until we
362 * reach a non-space char. Set the char after that (possibly at
363 * the original e) to nul. */
364 while (e-- > s) {
365 if (!isspace(*e)) {
366 break;
367 }
368 }
369 e[1] = '\0';
370
371 /* Next, advance past all leading space and return a ptr to the
372 * first non-space char; possibly the terminating nul. */
373 return skip_whitespace(s);
374}
375
Denis Vlasenko724d1962007-10-10 14:41:07 +0000376static void parse_config_file(void)
377{
Denys Vlasenko3770b6b2011-05-16 13:19:25 +0200378 /* Don't depend on the tools to combine strings. */
379 static const char config_file[] ALIGN1 = "/etc/busybox.conf";
380
Denys Vlasenko9be47022011-05-16 12:21:31 +0200381 struct suid_config_t *sct_head;
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000382 int applet_no;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000383 FILE *f;
384 const char *errmsg;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000385 unsigned lc;
386 smallint section;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000387 struct stat st;
388
Denis Vlasenko724d1962007-10-10 14:41:07 +0000389 ruid = getuid();
390 if (ruid == 0) /* run by root - don't need to even read config file */
391 return;
392
393 if ((stat(config_file, &st) != 0) /* No config file? */
394 || !S_ISREG(st.st_mode) /* Not a regular file? */
395 || (st.st_uid != 0) /* Not owned by root? */
396 || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */
Denys Vlasenko4566e172011-05-16 00:01:08 +0200397 || !(f = fopen_for_read(config_file)) /* Cannot open? */
Denis Vlasenko724d1962007-10-10 14:41:07 +0000398 ) {
399 return;
400 }
401
402 suid_cfg_readable = 1;
403 sct_head = NULL;
404 section = lc = 0;
405
406 while (1) {
Denys Vlasenko9be47022011-05-16 12:21:31 +0200407 char buffer[256];
408 char *s;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000409
Denys Vlasenko9be47022011-05-16 12:21:31 +0200410 if (!fgets(buffer, sizeof(buffer), f)) { /* Are we done? */
Denys Vlasenko4566e172011-05-16 00:01:08 +0200411 // Looks like bloat
412 //if (ferror(f)) { /* Make sure it wasn't a read error. */
413 // errmsg = "reading";
414 // goto pe_label;
415 //}
Denis Vlasenko724d1962007-10-10 14:41:07 +0000416 fclose(f);
417 suid_config = sct_head; /* Success, so set the pointer. */
418 return;
419 }
420
Denys Vlasenko9be47022011-05-16 12:21:31 +0200421 s = buffer;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000422 lc++; /* Got a (partial) line. */
423
424 /* If a line is too long for our buffer, we consider it an error.
425 * The following test does mistreat one corner case though.
426 * If the final line of the file does not end with a newline and
427 * yet exactly fills the buffer, it will be treated as too long
428 * even though there isn't really a problem. But it isn't really
429 * worth adding code to deal with such an unlikely situation, and
430 * we do err on the side of caution. Besides, the line would be
431 * too long if it did end with a newline. */
432 if (!strchr(s, '\n') && !feof(f)) {
Denys Vlasenko4566e172011-05-16 00:01:08 +0200433 errmsg = "line too long";
434 goto pe_label;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000435 }
436
437 /* Trim leading and trailing whitespace, ignoring comments, and
438 * check if the resulting string is empty. */
439 s = get_trimmed_slice(s, strchrnul(s, '#'));
440 if (!*s) {
441 continue;
442 }
443
444 /* Check for a section header. */
445
446 if (*s == '[') {
447 /* Unlike the old code, we ignore leading and trailing
448 * whitespace for the section name. We also require that
449 * there are no stray characters after the closing bracket. */
Denys Vlasenko9be47022011-05-16 12:21:31 +0200450 char *e = strchr(s, ']');
Denis Vlasenko724d1962007-10-10 14:41:07 +0000451 if (!e /* Missing right bracket? */
452 || e[1] /* Trailing characters? */
453 || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */
454 ) {
Denys Vlasenko4566e172011-05-16 00:01:08 +0200455 errmsg = "section header";
456 goto pe_label;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000457 }
458 /* Right now we only have one section so just check it.
459 * If more sections are added in the future, please don't
460 * resort to cascading ifs with multiple strcasecmp calls.
461 * That kind of bloated code is all too common. A loop
462 * and a string table would be a better choice unless the
463 * number of sections is very small. */
464 if (strcasecmp(s, "SUID") == 0) {
465 section = 1;
466 continue;
467 }
468 section = -1; /* Unknown section so set to skip. */
469 continue;
470 }
471
472 /* Process sections. */
473
474 if (section == 1) { /* SUID */
475 /* Since we trimmed leading and trailing space above, we're
476 * now looking for strings of the form
477 * <key>[::space::]*=[::space::]*<value>
478 * where both key and value could contain inner whitespace. */
479
480 /* First get the key (an applet name in our case). */
Denys Vlasenko9be47022011-05-16 12:21:31 +0200481 char *e = strchr(s, '=');
Denis Vlasenko724d1962007-10-10 14:41:07 +0000482 if (e) {
483 s = get_trimmed_slice(s, e);
484 }
485 if (!e || !*s) { /* Missing '=' or empty key. */
Denys Vlasenko4566e172011-05-16 00:01:08 +0200486 errmsg = "keyword";
487 goto pe_label;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000488 }
489
490 /* Ok, we have an applet name. Process the rhs if this
491 * applet is currently built in and ignore it otherwise.
492 * Note: this can hide config file bugs which only pop
493 * up when the busybox configuration is changed. */
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000494 applet_no = find_applet_by_name(s);
495 if (applet_no >= 0) {
Denys Vlasenko3770b6b2011-05-16 13:19:25 +0200496 unsigned i;
Denys Vlasenko9be47022011-05-16 12:21:31 +0200497 struct suid_config_t *sct;
498
Denis Vlasenko724d1962007-10-10 14:41:07 +0000499 /* Note: We currently don't check for duplicates!
500 * The last config line for each applet will be the
501 * one used since we insert at the head of the list.
502 * I suppose this could be considered a feature. */
Denys Vlasenko4566e172011-05-16 00:01:08 +0200503 sct = xzalloc(sizeof(*sct));
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000504 sct->m_applet = applet_no;
Denys Vlasenko4566e172011-05-16 00:01:08 +0200505 /*sct->m_mode = 0;*/
Denis Vlasenko724d1962007-10-10 14:41:07 +0000506 sct->m_next = sct_head;
507 sct_head = sct;
508
509 /* Get the specified mode. */
510
511 e = skip_whitespace(e+1);
512
513 for (i = 0; i < 3; i++) {
Denys Vlasenko3770b6b2011-05-16 13:19:25 +0200514 /* There are 4 chars for each of user/group/other.
515 * "x-xx" instead of "x-" are to make
516 * "idx > 3" check catch invalid chars.
517 */
518 static const char mode_chars[] ALIGN1 = "Ssx-" "Ssx-" "x-xx";
519 static const unsigned short mode_mask[] ALIGN2 = {
520 S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* Ssx- */
521 S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* Ssx- */
522 S_IXOTH, 0 /* x- */
523 };
524 const char *q = strchrnul(mode_chars + 4*i, *e);
525 unsigned idx = q - (mode_chars + 4*i);
526 if (idx > 3) {
Denys Vlasenko4566e172011-05-16 00:01:08 +0200527 errmsg = "mode";
528 goto pe_label;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000529 }
Denys Vlasenko3770b6b2011-05-16 13:19:25 +0200530 sct->m_mode |= mode_mask[q - mode_chars];
531 e++;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000532 }
533
Marek Polacekb0b88842011-04-16 17:33:43 +0200534 /* Now get the user/group info. */
Denis Vlasenko724d1962007-10-10 14:41:07 +0000535
536 s = skip_whitespace(e);
Denys Vlasenko351fec32011-05-16 14:30:26 +0200537 /* Default is 0.0, else parse USER.GROUP: */
538 if (*s) {
539 /* We require whitespace between mode and USER.GROUP */
540 if ((s == e) || !(e = strchr(s, '.'))) {
541 errmsg = "uid.gid";
542 goto pe_label;
543 }
544 *e = ':'; /* get_uidgid needs USER:GROUP syntax */
Denys Vlasenko526d8582015-10-19 04:27:17 +0200545 if (get_uidgid(&sct->m_ugid, s) == 0) {
Denys Vlasenko351fec32011-05-16 14:30:26 +0200546 errmsg = "unknown user/group";
547 goto pe_label;
548 }
Denis Vlasenko724d1962007-10-10 14:41:07 +0000549 }
550 }
551 continue;
552 }
553
554 /* Unknown sections are ignored. */
555
556 /* Encountering configuration lines prior to seeing a
557 * section header is treated as an error. This is how
558 * the old code worked, but it may not be desirable.
559 * We may want to simply ignore such lines in case they
560 * are used in some future version of busybox. */
561 if (!section) {
Denys Vlasenko4566e172011-05-16 00:01:08 +0200562 errmsg = "keyword outside section";
563 goto pe_label;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000564 }
Denis Vlasenko724d1962007-10-10 14:41:07 +0000565 } /* while (1) */
566
567 pe_label:
Denys Vlasenko9be47022011-05-16 12:21:31 +0200568 fclose(f);
Denys Vlasenko4566e172011-05-16 00:01:08 +0200569 bb_error_msg("parse error in %s, line %u: %s", config_file, lc, errmsg);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000570
Denis Vlasenko724d1962007-10-10 14:41:07 +0000571 /* Release any allocated memory before returning. */
Denys Vlasenko9be47022011-05-16 12:21:31 +0200572 llist_free((llist_t*)sct_head, NULL);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000573}
Denys Vlasenko8f0af3b2010-11-29 02:55:35 +0100574# else
Denis Vlasenko724d1962007-10-10 14:41:07 +0000575static inline void parse_config_file(void)
576{
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000577 IF_FEATURE_SUID(ruid = getuid();)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000578}
Denys Vlasenko8f0af3b2010-11-29 02:55:35 +0100579# endif /* FEATURE_SUID_CONFIG */
Denis Vlasenko724d1962007-10-10 14:41:07 +0000580
581
Denys Vlasenko9cc3d3a2016-12-23 02:42:26 +0100582# if ENABLE_FEATURE_SUID && NUM_APPLETS > 0
Cristian Ionescu-Idbohrnf1d76b62017-01-02 11:17:09 +0100583# if ENABLE_FEATURE_SUID_CONFIG
Denys Vlasenko9cc3d3a2016-12-23 02:42:26 +0100584/* check if u is member of group g */
585static int ingroup(uid_t u, gid_t g)
586{
587 struct group *grp = getgrgid(g);
588 if (grp) {
589 char **mem;
590 for (mem = grp->gr_mem; *mem; mem++) {
591 struct passwd *pwd = getpwnam(*mem);
592 if (pwd && (pwd->pw_uid == u))
593 return 1;
594 }
595 }
596 return 0;
597}
Cristian Ionescu-Idbohrnf1d76b62017-01-02 11:17:09 +0100598# endif
Denys Vlasenko9cc3d3a2016-12-23 02:42:26 +0100599
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000600static void check_suid(int applet_no)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000601{
602 gid_t rgid; /* real gid */
603
604 if (ruid == 0) /* set by parse_config_file() */
605 return; /* run by root - no need to check more */
606 rgid = getgid();
607
Denys Vlasenko8f0af3b2010-11-29 02:55:35 +0100608# if ENABLE_FEATURE_SUID_CONFIG
Denis Vlasenko724d1962007-10-10 14:41:07 +0000609 if (suid_cfg_readable) {
610 uid_t uid;
Denys Vlasenko9be47022011-05-16 12:21:31 +0200611 struct suid_config_t *sct;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000612 mode_t m;
613
614 for (sct = suid_config; sct; sct = sct->m_next) {
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000615 if (sct->m_applet == applet_no)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000616 goto found;
617 }
Denis Vlasenko15ca51e2007-10-29 19:25:45 +0000618 goto check_need_suid;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000619 found:
Denys Vlasenko3770b6b2011-05-16 13:19:25 +0200620 /* Is this user allowed to run this applet? */
Denis Vlasenko724d1962007-10-10 14:41:07 +0000621 m = sct->m_mode;
Denys Vlasenko4566e172011-05-16 00:01:08 +0200622 if (sct->m_ugid.uid == ruid)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000623 /* same uid */
624 m >>= 6;
Denys Vlasenko4566e172011-05-16 00:01:08 +0200625 else if ((sct->m_ugid.gid == rgid) || ingroup(ruid, sct->m_ugid.gid))
Denis Vlasenko724d1962007-10-10 14:41:07 +0000626 /* same group / in group */
627 m >>= 3;
Denys Vlasenko3770b6b2011-05-16 13:19:25 +0200628 if (!(m & S_IXOTH)) /* is x bit not set? */
Denys Vlasenkod83aff12011-05-16 13:53:19 +0200629 bb_error_msg_and_die("you have no permission to run this applet");
Denis Vlasenko724d1962007-10-10 14:41:07 +0000630
Denis Vlasenko724d1962007-10-10 14:41:07 +0000631 /* We set effective AND saved ids. If saved-id is not set
Denys Vlasenko3770b6b2011-05-16 13:19:25 +0200632 * like we do below, seteuid(0) can still later succeed! */
633
634 /* Are we directed to change gid
635 * (APPLET = *s* USER.GROUP or APPLET = *S* USER.GROUP)?
636 */
637 if (sct->m_mode & S_ISGID)
638 rgid = sct->m_ugid.gid;
639 /* else: we will set egid = rgid, thus dropping sgid effect */
Denis Vlasenko724d1962007-10-10 14:41:07 +0000640 if (setresgid(-1, rgid, rgid))
641 bb_perror_msg_and_die("setresgid");
642
Denys Vlasenko3770b6b2011-05-16 13:19:25 +0200643 /* Are we directed to change uid
644 * (APPLET = s** USER.GROUP or APPLET = S** USER.GROUP)?
645 */
Denis Vlasenko724d1962007-10-10 14:41:07 +0000646 uid = ruid;
647 if (sct->m_mode & S_ISUID)
Denys Vlasenko4566e172011-05-16 00:01:08 +0200648 uid = sct->m_ugid.uid;
Denys Vlasenko3770b6b2011-05-16 13:19:25 +0200649 /* else: we will set euid = ruid, thus dropping suid effect */
Denis Vlasenko724d1962007-10-10 14:41:07 +0000650 if (setresuid(-1, uid, uid))
651 bb_perror_msg_and_die("setresuid");
Denys Vlasenko3770b6b2011-05-16 13:19:25 +0200652
Denys Vlasenko9be47022011-05-16 12:21:31 +0200653 goto ret;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000654 }
Denys Vlasenko8f0af3b2010-11-29 02:55:35 +0100655# if !ENABLE_FEATURE_SUID_CONFIG_QUIET
Denis Vlasenko724d1962007-10-10 14:41:07 +0000656 {
657 static bool onetime = 0;
658
659 if (!onetime) {
660 onetime = 1;
Denys Vlasenko9be47022011-05-16 12:21:31 +0200661 bb_error_msg("using fallback suid method");
Denis Vlasenko724d1962007-10-10 14:41:07 +0000662 }
663 }
Denys Vlasenko8f0af3b2010-11-29 02:55:35 +0100664# endif
Denis Vlasenko15ca51e2007-10-29 19:25:45 +0000665 check_need_suid:
Denys Vlasenko8f0af3b2010-11-29 02:55:35 +0100666# endif
Denys Vlasenkob9f2d9f2011-01-18 13:58:01 +0100667 if (APPLET_SUID(applet_no) == BB_SUID_REQUIRE) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000668 /* Real uid is not 0. If euid isn't 0 too, suid bit
669 * is most probably not set on our executable */
670 if (geteuid())
Denis Vlasenko15ca51e2007-10-29 19:25:45 +0000671 bb_error_msg_and_die("must be suid to work properly");
Denys Vlasenkob9f2d9f2011-01-18 13:58:01 +0100672 } else if (APPLET_SUID(applet_no) == BB_SUID_DROP) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000673 xsetgid(rgid); /* drop all privileges */
674 xsetuid(ruid);
675 }
Denys Vlasenko9be47022011-05-16 12:21:31 +0200676# if ENABLE_FEATURE_SUID_CONFIG
Cristian Ionescu-Idbohrnea137aa2011-05-20 03:52:36 +0200677 ret: ;
Denys Vlasenko9be47022011-05-16 12:21:31 +0200678 llist_free((llist_t*)suid_config, NULL);
679# endif
Denis Vlasenko724d1962007-10-10 14:41:07 +0000680}
Denys Vlasenko8f0af3b2010-11-29 02:55:35 +0100681# else
682# define check_suid(x) ((void)0)
683# endif /* FEATURE_SUID */
Denis Vlasenko724d1962007-10-10 14:41:07 +0000684
685
Denys Vlasenko8f0af3b2010-11-29 02:55:35 +0100686# if ENABLE_FEATURE_INSTALLER
Denys Vlasenko5a7c7202010-04-20 21:02:57 -0400687static const char usr_bin [] ALIGN1 = "/usr/bin/";
688static const char usr_sbin[] ALIGN1 = "/usr/sbin/";
689static const char *const install_dir[] = {
690 &usr_bin [8], /* "/" */
691 &usr_bin [4], /* "/bin/" */
Denys Vlasenkod4d289a2010-10-12 04:18:05 +0200692 &usr_sbin[4] /* "/sbin/" */
Denys Vlasenko8f0af3b2010-11-29 02:55:35 +0100693# if !ENABLE_INSTALL_NO_USR
Denys Vlasenkod4d289a2010-10-12 04:18:05 +0200694 ,usr_bin
695 ,usr_sbin
Denys Vlasenko8f0af3b2010-11-29 02:55:35 +0100696# endif
Denys Vlasenko5a7c7202010-04-20 21:02:57 -0400697};
698
Denis Vlasenko724d1962007-10-10 14:41:07 +0000699/* create (sym)links for each applet */
Denys Vlasenko3b405432009-07-15 00:35:34 +0200700static void install_links(const char *busybox, int use_symbolic_links,
701 char *custom_install_dir)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000702{
703 /* directory table
704 * this should be consistent w/ the enum,
705 * busybox.h::bb_install_loc_t, or else... */
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000706 int (*lf)(const char *, const char *);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000707 char *fpc;
Ron Yorston610c4c32016-03-30 00:42:05 +0200708 const char *appname = applet_names;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000709 unsigned i;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000710 int rc;
711
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000712 lf = link;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000713 if (use_symbolic_links)
714 lf = symlink;
715
Denis Vlasenko745cd172007-11-29 03:31:20 +0000716 for (i = 0; i < ARRAY_SIZE(applet_main); i++) {
Denis Vlasenko724d1962007-10-10 14:41:07 +0000717 fpc = concat_path_file(
Denys Vlasenko3b405432009-07-15 00:35:34 +0200718 custom_install_dir ? custom_install_dir : install_dir[APPLET_INSTALL_LOC(i)],
Ron Yorston610c4c32016-03-30 00:42:05 +0200719 appname);
Denis Vlasenko745cd172007-11-29 03:31:20 +0000720 // debug: bb_error_msg("%slinking %s to busybox",
721 // use_symbolic_links ? "sym" : "", fpc);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000722 rc = lf(busybox, fpc);
723 if (rc != 0 && errno != EEXIST) {
724 bb_simple_perror_msg(fpc);
725 }
726 free(fpc);
Ron Yorston610c4c32016-03-30 00:42:05 +0200727 while (*appname++ != '\0')
728 continue;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000729 }
730}
Denys Vlasenko8e950682016-05-31 02:42:49 +0200731# elif ENABLE_BUSYBOX
Mike Frysingerf1999b52014-01-31 00:29:47 -0500732static void install_links(const char *busybox UNUSED_PARAM,
733 int use_symbolic_links UNUSED_PARAM,
734 char *custom_install_dir UNUSED_PARAM)
735{
736}
Denys Vlasenko8f0af3b2010-11-29 02:55:35 +0100737# endif
Denis Vlasenko724d1962007-10-10 14:41:07 +0000738
Denys Vlasenko8e950682016-05-31 02:42:49 +0200739# if ENABLE_BUSYBOX
Ron Yorston1b0dcc02016-07-05 14:07:50 +0100740static void run_applet_and_exit(const char *name, char **argv) NORETURN;
741
Denis Vlasenko724d1962007-10-10 14:41:07 +0000742/* If we were called as "busybox..." */
743static int busybox_main(char **argv)
744{
745 if (!argv[1]) {
746 /* Called without arguments */
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000747 const char *a;
Denys Vlasenko0149f022009-05-19 18:01:42 +0200748 int col;
749 unsigned output_width;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000750 help:
751 output_width = 80;
752 if (ENABLE_FEATURE_AUTOWIDTH) {
753 /* Obtain the terminal width */
Denys Vlasenko641caae2015-10-23 01:44:22 +0200754 output_width = get_terminal_width(2);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000755 }
Denis Vlasenko724d1962007-10-10 14:41:07 +0000756
Denis Vlasenko21278df2008-06-25 12:15:46 +0000757 dup2(1, 2);
Denys Vlasenko630dde12009-08-30 19:57:49 +0200758 full_write2_str(bb_banner); /* reuse const string */
759 full_write2_str(" multi-call binary.\n"); /* reuse */
760 full_write2_str(
Aaro Koskinen7b729ed2015-04-02 00:55:17 +0300761 "BusyBox is copyrighted by many authors between 1998-2015.\n"
Bradley M. Kuhn0e941d52012-07-13 11:38:38 -0400762 "Licensed under GPLv2. See source distribution for detailed\n"
763 "copyright notices.\n"
Denys Vlasenko5a7c7202010-04-20 21:02:57 -0400764 "\n"
Denys Vlasenko02b8b9b2012-05-14 23:52:57 +0200765 "Usage: busybox [function [arguments]...]\n"
Denys Vlasenkoba888262012-03-22 11:15:06 +0100766 " or: busybox --list"IF_FEATURE_INSTALLER("[-full]")"\n"
767 IF_FEATURE_INSTALLER(
768 " or: busybox --install [-s] [DIR]\n"
769 )
Denys Vlasenko5a7c7202010-04-20 21:02:57 -0400770 " or: function [arguments]...\n"
771 "\n"
Ron Yorstonae57af62015-05-30 17:13:52 +0100772 IF_NOT_FEATURE_SH_STANDALONE(
Denys Vlasenko5a7c7202010-04-20 21:02:57 -0400773 "\tBusyBox is a multi-call binary that combines many common Unix\n"
774 "\tutilities into a single executable. Most people will create a\n"
775 "\tlink to busybox for each function they wish to use and BusyBox\n"
776 "\twill act like whatever it was invoked as.\n"
Ron Yorstonae57af62015-05-30 17:13:52 +0100777 )
778 IF_FEATURE_SH_STANDALONE(
779 "\tBusyBox is a multi-call binary that combines many common Unix\n"
780 "\tutilities into a single executable. The shell in this build\n"
781 "\tis configured to run built-in utilities without $PATH search.\n"
782 "\tYou don't need to install a link to busybox for each utility.\n"
783 "\tTo run external program, use full path (/sbin/ip instead of ip).\n"
784 )
Denys Vlasenko5a7c7202010-04-20 21:02:57 -0400785 "\n"
786 "Currently defined functions:\n"
787 );
Denis Vlasenko724d1962007-10-10 14:41:07 +0000788 col = 0;
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000789 a = applet_names;
Denys Vlasenko0149f022009-05-19 18:01:42 +0200790 /* prevent last comma to be in the very last pos */
791 output_width--;
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000792 while (*a) {
Denys Vlasenko0149f022009-05-19 18:01:42 +0200793 int len2 = strlen(a) + 2;
794 if (col >= (int)output_width - len2) {
Denis Vlasenko79cedcb2008-04-08 21:13:28 +0000795 full_write2_str(",\n");
Denis Vlasenko724d1962007-10-10 14:41:07 +0000796 col = 0;
797 }
Denys Vlasenko0149f022009-05-19 18:01:42 +0200798 if (col == 0) {
799 col = 6;
800 full_write2_str("\t");
801 } else {
802 full_write2_str(", ");
803 }
Denis Vlasenko79cedcb2008-04-08 21:13:28 +0000804 full_write2_str(a);
Denys Vlasenko0149f022009-05-19 18:01:42 +0200805 col += len2;
806 a += len2 - 1;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000807 }
Denys Vlasenko7d877fc2016-11-28 01:29:28 +0100808 full_write2_str("\n");
Denis Vlasenko724d1962007-10-10 14:41:07 +0000809 return 0;
810 }
811
Denys Vlasenko8dff01d2015-03-12 17:48:34 +0100812 if (is_prefixed_with(argv[1], "--list")) {
Denys Vlasenko5a7c7202010-04-20 21:02:57 -0400813 unsigned i = 0;
814 const char *a = applet_names;
815 dup2(1, 2);
816 while (*a) {
Denys Vlasenko8e950682016-05-31 02:42:49 +0200817# if ENABLE_FEATURE_INSTALLER
Denys Vlasenkoba888262012-03-22 11:15:06 +0100818 if (argv[1][6]) /* --list-full? */
Denys Vlasenko5a7c7202010-04-20 21:02:57 -0400819 full_write2_str(install_dir[APPLET_INSTALL_LOC(i)] + 1);
Denys Vlasenko8e950682016-05-31 02:42:49 +0200820# endif
Denys Vlasenko5a7c7202010-04-20 21:02:57 -0400821 full_write2_str(a);
822 full_write2_str("\n");
823 i++;
Ron Yorston2b919582016-04-08 11:57:20 +0100824 while (*a++ != '\0')
825 continue;
Denys Vlasenko5a7c7202010-04-20 21:02:57 -0400826 }
827 return 0;
828 }
829
Denis Vlasenko724d1962007-10-10 14:41:07 +0000830 if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) {
Denys Vlasenko3b405432009-07-15 00:35:34 +0200831 int use_symbolic_links;
Denis Vlasenko724d1962007-10-10 14:41:07 +0000832 const char *busybox;
Denys Vlasenko4a2a86d2011-03-06 06:02:31 +0100833
Denis Vlasenko724d1962007-10-10 14:41:07 +0000834 busybox = xmalloc_readlink(bb_busybox_exec_path);
Denys Vlasenko4a2a86d2011-03-06 06:02:31 +0100835 if (!busybox) {
836 /* bb_busybox_exec_path is usually "/proc/self/exe".
837 * In chroot, readlink("/proc/self/exe") usually fails.
838 * In such case, better use argv[0] as symlink target
839 * if it is a full path name.
840 */
Denys Vlasenko5c942712011-03-12 06:08:28 +0100841 if (argv[0][0] != '/')
842 bb_error_msg_and_die("'%s' is not an absolute path", argv[0]);
843 busybox = argv[0];
Denys Vlasenko4a2a86d2011-03-06 06:02:31 +0100844 }
845 /* busybox --install [-s] [DIR]:
846 * -s: make symlinks
847 * DIR: directory to install links to
848 */
Denys Vlasenkoba888262012-03-22 11:15:06 +0100849 use_symbolic_links = (argv[2] && strcmp(argv[2], "-s") == 0 && ++argv);
Denys Vlasenko3b405432009-07-15 00:35:34 +0200850 install_links(busybox, use_symbolic_links, argv[2]);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000851 return 0;
852 }
853
854 if (strcmp(argv[1], "--help") == 0) {
855 /* "busybox --help [<applet>]" */
856 if (!argv[2])
857 goto help;
858 /* convert to "<applet> --help" */
859 argv[0] = argv[2];
860 argv[2] = NULL;
861 } else {
862 /* "busybox <applet> arg1 arg2 ..." */
863 argv++;
864 }
865 /* We support "busybox /a/path/to/applet args..." too. Allows for
866 * "#!/bin/busybox"-style wrappers */
867 applet_name = bb_get_last_path_component_nostrip(argv[0]);
868 run_applet_and_exit(applet_name, argv);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000869}
Denys Vlasenko8e950682016-05-31 02:42:49 +0200870# endif
Denis Vlasenko724d1962007-10-10 14:41:07 +0000871
Denys Vlasenkof4f8fe82016-07-05 21:43:28 +0200872# if NUM_APPLETS > 0
Denis Vlasenkodefc1ea2008-06-27 02:52:20 +0000873void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000874{
875 int argc = 1;
876
877 while (argv[argc])
878 argc++;
879
880 /* Reinit some shared global data */
Denis Vlasenko724d1962007-10-10 14:41:07 +0000881 xfunc_error_retval = EXIT_FAILURE;
Ron Yorston610c4c32016-03-30 00:42:05 +0200882 applet_name = bb_get_last_path_component_nostrip(argv[0]);
Denys Vlasenkocd7a38a2014-09-18 00:47:05 +0200883
Denys Vlasenkocd7a38a2014-09-18 00:47:05 +0200884 /* Special case. POSIX says "test --help"
885 * should be no different from e.g. "test --foo".
886 * Thus for "test", we skip --help check.
Denys Vlasenkode5edad2015-04-21 16:00:41 +0200887 * "true" and "false" are also special.
Denys Vlasenkocd7a38a2014-09-18 00:47:05 +0200888 */
Denys Vlasenkode5edad2015-04-21 16:00:41 +0200889 if (1
Denys Vlasenkof4f8fe82016-07-05 21:43:28 +0200890# if defined APPLET_NO_test
Denys Vlasenkode5edad2015-04-21 16:00:41 +0200891 && applet_no != APPLET_NO_test
Denys Vlasenkof4f8fe82016-07-05 21:43:28 +0200892# endif
893# if defined APPLET_NO_true
Denys Vlasenkode5edad2015-04-21 16:00:41 +0200894 && applet_no != APPLET_NO_true
Denys Vlasenkof4f8fe82016-07-05 21:43:28 +0200895# endif
896# if defined APPLET_NO_false
Denys Vlasenkode5edad2015-04-21 16:00:41 +0200897 && applet_no != APPLET_NO_false
Denys Vlasenkof4f8fe82016-07-05 21:43:28 +0200898# endif
Denys Vlasenkode5edad2015-04-21 16:00:41 +0200899 ) {
900 if (argc == 2 && strcmp(argv[1], "--help") == 0) {
901 /* Make "foo --help" exit with 0: */
902 xfunc_error_retval = 0;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000903 bb_show_usage();
Denys Vlasenko5d783552013-01-17 11:02:21 +0100904 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000905 }
Denis Vlasenko724d1962007-10-10 14:41:07 +0000906 if (ENABLE_FEATURE_SUID)
Denis Vlasenko1aa7e472007-11-28 06:49:03 +0000907 check_suid(applet_no);
Denys Vlasenko215b0ca2016-08-19 18:23:56 +0200908 xfunc_error_retval = applet_main[applet_no](argc, argv);
909 /* Note: applet_main() may also not return (die on a xfunc or such) */
910 xfunc_die();
Denis Vlasenko724d1962007-10-10 14:41:07 +0000911}
Denys Vlasenkof4f8fe82016-07-05 21:43:28 +0200912# endif /* NUM_APPLETS > 0 */
Denis Vlasenko724d1962007-10-10 14:41:07 +0000913
Denys Vlasenko7e8218f2016-11-18 21:42:44 +0100914# if ENABLE_BUSYBOX || NUM_APPLETS > 0
Ron Yorstonce824ae2016-06-07 12:12:07 +0100915static NORETURN void run_applet_and_exit(const char *name, char **argv)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000916{
Denys Vlasenko7e8218f2016-11-18 21:42:44 +0100917# if ENABLE_BUSYBOX
Denys Vlasenko8dff01d2015-03-12 17:48:34 +0100918 if (is_prefixed_with(name, "busybox"))
Denis Vlasenko724d1962007-10-10 14:41:07 +0000919 exit(busybox_main(argv));
Denys Vlasenko7e8218f2016-11-18 21:42:44 +0100920# endif
921# if NUM_APPLETS > 0
Ron Yorston610c4c32016-03-30 00:42:05 +0200922 /* find_applet_by_name() search is more expensive, so goes second */
Denys Vlasenkof4f8fe82016-07-05 21:43:28 +0200923 {
924 int applet = find_applet_by_name(name);
925 if (applet >= 0)
926 run_applet_no_and_exit(applet, argv);
927 }
Denys Vlasenko7e8218f2016-11-18 21:42:44 +0100928# endif
Ron Yorstonce824ae2016-06-07 12:12:07 +0100929
930 /*bb_error_msg_and_die("applet not found"); - links in printf */
931 full_write2_str(applet_name);
932 full_write2_str(": applet not found\n");
933 /* POSIX: "If a command is not found, the exit status shall be 127" */
934 exit(127);
Denis Vlasenko724d1962007-10-10 14:41:07 +0000935}
Denys Vlasenko7e8218f2016-11-18 21:42:44 +0100936# endif
Denis Vlasenko724d1962007-10-10 14:41:07 +0000937
Denis Vlasenko10f6fb12008-04-29 00:10:27 +0000938#endif /* !defined(SINGLE_APPLET_MAIN) */
Denis Vlasenko468aea22008-04-01 14:47:57 +0000939
940
Denis Vlasenko724d1962007-10-10 14:41:07 +0000941#if ENABLE_BUILD_LIBBUSYBOX
Denis Vlasenko85c24712008-03-17 09:04:04 +0000942int lbb_main(char **argv)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000943#else
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000944int main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko724d1962007-10-10 14:41:07 +0000945#endif
946{
Denys Vlasenkobc14f4d2016-04-03 16:06:42 +0200947#if 0
948 /* TODO: find a use for a block of memory between end of .bss
949 * and end of page. For example, I'm getting "_end:0x812e698 2408 bytes"
950 * - more than 2k of wasted memory (in this particular build)
951 * *per each running process*!
952 * (If your linker does not generate "_end" name, weak attribute
953 * makes &_end == NULL, end_len == 0 here.)
954 */
955 extern char _end[] __attribute__((weak));
956 unsigned end_len = (-(int)_end) & 0xfff;
957 printf("_end:%p %u bytes\n", &_end, end_len);
958#endif
959
Denys Vlasenko1f7c1672009-09-06 02:12:28 +0200960 /* Tweak malloc for reduced memory consumption */
Denys Vlasenko1f7c1672009-09-06 02:12:28 +0200961#ifdef M_TRIM_THRESHOLD
962 /* M_TRIM_THRESHOLD is the maximum amount of freed top-most memory
963 * to keep before releasing to the OS
964 * Default is way too big: 256k
965 */
Denys Vlasenkofe86d6b2011-06-03 21:39:42 +0200966 mallopt(M_TRIM_THRESHOLD, 8 * 1024);
Denys Vlasenko1f7c1672009-09-06 02:12:28 +0200967#endif
968#ifdef M_MMAP_THRESHOLD
969 /* M_MMAP_THRESHOLD is the request size threshold for using mmap()
970 * Default is too big: 256k
971 */
Denys Vlasenkofe86d6b2011-06-03 21:39:42 +0200972 mallopt(M_MMAP_THRESHOLD, 32 * 1024 - 256);
Denys Vlasenko1f7c1672009-09-06 02:12:28 +0200973#endif
Denys Vlasenko7f0ebbc2016-10-03 17:42:53 +0200974#if 0 /*def M_TOP_PAD*/
975 /* When the program break is increased, then M_TOP_PAD bytes are added
976 * to the sbrk(2) request. When the heap is trimmed because of free(3),
977 * this much free space is preserved at the top of the heap.
978 * glibc default seems to be way too big: 128k, but need to verify.
979 */
980 mallopt(M_TOP_PAD, 8 * 1024);
981#endif
Denys Vlasenko1f7c1672009-09-06 02:12:28 +0200982
Denis Vlasenko724d1962007-10-10 14:41:07 +0000983#if !BB_MMU
984 /* NOMMU re-exec trick sets high-order bit in first byte of name */
985 if (argv[0][0] & 0x80) {
986 re_execed = 1;
987 argv[0][0] &= 0x7f;
988 }
989#endif
Denys Vlasenko8f0af3b2010-11-29 02:55:35 +0100990
991#if defined(SINGLE_APPLET_MAIN)
Denys Vlasenko7e8218f2016-11-18 21:42:44 +0100992
Denys Vlasenko8f0af3b2010-11-29 02:55:35 +0100993 /* Only one applet is selected in .config */
Denys Vlasenko8dff01d2015-03-12 17:48:34 +0100994 if (argv[1] && is_prefixed_with(argv[0], "busybox")) {
Denys Vlasenko8f0af3b2010-11-29 02:55:35 +0100995 /* "busybox <applet> <params>" should still work as expected */
996 argv++;
997 }
998 /* applet_names in this case is just "applet\0\0" */
999 lbb_prepare(applet_names IF_FEATURE_INDIVIDUAL(, argv));
1000 return SINGLE_APPLET_MAIN(argc, argv);
Denys Vlasenko8f0af3b2010-11-29 02:55:35 +01001001
Denys Vlasenko7e8218f2016-11-18 21:42:44 +01001002#elif !ENABLE_BUSYBOX && NUM_APPLETS == 0
1003
1004 full_write2_str(bb_basename(argv[0]));
1005 full_write2_str(": no applets enabled\n");
1006 exit(127);
1007
1008#else
1009
1010 lbb_prepare("busybox" IF_FEATURE_INDIVIDUAL(, argv));
Denys Vlasenkof4f8fe82016-07-05 21:43:28 +02001011# if !ENABLE_BUSYBOX
Ron Yorstonba120812016-06-07 10:26:24 +01001012 if (argv[1] && is_prefixed_with(bb_basename(argv[0]), "busybox"))
1013 argv++;
Denys Vlasenkof4f8fe82016-07-05 21:43:28 +02001014# endif
Denis Vlasenko724d1962007-10-10 14:41:07 +00001015 applet_name = argv[0];
1016 if (applet_name[0] == '-')
1017 applet_name++;
1018 applet_name = bb_basename(applet_name);
Denis Vlasenko724d1962007-10-10 14:41:07 +00001019 parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */
Denis Vlasenko724d1962007-10-10 14:41:07 +00001020 run_applet_and_exit(applet_name, argv);
Denys Vlasenko7e8218f2016-11-18 21:42:44 +01001021
Denis Vlasenko468aea22008-04-01 14:47:57 +00001022#endif
Denis Vlasenko724d1962007-10-10 14:41:07 +00001023}