aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2009-04-07 10:52:40 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2009-04-07 10:52:40 +0000
commit50f3aa454785b6acc9ef19b94eff003a83bd0b01 (patch)
treec92e8407b58cc47e33878c265b09376660108a59
parentd4a24f2aae0b1e60b805062adf9ba51c011d75c7 (diff)
downloadbusybox-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.c304
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
2059void re_execute_shell(const char *s, int is_heredoc); /* never called */
2060#define clean_up_after_re_execute() ((void)0)
2061static 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
2091static void re_execute_shell(const char *s, int is_heredoc) NORETURN;
2092static 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
2164static 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
2056static void setup_heredoc(int fd, const char *heredoc) 2176static 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
2333static 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
2364static void re_execute_shell(const char *s) NORETURN;
2365static 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
2437static 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
2448static int run_list(struct pipe *pi); 2495static 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++;