diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-10-10 14:41:07 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-10-10 14:41:07 +0000 |
commit | 724d196c75e097535d475528002518d5322868c6 (patch) | |
tree | 94e4345625688d6071eacecf2bd1e87a9980e90f /applets | |
parent | 82d38dab917754c9c37aaa7e414a47318b5082fe (diff) | |
download | busybox-w32-724d196c75e097535d475528002518d5322868c6.tar.gz busybox-w32-724d196c75e097535d475528002518d5322868c6.tar.bz2 busybox-w32-724d196c75e097535d475528002518d5322868c6.zip |
Move applets/applet.c into libbb, allows to get rid of --whole-archive
(smaller code). Tested in static and shared mode.
Diffstat (limited to 'applets')
-rw-r--r-- | applets/applets.c | 541 |
1 files changed, 6 insertions, 535 deletions
diff --git a/applets/applets.c b/applets/applets.c index 3b4f395c9..bbf150c79 100644 --- a/applets/applets.c +++ b/applets/applets.c | |||
@@ -1,21 +1,15 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | 1 | /* vi: set sw=4 ts=4: */ |
2 | /* | 2 | /* |
3 | * Utility routines. | 3 | * Stub for linking busybox binary against libbusybox. |
4 | * | 4 | * |
5 | * Copyright (C) tons of folks. Tracking down who wrote what | 5 | * Copyright (C) 2007 Denis Vlasenko |
6 | * isn't something I'm going to worry about... If you wrote something | ||
7 | * here, please feel free to acknowledge your work. | ||
8 | * | 6 | * |
9 | * Based in part on code from sash, Copyright (c) 1999 by David I. Bell | 7 | * Licensed under GPLv2, see file License in this tarball for details. |
10 | * Permission has been granted to redistribute this code under the GPL. | ||
11 | * | ||
12 | * Licensed under GPLv2 or later, see file License in this tarball for details. | ||
13 | */ | 8 | */ |
14 | 9 | ||
15 | #include <assert.h> | 10 | #include <assert.h> |
16 | #include "busybox.h" | 11 | #include "busybox.h" |
17 | 12 | ||
18 | |||
19 | /* Apparently uclibc defines __GLIBC__ (compat trick?). Oh well. */ | 13 | /* Apparently uclibc defines __GLIBC__ (compat trick?). Oh well. */ |
20 | #if ENABLE_STATIC && defined(__GLIBC__) && !defined(__UCLIBC__) | 14 | #if ENABLE_STATIC && defined(__GLIBC__) && !defined(__UCLIBC__) |
21 | #warning Static linking against glibc produces buggy executables | 15 | #warning Static linking against glibc produces buggy executables |
@@ -27,532 +21,9 @@ | |||
27 | #error Aborting compilation. | 21 | #error Aborting compilation. |
28 | #endif | 22 | #endif |
29 | 23 | ||
30 | 24 | #if ENABLE_BUILD_LIBBUSYBOX | |
31 | const char *applet_name; | ||
32 | #if !BB_MMU | ||
33 | bool re_execed; | ||
34 | #endif | ||
35 | |||
36 | USE_FEATURE_SUID(static uid_t ruid;) /* real uid */ | ||
37 | |||
38 | #if ENABLE_FEATURE_SUID_CONFIG | ||
39 | |||
40 | /* applets[] is const, so we have to define this "override" structure */ | ||
41 | static struct BB_suid_config { | ||
42 | const struct bb_applet *m_applet; | ||
43 | uid_t m_uid; | ||
44 | gid_t m_gid; | ||
45 | mode_t m_mode; | ||
46 | struct BB_suid_config *m_next; | ||
47 | } *suid_config; | ||
48 | |||
49 | static bool suid_cfg_readable; | ||
50 | |||
51 | /* check if u is member of group g */ | ||
52 | static int ingroup(uid_t u, gid_t g) | ||
53 | { | ||
54 | struct group *grp = getgrgid(g); | ||
55 | |||
56 | if (grp) { | ||
57 | char **mem; | ||
58 | |||
59 | for (mem = grp->gr_mem; *mem; mem++) { | ||
60 | struct passwd *pwd = getpwnam(*mem); | ||
61 | |||
62 | if (pwd && (pwd->pw_uid == u)) | ||
63 | return 1; | ||
64 | } | ||
65 | } | ||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | /* This should probably be a libbb routine. In that case, | ||
70 | * I'd probably rename it to something like bb_trimmed_slice. | ||
71 | */ | ||
72 | static char *get_trimmed_slice(char *s, char *e) | ||
73 | { | ||
74 | /* First, consider the value at e to be nul and back up until we | ||
75 | * reach a non-space char. Set the char after that (possibly at | ||
76 | * the original e) to nul. */ | ||
77 | while (e-- > s) { | ||
78 | if (!isspace(*e)) { | ||
79 | break; | ||
80 | } | ||
81 | } | ||
82 | e[1] = '\0'; | ||
83 | |||
84 | /* Next, advance past all leading space and return a ptr to the | ||
85 | * first non-space char; possibly the terminating nul. */ | ||
86 | return skip_whitespace(s); | ||
87 | } | ||
88 | |||
89 | /* Don't depend on the tools to combine strings. */ | ||
90 | static const char config_file[] ALIGN1 = "/etc/busybox.conf"; | ||
91 | |||
92 | /* We don't supply a value for the nul, so an index adjustment is | ||
93 | * necessary below. Also, we use unsigned short here to save some | ||
94 | * space even though these are really mode_t values. */ | ||
95 | static const unsigned short mode_mask[] ALIGN2 = { | ||
96 | /* SST sst xxx --- */ | ||
97 | S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* user */ | ||
98 | S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* group */ | ||
99 | 0, S_IXOTH, S_IXOTH, 0 /* other */ | ||
100 | }; | ||
101 | |||
102 | #define parse_error(x) do { errmsg = x; goto pe_label; } while (0) | ||
103 | |||
104 | static void parse_config_file(void) | ||
105 | { | ||
106 | struct BB_suid_config *sct_head; | ||
107 | struct BB_suid_config *sct; | ||
108 | const struct bb_applet *applet; | ||
109 | FILE *f; | ||
110 | const char *errmsg; | ||
111 | char *s; | ||
112 | char *e; | ||
113 | int i; | ||
114 | unsigned lc; | ||
115 | smallint section; | ||
116 | char buffer[256]; | ||
117 | struct stat st; | ||
118 | |||
119 | assert(!suid_config); /* Should be set to NULL by bss init. */ | ||
120 | |||
121 | ruid = getuid(); | ||
122 | if (ruid == 0) /* run by root - don't need to even read config file */ | ||
123 | return; | ||
124 | |||
125 | if ((stat(config_file, &st) != 0) /* No config file? */ | ||
126 | || !S_ISREG(st.st_mode) /* Not a regular file? */ | ||
127 | || (st.st_uid != 0) /* Not owned by root? */ | ||
128 | || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */ | ||
129 | || !(f = fopen(config_file, "r")) /* Cannot open? */ | ||
130 | ) { | ||
131 | return; | ||
132 | } | ||
133 | |||
134 | suid_cfg_readable = 1; | ||
135 | sct_head = NULL; | ||
136 | section = lc = 0; | ||
137 | |||
138 | while (1) { | ||
139 | s = buffer; | ||
140 | |||
141 | if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */ | ||
142 | if (ferror(f)) { /* Make sure it wasn't a read error. */ | ||
143 | parse_error("reading"); | ||
144 | } | ||
145 | fclose(f); | ||
146 | suid_config = sct_head; /* Success, so set the pointer. */ | ||
147 | return; | ||
148 | } | ||
149 | |||
150 | lc++; /* Got a (partial) line. */ | ||
151 | |||
152 | /* If a line is too long for our buffer, we consider it an error. | ||
153 | * The following test does mistreat one corner case though. | ||
154 | * If the final line of the file does not end with a newline and | ||
155 | * yet exactly fills the buffer, it will be treated as too long | ||
156 | * even though there isn't really a problem. But it isn't really | ||
157 | * worth adding code to deal with such an unlikely situation, and | ||
158 | * we do err on the side of caution. Besides, the line would be | ||
159 | * too long if it did end with a newline. */ | ||
160 | if (!strchr(s, '\n') && !feof(f)) { | ||
161 | parse_error("line too long"); | ||
162 | } | ||
163 | |||
164 | /* Trim leading and trailing whitespace, ignoring comments, and | ||
165 | * check if the resulting string is empty. */ | ||
166 | s = get_trimmed_slice(s, strchrnul(s, '#')); | ||
167 | if (!*s) { | ||
168 | continue; | ||
169 | } | ||
170 | |||
171 | /* Check for a section header. */ | ||
172 | |||
173 | if (*s == '[') { | ||
174 | /* Unlike the old code, we ignore leading and trailing | ||
175 | * whitespace for the section name. We also require that | ||
176 | * there are no stray characters after the closing bracket. */ | ||
177 | e = strchr(s, ']'); | ||
178 | if (!e /* Missing right bracket? */ | ||
179 | || e[1] /* Trailing characters? */ | ||
180 | || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */ | ||
181 | ) { | ||
182 | parse_error("section header"); | ||
183 | } | ||
184 | /* Right now we only have one section so just check it. | ||
185 | * If more sections are added in the future, please don't | ||
186 | * resort to cascading ifs with multiple strcasecmp calls. | ||
187 | * That kind of bloated code is all too common. A loop | ||
188 | * and a string table would be a better choice unless the | ||
189 | * number of sections is very small. */ | ||
190 | if (strcasecmp(s, "SUID") == 0) { | ||
191 | section = 1; | ||
192 | continue; | ||
193 | } | ||
194 | section = -1; /* Unknown section so set to skip. */ | ||
195 | continue; | ||
196 | } | ||
197 | |||
198 | /* Process sections. */ | ||
199 | |||
200 | if (section == 1) { /* SUID */ | ||
201 | /* Since we trimmed leading and trailing space above, we're | ||
202 | * now looking for strings of the form | ||
203 | * <key>[::space::]*=[::space::]*<value> | ||
204 | * where both key and value could contain inner whitespace. */ | ||
205 | |||
206 | /* First get the key (an applet name in our case). */ | ||
207 | e = strchr(s, '='); | ||
208 | if (e) { | ||
209 | s = get_trimmed_slice(s, e); | ||
210 | } | ||
211 | if (!e || !*s) { /* Missing '=' or empty key. */ | ||
212 | parse_error("keyword"); | ||
213 | } | ||
214 | |||
215 | /* Ok, we have an applet name. Process the rhs if this | ||
216 | * applet is currently built in and ignore it otherwise. | ||
217 | * Note: this can hide config file bugs which only pop | ||
218 | * up when the busybox configuration is changed. */ | ||
219 | applet = find_applet_by_name(s); | ||
220 | if (applet) { | ||
221 | /* Note: We currently don't check for duplicates! | ||
222 | * The last config line for each applet will be the | ||
223 | * one used since we insert at the head of the list. | ||
224 | * I suppose this could be considered a feature. */ | ||
225 | sct = xmalloc(sizeof(struct BB_suid_config)); | ||
226 | sct->m_applet = applet; | ||
227 | sct->m_mode = 0; | ||
228 | sct->m_next = sct_head; | ||
229 | sct_head = sct; | ||
230 | |||
231 | /* Get the specified mode. */ | ||
232 | |||
233 | e = skip_whitespace(e+1); | ||
234 | |||
235 | for (i = 0; i < 3; i++) { | ||
236 | /* There are 4 chars + 1 nul for each of user/group/other. */ | ||
237 | static const char mode_chars[] ALIGN1 = "Ssx-\0" "Ssx-\0" "Ttx-"; | ||
238 | |||
239 | const char *q; | ||
240 | q = strchrnul(mode_chars + 5*i, *e++); | ||
241 | if (!*q) { | ||
242 | parse_error("mode"); | ||
243 | } | ||
244 | /* Adjust by -i to account for nul. */ | ||
245 | sct->m_mode |= mode_mask[(q - mode_chars) - i]; | ||
246 | } | ||
247 | |||
248 | /* Now get the the user/group info. */ | ||
249 | |||
250 | s = skip_whitespace(e); | ||
251 | |||
252 | /* Note: we require whitespace between the mode and the | ||
253 | * user/group info. */ | ||
254 | if ((s == e) || !(e = strchr(s, '.'))) { | ||
255 | parse_error("<uid>.<gid>"); | ||
256 | } | ||
257 | *e++ = '\0'; | ||
258 | |||
259 | /* We can't use get_ug_id here since it would exit() | ||
260 | * if a uid or gid was not found. Oh well... */ | ||
261 | sct->m_uid = bb_strtoul(s, NULL, 10); | ||
262 | if (errno) { | ||
263 | struct passwd *pwd = getpwnam(s); | ||
264 | if (!pwd) { | ||
265 | parse_error("user"); | ||
266 | } | ||
267 | sct->m_uid = pwd->pw_uid; | ||
268 | } | ||
269 | |||
270 | sct->m_gid = bb_strtoul(e, NULL, 10); | ||
271 | if (errno) { | ||
272 | struct group *grp; | ||
273 | grp = getgrnam(e); | ||
274 | if (!grp) { | ||
275 | parse_error("group"); | ||
276 | } | ||
277 | sct->m_gid = grp->gr_gid; | ||
278 | } | ||
279 | } | ||
280 | continue; | ||
281 | } | ||
282 | |||
283 | /* Unknown sections are ignored. */ | ||
284 | |||
285 | /* Encountering configuration lines prior to seeing a | ||
286 | * section header is treated as an error. This is how | ||
287 | * the old code worked, but it may not be desirable. | ||
288 | * We may want to simply ignore such lines in case they | ||
289 | * are used in some future version of busybox. */ | ||
290 | if (!section) { | ||
291 | parse_error("keyword outside section"); | ||
292 | } | ||
293 | |||
294 | } /* while (1) */ | ||
295 | |||
296 | pe_label: | ||
297 | fprintf(stderr, "Parse error in %s, line %d: %s\n", | ||
298 | config_file, lc, errmsg); | ||
299 | |||
300 | fclose(f); | ||
301 | /* Release any allocated memory before returning. */ | ||
302 | while (sct_head) { | ||
303 | sct = sct_head->m_next; | ||
304 | free(sct_head); | ||
305 | sct_head = sct; | ||
306 | } | ||
307 | } | ||
308 | #else | ||
309 | static inline void parse_config_file(void) | ||
310 | { | ||
311 | USE_FEATURE_SUID(ruid = getuid();) | ||
312 | } | ||
313 | #endif /* FEATURE_SUID_CONFIG */ | ||
314 | |||
315 | |||
316 | #if ENABLE_FEATURE_SUID | ||
317 | static void check_suid(const struct bb_applet *applet) | ||
318 | { | ||
319 | gid_t rgid; /* real gid */ | ||
320 | |||
321 | if (ruid == 0) /* set by parse_config_file() */ | ||
322 | return; /* run by root - no need to check more */ | ||
323 | rgid = getgid(); | ||
324 | |||
325 | #if ENABLE_FEATURE_SUID_CONFIG | ||
326 | if (suid_cfg_readable) { | ||
327 | uid_t uid; | ||
328 | struct BB_suid_config *sct; | ||
329 | mode_t m; | ||
330 | |||
331 | for (sct = suid_config; sct; sct = sct->m_next) { | ||
332 | if (sct->m_applet == applet) | ||
333 | goto found; | ||
334 | } | ||
335 | /* default: drop all privileges */ | ||
336 | xsetgid(rgid); | ||
337 | xsetuid(ruid); | ||
338 | return; | ||
339 | found: | ||
340 | m = sct->m_mode; | ||
341 | if (sct->m_uid == ruid) | ||
342 | /* same uid */ | ||
343 | m >>= 6; | ||
344 | else if ((sct->m_gid == rgid) || ingroup(ruid, sct->m_gid)) | ||
345 | /* same group / in group */ | ||
346 | m >>= 3; | ||
347 | |||
348 | if (!(m & S_IXOTH)) /* is x bit not set ? */ | ||
349 | bb_error_msg_and_die("you have no permission to run this applet!"); | ||
350 | |||
351 | /* _both_ sgid and group_exec have to be set for setegid */ | ||
352 | if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) | ||
353 | rgid = sct->m_gid; | ||
354 | /* else (no setegid) we will set egid = rgid */ | ||
355 | |||
356 | /* We set effective AND saved ids. If saved-id is not set | ||
357 | * like we do below, seteiud(0) can still later succeed! */ | ||
358 | if (setresgid(-1, rgid, rgid)) | ||
359 | bb_perror_msg_and_die("setresgid"); | ||
360 | |||
361 | /* do we have to set effective uid? */ | ||
362 | uid = ruid; | ||
363 | if (sct->m_mode & S_ISUID) | ||
364 | uid = sct->m_uid; | ||
365 | /* else (no seteuid) we will set euid = ruid */ | ||
366 | |||
367 | if (setresuid(-1, uid, uid)) | ||
368 | bb_perror_msg_and_die("setresuid"); | ||
369 | return; | ||
370 | } | ||
371 | #if !ENABLE_FEATURE_SUID_CONFIG_QUIET | ||
372 | { | ||
373 | static bool onetime = 0; | ||
374 | |||
375 | if (!onetime) { | ||
376 | onetime = 1; | ||
377 | fprintf(stderr, "Using fallback suid method\n"); | ||
378 | } | ||
379 | } | ||
380 | #endif | ||
381 | #endif | ||
382 | |||
383 | if (applet->need_suid == _BB_SUID_ALWAYS) { | ||
384 | /* Real uid is not 0. If euid isn't 0 too, suid bit | ||
385 | * is most probably not set on our executable */ | ||
386 | if (geteuid()) | ||
387 | bb_error_msg_and_die("applet requires root privileges!"); | ||
388 | } else if (applet->need_suid == _BB_SUID_NEVER) { | ||
389 | xsetgid(rgid); /* drop all privileges */ | ||
390 | xsetuid(ruid); | ||
391 | } | ||
392 | } | ||
393 | #else | ||
394 | #define check_suid(x) ((void)0) | ||
395 | #endif /* FEATURE_SUID */ | ||
396 | |||
397 | |||
398 | #if ENABLE_FEATURE_INSTALLER | ||
399 | /* create (sym)links for each applet */ | ||
400 | static void install_links(const char *busybox, int use_symbolic_links) | ||
401 | { | ||
402 | /* directory table | ||
403 | * this should be consistent w/ the enum, | ||
404 | * busybox.h::bb_install_loc_t, or else... */ | ||
405 | static const char usr_bin [] ALIGN1 = "/usr/bin"; | ||
406 | static const char usr_sbin[] ALIGN1 = "/usr/sbin"; | ||
407 | static const char *const install_dir[] = { | ||
408 | &usr_bin [8], /* "", equivalent to "/" for concat_path_file() */ | ||
409 | &usr_bin [4], /* "/bin" */ | ||
410 | &usr_sbin[4], /* "/sbin" */ | ||
411 | usr_bin, | ||
412 | usr_sbin | ||
413 | }; | ||
414 | |||
415 | int (*lf)(const char *, const char *) = link; | ||
416 | char *fpc; | ||
417 | int i; | ||
418 | int rc; | ||
419 | |||
420 | if (use_symbolic_links) | ||
421 | lf = symlink; | ||
422 | |||
423 | for (i = 0; applets[i].name != NULL; i++) { | ||
424 | fpc = concat_path_file( | ||
425 | install_dir[applets[i].install_loc], | ||
426 | applets[i].name); | ||
427 | rc = lf(busybox, fpc); | ||
428 | if (rc != 0 && errno != EEXIST) { | ||
429 | bb_simple_perror_msg(fpc); | ||
430 | } | ||
431 | free(fpc); | ||
432 | } | ||
433 | } | ||
434 | #else | ||
435 | #define install_links(x,y) ((void)0) | ||
436 | #endif /* FEATURE_INSTALLER */ | ||
437 | |||
438 | |||
439 | /* If we were called as "busybox..." */ | ||
440 | static int busybox_main(char **argv) | ||
441 | { | ||
442 | if (!argv[1]) { | ||
443 | /* Called without arguments */ | ||
444 | const struct bb_applet *a; | ||
445 | int col, output_width; | ||
446 | help: | ||
447 | output_width = 80; | ||
448 | if (ENABLE_FEATURE_AUTOWIDTH) { | ||
449 | /* Obtain the terminal width */ | ||
450 | get_terminal_width_height(0, &output_width, NULL); | ||
451 | } | ||
452 | /* leading tab and room to wrap */ | ||
453 | output_width -= sizeof("start-stop-daemon, ") + 8; | ||
454 | |||
455 | printf("%s multi-call binary\n", bb_banner); /* reuse const string... */ | ||
456 | printf("Copyright (C) 1998-2006 Erik Andersen, Rob Landley, and others.\n" | ||
457 | "Licensed under GPLv2. See source distribution for full notice.\n" | ||
458 | "\n" | ||
459 | "Usage: busybox [function] [arguments]...\n" | ||
460 | " or: [function] [arguments]...\n" | ||
461 | "\n" | ||
462 | "\tBusyBox is a multi-call binary that combines many common Unix\n" | ||
463 | "\tutilities into a single executable. Most people will create a\n" | ||
464 | "\tlink to busybox for each function they wish to use and BusyBox\n" | ||
465 | "\twill act like whatever it was invoked as!\n" | ||
466 | "\nCurrently defined functions:\n"); | ||
467 | col = 0; | ||
468 | a = applets; | ||
469 | while (a->name) { | ||
470 | if (col > output_width) { | ||
471 | puts(","); | ||
472 | col = 0; | ||
473 | } | ||
474 | col += printf("%s%s", (col ? ", " : "\t"), a->name); | ||
475 | a++; | ||
476 | } | ||
477 | puts("\n"); | ||
478 | return 0; | ||
479 | } | ||
480 | |||
481 | if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) { | ||
482 | const char *busybox; | ||
483 | busybox = xmalloc_readlink(bb_busybox_exec_path); | ||
484 | if (!busybox) | ||
485 | busybox = bb_busybox_exec_path; | ||
486 | /* -s makes symlinks */ | ||
487 | install_links(busybox, argv[2] && strcmp(argv[2], "-s") == 0); | ||
488 | return 0; | ||
489 | } | ||
490 | |||
491 | if (strcmp(argv[1], "--help") == 0) { | ||
492 | /* "busybox --help [<applet>]" */ | ||
493 | if (!argv[2]) | ||
494 | goto help; | ||
495 | /* convert to "<applet> --help" */ | ||
496 | argv[0] = argv[2]; | ||
497 | argv[2] = NULL; | ||
498 | } else { | ||
499 | /* "busybox <applet> arg1 arg2 ..." */ | ||
500 | argv++; | ||
501 | } | ||
502 | /* We support "busybox /a/path/to/applet args..." too. Allows for | ||
503 | * "#!/bin/busybox"-style wrappers */ | ||
504 | applet_name = bb_get_last_path_component_nostrip(argv[0]); | ||
505 | run_applet_and_exit(applet_name, argv); | ||
506 | bb_error_msg_and_die("applet not found"); | ||
507 | } | ||
508 | |||
509 | void run_appletstruct_and_exit(const struct bb_applet *applet, char **argv) | ||
510 | { | ||
511 | int argc = 1; | ||
512 | |||
513 | while (argv[argc]) | ||
514 | argc++; | ||
515 | |||
516 | /* Reinit some shared global data */ | ||
517 | optind = 1; | ||
518 | xfunc_error_retval = EXIT_FAILURE; | ||
519 | |||
520 | applet_name = applet->name; | ||
521 | if (argc == 2 && !strcmp(argv[1], "--help")) | ||
522 | bb_show_usage(); | ||
523 | if (ENABLE_FEATURE_SUID) | ||
524 | check_suid(applet); | ||
525 | exit(applet->main(argc, argv)); | ||
526 | } | ||
527 | |||
528 | void run_applet_and_exit(const char *name, char **argv) | ||
529 | { | ||
530 | const struct bb_applet *applet = find_applet_by_name(name); | ||
531 | if (applet) | ||
532 | run_appletstruct_and_exit(applet, argv); | ||
533 | if (!strncmp(name, "busybox", 7)) | ||
534 | exit(busybox_main(argv)); | ||
535 | } | ||
536 | |||
537 | |||
538 | int main(int argc, char **argv) | 25 | int main(int argc, char **argv) |
539 | { | 26 | { |
540 | bbox_prepare_main(argv); | 27 | return libbusybox_main(argc, argv); |
541 | |||
542 | #if !BB_MMU | ||
543 | /* NOMMU re-exec trick sets high-order bit in first byte of name */ | ||
544 | if (argv[0][0] & 0x80) { | ||
545 | re_execed = 1; | ||
546 | argv[0][0] &= 0x7f; | ||
547 | } | ||
548 | #endif | ||
549 | applet_name = argv[0]; | ||
550 | if (applet_name[0] == '-') | ||
551 | applet_name++; | ||
552 | applet_name = bb_basename(applet_name); | ||
553 | |||
554 | parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */ | ||
555 | |||
556 | run_applet_and_exit(applet_name, argv); | ||
557 | bb_error_msg_and_die("applet not found"); | ||
558 | } | 28 | } |
29 | #endif | ||