Contents of /trunk/mkinitrd-magellan/busybox/libbb/appletlib.c
Parent Directory | Revision Log
Revision 984 -
(show annotations)
(download)
Sun May 30 11:32:42 2010 UTC (14 years, 4 months ago) by niro
File MIME type: text/plain
File size: 21930 byte(s)
Sun May 30 11:32:42 2010 UTC (14 years, 4 months ago) by niro
File MIME type: text/plain
File size: 21930 byte(s)
-updated to busybox-1.16.1 and enabled blkid/uuid support in default config
1 | /* 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 | * |
9 | * Based in part on code from sash, Copyright (c) 1999 by David I. Bell |
10 | * Permission has been granted to redistribute this code under the GPL. |
11 | * |
12 | * Licensed under GPLv2 or later, see file License in this tarball for details. |
13 | */ |
14 | |
15 | /* We are trying to not use printf, this benefits the case when selected |
16 | * applets are really simple. Example: |
17 | * |
18 | * $ ./busybox |
19 | * ... |
20 | * Currently defined functions: |
21 | * basename, false, true |
22 | * |
23 | * $ size busybox |
24 | * text data bss dec hex filename |
25 | * 4473 52 72 4597 11f5 busybox |
26 | * |
27 | * FEATURE_INSTALLER or FEATURE_SUID will still link printf routines in. :( |
28 | */ |
29 | |
30 | #include "busybox.h" |
31 | #include <assert.h> |
32 | #include <malloc.h> |
33 | #include <sys/user.h> /* PAGE_SIZE */ |
34 | |
35 | |
36 | /* Declare <applet>_main() */ |
37 | #define PROTOTYPES |
38 | #include "applets.h" |
39 | #undef PROTOTYPES |
40 | |
41 | #if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE |
42 | /* Define usage_messages[] */ |
43 | static const char usage_messages[] ALIGN1 = "" |
44 | #define MAKE_USAGE |
45 | #include "usage.h" |
46 | #include "applets.h" |
47 | ; |
48 | #undef MAKE_USAGE |
49 | #else |
50 | #define usage_messages 0 |
51 | #endif /* SHOW_USAGE */ |
52 | |
53 | |
54 | /* Include generated applet names, pointers to <applet>_main, etc */ |
55 | #include "applet_tables.h" |
56 | /* ...and if applet_tables generator says we have only one applet... */ |
57 | #ifdef SINGLE_APPLET_MAIN |
58 | #undef ENABLE_FEATURE_INDIVIDUAL |
59 | #define ENABLE_FEATURE_INDIVIDUAL 1 |
60 | #undef IF_FEATURE_INDIVIDUAL |
61 | #define IF_FEATURE_INDIVIDUAL(...) __VA_ARGS__ |
62 | #endif |
63 | |
64 | |
65 | #if ENABLE_FEATURE_COMPRESS_USAGE |
66 | |
67 | #include "usage_compressed.h" |
68 | #include "unarchive.h" |
69 | |
70 | static const char *unpack_usage_messages(void) |
71 | { |
72 | char *outbuf = NULL; |
73 | bunzip_data *bd; |
74 | int i; |
75 | |
76 | i = start_bunzip(&bd, |
77 | /* src_fd: */ -1, |
78 | /* inbuf: */ (void *)packed_usage, |
79 | /* len: */ sizeof(packed_usage)); |
80 | /* read_bunzip can longjmp to start_bunzip, and ultimately |
81 | * end up here with i != 0 on read data errors! Not trivial */ |
82 | if (!i) { |
83 | /* Cannot use xmalloc: will leak bd in NOFORK case! */ |
84 | outbuf = malloc_or_warn(SIZEOF_usage_messages); |
85 | if (outbuf) |
86 | read_bunzip(bd, outbuf, SIZEOF_usage_messages); |
87 | } |
88 | dealloc_bunzip(bd); |
89 | return outbuf; |
90 | } |
91 | #define dealloc_usage_messages(s) free(s) |
92 | |
93 | #else |
94 | |
95 | #define unpack_usage_messages() usage_messages |
96 | #define dealloc_usage_messages(s) ((void)(s)) |
97 | |
98 | #endif /* FEATURE_COMPRESS_USAGE */ |
99 | |
100 | |
101 | static void full_write2_str(const char *str) |
102 | { |
103 | xwrite_str(STDERR_FILENO, str); |
104 | } |
105 | |
106 | void FAST_FUNC bb_show_usage(void) |
107 | { |
108 | if (ENABLE_SHOW_USAGE) { |
109 | #ifdef SINGLE_APPLET_STR |
110 | /* Imagine that this applet is "true". Dont suck in printf! */ |
111 | const char *p; |
112 | const char *usage_string = p = unpack_usage_messages(); |
113 | |
114 | if (*p == '\b') { |
115 | full_write2_str("No help available.\n\n"); |
116 | } else { |
117 | full_write2_str("Usage: "SINGLE_APPLET_STR" "); |
118 | full_write2_str(p); |
119 | full_write2_str("\n\n"); |
120 | } |
121 | if (ENABLE_FEATURE_CLEAN_UP) |
122 | dealloc_usage_messages((char*)usage_string); |
123 | #else |
124 | const char *p; |
125 | const char *usage_string = p = unpack_usage_messages(); |
126 | int ap = find_applet_by_name(applet_name); |
127 | |
128 | if (ap < 0) /* never happens, paranoia */ |
129 | xfunc_die(); |
130 | while (ap) { |
131 | while (*p++) continue; |
132 | ap--; |
133 | } |
134 | full_write2_str(bb_banner); |
135 | full_write2_str(" multi-call binary.\n"); |
136 | if (*p == '\b') |
137 | full_write2_str("\nNo help available.\n\n"); |
138 | else { |
139 | full_write2_str("\nUsage: "); |
140 | full_write2_str(applet_name); |
141 | full_write2_str(" "); |
142 | full_write2_str(p); |
143 | full_write2_str("\n\n"); |
144 | } |
145 | if (ENABLE_FEATURE_CLEAN_UP) |
146 | dealloc_usage_messages((char*)usage_string); |
147 | #endif |
148 | } |
149 | xfunc_die(); |
150 | } |
151 | |
152 | #if NUM_APPLETS > 8 |
153 | /* NB: any char pointer will work as well, not necessarily applet_names */ |
154 | static int applet_name_compare(const void *name, const void *v) |
155 | { |
156 | int i = (const char *)v - applet_names; |
157 | return strcmp(name, APPLET_NAME(i)); |
158 | } |
159 | #endif |
160 | int FAST_FUNC find_applet_by_name(const char *name) |
161 | { |
162 | #if NUM_APPLETS > 8 |
163 | /* Do a binary search to find the applet entry given the name. */ |
164 | const char *p; |
165 | p = bsearch(name, applet_names, ARRAY_SIZE(applet_main), 1, applet_name_compare); |
166 | if (!p) |
167 | return -1; |
168 | return p - applet_names; |
169 | #else |
170 | /* A version which does not pull in bsearch */ |
171 | int i = 0; |
172 | const char *p = applet_names; |
173 | while (i < NUM_APPLETS) { |
174 | if (strcmp(name, p) == 0) |
175 | return i; |
176 | p += strlen(p) + 1; |
177 | i++; |
178 | } |
179 | return -1; |
180 | #endif |
181 | } |
182 | |
183 | |
184 | void lbb_prepare(const char *applet |
185 | IF_FEATURE_INDIVIDUAL(, char **argv)) |
186 | MAIN_EXTERNALLY_VISIBLE; |
187 | void lbb_prepare(const char *applet |
188 | IF_FEATURE_INDIVIDUAL(, char **argv)) |
189 | { |
190 | #ifdef __GLIBC__ |
191 | (*(int **)&bb_errno) = __errno_location(); |
192 | barrier(); |
193 | #endif |
194 | applet_name = applet; |
195 | |
196 | /* Set locale for everybody except 'init' */ |
197 | if (ENABLE_LOCALE_SUPPORT && getpid() != 1) |
198 | setlocale(LC_ALL, ""); |
199 | |
200 | #if ENABLE_FEATURE_INDIVIDUAL |
201 | /* Redundant for busybox (run_applet_and_exit covers that case) |
202 | * but needed for "individual applet" mode */ |
203 | if (argv[1] && !argv[2] && strcmp(argv[1], "--help") == 0) { |
204 | /* Special case. POSIX says "test --help" |
205 | * should be no different from e.g. "test --foo". */ |
206 | if (!ENABLE_TEST || strcmp(applet_name, "test") != 0) |
207 | bb_show_usage(); |
208 | } |
209 | #endif |
210 | } |
211 | |
212 | /* The code below can well be in applets/applets.c, as it is used only |
213 | * for busybox binary, not "individual" binaries. |
214 | * However, keeping it here and linking it into libbusybox.so |
215 | * (together with remaining tiny applets/applets.o) |
216 | * makes it possible to avoid --whole-archive at link time. |
217 | * This makes (shared busybox) + libbusybox smaller. |
218 | * (--gc-sections would be even better....) |
219 | */ |
220 | |
221 | const char *applet_name; |
222 | #if !BB_MMU |
223 | bool re_execed; |
224 | #endif |
225 | |
226 | |
227 | /* If not built as a single-applet executable... */ |
228 | #if !defined(SINGLE_APPLET_MAIN) |
229 | |
230 | IF_FEATURE_SUID(static uid_t ruid;) /* real uid */ |
231 | |
232 | #if ENABLE_FEATURE_SUID_CONFIG |
233 | |
234 | /* applets[] is const, so we have to define this "override" structure */ |
235 | static struct BB_suid_config { |
236 | int m_applet; |
237 | uid_t m_uid; |
238 | gid_t m_gid; |
239 | mode_t m_mode; |
240 | struct BB_suid_config *m_next; |
241 | } *suid_config; |
242 | |
243 | static bool suid_cfg_readable; |
244 | |
245 | /* check if u is member of group g */ |
246 | static int ingroup(uid_t u, gid_t g) |
247 | { |
248 | struct group *grp = getgrgid(g); |
249 | |
250 | if (grp) { |
251 | char **mem; |
252 | |
253 | for (mem = grp->gr_mem; *mem; mem++) { |
254 | struct passwd *pwd = getpwnam(*mem); |
255 | |
256 | if (pwd && (pwd->pw_uid == u)) |
257 | return 1; |
258 | } |
259 | } |
260 | return 0; |
261 | } |
262 | |
263 | /* This should probably be a libbb routine. In that case, |
264 | * I'd probably rename it to something like bb_trimmed_slice. |
265 | */ |
266 | static char *get_trimmed_slice(char *s, char *e) |
267 | { |
268 | /* First, consider the value at e to be nul and back up until we |
269 | * reach a non-space char. Set the char after that (possibly at |
270 | * the original e) to nul. */ |
271 | while (e-- > s) { |
272 | if (!isspace(*e)) { |
273 | break; |
274 | } |
275 | } |
276 | e[1] = '\0'; |
277 | |
278 | /* Next, advance past all leading space and return a ptr to the |
279 | * first non-space char; possibly the terminating nul. */ |
280 | return skip_whitespace(s); |
281 | } |
282 | |
283 | /* Don't depend on the tools to combine strings. */ |
284 | static const char config_file[] ALIGN1 = "/etc/busybox.conf"; |
285 | |
286 | /* We don't supply a value for the nul, so an index adjustment is |
287 | * necessary below. Also, we use unsigned short here to save some |
288 | * space even though these are really mode_t values. */ |
289 | static const unsigned short mode_mask[] ALIGN2 = { |
290 | /* SST sst xxx --- */ |
291 | S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* user */ |
292 | S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* group */ |
293 | 0, S_IXOTH, S_IXOTH, 0 /* other */ |
294 | }; |
295 | |
296 | #define parse_error(x) do { errmsg = x; goto pe_label; } while (0) |
297 | |
298 | static void parse_config_file(void) |
299 | { |
300 | struct BB_suid_config *sct_head; |
301 | struct BB_suid_config *sct; |
302 | int applet_no; |
303 | FILE *f; |
304 | const char *errmsg; |
305 | char *s; |
306 | char *e; |
307 | int i; |
308 | unsigned lc; |
309 | smallint section; |
310 | char buffer[256]; |
311 | struct stat st; |
312 | |
313 | assert(!suid_config); /* Should be set to NULL by bss init. */ |
314 | |
315 | ruid = getuid(); |
316 | if (ruid == 0) /* run by root - don't need to even read config file */ |
317 | return; |
318 | |
319 | if ((stat(config_file, &st) != 0) /* No config file? */ |
320 | || !S_ISREG(st.st_mode) /* Not a regular file? */ |
321 | || (st.st_uid != 0) /* Not owned by root? */ |
322 | || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */ |
323 | || !(f = fopen_for_read(config_file)) /* Cannot open? */ |
324 | ) { |
325 | return; |
326 | } |
327 | |
328 | suid_cfg_readable = 1; |
329 | sct_head = NULL; |
330 | section = lc = 0; |
331 | |
332 | while (1) { |
333 | s = buffer; |
334 | |
335 | if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */ |
336 | // why? |
337 | if (ferror(f)) { /* Make sure it wasn't a read error. */ |
338 | parse_error("reading"); |
339 | } |
340 | fclose(f); |
341 | suid_config = sct_head; /* Success, so set the pointer. */ |
342 | return; |
343 | } |
344 | |
345 | lc++; /* Got a (partial) line. */ |
346 | |
347 | /* If a line is too long for our buffer, we consider it an error. |
348 | * The following test does mistreat one corner case though. |
349 | * If the final line of the file does not end with a newline and |
350 | * yet exactly fills the buffer, it will be treated as too long |
351 | * even though there isn't really a problem. But it isn't really |
352 | * worth adding code to deal with such an unlikely situation, and |
353 | * we do err on the side of caution. Besides, the line would be |
354 | * too long if it did end with a newline. */ |
355 | if (!strchr(s, '\n') && !feof(f)) { |
356 | parse_error("line too long"); |
357 | } |
358 | |
359 | /* Trim leading and trailing whitespace, ignoring comments, and |
360 | * check if the resulting string is empty. */ |
361 | s = get_trimmed_slice(s, strchrnul(s, '#')); |
362 | if (!*s) { |
363 | continue; |
364 | } |
365 | |
366 | /* Check for a section header. */ |
367 | |
368 | if (*s == '[') { |
369 | /* Unlike the old code, we ignore leading and trailing |
370 | * whitespace for the section name. We also require that |
371 | * there are no stray characters after the closing bracket. */ |
372 | e = strchr(s, ']'); |
373 | if (!e /* Missing right bracket? */ |
374 | || e[1] /* Trailing characters? */ |
375 | || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */ |
376 | ) { |
377 | parse_error("section header"); |
378 | } |
379 | /* Right now we only have one section so just check it. |
380 | * If more sections are added in the future, please don't |
381 | * resort to cascading ifs with multiple strcasecmp calls. |
382 | * That kind of bloated code is all too common. A loop |
383 | * and a string table would be a better choice unless the |
384 | * number of sections is very small. */ |
385 | if (strcasecmp(s, "SUID") == 0) { |
386 | section = 1; |
387 | continue; |
388 | } |
389 | section = -1; /* Unknown section so set to skip. */ |
390 | continue; |
391 | } |
392 | |
393 | /* Process sections. */ |
394 | |
395 | if (section == 1) { /* SUID */ |
396 | /* Since we trimmed leading and trailing space above, we're |
397 | * now looking for strings of the form |
398 | * <key>[::space::]*=[::space::]*<value> |
399 | * where both key and value could contain inner whitespace. */ |
400 | |
401 | /* First get the key (an applet name in our case). */ |
402 | e = strchr(s, '='); |
403 | if (e) { |
404 | s = get_trimmed_slice(s, e); |
405 | } |
406 | if (!e || !*s) { /* Missing '=' or empty key. */ |
407 | parse_error("keyword"); |
408 | } |
409 | |
410 | /* Ok, we have an applet name. Process the rhs if this |
411 | * applet is currently built in and ignore it otherwise. |
412 | * Note: this can hide config file bugs which only pop |
413 | * up when the busybox configuration is changed. */ |
414 | applet_no = find_applet_by_name(s); |
415 | if (applet_no >= 0) { |
416 | /* Note: We currently don't check for duplicates! |
417 | * The last config line for each applet will be the |
418 | * one used since we insert at the head of the list. |
419 | * I suppose this could be considered a feature. */ |
420 | sct = xmalloc(sizeof(struct BB_suid_config)); |
421 | sct->m_applet = applet_no; |
422 | sct->m_mode = 0; |
423 | sct->m_next = sct_head; |
424 | sct_head = sct; |
425 | |
426 | /* Get the specified mode. */ |
427 | |
428 | e = skip_whitespace(e+1); |
429 | |
430 | for (i = 0; i < 3; i++) { |
431 | /* There are 4 chars + 1 nul for each of user/group/other. */ |
432 | static const char mode_chars[] ALIGN1 = "Ssx-\0" "Ssx-\0" "Ttx-"; |
433 | |
434 | const char *q; |
435 | q = strchrnul(mode_chars + 5*i, *e++); |
436 | if (!*q) { |
437 | parse_error("mode"); |
438 | } |
439 | /* Adjust by -i to account for nul. */ |
440 | sct->m_mode |= mode_mask[(q - mode_chars) - i]; |
441 | } |
442 | |
443 | /* Now get the the user/group info. */ |
444 | |
445 | s = skip_whitespace(e); |
446 | |
447 | /* Note: we require whitespace between the mode and the |
448 | * user/group info. */ |
449 | if ((s == e) || !(e = strchr(s, '.'))) { |
450 | parse_error("<uid>.<gid>"); |
451 | } |
452 | *e++ = '\0'; |
453 | |
454 | /* We can't use get_ug_id here since it would exit() |
455 | * if a uid or gid was not found. Oh well... */ |
456 | sct->m_uid = bb_strtoul(s, NULL, 10); |
457 | if (errno) { |
458 | struct passwd *pwd = getpwnam(s); |
459 | if (!pwd) { |
460 | parse_error("user"); |
461 | } |
462 | sct->m_uid = pwd->pw_uid; |
463 | } |
464 | |
465 | sct->m_gid = bb_strtoul(e, NULL, 10); |
466 | if (errno) { |
467 | struct group *grp; |
468 | grp = getgrnam(e); |
469 | if (!grp) { |
470 | parse_error("group"); |
471 | } |
472 | sct->m_gid = grp->gr_gid; |
473 | } |
474 | } |
475 | continue; |
476 | } |
477 | |
478 | /* Unknown sections are ignored. */ |
479 | |
480 | /* Encountering configuration lines prior to seeing a |
481 | * section header is treated as an error. This is how |
482 | * the old code worked, but it may not be desirable. |
483 | * We may want to simply ignore such lines in case they |
484 | * are used in some future version of busybox. */ |
485 | if (!section) { |
486 | parse_error("keyword outside section"); |
487 | } |
488 | |
489 | } /* while (1) */ |
490 | |
491 | pe_label: |
492 | fprintf(stderr, "Parse error in %s, line %d: %s\n", |
493 | config_file, lc, errmsg); |
494 | |
495 | fclose(f); |
496 | /* Release any allocated memory before returning. */ |
497 | while (sct_head) { |
498 | sct = sct_head->m_next; |
499 | free(sct_head); |
500 | sct_head = sct; |
501 | } |
502 | } |
503 | #else |
504 | static inline void parse_config_file(void) |
505 | { |
506 | IF_FEATURE_SUID(ruid = getuid();) |
507 | } |
508 | #endif /* FEATURE_SUID_CONFIG */ |
509 | |
510 | |
511 | #if ENABLE_FEATURE_SUID |
512 | static void check_suid(int applet_no) |
513 | { |
514 | gid_t rgid; /* real gid */ |
515 | |
516 | if (ruid == 0) /* set by parse_config_file() */ |
517 | return; /* run by root - no need to check more */ |
518 | rgid = getgid(); |
519 | |
520 | #if ENABLE_FEATURE_SUID_CONFIG |
521 | if (suid_cfg_readable) { |
522 | uid_t uid; |
523 | struct BB_suid_config *sct; |
524 | mode_t m; |
525 | |
526 | for (sct = suid_config; sct; sct = sct->m_next) { |
527 | if (sct->m_applet == applet_no) |
528 | goto found; |
529 | } |
530 | goto check_need_suid; |
531 | found: |
532 | m = sct->m_mode; |
533 | if (sct->m_uid == ruid) |
534 | /* same uid */ |
535 | m >>= 6; |
536 | else if ((sct->m_gid == rgid) || ingroup(ruid, sct->m_gid)) |
537 | /* same group / in group */ |
538 | m >>= 3; |
539 | |
540 | if (!(m & S_IXOTH)) /* is x bit not set ? */ |
541 | bb_error_msg_and_die("you have no permission to run this applet!"); |
542 | |
543 | /* _both_ sgid and group_exec have to be set for setegid */ |
544 | if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) |
545 | rgid = sct->m_gid; |
546 | /* else (no setegid) we will set egid = rgid */ |
547 | |
548 | /* We set effective AND saved ids. If saved-id is not set |
549 | * like we do below, seteiud(0) can still later succeed! */ |
550 | if (setresgid(-1, rgid, rgid)) |
551 | bb_perror_msg_and_die("setresgid"); |
552 | |
553 | /* do we have to set effective uid? */ |
554 | uid = ruid; |
555 | if (sct->m_mode & S_ISUID) |
556 | uid = sct->m_uid; |
557 | /* else (no seteuid) we will set euid = ruid */ |
558 | |
559 | if (setresuid(-1, uid, uid)) |
560 | bb_perror_msg_and_die("setresuid"); |
561 | return; |
562 | } |
563 | #if !ENABLE_FEATURE_SUID_CONFIG_QUIET |
564 | { |
565 | static bool onetime = 0; |
566 | |
567 | if (!onetime) { |
568 | onetime = 1; |
569 | fprintf(stderr, "Using fallback suid method\n"); |
570 | } |
571 | } |
572 | #endif |
573 | check_need_suid: |
574 | #endif |
575 | if (APPLET_SUID(applet_no) == _BB_SUID_REQUIRE) { |
576 | /* Real uid is not 0. If euid isn't 0 too, suid bit |
577 | * is most probably not set on our executable */ |
578 | if (geteuid()) |
579 | bb_error_msg_and_die("must be suid to work properly"); |
580 | } else if (APPLET_SUID(applet_no) == _BB_SUID_DROP) { |
581 | xsetgid(rgid); /* drop all privileges */ |
582 | xsetuid(ruid); |
583 | } |
584 | } |
585 | #else |
586 | #define check_suid(x) ((void)0) |
587 | #endif /* FEATURE_SUID */ |
588 | |
589 | |
590 | #if ENABLE_FEATURE_INSTALLER |
591 | /* create (sym)links for each applet */ |
592 | static void install_links(const char *busybox, int use_symbolic_links, |
593 | char *custom_install_dir) |
594 | { |
595 | /* directory table |
596 | * this should be consistent w/ the enum, |
597 | * busybox.h::bb_install_loc_t, or else... */ |
598 | static const char usr_bin [] ALIGN1 = "/usr/bin"; |
599 | static const char usr_sbin[] ALIGN1 = "/usr/sbin"; |
600 | static const char *const install_dir[] = { |
601 | &usr_bin [8], /* "", equivalent to "/" for concat_path_file() */ |
602 | &usr_bin [4], /* "/bin" */ |
603 | &usr_sbin[4], /* "/sbin" */ |
604 | usr_bin, |
605 | usr_sbin |
606 | }; |
607 | |
608 | int (*lf)(const char *, const char *); |
609 | char *fpc; |
610 | unsigned i; |
611 | int rc; |
612 | |
613 | lf = link; |
614 | if (use_symbolic_links) |
615 | lf = symlink; |
616 | |
617 | for (i = 0; i < ARRAY_SIZE(applet_main); i++) { |
618 | fpc = concat_path_file( |
619 | custom_install_dir ? custom_install_dir : install_dir[APPLET_INSTALL_LOC(i)], |
620 | APPLET_NAME(i)); |
621 | // debug: bb_error_msg("%slinking %s to busybox", |
622 | // use_symbolic_links ? "sym" : "", fpc); |
623 | rc = lf(busybox, fpc); |
624 | if (rc != 0 && errno != EEXIST) { |
625 | bb_simple_perror_msg(fpc); |
626 | } |
627 | free(fpc); |
628 | } |
629 | } |
630 | #else |
631 | #define install_links(x,y,z) ((void)0) |
632 | #endif /* FEATURE_INSTALLER */ |
633 | |
634 | /* If we were called as "busybox..." */ |
635 | static int busybox_main(char **argv) |
636 | { |
637 | if (!argv[1]) { |
638 | /* Called without arguments */ |
639 | const char *a; |
640 | int col; |
641 | unsigned output_width; |
642 | help: |
643 | output_width = 80; |
644 | if (ENABLE_FEATURE_AUTOWIDTH) { |
645 | /* Obtain the terminal width */ |
646 | get_terminal_width_height(0, &output_width, NULL); |
647 | } |
648 | |
649 | dup2(1, 2); |
650 | full_write2_str(bb_banner); /* reuse const string */ |
651 | full_write2_str(" multi-call binary.\n"); /* reuse */ |
652 | full_write2_str( |
653 | "Copyright (C) 1998-2009 Erik Andersen, Rob Landley, Denys Vlasenko\n" |
654 | "and others. Licensed under GPLv2.\n" |
655 | "See source distribution for full notice.\n" |
656 | "\n" |
657 | "Usage: busybox [function] [arguments]...\n" |
658 | " or: function [arguments]...\n" |
659 | "\n" |
660 | "\tBusyBox is a multi-call binary that combines many common Unix\n" |
661 | "\tutilities into a single executable. Most people will create a\n" |
662 | "\tlink to busybox for each function they wish to use and BusyBox\n" |
663 | "\twill act like whatever it was invoked as.\n" |
664 | "\n" |
665 | "Currently defined functions:\n"); |
666 | col = 0; |
667 | a = applet_names; |
668 | /* prevent last comma to be in the very last pos */ |
669 | output_width--; |
670 | while (*a) { |
671 | int len2 = strlen(a) + 2; |
672 | if (col >= (int)output_width - len2) { |
673 | full_write2_str(",\n"); |
674 | col = 0; |
675 | } |
676 | if (col == 0) { |
677 | col = 6; |
678 | full_write2_str("\t"); |
679 | } else { |
680 | full_write2_str(", "); |
681 | } |
682 | full_write2_str(a); |
683 | col += len2; |
684 | a += len2 - 1; |
685 | } |
686 | full_write2_str("\n\n"); |
687 | return 0; |
688 | } |
689 | |
690 | if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) { |
691 | int use_symbolic_links; |
692 | const char *busybox; |
693 | busybox = xmalloc_readlink(bb_busybox_exec_path); |
694 | if (!busybox) |
695 | busybox = bb_busybox_exec_path; |
696 | /* busybox --install [-s] [DIR]: */ |
697 | /* -s: make symlinks */ |
698 | /* DIR: directory to install links to */ |
699 | use_symbolic_links = (argv[2] && strcmp(argv[2], "-s") == 0 && argv++); |
700 | install_links(busybox, use_symbolic_links, argv[2]); |
701 | return 0; |
702 | } |
703 | |
704 | if (strcmp(argv[1], "--help") == 0) { |
705 | /* "busybox --help [<applet>]" */ |
706 | if (!argv[2]) |
707 | goto help; |
708 | /* convert to "<applet> --help" */ |
709 | argv[0] = argv[2]; |
710 | argv[2] = NULL; |
711 | } else { |
712 | /* "busybox <applet> arg1 arg2 ..." */ |
713 | argv++; |
714 | } |
715 | /* We support "busybox /a/path/to/applet args..." too. Allows for |
716 | * "#!/bin/busybox"-style wrappers */ |
717 | applet_name = bb_get_last_path_component_nostrip(argv[0]); |
718 | run_applet_and_exit(applet_name, argv); |
719 | |
720 | /*bb_error_msg_and_die("applet not found"); - sucks in printf */ |
721 | full_write2_str(applet_name); |
722 | full_write2_str(": applet not found\n"); |
723 | xfunc_die(); |
724 | } |
725 | |
726 | void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv) |
727 | { |
728 | int argc = 1; |
729 | |
730 | while (argv[argc]) |
731 | argc++; |
732 | |
733 | /* Reinit some shared global data */ |
734 | xfunc_error_retval = EXIT_FAILURE; |
735 | |
736 | applet_name = APPLET_NAME(applet_no); |
737 | if (argc == 2 && strcmp(argv[1], "--help") == 0) { |
738 | /* Special case. POSIX says "test --help" |
739 | * should be no different from e.g. "test --foo". */ |
740 | //TODO: just compare applet_no with APPLET_NO_test |
741 | if (!ENABLE_TEST || strcmp(applet_name, "test") != 0) |
742 | bb_show_usage(); |
743 | } |
744 | if (ENABLE_FEATURE_SUID) |
745 | check_suid(applet_no); |
746 | exit(applet_main[applet_no](argc, argv)); |
747 | } |
748 | |
749 | void FAST_FUNC run_applet_and_exit(const char *name, char **argv) |
750 | { |
751 | int applet = find_applet_by_name(name); |
752 | if (applet >= 0) |
753 | run_applet_no_and_exit(applet, argv); |
754 | if (!strncmp(name, "busybox", 7)) |
755 | exit(busybox_main(argv)); |
756 | } |
757 | |
758 | #endif /* !defined(SINGLE_APPLET_MAIN) */ |
759 | |
760 | |
761 | |
762 | #if ENABLE_BUILD_LIBBUSYBOX |
763 | int lbb_main(char **argv) |
764 | #else |
765 | int main(int argc UNUSED_PARAM, char **argv) |
766 | #endif |
767 | { |
768 | /* Tweak malloc for reduced memory consumption */ |
769 | #ifndef PAGE_SIZE |
770 | # define PAGE_SIZE (4*1024) /* guess */ |
771 | #endif |
772 | #ifdef M_TRIM_THRESHOLD |
773 | /* M_TRIM_THRESHOLD is the maximum amount of freed top-most memory |
774 | * to keep before releasing to the OS |
775 | * Default is way too big: 256k |
776 | */ |
777 | mallopt(M_TRIM_THRESHOLD, 2 * PAGE_SIZE); |
778 | #endif |
779 | #ifdef M_MMAP_THRESHOLD |
780 | /* M_MMAP_THRESHOLD is the request size threshold for using mmap() |
781 | * Default is too big: 256k |
782 | */ |
783 | mallopt(M_MMAP_THRESHOLD, 8 * PAGE_SIZE - 256); |
784 | #endif |
785 | |
786 | #if defined(SINGLE_APPLET_MAIN) |
787 | /* Only one applet is selected by the user! */ |
788 | /* applet_names in this case is just "applet\0\0" */ |
789 | lbb_prepare(applet_names IF_FEATURE_INDIVIDUAL(, argv)); |
790 | return SINGLE_APPLET_MAIN(argc, argv); |
791 | #else |
792 | lbb_prepare("busybox" IF_FEATURE_INDIVIDUAL(, argv)); |
793 | |
794 | #if !BB_MMU |
795 | /* NOMMU re-exec trick sets high-order bit in first byte of name */ |
796 | if (argv[0][0] & 0x80) { |
797 | re_execed = 1; |
798 | argv[0][0] &= 0x7f; |
799 | } |
800 | #endif |
801 | applet_name = argv[0]; |
802 | if (applet_name[0] == '-') |
803 | applet_name++; |
804 | applet_name = bb_basename(applet_name); |
805 | |
806 | parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */ |
807 | |
808 | run_applet_and_exit(applet_name, argv); |
809 | |
810 | /*bb_error_msg_and_die("applet not found"); - sucks in printf */ |
811 | full_write2_str(applet_name); |
812 | full_write2_str(": applet not found\n"); |
813 | xfunc_die(); |
814 | #endif |
815 | } |