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