diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-07 10:52:40 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-07 10:52:40 +0000 |
commit | 50f3aa454785b6acc9ef19b94eff003a83bd0b01 (patch) | |
tree | c92e8407b58cc47e33878c265b09376660108a59 | |
parent | d4a24f2aae0b1e60b805062adf9ba51c011d75c7 (diff) | |
download | busybox-w32-50f3aa454785b6acc9ef19b94eff003a83bd0b01.tar.gz busybox-w32-50f3aa454785b6acc9ef19b94eff003a83bd0b01.tar.bz2 busybox-w32-50f3aa454785b6acc9ef19b94eff003a83bd0b01.zip |
hush: NOMMU-safe support of big heredocs
function old new delta
setup_heredoc 116 215 +99
-rw-r--r-- | shell/hush.c | 304 |
1 files changed, 177 insertions, 127 deletions
diff --git a/shell/hush.c b/shell/hush.c index dc59d73bb..f7e5fbc5b 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -22,7 +22,7 @@ | |||
22 | * | 22 | * |
23 | * Other credits: | 23 | * Other credits: |
24 | * o_addchr derived from similar w_addchar function in glibc-2.2. | 24 | * o_addchr derived from similar w_addchar function in glibc-2.2. |
25 | * setup_redirect, redirect_opt_num, and big chunks of main | 25 | * parse_redirect, redirect_opt_num, and big chunks of main |
26 | * and many builtins derived from contributions by Erik Andersen. | 26 | * and many builtins derived from contributions by Erik Andersen. |
27 | * Miscellaneous bugfixes from Matt Kraai. | 27 | * Miscellaneous bugfixes from Matt Kraai. |
28 | * | 28 | * |
@@ -75,6 +75,9 @@ | |||
75 | #endif | 75 | #endif |
76 | #include "math.h" | 76 | #include "math.h" |
77 | #include "match.h" | 77 | #include "match.h" |
78 | #ifndef PIPE_BUF | ||
79 | # define PIPE_BUF 4096 /* amount of buffering in a pipe */ | ||
80 | #endif | ||
78 | 81 | ||
79 | #ifdef WANT_TO_TEST_NOMMU | 82 | #ifdef WANT_TO_TEST_NOMMU |
80 | # undef BB_MMU | 83 | # undef BB_MMU |
@@ -2052,26 +2055,186 @@ static char **expand_assignments(char **argv, int count) | |||
2052 | } | 2055 | } |
2053 | 2056 | ||
2054 | 2057 | ||
2055 | //TODO: fix big string case! | 2058 | #if BB_MMU |
2059 | void re_execute_shell(const char *s, int is_heredoc); /* never called */ | ||
2060 | #define clean_up_after_re_execute() ((void)0) | ||
2061 | static void reset_traps_to_defaults(void) | ||
2062 | { | ||
2063 | unsigned sig; | ||
2064 | int dirty; | ||
2065 | |||
2066 | if (!G.traps) | ||
2067 | return; | ||
2068 | dirty = 0; | ||
2069 | for (sig = 0; sig < NSIG; sig++) { | ||
2070 | if (!G.traps[sig]) | ||
2071 | continue; | ||
2072 | free(G.traps[sig]); | ||
2073 | G.traps[sig] = NULL; | ||
2074 | /* There is no signal for 0 (EXIT) */ | ||
2075 | if (sig == 0) | ||
2076 | continue; | ||
2077 | /* there was a trap handler, we are removing it | ||
2078 | * (if sig has non-DFL handling, | ||
2079 | * we don't need to do anything) */ | ||
2080 | if (sig < 32 && (G.non_DFL_mask & (1 << sig))) | ||
2081 | continue; | ||
2082 | sigdelset(&G.blocked_set, sig); | ||
2083 | dirty = 1; | ||
2084 | } | ||
2085 | if (dirty) | ||
2086 | sigprocmask(SIG_SETMASK, &G.blocked_set, NULL); | ||
2087 | } | ||
2088 | |||
2089 | #else /* !BB_MMU */ | ||
2090 | |||
2091 | static void re_execute_shell(const char *s, int is_heredoc) NORETURN; | ||
2092 | static void re_execute_shell(const char *s, int is_heredoc) | ||
2093 | { | ||
2094 | char param_buf[sizeof("-$%x:%x:%x:%x") + sizeof(unsigned) * 4]; | ||
2095 | struct variable *cur; | ||
2096 | char **argv, **pp, **pp2; | ||
2097 | unsigned cnt; | ||
2098 | |||
2099 | sprintf(param_buf, "-$%x:%x:%x" USE_HUSH_LOOPS(":%x") | ||
2100 | , (unsigned) G.root_pid | ||
2101 | , (unsigned) G.last_bg_pid | ||
2102 | , (unsigned) G.last_exitcode | ||
2103 | USE_HUSH_LOOPS(, G.depth_of_loop) | ||
2104 | ); | ||
2105 | /* 1:hush 2:-$<pid>:<pid>:<exitcode>:<depth> <vars...> | ||
2106 | * 3:-c 4:<cmd> <argN...> 5:NULL | ||
2107 | */ | ||
2108 | cnt = 5 + G.global_argc; | ||
2109 | for (cur = G.top_var; cur; cur = cur->next) { | ||
2110 | if (!cur->flg_export || cur->flg_read_only) | ||
2111 | cnt += 2; | ||
2112 | } | ||
2113 | G.argv_from_re_execing = pp = xzalloc(sizeof(argv[0]) * cnt); | ||
2114 | *pp++ = (char *) G.argv0_for_re_execing; | ||
2115 | *pp++ = param_buf; | ||
2116 | for (cur = G.top_var; cur; cur = cur->next) { | ||
2117 | if (cur->varstr == hush_version_str) | ||
2118 | continue; | ||
2119 | if (cur->flg_read_only) { | ||
2120 | *pp++ = (char *) "-R"; | ||
2121 | *pp++ = cur->varstr; | ||
2122 | } else if (!cur->flg_export) { | ||
2123 | *pp++ = (char *) "-V"; | ||
2124 | *pp++ = cur->varstr; | ||
2125 | } | ||
2126 | } | ||
2127 | //TODO: pass functions | ||
2128 | /* We can pass activated traps here. Say, -Tnn:trap_string | ||
2129 | * | ||
2130 | * However, POSIX says that subshells reset signals with traps | ||
2131 | * to SIG_DFL. | ||
2132 | * I tested bash-3.2 and it not only does that with true subshells | ||
2133 | * of the form ( list ), but with any forked children shells. | ||
2134 | * I set trap "echo W" WINCH; and then tried: | ||
2135 | * | ||
2136 | * { echo 1; sleep 20; echo 2; } & | ||
2137 | * while true; do echo 1; sleep 20; echo 2; break; done & | ||
2138 | * true | { echo 1; sleep 20; echo 2; } | cat | ||
2139 | * | ||
2140 | * In all these cases sending SIGWINCH to the child shell | ||
2141 | * did not run the trap. If I add trap "echo V" WINCH; | ||
2142 | * _inside_ group (just before echo 1), it works. | ||
2143 | * | ||
2144 | * I conclude it means we don't need to pass active traps here. | ||
2145 | * exec syscall below resets them to SIG_DFL for us. | ||
2146 | */ | ||
2147 | *pp++ = (char *) (is_heredoc ? "-<" : "-c"); | ||
2148 | *pp++ = (char *) s; | ||
2149 | pp2 = G.global_argv; | ||
2150 | while (*pp2) | ||
2151 | *pp++ = *pp2++; | ||
2152 | /* *pp = NULL; - is already there */ | ||
2153 | |||
2154 | debug_printf_exec("re_execute_shell pid:%d cmd:'%s'\n", getpid(), s); | ||
2155 | sigprocmask(SIG_SETMASK, &G.inherited_set, NULL); | ||
2156 | execv(bb_busybox_exec_path, G.argv_from_re_execing); | ||
2157 | /* Fallback. Useful for init=/bin/hush usage etc */ | ||
2158 | if (G.argv0_for_re_execing[0] == '/') | ||
2159 | execv(G.argv0_for_re_execing, G.argv_from_re_execing); | ||
2160 | xfunc_error_retval = 127; | ||
2161 | bb_error_msg_and_die("can't re-execute the shell"); | ||
2162 | } | ||
2163 | |||
2164 | static void clean_up_after_re_execute(void) | ||
2165 | { | ||
2166 | char **pp = G.argv_from_re_execing; | ||
2167 | if (pp) { | ||
2168 | /* Must match re_execute_shell's allocations (if any) */ | ||
2169 | free(pp); | ||
2170 | G.argv_from_re_execing = NULL; | ||
2171 | } | ||
2172 | } | ||
2173 | #endif /* !BB_MMU */ | ||
2174 | |||
2175 | |||
2056 | static void setup_heredoc(int fd, const char *heredoc) | 2176 | static void setup_heredoc(int fd, const char *heredoc) |
2057 | { | 2177 | { |
2058 | struct fd_pair pair; | 2178 | struct fd_pair pair; |
2059 | pid_t pid; | 2179 | pid_t pid; |
2180 | int len, written; | ||
2060 | 2181 | ||
2061 | xpiped_pair(pair); | 2182 | xpiped_pair(pair); |
2183 | xmove_fd(pair.rd, fd); | ||
2184 | |||
2185 | len = strlen(heredoc); | ||
2186 | /* Try writing without forking. Newer kernels have | ||
2187 | * dynamically growing pipes. Must use non-blocking write! */ | ||
2188 | ndelay_on(pair.wr); | ||
2189 | while (1) { | ||
2190 | written = write(pair.wr, heredoc, len); | ||
2191 | if (written <= 0) | ||
2192 | break; | ||
2193 | len -= written; | ||
2194 | if (len == 0) { | ||
2195 | close(pair.wr); | ||
2196 | return; | ||
2197 | } | ||
2198 | heredoc += written; | ||
2199 | } | ||
2200 | ndelay_off(pair.wr); | ||
2201 | |||
2202 | /* Okay, pipe buffer was not big enough */ | ||
2203 | /* Note: we must not create a stray child (bastard? :) | ||
2204 | * for the unsuspecting parent process. We create a grandchild | ||
2205 | * and exit before we exec the process which consumes heredoc | ||
2206 | * (that exec happens after we return from this function) */ | ||
2062 | pid = vfork(); | 2207 | pid = vfork(); |
2063 | if (pid < 0) | 2208 | if (pid < 0) |
2064 | bb_perror_msg_and_die("vfork"); | 2209 | bb_perror_msg_and_die("vfork"); |
2065 | if (pid == 0) { /* child */ | 2210 | if (pid == 0) { |
2066 | die_sleep = 0; | 2211 | /* child */ |
2067 | close(pair.rd); | 2212 | pid = BB_MMU ? fork() : vfork(); |
2068 | xwrite_str(pair.wr, heredoc); | 2213 | if (pid < 0) |
2214 | bb_perror_msg_and_die(BB_MMU ? "fork" : "vfork"); | ||
2215 | if (pid != 0) | ||
2216 | _exit(0); | ||
2217 | /* grandchild */ | ||
2218 | close(fd); /* read side of the pipe */ | ||
2219 | #if BB_MMU | ||
2220 | full_write(pair.wr, heredoc, len); | ||
2069 | _exit(0); | 2221 | _exit(0); |
2222 | #else | ||
2223 | /* Delegate blocking writes to another process */ | ||
2224 | # if ENABLE_HUSH_JOB | ||
2225 | die_sleep = 0; /* do not restore tty pgrp on xfunc death */ | ||
2226 | # endif | ||
2227 | xmove_fd(pair.wr, STDOUT_FILENO); | ||
2228 | re_execute_shell(heredoc, 1); | ||
2229 | #endif | ||
2070 | } | 2230 | } |
2071 | /* parent */ | 2231 | /* parent */ |
2072 | die_sleep = -1; | 2232 | #if ENABLE_HUSH_JOB |
2233 | die_sleep = -1; /* restore tty pgrp on xfunc death */ | ||
2234 | #endif | ||
2235 | clean_up_after_re_execute(); | ||
2073 | close(pair.wr); | 2236 | close(pair.wr); |
2074 | xmove_fd(pair.rd, fd); | 2237 | wait(NULL); /* wiat till child has died */ |
2075 | } | 2238 | } |
2076 | 2239 | ||
2077 | /* squirrel != NULL means we squirrel away copies of stdin, stdout, | 2240 | /* squirrel != NULL means we squirrel away copies of stdin, stdout, |
@@ -2329,122 +2492,6 @@ static void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
2329 | _exit(EXIT_FAILURE); | 2492 | _exit(EXIT_FAILURE); |
2330 | } | 2493 | } |
2331 | 2494 | ||
2332 | #if BB_MMU | ||
2333 | static void reset_traps_to_defaults(void) | ||
2334 | { | ||
2335 | unsigned sig; | ||
2336 | int dirty; | ||
2337 | |||
2338 | if (!G.traps) | ||
2339 | return; | ||
2340 | dirty = 0; | ||
2341 | for (sig = 0; sig < NSIG; sig++) { | ||
2342 | if (!G.traps[sig]) | ||
2343 | continue; | ||
2344 | free(G.traps[sig]); | ||
2345 | G.traps[sig] = NULL; | ||
2346 | /* There is no signal for 0 (EXIT) */ | ||
2347 | if (sig == 0) | ||
2348 | continue; | ||
2349 | /* there was a trap handler, we are removing it | ||
2350 | * (if sig has non-DFL handling, | ||
2351 | * we don't need to do anything) */ | ||
2352 | if (sig < 32 && (G.non_DFL_mask & (1 << sig))) | ||
2353 | continue; | ||
2354 | sigdelset(&G.blocked_set, sig); | ||
2355 | dirty = 1; | ||
2356 | } | ||
2357 | if (dirty) | ||
2358 | sigprocmask(SIG_SETMASK, &G.blocked_set, NULL); | ||
2359 | } | ||
2360 | #define clean_up_after_re_execute() ((void)0) | ||
2361 | |||
2362 | #else /* !BB_MMU */ | ||
2363 | |||
2364 | static void re_execute_shell(const char *s) NORETURN; | ||
2365 | static void re_execute_shell(const char *s) | ||
2366 | { | ||
2367 | char param_buf[sizeof("-$%x:%x:%x:%x") + sizeof(unsigned) * 4]; | ||
2368 | struct variable *cur; | ||
2369 | char **argv, **pp, **pp2; | ||
2370 | unsigned cnt; | ||
2371 | |||
2372 | sprintf(param_buf, "-$%x:%x:%x" USE_HUSH_LOOPS(":%x") | ||
2373 | , (unsigned) G.root_pid | ||
2374 | , (unsigned) G.last_bg_pid | ||
2375 | , (unsigned) G.last_exitcode | ||
2376 | USE_HUSH_LOOPS(, G.depth_of_loop) | ||
2377 | ); | ||
2378 | /* 1:hush 2:-$<pid>:<pid>:<exitcode>:<depth> <vars...> | ||
2379 | * 3:-c 4:<cmd> <argN...> 5:NULL | ||
2380 | */ | ||
2381 | cnt = 5 + G.global_argc; | ||
2382 | for (cur = G.top_var; cur; cur = cur->next) { | ||
2383 | if (!cur->flg_export || cur->flg_read_only) | ||
2384 | cnt += 2; | ||
2385 | } | ||
2386 | G.argv_from_re_execing = pp = xzalloc(sizeof(argv[0]) * cnt); | ||
2387 | *pp++ = (char *) G.argv0_for_re_execing; | ||
2388 | *pp++ = param_buf; | ||
2389 | for (cur = G.top_var; cur; cur = cur->next) { | ||
2390 | if (cur->varstr == hush_version_str) | ||
2391 | continue; | ||
2392 | if (cur->flg_read_only) { | ||
2393 | *pp++ = (char *) "-R"; | ||
2394 | *pp++ = cur->varstr; | ||
2395 | } else if (!cur->flg_export) { | ||
2396 | *pp++ = (char *) "-V"; | ||
2397 | *pp++ = cur->varstr; | ||
2398 | } | ||
2399 | } | ||
2400 | //TODO: pass functions | ||
2401 | /* We can pass activated traps here. Say, -Tnn:trap_string | ||
2402 | * | ||
2403 | * However, POSIX says that subshells reset signals with traps | ||
2404 | * to SIG_DFL. | ||
2405 | * I tested bash-3.2 and it not only does that with true subshells | ||
2406 | * of the form ( list ), but with any forked children shells. | ||
2407 | * I set trap "echo W" WINCH; and then tried: | ||
2408 | * | ||
2409 | * { echo 1; sleep 20; echo 2; } & | ||
2410 | * while true; do echo 1; sleep 20; echo 2; break; done & | ||
2411 | * true | { echo 1; sleep 20; echo 2; } | cat | ||
2412 | * | ||
2413 | * In all these cases sending SIGWINCH to the child shell | ||
2414 | * did not run the trap. If I add trap "echo V" WINCH; | ||
2415 | * _inside_ group (just before echo 1), it works. | ||
2416 | * | ||
2417 | * I conclude it means we don't need to pass active traps here. | ||
2418 | * exec syscall below resets them to SIG_DFL for us. | ||
2419 | */ | ||
2420 | *pp++ = (char *) "-c"; | ||
2421 | *pp++ = (char *) s; | ||
2422 | pp2 = G.global_argv; | ||
2423 | while (*pp2) | ||
2424 | *pp++ = *pp2++; | ||
2425 | /* *pp = NULL; - is already there */ | ||
2426 | |||
2427 | debug_printf_exec("re_execute_shell pid:%d cmd:'%s'\n", getpid(), s); | ||
2428 | sigprocmask(SIG_SETMASK, &G.inherited_set, NULL); | ||
2429 | execv(bb_busybox_exec_path, G.argv_from_re_execing); | ||
2430 | /* Fallback. Useful for init=/bin/hush usage etc */ | ||
2431 | if (G.argv0_for_re_execing[0] == '/') | ||
2432 | execv(G.argv0_for_re_execing, G.argv_from_re_execing); | ||
2433 | xfunc_error_retval = 127; | ||
2434 | bb_error_msg_and_die("can't re-execute the shell"); | ||
2435 | } | ||
2436 | |||
2437 | static void clean_up_after_re_execute(void) | ||
2438 | { | ||
2439 | char **pp = G.argv_from_re_execing; | ||
2440 | if (pp) { | ||
2441 | /* Must match re_execute_shell's allocations (if any) */ | ||
2442 | free(pp); | ||
2443 | G.argv_from_re_execing = NULL; | ||
2444 | } | ||
2445 | } | ||
2446 | #endif | ||
2447 | |||
2448 | static int run_list(struct pipe *pi); | 2495 | static int run_list(struct pipe *pi); |
2449 | 2496 | ||
2450 | /* Called after [v]fork() in run_pipe | 2497 | /* Called after [v]fork() in run_pipe |
@@ -2477,7 +2524,7 @@ static void pseudo_exec(nommu_save_t *nommu_save, | |||
2477 | * since this process is about to exit */ | 2524 | * since this process is about to exit */ |
2478 | _exit(rcode); | 2525 | _exit(rcode); |
2479 | #else | 2526 | #else |
2480 | re_execute_shell(command->group_as_string); | 2527 | re_execute_shell(command->group_as_string, 0); |
2481 | #endif | 2528 | #endif |
2482 | } | 2529 | } |
2483 | 2530 | ||
@@ -4105,7 +4152,7 @@ static FILE *generate_stream_from_string(const char *s) | |||
4105 | * huge=`cat BIG` # was blocking here forever | 4152 | * huge=`cat BIG` # was blocking here forever |
4106 | * echo OK | 4153 | * echo OK |
4107 | */ | 4154 | */ |
4108 | re_execute_shell(s); | 4155 | re_execute_shell(s, 0); |
4109 | #endif | 4156 | #endif |
4110 | } | 4157 | } |
4111 | 4158 | ||
@@ -5317,7 +5364,7 @@ int hush_main(int argc, char **argv) | |||
5317 | while (1) { | 5364 | while (1) { |
5318 | opt = getopt(argc, argv, "c:xins" | 5365 | opt = getopt(argc, argv, "c:xins" |
5319 | #if !BB_MMU | 5366 | #if !BB_MMU |
5320 | "$:!:?:D:R:V:" | 5367 | "<:$:!:?:D:R:V:" |
5321 | #endif | 5368 | #endif |
5322 | ); | 5369 | ); |
5323 | if (opt <= 0) | 5370 | if (opt <= 0) |
@@ -5346,6 +5393,9 @@ int hush_main(int argc, char **argv) | |||
5346 | * operate, so simply do nothing here. */ | 5393 | * operate, so simply do nothing here. */ |
5347 | break; | 5394 | break; |
5348 | #if !BB_MMU | 5395 | #if !BB_MMU |
5396 | case '<': /* "big heredoc" support */ | ||
5397 | full_write(STDOUT_FILENO, optarg, strlen(optarg)); | ||
5398 | _exit(0); | ||
5349 | case '$': | 5399 | case '$': |
5350 | G.root_pid = bb_strtou(optarg, &optarg, 16); | 5400 | G.root_pid = bb_strtou(optarg, &optarg, 16); |
5351 | optarg++; | 5401 | optarg++; |