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 | |
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.
-rw-r--r-- | applets/applets.c | 541 | ||||
-rw-r--r-- | include/busybox.h | 3 | ||||
-rw-r--r-- | libbb/appletlib.c | 553 | ||||
-rwxr-xr-x | scripts/trylink | 17 |
4 files changed, 566 insertions, 548 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 | ||
diff --git a/include/busybox.h b/include/busybox.h index 1ab51cf78..1c23b9bb0 100644 --- a/include/busybox.h +++ b/include/busybox.h | |||
@@ -52,5 +52,8 @@ struct bb_applet { | |||
52 | extern const struct bb_applet applets[]; | 52 | extern const struct bb_applet applets[]; |
53 | extern const unsigned short NUM_APPLETS; | 53 | extern const unsigned short NUM_APPLETS; |
54 | void bbox_prepare_main(char **argv); | 54 | void bbox_prepare_main(char **argv); |
55 | #if ENABLE_BUILD_LIBBUSYBOX | ||
56 | int libbusybox_main(int argc, char **argv); | ||
57 | #endif | ||
55 | 58 | ||
56 | #endif /* _BB_INTERNAL_H_ */ | 59 | #endif /* _BB_INTERNAL_H_ */ |
diff --git a/libbb/appletlib.c b/libbb/appletlib.c index cfa60a940..7808df501 100644 --- a/libbb/appletlib.c +++ b/libbb/appletlib.c | |||
@@ -1,11 +1,15 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | 1 | /* vi: set sw=4 ts=4: */ |
2 | /* | 2 | /* |
3 | * Support for main() which needs to end up in libbusybox, not busybox, | 3 | * Utility routines. |
4 | * if one builds libbusybox. | ||
5 | * | 4 | * |
6 | * Copyright (C) 2007 Denys Vlasenko <vda.linux@googlemail.com> | 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. | ||
7 | * | 8 | * |
8 | * Licensed under GPLv2, see file License in this tarball for details. | 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. | ||
9 | */ | 13 | */ |
10 | 14 | ||
11 | #include <assert.h> | 15 | #include <assert.h> |
@@ -140,3 +144,544 @@ void bbox_prepare_main(char **argv) | |||
140 | bb_show_usage(); | 144 | bb_show_usage(); |
141 | #endif | 145 | #endif |
142 | } | 146 | } |
147 | |||
148 | /* The code below can well be in applets/applets.c, as it is used only | ||
149 | * for busybox binary, not "individual" binaries. | ||
150 | * However, keeping it here and linking it into libbusybox.so | ||
151 | * (together with remaining tiny applets/applets.o) | ||
152 | * makes it possible to avoid --whole-archive at link time. | ||
153 | * This makes (shared busybox) + libbusybox smaller. | ||
154 | * (--gc-sections would be even better....) | ||
155 | */ | ||
156 | |||
157 | const char *applet_name; | ||
158 | #if !BB_MMU | ||
159 | bool re_execed; | ||
160 | #endif | ||
161 | |||
162 | USE_FEATURE_SUID(static uid_t ruid;) /* real uid */ | ||
163 | |||
164 | #if ENABLE_FEATURE_SUID_CONFIG | ||
165 | |||
166 | /* applets[] is const, so we have to define this "override" structure */ | ||
167 | static struct BB_suid_config { | ||
168 | const struct bb_applet *m_applet; | ||
169 | uid_t m_uid; | ||
170 | gid_t m_gid; | ||
171 | mode_t m_mode; | ||
172 | struct BB_suid_config *m_next; | ||
173 | } *suid_config; | ||
174 | |||
175 | static bool suid_cfg_readable; | ||
176 | |||
177 | /* check if u is member of group g */ | ||
178 | static int ingroup(uid_t u, gid_t g) | ||
179 | { | ||
180 | struct group *grp = getgrgid(g); | ||
181 | |||
182 | if (grp) { | ||
183 | char **mem; | ||
184 | |||
185 | for (mem = grp->gr_mem; *mem; mem++) { | ||
186 | struct passwd *pwd = getpwnam(*mem); | ||
187 | |||
188 | if (pwd && (pwd->pw_uid == u)) | ||
189 | return 1; | ||
190 | } | ||
191 | } | ||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | /* This should probably be a libbb routine. In that case, | ||
196 | * I'd probably rename it to something like bb_trimmed_slice. | ||
197 | */ | ||
198 | static char *get_trimmed_slice(char *s, char *e) | ||
199 | { | ||
200 | /* First, consider the value at e to be nul and back up until we | ||
201 | * reach a non-space char. Set the char after that (possibly at | ||
202 | * the original e) to nul. */ | ||
203 | while (e-- > s) { | ||
204 | if (!isspace(*e)) { | ||
205 | break; | ||
206 | } | ||
207 | } | ||
208 | e[1] = '\0'; | ||
209 | |||
210 | /* Next, advance past all leading space and return a ptr to the | ||
211 | * first non-space char; possibly the terminating nul. */ | ||
212 | return skip_whitespace(s); | ||
213 | } | ||
214 | |||
215 | /* Don't depend on the tools to combine strings. */ | ||
216 | static const char config_file[] ALIGN1 = "/etc/busybox.conf"; | ||
217 | |||
218 | /* We don't supply a value for the nul, so an index adjustment is | ||
219 | * necessary below. Also, we use unsigned short here to save some | ||
220 | * space even though these are really mode_t values. */ | ||
221 | static const unsigned short mode_mask[] ALIGN2 = { | ||
222 | /* SST sst xxx --- */ | ||
223 | S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* user */ | ||
224 | S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* group */ | ||
225 | 0, S_IXOTH, S_IXOTH, 0 /* other */ | ||
226 | }; | ||
227 | |||
228 | #define parse_error(x) do { errmsg = x; goto pe_label; } while (0) | ||
229 | |||
230 | static void parse_config_file(void) | ||
231 | { | ||
232 | struct BB_suid_config *sct_head; | ||
233 | struct BB_suid_config *sct; | ||
234 | const struct bb_applet *applet; | ||
235 | FILE *f; | ||
236 | const char *errmsg; | ||
237 | char *s; | ||
238 | char *e; | ||
239 | int i; | ||
240 | unsigned lc; | ||
241 | smallint section; | ||
242 | char buffer[256]; | ||
243 | struct stat st; | ||
244 | |||
245 | assert(!suid_config); /* Should be set to NULL by bss init. */ | ||
246 | |||
247 | ruid = getuid(); | ||
248 | if (ruid == 0) /* run by root - don't need to even read config file */ | ||
249 | return; | ||
250 | |||
251 | if ((stat(config_file, &st) != 0) /* No config file? */ | ||
252 | || !S_ISREG(st.st_mode) /* Not a regular file? */ | ||
253 | || (st.st_uid != 0) /* Not owned by root? */ | ||
254 | || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */ | ||
255 | || !(f = fopen(config_file, "r")) /* Cannot open? */ | ||
256 | ) { | ||
257 | return; | ||
258 | } | ||
259 | |||
260 | suid_cfg_readable = 1; | ||
261 | sct_head = NULL; | ||
262 | section = lc = 0; | ||
263 | |||
264 | while (1) { | ||
265 | s = buffer; | ||
266 | |||
267 | if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */ | ||
268 | if (ferror(f)) { /* Make sure it wasn't a read error. */ | ||
269 | parse_error("reading"); | ||
270 | } | ||
271 | fclose(f); | ||
272 | suid_config = sct_head; /* Success, so set the pointer. */ | ||
273 | return; | ||
274 | } | ||
275 | |||
276 | lc++; /* Got a (partial) line. */ | ||
277 | |||
278 | /* If a line is too long for our buffer, we consider it an error. | ||
279 | * The following test does mistreat one corner case though. | ||
280 | * If the final line of the file does not end with a newline and | ||
281 | * yet exactly fills the buffer, it will be treated as too long | ||
282 | * even though there isn't really a problem. But it isn't really | ||
283 | * worth adding code to deal with such an unlikely situation, and | ||
284 | * we do err on the side of caution. Besides, the line would be | ||
285 | * too long if it did end with a newline. */ | ||
286 | if (!strchr(s, '\n') && !feof(f)) { | ||
287 | parse_error("line too long"); | ||
288 | } | ||
289 | |||
290 | /* Trim leading and trailing whitespace, ignoring comments, and | ||
291 | * check if the resulting string is empty. */ | ||
292 | s = get_trimmed_slice(s, strchrnul(s, '#')); | ||
293 | if (!*s) { | ||
294 | continue; | ||
295 | } | ||
296 | |||
297 | /* Check for a section header. */ | ||
298 | |||
299 | if (*s == '[') { | ||
300 | /* Unlike the old code, we ignore leading and trailing | ||
301 | * whitespace for the section name. We also require that | ||
302 | * there are no stray characters after the closing bracket. */ | ||
303 | e = strchr(s, ']'); | ||
304 | if (!e /* Missing right bracket? */ | ||
305 | || e[1] /* Trailing characters? */ | ||
306 | || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */ | ||
307 | ) { | ||
308 | parse_error("section header"); | ||
309 | } | ||
310 | /* Right now we only have one section so just check it. | ||
311 | * If more sections are added in the future, please don't | ||
312 | * resort to cascading ifs with multiple strcasecmp calls. | ||
313 | * That kind of bloated code is all too common. A loop | ||
314 | * and a string table would be a better choice unless the | ||
315 | * number of sections is very small. */ | ||
316 | if (strcasecmp(s, "SUID") == 0) { | ||
317 | section = 1; | ||
318 | continue; | ||
319 | } | ||
320 | section = -1; /* Unknown section so set to skip. */ | ||
321 | continue; | ||
322 | } | ||
323 | |||
324 | /* Process sections. */ | ||
325 | |||
326 | if (section == 1) { /* SUID */ | ||
327 | /* Since we trimmed leading and trailing space above, we're | ||
328 | * now looking for strings of the form | ||
329 | * <key>[::space::]*=[::space::]*<value> | ||
330 | * where both key and value could contain inner whitespace. */ | ||
331 | |||
332 | /* First get the key (an applet name in our case). */ | ||
333 | e = strchr(s, '='); | ||
334 | if (e) { | ||
335 | s = get_trimmed_slice(s, e); | ||
336 | } | ||
337 | if (!e || !*s) { /* Missing '=' or empty key. */ | ||
338 | parse_error("keyword"); | ||
339 | } | ||
340 | |||
341 | /* Ok, we have an applet name. Process the rhs if this | ||
342 | * applet is currently built in and ignore it otherwise. | ||
343 | * Note: this can hide config file bugs which only pop | ||
344 | * up when the busybox configuration is changed. */ | ||
345 | applet = find_applet_by_name(s); | ||
346 | if (applet) { | ||
347 | /* Note: We currently don't check for duplicates! | ||
348 | * The last config line for each applet will be the | ||
349 | * one used since we insert at the head of the list. | ||
350 | * I suppose this could be considered a feature. */ | ||
351 | sct = xmalloc(sizeof(struct BB_suid_config)); | ||
352 | sct->m_applet = applet; | ||
353 | sct->m_mode = 0; | ||
354 | sct->m_next = sct_head; | ||
355 | sct_head = sct; | ||
356 | |||
357 | /* Get the specified mode. */ | ||
358 | |||
359 | e = skip_whitespace(e+1); | ||
360 | |||
361 | for (i = 0; i < 3; i++) { | ||
362 | /* There are 4 chars + 1 nul for each of user/group/other. */ | ||
363 | static const char mode_chars[] ALIGN1 = "Ssx-\0" "Ssx-\0" "Ttx-"; | ||
364 | |||
365 | const char *q; | ||
366 | q = strchrnul(mode_chars + 5*i, *e++); | ||
367 | if (!*q) { | ||
368 | parse_error("mode"); | ||
369 | } | ||
370 | /* Adjust by -i to account for nul. */ | ||
371 | sct->m_mode |= mode_mask[(q - mode_chars) - i]; | ||
372 | } | ||
373 | |||
374 | /* Now get the the user/group info. */ | ||
375 | |||
376 | s = skip_whitespace(e); | ||
377 | |||
378 | /* Note: we require whitespace between the mode and the | ||
379 | * user/group info. */ | ||
380 | if ((s == e) || !(e = strchr(s, '.'))) { | ||
381 | parse_error("<uid>.<gid>"); | ||
382 | } | ||
383 | *e++ = '\0'; | ||
384 | |||
385 | /* We can't use get_ug_id here since it would exit() | ||
386 | * if a uid or gid was not found. Oh well... */ | ||
387 | sct->m_uid = bb_strtoul(s, NULL, 10); | ||
388 | if (errno) { | ||
389 | struct passwd *pwd = getpwnam(s); | ||
390 | if (!pwd) { | ||
391 | parse_error("user"); | ||
392 | } | ||
393 | sct->m_uid = pwd->pw_uid; | ||
394 | } | ||
395 | |||
396 | sct->m_gid = bb_strtoul(e, NULL, 10); | ||
397 | if (errno) { | ||
398 | struct group *grp; | ||
399 | grp = getgrnam(e); | ||
400 | if (!grp) { | ||
401 | parse_error("group"); | ||
402 | } | ||
403 | sct->m_gid = grp->gr_gid; | ||
404 | } | ||
405 | } | ||
406 | continue; | ||
407 | } | ||
408 | |||
409 | /* Unknown sections are ignored. */ | ||
410 | |||
411 | /* Encountering configuration lines prior to seeing a | ||
412 | * section header is treated as an error. This is how | ||
413 | * the old code worked, but it may not be desirable. | ||
414 | * We may want to simply ignore such lines in case they | ||
415 | * are used in some future version of busybox. */ | ||
416 | if (!section) { | ||
417 | parse_error("keyword outside section"); | ||
418 | } | ||
419 | |||
420 | } /* while (1) */ | ||
421 | |||
422 | pe_label: | ||
423 | fprintf(stderr, "Parse error in %s, line %d: %s\n", | ||
424 | config_file, lc, errmsg); | ||
425 | |||
426 | fclose(f); | ||
427 | /* Release any allocated memory before returning. */ | ||
428 | while (sct_head) { | ||
429 | sct = sct_head->m_next; | ||
430 | free(sct_head); | ||
431 | sct_head = sct; | ||
432 | } | ||
433 | } | ||
434 | #else | ||
435 | static inline void parse_config_file(void) | ||
436 | { | ||
437 | USE_FEATURE_SUID(ruid = getuid();) | ||
438 | } | ||
439 | #endif /* FEATURE_SUID_CONFIG */ | ||
440 | |||
441 | |||
442 | #if ENABLE_FEATURE_SUID | ||
443 | static void check_suid(const struct bb_applet *applet) | ||
444 | { | ||
445 | gid_t rgid; /* real gid */ | ||
446 | |||
447 | if (ruid == 0) /* set by parse_config_file() */ | ||
448 | return; /* run by root - no need to check more */ | ||
449 | rgid = getgid(); | ||
450 | |||
451 | #if ENABLE_FEATURE_SUID_CONFIG | ||
452 | if (suid_cfg_readable) { | ||
453 | uid_t uid; | ||
454 | struct BB_suid_config *sct; | ||
455 | mode_t m; | ||
456 | |||
457 | for (sct = suid_config; sct; sct = sct->m_next) { | ||
458 | if (sct->m_applet == applet) | ||
459 | goto found; | ||
460 | } | ||
461 | /* default: drop all privileges */ | ||
462 | xsetgid(rgid); | ||
463 | xsetuid(ruid); | ||
464 | return; | ||
465 | found: | ||
466 | m = sct->m_mode; | ||
467 | if (sct->m_uid == ruid) | ||
468 | /* same uid */ | ||
469 | m >>= 6; | ||
470 | else if ((sct->m_gid == rgid) || ingroup(ruid, sct->m_gid)) | ||
471 | /* same group / in group */ | ||
472 | m >>= 3; | ||
473 | |||
474 | if (!(m & S_IXOTH)) /* is x bit not set ? */ | ||
475 | bb_error_msg_and_die("you have no permission to run this applet!"); | ||
476 | |||
477 | /* _both_ sgid and group_exec have to be set for setegid */ | ||
478 | if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) | ||
479 | rgid = sct->m_gid; | ||
480 | /* else (no setegid) we will set egid = rgid */ | ||
481 | |||
482 | /* We set effective AND saved ids. If saved-id is not set | ||
483 | * like we do below, seteiud(0) can still later succeed! */ | ||
484 | if (setresgid(-1, rgid, rgid)) | ||
485 | bb_perror_msg_and_die("setresgid"); | ||
486 | |||
487 | /* do we have to set effective uid? */ | ||
488 | uid = ruid; | ||
489 | if (sct->m_mode & S_ISUID) | ||
490 | uid = sct->m_uid; | ||
491 | /* else (no seteuid) we will set euid = ruid */ | ||
492 | |||
493 | if (setresuid(-1, uid, uid)) | ||
494 | bb_perror_msg_and_die("setresuid"); | ||
495 | return; | ||
496 | } | ||
497 | #if !ENABLE_FEATURE_SUID_CONFIG_QUIET | ||
498 | { | ||
499 | static bool onetime = 0; | ||
500 | |||
501 | if (!onetime) { | ||
502 | onetime = 1; | ||
503 | fprintf(stderr, "Using fallback suid method\n"); | ||
504 | } | ||
505 | } | ||
506 | #endif | ||
507 | #endif | ||
508 | |||
509 | if (applet->need_suid == _BB_SUID_ALWAYS) { | ||
510 | /* Real uid is not 0. If euid isn't 0 too, suid bit | ||
511 | * is most probably not set on our executable */ | ||
512 | if (geteuid()) | ||
513 | bb_error_msg_and_die("applet requires root privileges!"); | ||
514 | } else if (applet->need_suid == _BB_SUID_NEVER) { | ||
515 | xsetgid(rgid); /* drop all privileges */ | ||
516 | xsetuid(ruid); | ||
517 | } | ||
518 | } | ||
519 | #else | ||
520 | #define check_suid(x) ((void)0) | ||
521 | #endif /* FEATURE_SUID */ | ||
522 | |||
523 | |||
524 | #if ENABLE_FEATURE_INSTALLER | ||
525 | /* create (sym)links for each applet */ | ||
526 | static void install_links(const char *busybox, int use_symbolic_links) | ||
527 | { | ||
528 | /* directory table | ||
529 | * this should be consistent w/ the enum, | ||
530 | * busybox.h::bb_install_loc_t, or else... */ | ||
531 | static const char usr_bin [] ALIGN1 = "/usr/bin"; | ||
532 | static const char usr_sbin[] ALIGN1 = "/usr/sbin"; | ||
533 | static const char *const install_dir[] = { | ||
534 | &usr_bin [8], /* "", equivalent to "/" for concat_path_file() */ | ||
535 | &usr_bin [4], /* "/bin" */ | ||
536 | &usr_sbin[4], /* "/sbin" */ | ||
537 | usr_bin, | ||
538 | usr_sbin | ||
539 | }; | ||
540 | |||
541 | int (*lf)(const char *, const char *) = link; | ||
542 | char *fpc; | ||
543 | int i; | ||
544 | int rc; | ||
545 | |||
546 | if (use_symbolic_links) | ||
547 | lf = symlink; | ||
548 | |||
549 | for (i = 0; applets[i].name != NULL; i++) { | ||
550 | fpc = concat_path_file( | ||
551 | install_dir[applets[i].install_loc], | ||
552 | applets[i].name); | ||
553 | rc = lf(busybox, fpc); | ||
554 | if (rc != 0 && errno != EEXIST) { | ||
555 | bb_simple_perror_msg(fpc); | ||
556 | } | ||
557 | free(fpc); | ||
558 | } | ||
559 | } | ||
560 | #else | ||
561 | #define install_links(x,y) ((void)0) | ||
562 | #endif /* FEATURE_INSTALLER */ | ||
563 | |||
564 | /* If we were called as "busybox..." */ | ||
565 | static int busybox_main(char **argv) | ||
566 | { | ||
567 | if (!argv[1]) { | ||
568 | /* Called without arguments */ | ||
569 | const struct bb_applet *a; | ||
570 | int col, output_width; | ||
571 | help: | ||
572 | output_width = 80; | ||
573 | if (ENABLE_FEATURE_AUTOWIDTH) { | ||
574 | /* Obtain the terminal width */ | ||
575 | get_terminal_width_height(0, &output_width, NULL); | ||
576 | } | ||
577 | /* leading tab and room to wrap */ | ||
578 | output_width -= sizeof("start-stop-daemon, ") + 8; | ||
579 | |||
580 | printf("%s multi-call binary\n", bb_banner); /* reuse const string... */ | ||
581 | printf("Copyright (C) 1998-2006 Erik Andersen, Rob Landley, and others.\n" | ||
582 | "Licensed under GPLv2. See source distribution for full notice.\n" | ||
583 | "\n" | ||
584 | "Usage: busybox [function] [arguments]...\n" | ||
585 | " or: [function] [arguments]...\n" | ||
586 | "\n" | ||
587 | "\tBusyBox is a multi-call binary that combines many common Unix\n" | ||
588 | "\tutilities into a single executable. Most people will create a\n" | ||
589 | "\tlink to busybox for each function they wish to use and BusyBox\n" | ||
590 | "\twill act like whatever it was invoked as!\n" | ||
591 | "\nCurrently defined functions:\n"); | ||
592 | col = 0; | ||
593 | a = applets; | ||
594 | while (a->name) { | ||
595 | if (col > output_width) { | ||
596 | puts(","); | ||
597 | col = 0; | ||
598 | } | ||
599 | col += printf("%s%s", (col ? ", " : "\t"), a->name); | ||
600 | a++; | ||
601 | } | ||
602 | puts("\n"); | ||
603 | return 0; | ||
604 | } | ||
605 | |||
606 | if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) { | ||
607 | const char *busybox; | ||
608 | busybox = xmalloc_readlink(bb_busybox_exec_path); | ||
609 | if (!busybox) | ||
610 | busybox = bb_busybox_exec_path; | ||
611 | /* -s makes symlinks */ | ||
612 | install_links(busybox, argv[2] && strcmp(argv[2], "-s") == 0); | ||
613 | return 0; | ||
614 | } | ||
615 | |||
616 | if (strcmp(argv[1], "--help") == 0) { | ||
617 | /* "busybox --help [<applet>]" */ | ||
618 | if (!argv[2]) | ||
619 | goto help; | ||
620 | /* convert to "<applet> --help" */ | ||
621 | argv[0] = argv[2]; | ||
622 | argv[2] = NULL; | ||
623 | } else { | ||
624 | /* "busybox <applet> arg1 arg2 ..." */ | ||
625 | argv++; | ||
626 | } | ||
627 | /* We support "busybox /a/path/to/applet args..." too. Allows for | ||
628 | * "#!/bin/busybox"-style wrappers */ | ||
629 | applet_name = bb_get_last_path_component_nostrip(argv[0]); | ||
630 | run_applet_and_exit(applet_name, argv); | ||
631 | bb_error_msg_and_die("applet not found"); | ||
632 | } | ||
633 | |||
634 | void run_appletstruct_and_exit(const struct bb_applet *applet, char **argv) | ||
635 | { | ||
636 | int argc = 1; | ||
637 | |||
638 | while (argv[argc]) | ||
639 | argc++; | ||
640 | |||
641 | /* Reinit some shared global data */ | ||
642 | optind = 1; | ||
643 | xfunc_error_retval = EXIT_FAILURE; | ||
644 | |||
645 | applet_name = applet->name; | ||
646 | if (argc == 2 && !strcmp(argv[1], "--help")) | ||
647 | bb_show_usage(); | ||
648 | if (ENABLE_FEATURE_SUID) | ||
649 | check_suid(applet); | ||
650 | exit(applet->main(argc, argv)); | ||
651 | } | ||
652 | |||
653 | void run_applet_and_exit(const char *name, char **argv) | ||
654 | { | ||
655 | const struct bb_applet *applet = find_applet_by_name(name); | ||
656 | if (applet) | ||
657 | run_appletstruct_and_exit(applet, argv); | ||
658 | if (!strncmp(name, "busybox", 7)) | ||
659 | exit(busybox_main(argv)); | ||
660 | } | ||
661 | |||
662 | |||
663 | #if ENABLE_BUILD_LIBBUSYBOX | ||
664 | int libbusybox_main(int argc, char **argv) | ||
665 | #else | ||
666 | int main(int argc, char **argv) | ||
667 | #endif | ||
668 | { | ||
669 | bbox_prepare_main(argv); | ||
670 | |||
671 | #if !BB_MMU | ||
672 | /* NOMMU re-exec trick sets high-order bit in first byte of name */ | ||
673 | if (argv[0][0] & 0x80) { | ||
674 | re_execed = 1; | ||
675 | argv[0][0] &= 0x7f; | ||
676 | } | ||
677 | #endif | ||
678 | applet_name = argv[0]; | ||
679 | if (applet_name[0] == '-') | ||
680 | applet_name++; | ||
681 | applet_name = bb_basename(applet_name); | ||
682 | |||
683 | parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */ | ||
684 | |||
685 | run_applet_and_exit(applet_name, argv); | ||
686 | bb_error_msg_and_die("applet not found"); | ||
687 | } | ||
diff --git a/scripts/trylink b/scripts/trylink index fc87df032..6292dc659 100755 --- a/scripts/trylink +++ b/scripts/trylink | |||
@@ -19,18 +19,16 @@ debug=false | |||
19 | # List of files to link: | 19 | # List of files to link: |
20 | # $l_list == --start-group -llib1 -llib2 --end-group | 20 | # $l_list == --start-group -llib1 -llib2 --end-group |
21 | # --start-group $O_FILES $A_FILES --end-group | 21 | # --start-group $O_FILES $A_FILES --end-group |
22 | # or for shlib: | ||
23 | # --start-group --whole-archive $A_FILES --no-whole-archive --end-group | ||
24 | # otherwise we don't pull in all parts of *.a files. Now we pull in too much. | ||
25 | # FIXME: make it work without --whole-archive, or better, with --gc-sections. | ||
26 | # | 22 | # |
27 | # Shared library link: | 23 | # Shared library link: |
28 | # -shared self-explanatory | 24 | # -shared self-explanatory |
29 | # -fPIC position-independent code | 25 | # -fPIC position-independent code |
30 | # -Wl,--enable-new-dtags ? | 26 | # --enable-new-dtags ? |
31 | # -Wl,-z,combreloc ? | 27 | # -z,combreloc ? |
32 | # -Wl,-soname="libbusybox.so.$BB_VER" | 28 | # -soname="libbusybox.so.$BB_VER" |
33 | # -static Not used, but may be useful! manpage: | 29 | # --undefined=libbusybox_main Seed name to start pulling from |
30 | # (otherwise we'll need --whole-archive) | ||
31 | # -static Not used, but may be useful! manpage: | ||
34 | # "... This option can be used with -shared. | 32 | # "... This option can be used with -shared. |
35 | # Doing so means that a shared library | 33 | # Doing so means that a shared library |
36 | # is being created but that all of the library's | 34 | # is being created but that all of the library's |
@@ -174,9 +172,10 @@ if test "$CONFIG_BUILD_LIBBUSYBOX" = y; then | |||
174 | -Wl,--enable-new-dtags \ | 172 | -Wl,--enable-new-dtags \ |
175 | -Wl,-z,combreloc \ | 173 | -Wl,-z,combreloc \ |
176 | -Wl,-soname="libbusybox.so.$BB_VER" \ | 174 | -Wl,-soname="libbusybox.so.$BB_VER" \ |
175 | -Wl,--undefined=libbusybox_main \ | ||
177 | -Wl,--sort-common \ | 176 | -Wl,--sort-common \ |
178 | -Wl,--sort-section -Wl,alignment \ | 177 | -Wl,--sort-section -Wl,alignment \ |
179 | -Wl,--start-group -Wl,--whole-archive $A_FILES -Wl,--no-whole-archive -Wl,--end-group \ | 178 | -Wl,--start-group $A_FILES -Wl,--end-group \ |
180 | $l_list \ | 179 | $l_list \ |
181 | -Wl,--warn-common \ | 180 | -Wl,--warn-common \ |
182 | -Wl,-Map -Wl,$EXE.map \ | 181 | -Wl,-Map -Wl,$EXE.map \ |