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