blob: b4580a5b46357982c839a53e5adb6bccb22bd7ad [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
18#define PROTOTYPES
19#include "applets.h"
20#undef PROTOTYPES
21
Eric Andersenaad1a882001-03-16 22:47:14 +000022
Denis Vlasenko07159f02006-11-09 00:00:12 +000023/* Apparently uclibc defines __GLIBC__ (compat trick?). Oh well. */
24#if ENABLE_STATIC && defined(__GLIBC__) && !defined(__UCLIBC__)
Denis Vlasenkodf518922006-10-20 13:42:57 +000025#warning Static linking against glibc produces buggy executables
Denis Vlasenko067e3f02006-11-10 23:25:53 +000026#warning (glibc does not cope well with ld --gc-sections).
Denis Vlasenkoafea46b2006-10-29 19:37:13 +000027#warning See sources.redhat.com/bugzilla/show_bug.cgi?id=3400
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000028#warning Note that glibc is unsuitable for static linking anyway.
29#warning If you still want to do it, remove -Wl,--gc-sections
30#warning from top-level Makefile and remove this warning.
Denis Vlasenkodf518922006-10-20 13:42:57 +000031#endif
32
Rob Landley7e21d5f2006-04-27 23:34:46 +000033#if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE
34static const char usage_messages[] =
Eric Andersen674b08a2004-04-06 14:28:35 +000035#define MAKE_USAGE
36#include "usage.h"
Eric Andersen674b08a2004-04-06 14:28:35 +000037#include "applets.h"
Eric Andersen674b08a2004-04-06 14:28:35 +000038;
Eric Andersen674b08a2004-04-06 14:28:35 +000039#undef MAKE_USAGE
Rob Landley73f54702006-05-01 00:53:40 +000040#else
41#define usage_messages 0
Bernhard Reutner-Fischer81901a02006-03-31 18:43:55 +000042#endif /* ENABLE_SHOW_USAGE */
Rob Landley7e21d5f2006-04-27 23:34:46 +000043
Eric Andersen2ccfef22001-03-19 19:30:24 +000044#undef APPLET
45#undef APPLET_NOUSAGE
46#undef PROTOTYPES
47#include "applets.h"
48
Glenn L McGrath7fc504c2004-02-22 11:13:28 +000049static struct BB_applet *applet_using;
Eric Andersenaad1a882001-03-16 22:47:14 +000050
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +000051/* The -1 arises because of the {0,NULL,0,-1} entry. */
52const unsigned short NUM_APPLETS = (sizeof(applets) / sizeof(struct BB_applet) - 1);
Eric Andersen2ccfef22001-03-19 19:30:24 +000053
Robert Grieblc9aca452002-06-04 20:06:25 +000054
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +000055#if ENABLE_FEATURE_SUID_CONFIG
Robert Grieblc9aca452002-06-04 20:06:25 +000056
Robert Grieblc9aca452002-06-04 20:06:25 +000057#include <ctype.h>
Robert Grieblc9aca452002-06-04 20:06:25 +000058
Glenn L McGrathb37367a2002-08-22 13:12:40 +000059/* applets [] is const, so we have to define this "override" structure */
Bernhard Reutner-Fischer6973abc2005-10-28 09:45:07 +000060static struct BB_suid_config
Glenn L McGrath2faee7b2003-05-26 14:09:12 +000061{
Denis Vlasenko01a74f92006-09-23 16:34:39 +000062 struct BB_applet *m_applet;
Robert Grieblc9aca452002-06-04 20:06:25 +000063
Denis Vlasenko01a74f92006-09-23 16:34:39 +000064 uid_t m_uid;
65 gid_t m_gid;
66 mode_t m_mode;
Glenn L McGrathb37367a2002-08-22 13:12:40 +000067
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
Manuel Novoa III 7b565a02004-02-17 10:16:21 +000071static int 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 }
104 e[1] = 0;
105
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
Glenn L McGrath2faee7b2003-05-26 14:09:12 +0000111
Denis Vlasenkoa41fdf32007-01-29 22:51:00 +0000112#define parse_error(x) do { errmsg = x; goto pe_label; } while(0)
Glenn L McGrath2faee7b2003-05-26 14:09:12 +0000113
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000114/* Don't depend on the tools to combine strings. */
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000115static const char config_file[] = "/etc/busybox.conf";
Glenn L McGrath2faee7b2003-05-26 14:09:12 +0000116
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000117/* There are 4 chars + 1 nul for each of user/group/other. */
118static const char mode_chars[] = "Ssx-\0Ssx-\0Ttx-";
119
120/* We don't supply a value for the nul, so an index adjustment is
121 * necessary below. Also, we use unsigned short here to save some
122 * space even though these are really mode_t values. */
123static const unsigned short mode_mask[] = {
124 /* SST sst xxx --- */
125 S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* user */
126 S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* group */
127 0, S_IXOTH, S_IXOTH, 0 /* other */
128};
129
130static void parse_config_file(void)
Glenn L McGrath2faee7b2003-05-26 14:09:12 +0000131{
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000132 struct BB_suid_config *sct_head;
133 struct BB_suid_config *sct;
134 struct BB_applet *applet;
135 FILE *f;
Denis Vlasenkoa41fdf32007-01-29 22:51:00 +0000136 const char *errmsg;
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000137 char *s;
138 char *e;
139 int i, lc, section;
140 char buffer[256];
141 struct stat st;
Glenn L McGrath2faee7b2003-05-26 14:09:12 +0000142
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000143 assert(!suid_config); /* Should be set to NULL by bss init. */
Glenn L McGrath2faee7b2003-05-26 14:09:12 +0000144
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000145 if ((stat(config_file, &st) != 0) /* No config file? */
146 || !S_ISREG(st.st_mode) /* Not a regular file? */
147 || (st.st_uid != 0) /* Not owned by root? */
148 || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */
149 || !(f = fopen(config_file, "r")) /* Cannot open? */
150 ) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000151 return;
Robert Grieblc9aca452002-06-04 20:06:25 +0000152 }
Glenn L McGrathb37367a2002-08-22 13:12:40 +0000153
Manuel Novoa III 7b565a02004-02-17 10:16:21 +0000154 suid_cfg_readable = 1;
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000155 sct_head = NULL;
156 section = lc = 0;
Robert Grieblc9aca452002-06-04 20:06:25 +0000157
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000158 do {
159 s = buffer;
160
161 if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */
162 if (ferror(f)) { /* Make sure it wasn't a read error. */
163 parse_error("reading");
164 }
165 fclose(f);
166 suid_config = sct_head; /* Success, so set the pointer. */
167 return;
168 }
169
170 lc++; /* Got a (partial) line. */
171
172 /* If a line is too long for our buffer, we consider it an error.
173 * The following test does mistreat one corner case though.
174 * If the final line of the file does not end with a newline and
175 * yet exactly fills the buffer, it will be treated as too long
176 * even though there isn't really a problem. But it isn't really
177 * worth adding code to deal with such an unlikely situation, and
178 * we do err on the side of caution. Besides, the line would be
179 * too long if it did end with a newline. */
180 if (!strchr(s, '\n') && !feof(f)) {
181 parse_error("line too long");
182 }
183
184 /* Trim leading and trailing whitespace, ignoring comments, and
185 * check if the resulting string is empty. */
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000186 s = get_trimmed_slice(s, strchrnul(s, '#'));
187 if (!*s) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000188 continue;
189 }
190
191 /* Check for a section header. */
192
193 if (*s == '[') {
194 /* Unlike the old code, we ignore leading and trailing
195 * whitespace for the section name. We also require that
196 * there are no stray characters after the closing bracket. */
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000197 e = strchr(s, ']');
198 if (!e /* Missing right bracket? */
199 || e[1] /* Trailing characters? */
200 || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */
201 ) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000202 parse_error("section header");
203 }
204 /* Right now we only have one section so just check it.
205 * If more sections are added in the future, please don't
206 * resort to cascading ifs with multiple strcasecmp calls.
207 * That kind of bloated code is all too common. A loop
208 * and a string table would be a better choice unless the
209 * number of sections is very small. */
210 if (strcasecmp(s, "SUID") == 0) {
211 section = 1;
212 continue;
213 }
214 section = -1; /* Unknown section so set to skip. */
215 continue;
216 }
217
218 /* Process sections. */
219
220 if (section == 1) { /* SUID */
221 /* Since we trimmed leading and trailing space above, we're
222 * now looking for strings of the form
223 * <key>[::space::]*=[::space::]*<value>
224 * where both key and value could contain inner whitespace. */
225
226 /* First get the key (an applet name in our case). */
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000227 e = strchr(s, '=');
228 if (e) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000229 s = get_trimmed_slice(s, e);
230 }
231 if (!e || !*s) { /* Missing '=' or empty key. */
232 parse_error("keyword");
233 }
234
235 /* Ok, we have an applet name. Process the rhs if this
236 * applet is currently built in and ignore it otherwise.
237 * Note: This can hide config file bugs which only pop
238 * up when the busybox configuration is changed. */
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000239 applet = find_applet_by_name(s);
240 if (applet) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000241 /* Note: We currently don't check for duplicates!
242 * The last config line for each applet will be the
243 * one used since we insert at the head of the list.
244 * I suppose this could be considered a feature. */
245 sct = xmalloc(sizeof(struct BB_suid_config));
246 sct->m_applet = applet;
247 sct->m_mode = 0;
248 sct->m_next = sct_head;
249 sct_head = sct;
250
251 /* Get the specified mode. */
252
Rob Landleyea224be2006-06-18 20:20:07 +0000253 e = skip_whitespace(e+1);
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000254
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000255 for (i = 0; i < 3; i++) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000256 const char *q;
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000257 q = strchrnul(mode_chars + 5*i, *e++);
258 if (!*q) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000259 parse_error("mode");
260 }
261 /* Adjust by -i to account for nul. */
262 sct->m_mode |= mode_mask[(q - mode_chars) - i];
263 }
264
265 /* Now get the the user/group info. */
Bernhard Reutner-Fischer7ca61b62006-01-15 14:04:57 +0000266
Rob Landleyea224be2006-06-18 20:20:07 +0000267 s = skip_whitespace(e);
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000268
269 /* Note: We require whitespace between the mode and the
270 * user/group info. */
271 if ((s == e) || !(e = strchr(s, '.'))) {
272 parse_error("<uid>.<gid>");
273 }
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000274 *e++ = '\0';
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000275
276 /* We can't use get_ug_id here since it would exit()
277 * if a uid or gid was not found. Oh well... */
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000278 sct->m_uid = bb_strtoul(s, NULL, 10);
279 if (errno) {
280 struct passwd *pwd = getpwnam(s);
281 if (!pwd) {
282 parse_error("user");
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000283 }
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000284 sct->m_uid = pwd->pw_uid;
285 }
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000286
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000287 sct->m_gid = bb_strtoul(e, NULL, 10);
288 if (errno) {
289 struct group *grp;
290 grp = getgrnam(e);
291 if (!grp) {
292 parse_error("group");
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000293 }
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000294 sct->m_gid = grp->gr_gid;
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000295 }
296 }
297 continue;
298 }
299
300 /* Unknown sections are ignored. */
301
302 /* Encountering configuration lines prior to seeing a
303 * section header is treated as an error. This is how
Eric Andersenaff114c2004-04-14 17:51:38 +0000304 * the old code worked, but it may not be desirable.
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000305 * We may want to simply ignore such lines in case they
306 * are used in some future version of busybox. */
307 if (!section) {
308 parse_error("keyword outside section");
309 }
310
311 } while (1);
312
313 pe_label:
314 fprintf(stderr, "Parse error in %s, line %d: %s\n",
Denis Vlasenkoa41fdf32007-01-29 22:51:00 +0000315 config_file, lc, errmsg);
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000316
317 fclose(f);
318 /* Release any allocated memory before returning. */
319 while (sct_head) {
320 sct = sct_head->m_next;
321 free(sct_head);
322 sct_head = sct;
323 }
Robert Grieblc9aca452002-06-04 20:06:25 +0000324}
325
Rob Landley8a7a6782005-09-05 04:13:33 +0000326#else
Denis Vlasenko218f2f42007-01-24 22:02:01 +0000327#define parse_config_file() ((void)0)
Rob Landley8a7a6782005-09-05 04:13:33 +0000328#endif /* CONFIG_FEATURE_SUID_CONFIG */
329
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000330#if ENABLE_FEATURE_SUID
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000331static void check_suid(struct BB_applet *applet)
Rob Landley8a7a6782005-09-05 04:13:33 +0000332{
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000333 uid_t ruid = getuid(); /* real [ug]id */
334 uid_t rgid = getgid();
Rob Landley8a7a6782005-09-05 04:13:33 +0000335
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000336#if ENABLE_FEATURE_SUID_CONFIG
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000337 if (suid_cfg_readable) {
338 struct BB_suid_config *sct;
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000339 mode_t m;
Rob Landley8a7a6782005-09-05 04:13:33 +0000340
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000341 for (sct = suid_config; sct; sct = sct->m_next) {
342 if (sct->m_applet == applet)
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000343 goto found;
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000344 }
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000345 /* default: drop all privileges */
346 xsetgid(rgid);
347 xsetuid(ruid);
348 return;
349 found:
350 m = sct->m_mode;
351 if (sct->m_uid == ruid)
352 /* same uid */
353 m >>= 6;
354 else if ((sct->m_gid == rgid) || ingroup(ruid, sct->m_gid))
355 /* same group / in group */
356 m >>= 3;
Rob Landley8a7a6782005-09-05 04:13:33 +0000357
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000358 if (!(m & S_IXOTH)) /* is x bit not set ? */
359 bb_error_msg_and_die("you have no permission to run this applet!");
Rob Landley8a7a6782005-09-05 04:13:33 +0000360
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000361 if (sct->m_gid != 0) {
362 /* _both_ have to be set for sgid */
363 if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
364 xsetgid(sct->m_gid);
365 } else xsetgid(rgid); /* no sgid -> drop */
366 }
367 if (sct->m_uid != 0) {
368 if (sct->m_mode & S_ISUID) xsetuid(sct->m_uid);
369 else xsetuid(ruid); /* no suid -> drop */
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000370 }
371 return;
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000372 }
373#if !ENABLE_FEATURE_SUID_CONFIG_QUIET
374 {
375 static smallint onetime = 0;
Rob Landley8a7a6782005-09-05 04:13:33 +0000376
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000377 if (!onetime) {
378 onetime = 1;
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000379 fprintf(stderr, "Using fallback suid method\n");
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000380 }
Rob Landley8a7a6782005-09-05 04:13:33 +0000381 }
382#endif
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000383#endif
Robert Grieblc9aca452002-06-04 20:06:25 +0000384
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000385 if (applet->need_suid == _BB_SUID_ALWAYS) {
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000386 if (geteuid())
387 bb_error_msg_and_die("applet requires root privileges!");
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000388 } else if (applet->need_suid == _BB_SUID_NEVER) {
389 xsetgid(rgid); /* drop all privileges */
390 xsetuid(ruid);
391 }
Rob Landley8a7a6782005-09-05 04:13:33 +0000392}
393#else
394#define check_suid(x)
395#endif /* CONFIG_FEATURE_SUID */
396
397
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{
406 int input[2], output[2], pid;
407 char *buf;
408
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000409 if (pipe(input) < 0 || pipe(output) < 0)
Rob Landley7e21d5f2006-04-27 23:34:46 +0000410 exit(1);
411
412 pid = fork();
413 switch (pid) {
414 case -1: /* error */
415 exit(1);
416 case 0: /* child */
417 close(input[1]);
418 close(output[0]);
419 uncompressStream(input[0], output[1]);
420 exit(0);
421 }
422 /* parent */
423
424 close(input[0]);
425 close(output[1]);
426 pid = fork();
427 switch (pid) {
428 case -1: /* error */
429 exit(1);
430 case 0: /* child */
Rob Landley53437472006-07-16 08:14:35 +0000431 full_write(input[1], packed_usage, sizeof(packed_usage));
Rob Landley7e21d5f2006-04-27 23:34:46 +0000432 exit(0);
433 }
434 /* parent */
435 close(input[1]);
436
437 buf = xmalloc(SIZEOF_usage_messages);
Rob Landley53437472006-07-16 08:14:35 +0000438 full_read(output[0], buf, SIZEOF_usage_messages);
Rob Landley7e21d5f2006-04-27 23:34:46 +0000439 return buf;
440}
441
442#else
Rob Landley1801e9c2006-05-03 20:19:14 +0000443#define unpack_usage_messages() usage_messages
Rob Landley7e21d5f2006-04-27 23:34:46 +0000444#endif /* ENABLE_FEATURE_COMPRESS_USAGE */
Rob Landley8a7a6782005-09-05 04:13:33 +0000445
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000446void bb_show_usage(void)
Rob Landley8a7a6782005-09-05 04:13:33 +0000447{
Rob Landley7e21d5f2006-04-27 23:34:46 +0000448 if (ENABLE_SHOW_USAGE) {
449 const char *format_string;
450 const char *usage_string = unpack_usage_messages();
451 int i;
Rob Landley8a7a6782005-09-05 04:13:33 +0000452
Rob Landley7e21d5f2006-04-27 23:34:46 +0000453 for (i = applet_using - applets; i > 0;)
454 if (!*usage_string++) --i;
455
456 format_string = "%s\n\nUsage: %s %s\n\n";
457 if (*usage_string == '\b')
458 format_string = "%s\n\nNo help available.\n\n";
Denis Vlasenko92258542006-11-01 10:25:35 +0000459 fprintf(stderr, format_string, bb_msg_full_version,
Rob Landley7e21d5f2006-04-27 23:34:46 +0000460 applet_using->name, usage_string);
Rob Landley8a7a6782005-09-05 04:13:33 +0000461 }
Rob Landley8a7a6782005-09-05 04:13:33 +0000462
Denis Vlasenko40920822006-10-03 20:28:06 +0000463 exit(xfunc_error_retval);
Rob Landley8a7a6782005-09-05 04:13:33 +0000464}
465
Rob Landley53437472006-07-16 08:14:35 +0000466static int applet_name_compare(const void *name, const void *vapplet)
Rob Landley8a7a6782005-09-05 04:13:33 +0000467{
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000468 const struct BB_applet *applet = vapplet;
Rob Landley8a7a6782005-09-05 04:13:33 +0000469
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000470 return strcmp(name, applet->name);
Rob Landley8a7a6782005-09-05 04:13:33 +0000471}
472
Rob Landley53437472006-07-16 08:14:35 +0000473struct BB_applet *find_applet_by_name(const char *name)
Rob Landley8a7a6782005-09-05 04:13:33 +0000474{
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000475 return bsearch(name, applets, NUM_APPLETS, sizeof(struct BB_applet),
476 applet_name_compare);
Rob Landley8a7a6782005-09-05 04:13:33 +0000477}
478
Rob Landley53437472006-07-16 08:14:35 +0000479void run_applet_by_name(const char *name, int argc, char **argv)
Rob Landley8a7a6782005-09-05 04:13:33 +0000480{
Denis Vlasenko0ee39992006-12-24 15:23:28 +0000481 if (ENABLE_FEATURE_SUID_CONFIG)
482 parse_config_file();
Rob Landley8a7a6782005-09-05 04:13:33 +0000483
Rob Landley8a7a6782005-09-05 04:13:33 +0000484 /* Do a binary search to find the applet entry given the name. */
485 applet_using = find_applet_by_name(name);
Rob Landley53437472006-07-16 08:14:35 +0000486 if (applet_using) {
Denis Vlasenko8f8f2682006-10-03 21:00:43 +0000487 applet_name = applet_using->name;
Denis Vlasenko0ee39992006-12-24 15:23:28 +0000488 if (argc == 2 && !strcmp(argv[1], "--help"))
489 bb_show_usage();
490 if (ENABLE_FEATURE_SUID)
491 check_suid(applet_using);
492 exit(applet_using->main(argc, argv));
Rob Landley8a7a6782005-09-05 04:13:33 +0000493 }
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000494 if (!strncmp(name, "busybox", 7))
495 exit(busybox_main(argc, argv));
Rob Landley8a7a6782005-09-05 04:13:33 +0000496}