blob: 958defe754f1fd74771bfb696b9ee30e8e086265 [file] [log] [blame]
Eric Andersenaad1a882001-03-16 22:47:14 +00001/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * 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.
8 *
Glenn L McGrath2faee7b2003-05-26 14:09:12 +00009 * Based in part on code from sash, Copyright (c) 1999 by David I. Bell
Eric Andersenaad1a882001-03-16 22:47:14 +000010 * Permission has been granted to redistribute this code under the GPL.
11 *
Rob Landley73f54702006-05-01 00:53:40 +000012 * Licensed under GPLv2 or later, see file License in this tarball for details.
Eric Andersenaad1a882001-03-16 22:47:14 +000013 */
14
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +000015#include <assert.h>
Denis Vlasenkoc7ba8b92007-02-03 17:27:14 +000016#include "busybox.h"
17
Denis Vlasenko07159f02006-11-09 00:00:12 +000018/* Apparently uclibc defines __GLIBC__ (compat trick?). Oh well. */
19#if ENABLE_STATIC && defined(__GLIBC__) && !defined(__UCLIBC__)
Denis Vlasenkodf518922006-10-20 13:42:57 +000020#warning Static linking against glibc produces buggy executables
Denis Vlasenko067e3f02006-11-10 23:25:53 +000021#warning (glibc does not cope well with ld --gc-sections).
Denis Vlasenkoafea46b2006-10-29 19:37:13 +000022#warning See sources.redhat.com/bugzilla/show_bug.cgi?id=3400
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000023#warning Note that glibc is unsuitable for static linking anyway.
24#warning If you still want to do it, remove -Wl,--gc-sections
25#warning from top-level Makefile and remove this warning.
Denis Vlasenkodf518922006-10-20 13:42:57 +000026#endif
27
Denis Vlasenko32b633a2007-04-09 03:05:48 +000028
29/* Declare <applet>_main() */
30#define PROTOTYPES
31#include "applets.h"
32#undef PROTOTYPES
33
Rob Landley7e21d5f2006-04-27 23:34:46 +000034#if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE
Denis Vlasenko32b633a2007-04-09 03:05:48 +000035/* Define usage_messages[] */
Rob Landley7e21d5f2006-04-27 23:34:46 +000036static const char usage_messages[] =
Eric Andersen674b08a2004-04-06 14:28:35 +000037#define MAKE_USAGE
38#include "usage.h"
Eric Andersen674b08a2004-04-06 14:28:35 +000039#include "applets.h"
Eric Andersen674b08a2004-04-06 14:28:35 +000040;
Eric Andersen674b08a2004-04-06 14:28:35 +000041#undef MAKE_USAGE
Rob Landley73f54702006-05-01 00:53:40 +000042#else
43#define usage_messages 0
Denis Vlasenko32b633a2007-04-09 03:05:48 +000044#endif /* SHOW_USAGE */
Rob Landley7e21d5f2006-04-27 23:34:46 +000045
Denis Vlasenko335b63d2007-04-10 21:38:30 +000046/* Define struct bb_applet applets[] */
Eric Andersen2ccfef22001-03-19 19:30:24 +000047#include "applets.h"
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +000048/* The -1 arises because of the {0,NULL,0,-1} entry. */
Denis Vlasenko335b63d2007-04-10 21:38:30 +000049const unsigned short NUM_APPLETS = sizeof(applets) / sizeof(applets[0]) - 1;
Denis Vlasenko32b633a2007-04-09 03:05:48 +000050
51
Denis Vlasenko335b63d2007-04-10 21:38:30 +000052const struct bb_applet *current_applet;
Denis Vlasenko32b633a2007-04-09 03:05:48 +000053const char *applet_name ATTRIBUTE_EXTERNALLY_VISIBLE;
Denis Vlasenko473dae02007-04-11 07:04:23 +000054#if !BB_MMU
Bernhard Reutner-Fischer163516d2007-04-10 14:16:19 +000055bool re_execed;
Denis Vlasenko32b633a2007-04-09 03:05:48 +000056#endif
57
Eric Andersen2ccfef22001-03-19 19:30:24 +000058
Robert Grieblc9aca452002-06-04 20:06:25 +000059
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +000060#if ENABLE_FEATURE_SUID_CONFIG
Robert Grieblc9aca452002-06-04 20:06:25 +000061
Denis Vlasenko32b633a2007-04-09 03:05:48 +000062/* applets[] is const, so we have to define this "override" structure */
Denis Vlasenkoc44ab012007-04-09 03:11:58 +000063static struct BB_suid_config {
Denis Vlasenko335b63d2007-04-10 21:38:30 +000064 const struct bb_applet *m_applet;
Denis Vlasenko01a74f92006-09-23 16:34:39 +000065 uid_t m_uid;
66 gid_t m_gid;
67 mode_t m_mode;
Denis Vlasenko01a74f92006-09-23 16:34:39 +000068 struct BB_suid_config *m_next;
Bernhard Reutner-Fischer6973abc2005-10-28 09:45:07 +000069} *suid_config;
Robert Grieblc9aca452002-06-04 20:06:25 +000070
Bernhard Reutner-Fischer163516d2007-04-10 14:16:19 +000071static bool suid_cfg_readable;
Robert Grieblc9aca452002-06-04 20:06:25 +000072
Glenn L McGrathb37367a2002-08-22 13:12:40 +000073/* check if u is member of group g */
Denis Vlasenko01a74f92006-09-23 16:34:39 +000074static int ingroup(uid_t u, gid_t g)
Robert Grieblc9aca452002-06-04 20:06:25 +000075{
Denis Vlasenko01a74f92006-09-23 16:34:39 +000076 struct group *grp = getgrgid(g);
Glenn L McGrathb37367a2002-08-22 13:12:40 +000077
Denis Vlasenko01a74f92006-09-23 16:34:39 +000078 if (grp) {
79 char **mem;
Glenn L McGrathb37367a2002-08-22 13:12:40 +000080
Denis Vlasenko01a74f92006-09-23 16:34:39 +000081 for (mem = grp->gr_mem; *mem; mem++) {
82 struct passwd *pwd = getpwnam(*mem);
Glenn L McGrathb37367a2002-08-22 13:12:40 +000083
Denis Vlasenko01a74f92006-09-23 16:34:39 +000084 if (pwd && (pwd->pw_uid == u))
85 return 1;
86 }
Robert Grieblc9aca452002-06-04 20:06:25 +000087 }
Denis Vlasenko01a74f92006-09-23 16:34:39 +000088 return 0;
Robert Grieblc9aca452002-06-04 20:06:25 +000089}
90
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +000091/* This should probably be a libbb routine. In that case,
92 * I'd probably rename it to something like bb_trimmed_slice.
93 */
94static char *get_trimmed_slice(char *s, char *e)
95{
96 /* First, consider the value at e to be nul and back up until we
97 * reach a non-space char. Set the char after that (possibly at
98 * the original e) to nul. */
99 while (e-- > s) {
100 if (!isspace(*e)) {
101 break;
102 }
103 }
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000104 e[1] = '\0';
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000105
106 /* Next, advance past all leading space and return a ptr to the
107 * first non-space char; possibly the terminating nul. */
Rob Landleyea224be2006-06-18 20:20:07 +0000108 return skip_whitespace(s);
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000109}
110
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000111/* Don't depend on the tools to combine strings. */
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000112static const char config_file[] = "/etc/busybox.conf";
Glenn L McGrath2faee7b2003-05-26 14:09:12 +0000113
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000114/* There are 4 chars + 1 nul for each of user/group/other. */
115static const char mode_chars[] = "Ssx-\0Ssx-\0Ttx-";
116
117/* We don't supply a value for the nul, so an index adjustment is
118 * necessary below. Also, we use unsigned short here to save some
119 * space even though these are really mode_t values. */
120static const unsigned short mode_mask[] = {
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000121 /* SST sst xxx --- */
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000122 S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* user */
123 S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* group */
124 0, S_IXOTH, S_IXOTH, 0 /* other */
125};
126
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000127#define parse_error(x) do { errmsg = x; goto pe_label; } while(0)
128
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000129static void parse_config_file(void)
Glenn L McGrath2faee7b2003-05-26 14:09:12 +0000130{
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000131 struct BB_suid_config *sct_head;
132 struct BB_suid_config *sct;
Denis Vlasenko335b63d2007-04-10 21:38:30 +0000133 const struct bb_applet *applet;
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000134 FILE *f;
Denis Vlasenkoa41fdf32007-01-29 22:51:00 +0000135 const char *errmsg;
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000136 char *s;
137 char *e;
Bernhard Reutner-Fischer163516d2007-04-10 14:16:19 +0000138 int i;
139 unsigned lc;
140 smallint section;
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000141 char buffer[256];
142 struct stat st;
Glenn L McGrath2faee7b2003-05-26 14:09:12 +0000143
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000144 assert(!suid_config); /* Should be set to NULL by bss init. */
Glenn L McGrath2faee7b2003-05-26 14:09:12 +0000145
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000146 if ((stat(config_file, &st) != 0) /* No config file? */
147 || !S_ISREG(st.st_mode) /* Not a regular file? */
148 || (st.st_uid != 0) /* Not owned by root? */
149 || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */
150 || !(f = fopen(config_file, "r")) /* Cannot open? */
151 ) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000152 return;
Robert Grieblc9aca452002-06-04 20:06:25 +0000153 }
Glenn L McGrathb37367a2002-08-22 13:12:40 +0000154
Manuel Novoa III 7b565a02004-02-17 10:16:21 +0000155 suid_cfg_readable = 1;
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000156 sct_head = NULL;
157 section = lc = 0;
Robert Grieblc9aca452002-06-04 20:06:25 +0000158
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000159 while (1) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000160 s = buffer;
161
162 if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */
163 if (ferror(f)) { /* Make sure it wasn't a read error. */
164 parse_error("reading");
165 }
166 fclose(f);
167 suid_config = sct_head; /* Success, so set the pointer. */
168 return;
169 }
170
171 lc++; /* Got a (partial) line. */
172
173 /* If a line is too long for our buffer, we consider it an error.
174 * The following test does mistreat one corner case though.
175 * If the final line of the file does not end with a newline and
176 * yet exactly fills the buffer, it will be treated as too long
177 * even though there isn't really a problem. But it isn't really
178 * worth adding code to deal with such an unlikely situation, and
179 * we do err on the side of caution. Besides, the line would be
180 * too long if it did end with a newline. */
181 if (!strchr(s, '\n') && !feof(f)) {
182 parse_error("line too long");
183 }
184
185 /* Trim leading and trailing whitespace, ignoring comments, and
186 * check if the resulting string is empty. */
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000187 s = get_trimmed_slice(s, strchrnul(s, '#'));
188 if (!*s) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000189 continue;
190 }
191
192 /* Check for a section header. */
193
194 if (*s == '[') {
195 /* Unlike the old code, we ignore leading and trailing
196 * whitespace for the section name. We also require that
197 * there are no stray characters after the closing bracket. */
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000198 e = strchr(s, ']');
199 if (!e /* Missing right bracket? */
200 || e[1] /* Trailing characters? */
201 || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */
202 ) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000203 parse_error("section header");
204 }
205 /* Right now we only have one section so just check it.
206 * If more sections are added in the future, please don't
207 * resort to cascading ifs with multiple strcasecmp calls.
208 * That kind of bloated code is all too common. A loop
209 * and a string table would be a better choice unless the
210 * number of sections is very small. */
211 if (strcasecmp(s, "SUID") == 0) {
212 section = 1;
213 continue;
214 }
215 section = -1; /* Unknown section so set to skip. */
216 continue;
217 }
218
219 /* Process sections. */
220
221 if (section == 1) { /* SUID */
222 /* Since we trimmed leading and trailing space above, we're
223 * now looking for strings of the form
224 * <key>[::space::]*=[::space::]*<value>
225 * where both key and value could contain inner whitespace. */
226
227 /* First get the key (an applet name in our case). */
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000228 e = strchr(s, '=');
229 if (e) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000230 s = get_trimmed_slice(s, e);
231 }
232 if (!e || !*s) { /* Missing '=' or empty key. */
233 parse_error("keyword");
234 }
235
236 /* Ok, we have an applet name. Process the rhs if this
237 * applet is currently built in and ignore it otherwise.
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000238 * Note: this can hide config file bugs which only pop
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000239 * up when the busybox configuration is changed. */
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000240 applet = find_applet_by_name(s);
241 if (applet) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000242 /* Note: We currently don't check for duplicates!
243 * The last config line for each applet will be the
244 * one used since we insert at the head of the list.
245 * I suppose this could be considered a feature. */
246 sct = xmalloc(sizeof(struct BB_suid_config));
247 sct->m_applet = applet;
248 sct->m_mode = 0;
249 sct->m_next = sct_head;
250 sct_head = sct;
251
252 /* Get the specified mode. */
253
Rob Landleyea224be2006-06-18 20:20:07 +0000254 e = skip_whitespace(e+1);
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000255
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000256 for (i = 0; i < 3; i++) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000257 const char *q;
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000258 q = strchrnul(mode_chars + 5*i, *e++);
259 if (!*q) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000260 parse_error("mode");
261 }
262 /* Adjust by -i to account for nul. */
263 sct->m_mode |= mode_mask[(q - mode_chars) - i];
264 }
265
266 /* Now get the the user/group info. */
Bernhard Reutner-Fischer7ca61b62006-01-15 14:04:57 +0000267
Rob Landleyea224be2006-06-18 20:20:07 +0000268 s = skip_whitespace(e);
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000269
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000270 /* Note: we require whitespace between the mode and the
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000271 * user/group info. */
272 if ((s == e) || !(e = strchr(s, '.'))) {
273 parse_error("<uid>.<gid>");
274 }
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000275 *e++ = '\0';
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000276
277 /* We can't use get_ug_id here since it would exit()
278 * if a uid or gid was not found. Oh well... */
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000279 sct->m_uid = bb_strtoul(s, NULL, 10);
280 if (errno) {
281 struct passwd *pwd = getpwnam(s);
282 if (!pwd) {
283 parse_error("user");
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000284 }
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000285 sct->m_uid = pwd->pw_uid;
286 }
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000287
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000288 sct->m_gid = bb_strtoul(e, NULL, 10);
289 if (errno) {
290 struct group *grp;
291 grp = getgrnam(e);
292 if (!grp) {
293 parse_error("group");
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000294 }
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000295 sct->m_gid = grp->gr_gid;
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000296 }
297 }
298 continue;
299 }
300
301 /* Unknown sections are ignored. */
302
303 /* Encountering configuration lines prior to seeing a
304 * section header is treated as an error. This is how
Eric Andersenaff114c2004-04-14 17:51:38 +0000305 * the old code worked, but it may not be desirable.
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000306 * We may want to simply ignore such lines in case they
307 * are used in some future version of busybox. */
308 if (!section) {
309 parse_error("keyword outside section");
310 }
311
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000312 } /* while (1) */
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000313
314 pe_label:
315 fprintf(stderr, "Parse error in %s, line %d: %s\n",
Denis Vlasenkoa41fdf32007-01-29 22:51:00 +0000316 config_file, lc, errmsg);
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000317
318 fclose(f);
319 /* Release any allocated memory before returning. */
320 while (sct_head) {
321 sct = sct_head->m_next;
322 free(sct_head);
323 sct_head = sct;
324 }
Robert Grieblc9aca452002-06-04 20:06:25 +0000325}
Rob Landley8a7a6782005-09-05 04:13:33 +0000326#else
Denis Vlasenko218f2f42007-01-24 22:02:01 +0000327#define parse_config_file() ((void)0)
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000328#endif /* FEATURE_SUID_CONFIG */
329
Rob Landley8a7a6782005-09-05 04:13:33 +0000330
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000331#if ENABLE_FEATURE_SUID
Denis Vlasenko335b63d2007-04-10 21:38:30 +0000332static void check_suid(const struct bb_applet *applet)
Rob Landley8a7a6782005-09-05 04:13:33 +0000333{
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000334 uid_t ruid = getuid(); /* real [ug]id */
335 uid_t rgid = getgid();
Rob Landley8a7a6782005-09-05 04:13:33 +0000336
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000337#if ENABLE_FEATURE_SUID_CONFIG
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000338 if (suid_cfg_readable) {
339 struct BB_suid_config *sct;
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000340 mode_t m;
Rob Landley8a7a6782005-09-05 04:13:33 +0000341
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000342 for (sct = suid_config; sct; sct = sct->m_next) {
343 if (sct->m_applet == applet)
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000344 goto found;
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000345 }
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000346 /* default: drop all privileges */
347 xsetgid(rgid);
348 xsetuid(ruid);
349 return;
350 found:
351 m = sct->m_mode;
352 if (sct->m_uid == ruid)
353 /* same uid */
354 m >>= 6;
355 else if ((sct->m_gid == rgid) || ingroup(ruid, sct->m_gid))
356 /* same group / in group */
357 m >>= 3;
Rob Landley8a7a6782005-09-05 04:13:33 +0000358
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000359 if (!(m & S_IXOTH)) /* is x bit not set ? */
360 bb_error_msg_and_die("you have no permission to run this applet!");
Rob Landley8a7a6782005-09-05 04:13:33 +0000361
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000362 if (sct->m_gid != 0) {
363 /* _both_ have to be set for sgid */
364 if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
365 xsetgid(sct->m_gid);
366 } else xsetgid(rgid); /* no sgid -> drop */
367 }
368 if (sct->m_uid != 0) {
369 if (sct->m_mode & S_ISUID) xsetuid(sct->m_uid);
370 else xsetuid(ruid); /* no suid -> drop */
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000371 }
372 return;
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000373 }
374#if !ENABLE_FEATURE_SUID_CONFIG_QUIET
375 {
Bernhard Reutner-Fischer163516d2007-04-10 14:16:19 +0000376 static bool onetime = 0;
Rob Landley8a7a6782005-09-05 04:13:33 +0000377
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000378 if (!onetime) {
379 onetime = 1;
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000380 fprintf(stderr, "Using fallback suid method\n");
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000381 }
Rob Landley8a7a6782005-09-05 04:13:33 +0000382 }
383#endif
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000384#endif
Robert Grieblc9aca452002-06-04 20:06:25 +0000385
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000386 if (applet->need_suid == _BB_SUID_ALWAYS) {
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000387 if (geteuid())
388 bb_error_msg_and_die("applet requires root privileges!");
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000389 } else if (applet->need_suid == _BB_SUID_NEVER) {
390 xsetgid(rgid); /* drop all privileges */
391 xsetuid(ruid);
392 }
Rob Landley8a7a6782005-09-05 04:13:33 +0000393}
394#else
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000395#define check_suid(x) ((void)0)
396#endif /* FEATURE_SUID */
Rob Landley8a7a6782005-09-05 04:13:33 +0000397
398
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000399#if ENABLE_FEATURE_COMPRESS_USAGE
Rob Landley8a7a6782005-09-05 04:13:33 +0000400
Rob Landley7e21d5f2006-04-27 23:34:46 +0000401#include "usage_compressed.h"
402#include "unarchive.h"
403
404static const char *unpack_usage_messages(void)
405{
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000406 char *outbuf = NULL;
407 bunzip_data *bd;
408 int i;
Rob Landley7e21d5f2006-04-27 23:34:46 +0000409
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000410 i = start_bunzip(&bd,
411 /* src_fd: */ -1,
412 /* inbuf: */ packed_usage,
413 /* len: */ sizeof(packed_usage));
414 /* read_bunzip can longjmp to start_bunzip, and ultimately
415 * end up here with i != 0 on read data errors! Not trivial */
416 if (!i) {
417 /* Cannot use xmalloc: will leak bd in NOFORK case! */
418 outbuf = malloc_or_warn(SIZEOF_usage_messages);
419 if (outbuf)
420 read_bunzip(bd, outbuf, SIZEOF_usage_messages);
Rob Landley7e21d5f2006-04-27 23:34:46 +0000421 }
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000422 dealloc_bunzip(bd);
423 return outbuf;
Rob Landley7e21d5f2006-04-27 23:34:46 +0000424}
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000425#define dealloc_usage_messages(s) free(s)
426
Rob Landley7e21d5f2006-04-27 23:34:46 +0000427#else
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000428
Rob Landley1801e9c2006-05-03 20:19:14 +0000429#define unpack_usage_messages() usage_messages
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000430#define dealloc_usage_messages(s) ((void)(s))
431
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000432#endif /* FEATURE_COMPRESS_USAGE */
433
Rob Landley8a7a6782005-09-05 04:13:33 +0000434
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000435void bb_show_usage(void)
Rob Landley8a7a6782005-09-05 04:13:33 +0000436{
Rob Landley7e21d5f2006-04-27 23:34:46 +0000437 if (ENABLE_SHOW_USAGE) {
438 const char *format_string;
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000439 const char *p;
440 const char *usage_string = p = unpack_usage_messages();
Rob Landley7e21d5f2006-04-27 23:34:46 +0000441 int i;
Rob Landley8a7a6782005-09-05 04:13:33 +0000442
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000443 i = current_applet - applets;
444 while (i) {
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000445 while (*p++) continue;
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000446 i--;
447 }
Rob Landley7e21d5f2006-04-27 23:34:46 +0000448
449 format_string = "%s\n\nUsage: %s %s\n\n";
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000450 if (*p == '\b')
Rob Landley7e21d5f2006-04-27 23:34:46 +0000451 format_string = "%s\n\nNo help available.\n\n";
Denis Vlasenko92258542006-11-01 10:25:35 +0000452 fprintf(stderr, format_string, bb_msg_full_version,
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000453 applet_name, p);
454 dealloc_usage_messages((char*)usage_string);
Rob Landley8a7a6782005-09-05 04:13:33 +0000455 }
Denis Vlasenko335b63d2007-04-10 21:38:30 +0000456 xfunc_die();
Rob Landley8a7a6782005-09-05 04:13:33 +0000457}
458
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000459
Rob Landley53437472006-07-16 08:14:35 +0000460static int applet_name_compare(const void *name, const void *vapplet)
Rob Landley8a7a6782005-09-05 04:13:33 +0000461{
Denis Vlasenko335b63d2007-04-10 21:38:30 +0000462 const struct bb_applet *applet = vapplet;
Rob Landley8a7a6782005-09-05 04:13:33 +0000463
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000464 return strcmp(name, applet->name);
Rob Landley8a7a6782005-09-05 04:13:33 +0000465}
466
Denis Vlasenko335b63d2007-04-10 21:38:30 +0000467const struct bb_applet *find_applet_by_name(const char *name)
Rob Landley8a7a6782005-09-05 04:13:33 +0000468{
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000469 /* Do a binary search to find the applet entry given the name. */
Denis Vlasenko335b63d2007-04-10 21:38:30 +0000470 return bsearch(name, applets, NUM_APPLETS, sizeof(applets[0]),
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000471 applet_name_compare);
Rob Landley8a7a6782005-09-05 04:13:33 +0000472}
473
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000474
475#if ENABLE_FEATURE_INSTALLER
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000476/* create (sym)links for each applet */
477static void install_links(const char *busybox, int use_symbolic_links)
478{
Denis Vlasenko335b63d2007-04-10 21:38:30 +0000479 /* directory table
480 * this should be consistent w/ the enum,
481 * busybox.h::bb_install_loc_t, or else... */
482 static const char usr_bin [] = "/usr/bin";
483 static const char usr_sbin[] = "/usr/sbin";
484 static const char *const install_dir[] = {
485 &usr_bin [8], /* "", equivalent to "/" for concat_path_file() */
486 &usr_bin [4], /* "/bin" */
487 &usr_sbin[4], /* "/sbin" */
488 usr_bin,
489 usr_sbin
490 };
491
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000492 int (*lf)(const char *, const char *) = link;
493 char *fpc;
494 int i;
495 int rc;
496
497 if (use_symbolic_links)
498 lf = symlink;
499
500 for (i = 0; applets[i].name != NULL; i++) {
501 fpc = concat_path_file(
Denis Vlasenko335b63d2007-04-10 21:38:30 +0000502 install_dir[applets[i].install_loc],
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000503 applets[i].name);
504 rc = lf(busybox, fpc);
505 if (rc != 0 && errno != EEXIST) {
506 bb_perror_msg("%s", fpc);
507 }
508 free(fpc);
509 }
510}
511#else
512#define install_links(x,y) ((void)0)
513#endif /* FEATURE_INSTALLER */
514
515
516/* If we were called as "busybox..." */
517static int busybox_main(int argc, char **argv)
518{
519 if (ENABLE_FEATURE_INSTALLER && argc > 1 && !strcmp(argv[1], "--install")) {
520 int use_symbolic_links = 0;
521 char *busybox;
522
523 /* to use symlinks, or not to use symlinks... */
524 if (argc > 2)
525 if (strcmp(argv[2], "-s") == 0)
526 use_symbolic_links = 1;
527
528 /* link */
529 busybox = xmalloc_readlink_or_warn("/proc/self/exe");
530 if (!busybox)
531 return 1;
532 install_links(busybox, use_symbolic_links);
533 if (ENABLE_FEATURE_CLEAN_UP)
534 free(busybox);
535 return 0;
536 }
537
538 /* Deal with --help. Also print help when called with no arguments */
539
540 if (argc == 1 || !strcmp(argv[1], "--help") ) {
541 if (argc > 2) {
542 /* set name for proper "<name>: applet not found" */
543 applet_name = argv[2];
Denis Vlasenkoe4f2d062007-04-11 17:03:19 +0000544 run_applet_and_exit(applet_name, 2, argv);
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000545 } else {
Denis Vlasenko335b63d2007-04-10 21:38:30 +0000546 const struct bb_applet *a;
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000547 int col, output_width;
548
549 output_width = 80 - sizeof("start-stop-daemon, ") - 8;
550 if (ENABLE_FEATURE_AUTOWIDTH) {
551 /* Obtain the terminal width. */
552 get_terminal_width_height(0, &output_width, NULL);
553 /* leading tab and room to wrap */
554 output_width -= sizeof("start-stop-daemon, ") + 8;
555 }
556
557 printf("%s\n"
558 "Copyright (C) 1998-2006  Erik Andersen, Rob Landley, and others.\n"
559 "Licensed under GPLv2.  See source distribution for full notice.\n"
560 "\n"
561 "Usage: busybox [function] [arguments]...\n"
562 " or: [function] [arguments]...\n"
563 "\n"
564 "\tBusyBox is a multi-call binary that combines many common Unix\n"
565 "\tutilities into a single executable. Most people will create a\n"
566 "\tlink to busybox for each function they wish to use and BusyBox\n"
567 "\twill act like whatever it was invoked as!\n"
568 "\nCurrently defined functions:\n", bb_msg_full_version);
569 col = 0;
570 a = applets;
571 while (a->name) {
572 col += printf("%s%s", (col ? ", " : "\t"), a->name);
573 a++;
574 if (col > output_width && a->name) {
575 puts(",");
576 col = 0;
577 }
578 }
579 puts("\n");
580 return 0;
581 }
Denis Vlasenko2dfdd442007-04-09 03:29:43 +0000582 } else {
583 /* we want "<argv[1]>: applet not found", not "busybox: ..." */
584 applet_name = argv[1];
Denis Vlasenkoe4f2d062007-04-11 17:03:19 +0000585 run_applet_and_exit(argv[1], argc - 1, argv + 1);
Denis Vlasenko2dfdd442007-04-09 03:29:43 +0000586 }
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000587
588 bb_error_msg_and_die("applet not found");
589}
590
Denis Vlasenkoc44ab012007-04-09 03:11:58 +0000591void run_current_applet_and_exit(int argc, char **argv)
592{
593 applet_name = current_applet->name;
594 if (argc == 2 && !strcmp(argv[1], "--help"))
595 bb_show_usage();
596 if (ENABLE_FEATURE_SUID)
597 check_suid(current_applet);
598 exit(current_applet->main(argc, argv));
599}
600
Denis Vlasenkoe4f2d062007-04-11 17:03:19 +0000601void run_applet_and_exit(const char *name, int argc, char **argv)
Rob Landley8a7a6782005-09-05 04:13:33 +0000602{
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000603 current_applet = find_applet_by_name(name);
Denis Vlasenkoc44ab012007-04-09 03:11:58 +0000604 if (current_applet)
605 run_current_applet_and_exit(argc, argv);
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000606 if (!strncmp(name, "busybox", 7))
607 exit(busybox_main(argc, argv));
Rob Landley8a7a6782005-09-05 04:13:33 +0000608}
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000609
610
611int main(int argc, char **argv)
612{
613 const char *s;
614
Denis Vlasenko473dae02007-04-11 07:04:23 +0000615#if !BB_MMU
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000616 /* NOMMU re-exec trick sets high-order bit in first byte of name */
Denis Vlasenkof1a71412007-04-10 23:32:37 +0000617 if (argv[0][0] & 0x80) {
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000618 re_execed = 1;
Denis Vlasenkof1a71412007-04-10 23:32:37 +0000619 argv[0][0] &= 0x7f;
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000620 }
621#endif
Denis Vlasenkof1a71412007-04-10 23:32:37 +0000622 applet_name = argv[0];
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000623 if (applet_name[0] == '-')
624 applet_name++;
625 s = strrchr(applet_name, '/');
626 if (s)
627 applet_name = s + 1;
628
629 if (ENABLE_FEATURE_SUID_CONFIG)
630 parse_config_file();
631
632 /* Set locale for everybody except 'init' */
633 if (ENABLE_LOCALE_SUPPORT && getpid() != 1)
634 setlocale(LC_ALL, "");
635
Denis Vlasenkoe4f2d062007-04-11 17:03:19 +0000636 run_applet_and_exit(applet_name, argc, argv);
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000637 bb_error_msg_and_die("applet not found");
638}