diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-02-10 12:10:08 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-02-10 12:10:08 +0000 |
commit | 05743d79496cf96e9f6f645b6bbc165d51e6aa5c (patch) | |
tree | 50fb492810d898cdcafe63009bbb08a1dde039ca | |
parent | 68e8e96d7fba018b5b2354434fe0ae95fdfffc4f (diff) | |
download | busybox-w32-05743d79496cf96e9f6f645b6bbc165d51e6aa5c.tar.gz busybox-w32-05743d79496cf96e9f6f645b6bbc165d51e6aa5c.tar.bz2 busybox-w32-05743d79496cf96e9f6f645b6bbc165d51e6aa5c.zip |
hush: reinstate `cmd` handling for NOMMU (with fat big warning).
hush: fix a case where none of pipe members could be started
because of fork failure
hush: rename functions: xxx_real -> xxx
hush: try to add a bit more of vfork-friendliness
hush: add rudimentary design docs
hush: add TODO (newly discovered bug with globbing)
-rw-r--r-- | include/libbb.h | 3 | ||||
-rw-r--r-- | shell/hush.c | 192 | ||||
-rw-r--r-- | shell/hush_doc.txt | 39 | ||||
-rwxr-xr-x | shell/hush_test/run-all | 5 | ||||
-rw-r--r-- | shell/hush_test/zbad2 | 19 |
5 files changed, 166 insertions, 92 deletions
diff --git a/include/libbb.h b/include/libbb.h index 3ef03d6c9..b405df541 100644 --- a/include/libbb.h +++ b/include/libbb.h | |||
@@ -664,6 +664,9 @@ enum { | |||
664 | void re_exec(char **argv) ATTRIBUTE_NORETURN; | 664 | void re_exec(char **argv) ATTRIBUTE_NORETURN; |
665 | void forkexit_or_rexec(char **argv); | 665 | void forkexit_or_rexec(char **argv); |
666 | extern bool re_execed; | 666 | extern bool re_execed; |
667 | int BUG_fork_is_unavailable_on_nommu(void); | ||
668 | int BUG_daemon_is_unavailable_on_nommu(void); | ||
669 | void BUG_bb_daemonize_is_unavailable_on_nommu(void); | ||
667 | # define fork() BUG_fork_is_unavailable_on_nommu() | 670 | # define fork() BUG_fork_is_unavailable_on_nommu() |
668 | # define daemon(a,b) BUG_daemon_is_unavailable_on_nommu() | 671 | # define daemon(a,b) BUG_daemon_is_unavailable_on_nommu() |
669 | # define bb_daemonize(a) BUG_bb_daemonize_is_unavailable_on_nommu() | 672 | # define bb_daemonize(a) BUG_bb_daemonize_is_unavailable_on_nommu() |
diff --git a/shell/hush.c b/shell/hush.c index a75407634..f3ea1a212 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -84,12 +84,24 @@ | |||
84 | #include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */ | 84 | #include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */ |
85 | 85 | ||
86 | 86 | ||
87 | #if !BB_MMU | 87 | #if !BB_MMU && ENABLE_HUSH_TICK |
88 | /* A bit drastic. Can allow some simpler commands | 88 | //#undef ENABLE_HUSH_TICK |
89 | * by analysing command in generate_stream_from_list() | 89 | //#define ENABLE_HUSH_TICK 0 |
90 | */ | 90 | #warning On NOMMU, hush command substitution is dangerous. |
91 | #undef ENABLE_HUSH_TICK | 91 | #warning Dont use it for commands which produce lots of output. |
92 | #define ENABLE_HUSH_TICK 0 | 92 | #warning For more info see shell/hush.c, generate_stream_from_list(). |
93 | #endif | ||
94 | |||
95 | #if !BB_MMU && ENABLE_HUSH_JOB | ||
96 | #undef ENABLE_HUSH_JOB | ||
97 | #define ENABLE_HUSH_JOB 0 | ||
98 | #endif | ||
99 | |||
100 | #if !ENABLE_HUSH_INTERACTIVE | ||
101 | #undef ENABLE_FEATURE_EDITING | ||
102 | #define ENABLE_FEATURE_EDITING 0 | ||
103 | #undef ENABLE_FEATURE_EDITING_FANCY_PROMPT | ||
104 | #define ENABLE_FEATURE_EDITING_FANCY_PROMPT 0 | ||
93 | #endif | 105 | #endif |
94 | 106 | ||
95 | 107 | ||
@@ -176,13 +188,6 @@ void xxfree(void *ptr) | |||
176 | #endif | 188 | #endif |
177 | 189 | ||
178 | 190 | ||
179 | #if !ENABLE_HUSH_INTERACTIVE | ||
180 | #undef ENABLE_FEATURE_EDITING | ||
181 | #define ENABLE_FEATURE_EDITING 0 | ||
182 | #undef ENABLE_FEATURE_EDITING_FANCY_PROMPT | ||
183 | #define ENABLE_FEATURE_EDITING_FANCY_PROMPT 0 | ||
184 | #endif | ||
185 | |||
186 | #define SPECIAL_VAR_SYMBOL 3 | 191 | #define SPECIAL_VAR_SYMBOL 3 |
187 | 192 | ||
188 | #define PARSEFLAG_EXIT_FROM_LOOP 1 | 193 | #define PARSEFLAG_EXIT_FROM_LOOP 1 |
@@ -508,10 +513,10 @@ static int free_pipe_list(struct pipe *head, int indent); | |||
508 | static int free_pipe(struct pipe *pi, int indent); | 513 | static int free_pipe(struct pipe *pi, int indent); |
509 | /* really run the final data structures: */ | 514 | /* really run the final data structures: */ |
510 | static int setup_redirects(struct child_prog *prog, int squirrel[]); | 515 | static int setup_redirects(struct child_prog *prog, int squirrel[]); |
511 | static int run_list_real(struct pipe *pi); | 516 | static int run_list(struct pipe *pi); |
512 | static void pseudo_exec_argv(char **argv) ATTRIBUTE_NORETURN; | 517 | static void pseudo_exec_argv(char **argv) ATTRIBUTE_NORETURN; |
513 | static void pseudo_exec(struct child_prog *child) ATTRIBUTE_NORETURN; | 518 | static void pseudo_exec(struct child_prog *child) ATTRIBUTE_NORETURN; |
514 | static int run_pipe_real(struct pipe *pi); | 519 | static int run_pipe(struct pipe *pi); |
515 | /* extended glob support: */ | 520 | /* extended glob support: */ |
516 | static char **globhack(const char *src, char **strings); | 521 | static char **globhack(const char *src, char **strings); |
517 | static int glob_needed(const char *s); | 522 | static int glob_needed(const char *s); |
@@ -1416,7 +1421,7 @@ static void restore_redirects(int squirrel[]) | |||
1416 | } | 1421 | } |
1417 | } | 1422 | } |
1418 | 1423 | ||
1419 | /* Called after [v]fork() in run_pipe_real(), or from builtin_exec(). | 1424 | /* Called after [v]fork() in run_pipe(), or from builtin_exec(). |
1420 | * Never returns. | 1425 | * Never returns. |
1421 | * XXX no exit() here. If you don't exec, use _exit instead. | 1426 | * XXX no exit() here. If you don't exec, use _exit instead. |
1422 | * The at_exit handlers apparently confuse the calling process, | 1427 | * The at_exit handlers apparently confuse the calling process, |
@@ -1438,9 +1443,8 @@ static void pseudo_exec_argv(char **argv) | |||
1438 | /* If a variable is assigned in a forest, and nobody listens, | 1443 | /* If a variable is assigned in a forest, and nobody listens, |
1439 | * was it ever really set? | 1444 | * was it ever really set? |
1440 | */ | 1445 | */ |
1441 | if (argv[0] == NULL) { | 1446 | if (!argv[0]) |
1442 | _exit(EXIT_SUCCESS); | 1447 | _exit(EXIT_SUCCESS); |
1443 | } | ||
1444 | 1448 | ||
1445 | argv = expand_strvec_to_strvec(argv); | 1449 | argv = expand_strvec_to_strvec(argv); |
1446 | 1450 | ||
@@ -1484,15 +1488,15 @@ static void pseudo_exec_argv(char **argv) | |||
1484 | _exit(1); | 1488 | _exit(1); |
1485 | } | 1489 | } |
1486 | 1490 | ||
1487 | /* Called after [v]fork() in run_pipe_real() | 1491 | /* Called after [v]fork() in run_pipe() |
1488 | */ | 1492 | */ |
1489 | static void pseudo_exec(struct child_prog *child) | 1493 | static void pseudo_exec(struct child_prog *child) |
1490 | { | 1494 | { |
1491 | // FIXME: buggy wrt NOMMU! Must not modify any global data | 1495 | // FIXME: buggy wrt NOMMU! Must not modify any global data |
1492 | // until it does exec/_exit, but currently it does. | 1496 | // until it does exec/_exit, but currently it does |
1493 | if (child->argv) { | 1497 | // (puts malloc'ed stuff into environment) |
1498 | if (child->argv) | ||
1494 | pseudo_exec_argv(child->argv); | 1499 | pseudo_exec_argv(child->argv); |
1495 | } | ||
1496 | 1500 | ||
1497 | if (child->group) { | 1501 | if (child->group) { |
1498 | #if !BB_MMU | 1502 | #if !BB_MMU |
@@ -1501,11 +1505,12 @@ static void pseudo_exec(struct child_prog *child) | |||
1501 | int rcode; | 1505 | int rcode; |
1502 | 1506 | ||
1503 | #if ENABLE_HUSH_INTERACTIVE | 1507 | #if ENABLE_HUSH_INTERACTIVE |
1504 | debug_printf_exec("pseudo_exec: setting interactive_fd=0\n"); | 1508 | // run_list_level now takes care of it? |
1505 | interactive_fd = 0; /* crucial!!!! */ | 1509 | // debug_printf_exec("pseudo_exec: setting interactive_fd=0\n"); |
1510 | // interactive_fd = 0; /* crucial!!!! */ | ||
1506 | #endif | 1511 | #endif |
1507 | debug_printf_exec("pseudo_exec: run_list_real\n"); | 1512 | debug_printf_exec("pseudo_exec: run_list\n"); |
1508 | rcode = run_list_real(child->group); | 1513 | rcode = run_list(child->group); |
1509 | /* OK to leak memory by not calling free_pipe_list, | 1514 | /* OK to leak memory by not calling free_pipe_list, |
1510 | * since this process is about to exit */ | 1515 | * since this process is about to exit */ |
1511 | _exit(rcode); | 1516 | _exit(rcode); |
@@ -1672,7 +1677,7 @@ static int checkjobs(struct pipe* fg_pipe) | |||
1672 | if (dead) { | 1677 | if (dead) { |
1673 | fg_pipe->progs[i].pid = 0; | 1678 | fg_pipe->progs[i].pid = 0; |
1674 | fg_pipe->running_progs--; | 1679 | fg_pipe->running_progs--; |
1675 | if (i == fg_pipe->num_progs-1) | 1680 | if (i == fg_pipe->num_progs - 1) |
1676 | /* last process gives overall exitstatus */ | 1681 | /* last process gives overall exitstatus */ |
1677 | rcode = WEXITSTATUS(status); | 1682 | rcode = WEXITSTATUS(status); |
1678 | } else { | 1683 | } else { |
@@ -1753,13 +1758,13 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe) | |||
1753 | } | 1758 | } |
1754 | #endif | 1759 | #endif |
1755 | 1760 | ||
1756 | /* run_pipe_real() starts all the jobs, but doesn't wait for anything | 1761 | /* run_pipe() starts all the jobs, but doesn't wait for anything |
1757 | * to finish. See checkjobs(). | 1762 | * to finish. See checkjobs(). |
1758 | * | 1763 | * |
1759 | * return code is normally -1, when the caller has to wait for children | 1764 | * return code is normally -1, when the caller has to wait for children |
1760 | * to finish to determine the exit status of the pipe. If the pipe | 1765 | * to finish to determine the exit status of the pipe. If the pipe |
1761 | * is a simple builtin command, however, the action is done by the | 1766 | * is a simple builtin command, however, the action is done by the |
1762 | * time run_pipe_real returns, and the exit code is provided as the | 1767 | * time run_pipe returns, and the exit code is provided as the |
1763 | * return value. | 1768 | * return value. |
1764 | * | 1769 | * |
1765 | * The input of the pipe is always stdin, the output is always | 1770 | * The input of the pipe is always stdin, the output is always |
@@ -1772,7 +1777,7 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe) | |||
1772 | * Returns -1 only if started some children. IOW: we have to | 1777 | * Returns -1 only if started some children. IOW: we have to |
1773 | * mask out retvals of builtins etc with 0xff! | 1778 | * mask out retvals of builtins etc with 0xff! |
1774 | */ | 1779 | */ |
1775 | static int run_pipe_real(struct pipe *pi) | 1780 | static int run_pipe(struct pipe *pi) |
1776 | { | 1781 | { |
1777 | int i; | 1782 | int i; |
1778 | int nextin; | 1783 | int nextin; |
@@ -1785,7 +1790,7 @@ static int run_pipe_real(struct pipe *pi) | |||
1785 | int rcode; | 1790 | int rcode; |
1786 | const int single_fg = (pi->num_progs == 1 && pi->followup != PIPE_BG); | 1791 | const int single_fg = (pi->num_progs == 1 && pi->followup != PIPE_BG); |
1787 | 1792 | ||
1788 | debug_printf_exec("run_pipe_real start: single_fg=%d\n", single_fg); | 1793 | debug_printf_exec("run_pipe start: single_fg=%d\n", single_fg); |
1789 | 1794 | ||
1790 | #if ENABLE_HUSH_JOB | 1795 | #if ENABLE_HUSH_JOB |
1791 | pi->pgrp = -1; | 1796 | pi->pgrp = -1; |
@@ -1801,11 +1806,11 @@ static int run_pipe_real(struct pipe *pi) | |||
1801 | if (single_fg && child->group && child->subshell == 0) { | 1806 | if (single_fg && child->group && child->subshell == 0) { |
1802 | debug_printf("non-subshell grouping\n"); | 1807 | debug_printf("non-subshell grouping\n"); |
1803 | setup_redirects(child, squirrel); | 1808 | setup_redirects(child, squirrel); |
1804 | debug_printf_exec(": run_list_real\n"); | 1809 | debug_printf_exec(": run_list\n"); |
1805 | rcode = run_list_real(child->group); | 1810 | rcode = run_list(child->group) & 0xff; |
1806 | restore_redirects(squirrel); | 1811 | restore_redirects(squirrel); |
1807 | debug_printf_exec("run_pipe_real return %d\n", rcode); | 1812 | debug_printf_exec("run_pipe return %d\n", rcode); |
1808 | return rcode; // do we need to add '... & 0xff' ? | 1813 | return rcode; |
1809 | } | 1814 | } |
1810 | 1815 | ||
1811 | if (single_fg && child->argv != NULL) { | 1816 | if (single_fg && child->argv != NULL) { |
@@ -1847,7 +1852,7 @@ static int run_pipe_real(struct pipe *pi) | |||
1847 | rcode = x->function(argv_expanded) & 0xff; | 1852 | rcode = x->function(argv_expanded) & 0xff; |
1848 | free(argv_expanded); | 1853 | free(argv_expanded); |
1849 | restore_redirects(squirrel); | 1854 | restore_redirects(squirrel); |
1850 | debug_printf_exec("run_pipe_real return %d\n", rcode); | 1855 | debug_printf_exec("run_pipe return %d\n", rcode); |
1851 | return rcode; | 1856 | return rcode; |
1852 | } | 1857 | } |
1853 | } | 1858 | } |
@@ -1864,7 +1869,7 @@ static int run_pipe_real(struct pipe *pi) | |||
1864 | rcode = run_nofork_applet_prime(&nofork_save, a, argv_expanded) & 0xff; | 1869 | rcode = run_nofork_applet_prime(&nofork_save, a, argv_expanded) & 0xff; |
1865 | free(argv_expanded); | 1870 | free(argv_expanded); |
1866 | restore_redirects(squirrel); | 1871 | restore_redirects(squirrel); |
1867 | debug_printf_exec("run_pipe_real return %d\n", rcode); | 1872 | debug_printf_exec("run_pipe return %d\n", rcode); |
1868 | return rcode; | 1873 | return rcode; |
1869 | } | 1874 | } |
1870 | } | 1875 | } |
@@ -1892,24 +1897,22 @@ static int run_pipe_real(struct pipe *pi) | |||
1892 | if ((i + 1) < pi->num_progs) | 1897 | if ((i + 1) < pi->num_progs) |
1893 | xpipe(pipefds); | 1898 | xpipe(pipefds); |
1894 | 1899 | ||
1895 | #if BB_MMU | 1900 | child->pid = BB_MMU ? fork() : vfork(); |
1896 | child->pid = fork(); | ||
1897 | #else | ||
1898 | child->pid = vfork(); | ||
1899 | #endif | ||
1900 | if (!child->pid) { /* child */ | 1901 | if (!child->pid) { /* child */ |
1901 | #if ENABLE_HUSH_JOB | 1902 | #if ENABLE_HUSH_JOB |
1902 | /* Every child adds itself to new process group | 1903 | /* Every child adds itself to new process group |
1903 | * with pgid == pid of first child in pipe */ | 1904 | * with pgid == pid_of_first_child_in_pipe */ |
1904 | if (run_list_level == 1 && interactive_fd) { | 1905 | if (run_list_level == 1 && interactive_fd) { |
1906 | pid_t pgrp; | ||
1905 | /* Don't do pgrp restore anymore on fatal signals */ | 1907 | /* Don't do pgrp restore anymore on fatal signals */ |
1906 | set_fatal_sighandler(SIG_DFL); | 1908 | set_fatal_sighandler(SIG_DFL); |
1907 | if (pi->pgrp < 0) /* true for 1st process only */ | 1909 | pgrp = pi->pgrp; |
1908 | pi->pgrp = getpid(); | 1910 | if (pgrp < 0) /* true for 1st process only */ |
1909 | if (setpgid(0, pi->pgrp) == 0 && pi->followup != PIPE_BG) { | 1911 | pgrp = getpid(); |
1912 | if (setpgid(0, pgrp) == 0 && pi->followup != PIPE_BG) { | ||
1910 | /* We do it in *every* child, not just first, | 1913 | /* We do it in *every* child, not just first, |
1911 | * to avoid races */ | 1914 | * to avoid races */ |
1912 | tcsetpgrp(interactive_fd, pi->pgrp); | 1915 | tcsetpgrp(interactive_fd, pgrp); |
1913 | } | 1916 | } |
1914 | } | 1917 | } |
1915 | #endif | 1918 | #endif |
@@ -1930,7 +1933,7 @@ static int run_pipe_real(struct pipe *pi) | |||
1930 | 1933 | ||
1931 | if (child->pid < 0) { /* [v]fork failed */ | 1934 | if (child->pid < 0) { /* [v]fork failed */ |
1932 | /* Clearly indicate, was it fork or vfork */ | 1935 | /* Clearly indicate, was it fork or vfork */ |
1933 | bb_perror_msg(BB_MMU ? "cannot fork" : "cannot vfork"); | 1936 | bb_perror_msg(BB_MMU ? "fork" : "vfork"); |
1934 | } else { | 1937 | } else { |
1935 | pi->running_progs++; | 1938 | pi->running_progs++; |
1936 | #if ENABLE_HUSH_JOB | 1939 | #if ENABLE_HUSH_JOB |
@@ -1948,7 +1951,12 @@ static int run_pipe_real(struct pipe *pi) | |||
1948 | nextin = pipefds[0]; | 1951 | nextin = pipefds[0]; |
1949 | } | 1952 | } |
1950 | 1953 | ||
1951 | debug_printf_exec("run_pipe_real return -1\n"); | 1954 | if (!pi->running_progs) { |
1955 | debug_printf_exec("run_pipe return 1 (all forks failed, no children)\n"); | ||
1956 | return 1; | ||
1957 | } | ||
1958 | |||
1959 | debug_printf_exec("run_pipe return -1 (%u children started)\n", pi->running_progs); | ||
1952 | return -1; | 1960 | return -1; |
1953 | } | 1961 | } |
1954 | 1962 | ||
@@ -2017,7 +2025,7 @@ static void debug_print_tree(struct pipe *pi, int lvl) | |||
2017 | 2025 | ||
2018 | /* NB: called by pseudo_exec, and therefore must not modify any | 2026 | /* NB: called by pseudo_exec, and therefore must not modify any |
2019 | * global data until exec/_exit (we can be a child after vfork!) */ | 2027 | * global data until exec/_exit (we can be a child after vfork!) */ |
2020 | static int run_list_real(struct pipe *pi) | 2028 | static int run_list(struct pipe *pi) |
2021 | { | 2029 | { |
2022 | struct pipe *rpipe; | 2030 | struct pipe *rpipe; |
2023 | #if ENABLE_HUSH_LOOPS | 2031 | #if ENABLE_HUSH_LOOPS |
@@ -2026,7 +2034,6 @@ static int run_list_real(struct pipe *pi) | |||
2026 | char **for_list = NULL; | 2034 | char **for_list = NULL; |
2027 | int flag_rep = 0; | 2035 | int flag_rep = 0; |
2028 | #endif | 2036 | #endif |
2029 | int save_num_progs; | ||
2030 | int flag_skip = 1; | 2037 | int flag_skip = 1; |
2031 | int rcode = 0; /* probably for gcc only */ | 2038 | int rcode = 0; /* probably for gcc only */ |
2032 | int flag_restore = 0; | 2039 | int flag_restore = 0; |
@@ -2038,7 +2045,7 @@ static int run_list_real(struct pipe *pi) | |||
2038 | reserved_style rword; | 2045 | reserved_style rword; |
2039 | reserved_style skip_more_for_this_rword = RES_XXXX; | 2046 | reserved_style skip_more_for_this_rword = RES_XXXX; |
2040 | 2047 | ||
2041 | debug_printf_exec("run_list_real start lvl %d\n", run_list_level + 1); | 2048 | debug_printf_exec("run_list start lvl %d\n", run_list_level + 1); |
2042 | 2049 | ||
2043 | #if ENABLE_HUSH_LOOPS | 2050 | #if ENABLE_HUSH_LOOPS |
2044 | /* check syntax for "for" */ | 2051 | /* check syntax for "for" */ |
@@ -2047,7 +2054,7 @@ static int run_list_real(struct pipe *pi) | |||
2047 | && (rpipe->next == NULL) | 2054 | && (rpipe->next == NULL) |
2048 | ) { | 2055 | ) { |
2049 | syntax("malformed for"); /* no IN or no commands after IN */ | 2056 | syntax("malformed for"); /* no IN or no commands after IN */ |
2050 | debug_printf_exec("run_list_real lvl %d return 1\n", run_list_level); | 2057 | debug_printf_exec("run_list lvl %d return 1\n", run_list_level); |
2051 | return 1; | 2058 | return 1; |
2052 | } | 2059 | } |
2053 | if ((rpipe->res_word == RES_IN && rpipe->next->res_word == RES_IN && rpipe->next->progs[0].argv != NULL) | 2060 | if ((rpipe->res_word == RES_IN && rpipe->next->res_word == RES_IN && rpipe->next->progs[0].argv != NULL) |
@@ -2055,7 +2062,7 @@ static int run_list_real(struct pipe *pi) | |||
2055 | ) { | 2062 | ) { |
2056 | /* TODO: what is tested in the first condition? */ | 2063 | /* TODO: what is tested in the first condition? */ |
2057 | syntax("malformed for"); /* 2nd condition: not followed by IN */ | 2064 | syntax("malformed for"); /* 2nd condition: not followed by IN */ |
2058 | debug_printf_exec("run_list_real lvl %d return 1\n", run_list_level); | 2065 | debug_printf_exec("run_list lvl %d return 1\n", run_list_level); |
2059 | return 1; | 2066 | return 1; |
2060 | } | 2067 | } |
2061 | } | 2068 | } |
@@ -2103,9 +2110,10 @@ static int run_list_real(struct pipe *pi) | |||
2103 | signal_SA_RESTART(SIGTSTP, handler_ctrl_z); | 2110 | signal_SA_RESTART(SIGTSTP, handler_ctrl_z); |
2104 | signal(SIGINT, handler_ctrl_c); | 2111 | signal(SIGINT, handler_ctrl_c); |
2105 | } | 2112 | } |
2106 | #endif | 2113 | #endif /* JOB */ |
2107 | 2114 | ||
2108 | for (; pi; pi = flag_restore ? rpipe : pi->next) { | 2115 | for (; pi; pi = flag_restore ? rpipe : pi->next) { |
2116 | //why? int save_num_progs; | ||
2109 | rword = pi->res_word; | 2117 | rword = pi->res_word; |
2110 | #if ENABLE_HUSH_LOOPS | 2118 | #if ENABLE_HUSH_LOOPS |
2111 | if (rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR) { | 2119 | if (rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR) { |
@@ -2178,12 +2186,12 @@ static int run_list_real(struct pipe *pi) | |||
2178 | #endif | 2186 | #endif |
2179 | if (pi->num_progs == 0) | 2187 | if (pi->num_progs == 0) |
2180 | continue; | 2188 | continue; |
2181 | save_num_progs = pi->num_progs; /* save number of programs */ | 2189 | //why? save_num_progs = pi->num_progs; |
2182 | debug_printf_exec(": run_pipe_real with %d members\n", pi->num_progs); | 2190 | debug_printf_exec(": run_pipe with %d members\n", pi->num_progs); |
2183 | rcode = run_pipe_real(pi); | 2191 | rcode = run_pipe(pi); |
2184 | if (rcode != -1) { | 2192 | if (rcode != -1) { |
2185 | /* We only ran a builtin: rcode was set by the return value | 2193 | /* We only ran a builtin: rcode was set by the return value |
2186 | * of run_pipe_real(), and we don't need to wait for anything. */ | 2194 | * of run_pipe(), and we don't need to wait for anything. */ |
2187 | } else if (pi->followup == PIPE_BG) { | 2195 | } else if (pi->followup == PIPE_BG) { |
2188 | /* What does bash do with attempts to background builtins? */ | 2196 | /* What does bash do with attempts to background builtins? */ |
2189 | /* Even bash 3.2 doesn't do that well with nested bg: | 2197 | /* Even bash 3.2 doesn't do that well with nested bg: |
@@ -2196,7 +2204,6 @@ static int run_list_real(struct pipe *pi) | |||
2196 | rcode = EXIT_SUCCESS; | 2204 | rcode = EXIT_SUCCESS; |
2197 | } else { | 2205 | } else { |
2198 | #if ENABLE_HUSH_JOB | 2206 | #if ENABLE_HUSH_JOB |
2199 | /* Paranoia, just "interactive_fd" should be enough? */ | ||
2200 | if (run_list_level == 1 && interactive_fd) { | 2207 | if (run_list_level == 1 && interactive_fd) { |
2201 | /* waits for completion, then fg's main shell */ | 2208 | /* waits for completion, then fg's main shell */ |
2202 | rcode = checkjobs_and_fg_shell(pi); | 2209 | rcode = checkjobs_and_fg_shell(pi); |
@@ -2210,7 +2217,7 @@ static int run_list_real(struct pipe *pi) | |||
2210 | } | 2217 | } |
2211 | debug_printf_exec(": setting last_return_code=%d\n", rcode); | 2218 | debug_printf_exec(": setting last_return_code=%d\n", rcode); |
2212 | last_return_code = rcode; | 2219 | last_return_code = rcode; |
2213 | pi->num_progs = save_num_progs; /* restore number of programs */ | 2220 | //why? pi->num_progs = save_num_progs; |
2214 | #if ENABLE_HUSH_IF | 2221 | #if ENABLE_HUSH_IF |
2215 | if (rword == RES_IF || rword == RES_ELIF) | 2222 | if (rword == RES_IF || rword == RES_ELIF) |
2216 | next_if_code = rcode; /* can be overwritten a number of times */ | 2223 | next_if_code = rcode; /* can be overwritten a number of times */ |
@@ -2241,7 +2248,7 @@ static int run_list_real(struct pipe *pi) | |||
2241 | signal(SIGINT, SIG_IGN); | 2248 | signal(SIGINT, SIG_IGN); |
2242 | } | 2249 | } |
2243 | #endif | 2250 | #endif |
2244 | debug_printf_exec("run_list_real lvl %d return %d\n", run_list_level + 1, rcode); | 2251 | debug_printf_exec("run_list lvl %d return %d\n", run_list_level + 1, rcode); |
2245 | return rcode; | 2252 | return rcode; |
2246 | } | 2253 | } |
2247 | 2254 | ||
@@ -2315,19 +2322,19 @@ static int free_pipe_list(struct pipe *head, int indent) | |||
2315 | } | 2322 | } |
2316 | 2323 | ||
2317 | /* Select which version we will use */ | 2324 | /* Select which version we will use */ |
2318 | static int run_list(struct pipe *pi) | 2325 | static int run_and_free_list(struct pipe *pi) |
2319 | { | 2326 | { |
2320 | int rcode = 0; | 2327 | int rcode = 0; |
2321 | debug_printf_exec("run_list entered\n"); | 2328 | debug_printf_exec("run_and_free_list entered\n"); |
2322 | if (fake_mode == 0) { | 2329 | if (!fake_mode) { |
2323 | debug_printf_exec(": run_list_real with %d members\n", pi->num_progs); | 2330 | debug_printf_exec(": run_list with %d members\n", pi->num_progs); |
2324 | rcode = run_list_real(pi); | 2331 | rcode = run_list(pi); |
2325 | } | 2332 | } |
2326 | /* free_pipe_list has the side effect of clearing memory. | 2333 | /* free_pipe_list has the side effect of clearing memory. |
2327 | * In the long run that function can be merged with run_list_real, | 2334 | * In the long run that function can be merged with run_list, |
2328 | * but doing that now would hobble the debugging effort. */ | 2335 | * but doing that now would hobble the debugging effort. */ |
2329 | free_pipe_list(pi, 0); | 2336 | free_pipe_list(pi, /* indent: */ 0); |
2330 | debug_printf_exec("run_list return %d\n", rcode); | 2337 | debug_printf_exec("run_nad_free_list return %d\n", rcode); |
2331 | return rcode; | 2338 | return rcode; |
2332 | } | 2339 | } |
2333 | 2340 | ||
@@ -3221,15 +3228,17 @@ static FILE *generate_stream_from_list(struct pipe *head) | |||
3221 | int pid, channel[2]; | 3228 | int pid, channel[2]; |
3222 | 3229 | ||
3223 | xpipe(channel); | 3230 | xpipe(channel); |
3224 | pid = fork(); | 3231 | /* *** NOMMU WARNING *** */ |
3225 | if (pid < 0) { | 3232 | /* By using vfork here, we suspend parent till child exits or execs. |
3226 | bb_perror_msg_and_die("fork"); | 3233 | * If child will not do it before it fills the pipe, it can block forever |
3227 | } else if (pid == 0) { | 3234 | * in write(STDOUT_FILENO), and parent (shell) will be also stuck. |
3235 | */ | ||
3236 | pid = BB_MMU ? fork() : vfork(); | ||
3237 | if (pid < 0) | ||
3238 | bb_perror_msg_and_die(BB_MMU ? "fork" : "vfork"); | ||
3239 | if (pid == 0) { /* child */ | ||
3228 | close(channel[0]); | 3240 | close(channel[0]); |
3229 | if (channel[1] != 1) { | 3241 | xmove_fd(channel[1], 1); |
3230 | dup2(channel[1], 1); | ||
3231 | close(channel[1]); | ||
3232 | } | ||
3233 | /* Prevent it from trying to handle ctrl-z etc */ | 3242 | /* Prevent it from trying to handle ctrl-z etc */ |
3234 | #if ENABLE_HUSH_JOB | 3243 | #if ENABLE_HUSH_JOB |
3235 | run_list_level = 1; | 3244 | run_list_level = 1; |
@@ -3241,11 +3250,12 @@ static FILE *generate_stream_from_list(struct pipe *head) | |||
3241 | * everywhere outside actual command execution. */ | 3250 | * everywhere outside actual command execution. */ |
3242 | /*set_jobctrl_sighandler(SIG_IGN);*/ | 3251 | /*set_jobctrl_sighandler(SIG_IGN);*/ |
3243 | set_misc_sighandler(SIG_DFL); | 3252 | set_misc_sighandler(SIG_DFL); |
3244 | _exit(run_list_real(head)); /* leaks memory */ | 3253 | _exit(run_list(head)); /* leaks memory */ |
3245 | } | 3254 | } |
3246 | close(channel[1]); | 3255 | close(channel[1]); |
3247 | pf = fdopen(channel[0], "r"); | 3256 | pf = fdopen(channel[0], "r"); |
3248 | return pf; | 3257 | return pf; |
3258 | /* head is freed by the caller */ | ||
3249 | } | 3259 | } |
3250 | 3260 | ||
3251 | /* Return code is exit status of the process that is run. */ | 3261 | /* Return code is exit status of the process that is run. */ |
@@ -3269,7 +3279,8 @@ static int process_command_subs(o_string *dest, struct p_context *ctx, | |||
3269 | b_free(&result); | 3279 | b_free(&result); |
3270 | 3280 | ||
3271 | p = generate_stream_from_list(inner.list_head); | 3281 | p = generate_stream_from_list(inner.list_head); |
3272 | if (p == NULL) return 1; | 3282 | if (p == NULL) |
3283 | return 1; | ||
3273 | close_on_exec_on(fileno(p)); | 3284 | close_on_exec_on(fileno(p)); |
3274 | setup_file_in_str(&pipe_str, p); | 3285 | setup_file_in_str(&pipe_str, p); |
3275 | 3286 | ||
@@ -3294,7 +3305,7 @@ static int process_command_subs(o_string *dest, struct p_context *ctx, | |||
3294 | * at the same time. That would be a lot of work, and contrary | 3305 | * at the same time. That would be a lot of work, and contrary |
3295 | * to the KISS philosophy of this program. */ | 3306 | * to the KISS philosophy of this program. */ |
3296 | retcode = fclose(p); | 3307 | retcode = fclose(p); |
3297 | free_pipe_list(inner.list_head, 0); | 3308 | free_pipe_list(inner.list_head, /* indent: */ 0); |
3298 | debug_printf("closed FILE from child, retcode=%d\n", retcode); | 3309 | debug_printf("closed FILE from child, retcode=%d\n", retcode); |
3299 | return retcode; | 3310 | return retcode; |
3300 | } | 3311 | } |
@@ -3674,8 +3685,8 @@ static int parse_and_run_stream(struct in_str *inp, int parse_flag) | |||
3674 | done_word(&temp, &ctx); | 3685 | done_word(&temp, &ctx); |
3675 | done_pipe(&ctx, PIPE_SEQ); | 3686 | done_pipe(&ctx, PIPE_SEQ); |
3676 | debug_print_tree(ctx.list_head, 0); | 3687 | debug_print_tree(ctx.list_head, 0); |
3677 | debug_printf_exec("parse_stream_outer: run_list\n"); | 3688 | debug_printf_exec("parse_stream_outer: run_and_free_list\n"); |
3678 | run_list(ctx.list_head); | 3689 | run_and_free_list(ctx.list_head); |
3679 | } else { | 3690 | } else { |
3680 | if (ctx.old_flag != 0) { | 3691 | if (ctx.old_flag != 0) { |
3681 | free(ctx.stack); | 3692 | free(ctx.stack); |
@@ -3684,7 +3695,7 @@ static int parse_and_run_stream(struct in_str *inp, int parse_flag) | |||
3684 | temp.nonnull = 0; | 3695 | temp.nonnull = 0; |
3685 | temp.o_quote = 0; | 3696 | temp.o_quote = 0; |
3686 | inp->p = NULL; | 3697 | inp->p = NULL; |
3687 | free_pipe_list(ctx.list_head, 0); | 3698 | free_pipe_list(ctx.list_head, /* indent: */ 0); |
3688 | } | 3699 | } |
3689 | b_free(&temp); | 3700 | b_free(&temp); |
3690 | } while (rcode != -1 && !(parse_flag & PARSEFLAG_EXIT_FROM_LOOP)); /* loop on syntax errors, return on EOF */ | 3701 | } while (rcode != -1 && !(parse_flag & PARSEFLAG_EXIT_FROM_LOOP)); /* loop on syntax errors, return on EOF */ |
@@ -3898,15 +3909,14 @@ int hush_main(int argc, char **argv) | |||
3898 | 3909 | ||
3899 | if (argv[optind] == NULL) { | 3910 | if (argv[optind] == NULL) { |
3900 | opt = parse_and_run_file(stdin); | 3911 | opt = parse_and_run_file(stdin); |
3901 | goto final_return; | 3912 | } else { |
3913 | debug_printf("\nrunning script '%s'\n", argv[optind]); | ||
3914 | global_argv = argv + optind; | ||
3915 | global_argc = argc - optind; | ||
3916 | input = xfopen(argv[optind], "r"); | ||
3917 | opt = parse_and_run_file(input); | ||
3902 | } | 3918 | } |
3903 | 3919 | ||
3904 | debug_printf("\nrunning script '%s'\n", argv[optind]); | ||
3905 | global_argv = argv + optind; | ||
3906 | global_argc = argc - optind; | ||
3907 | input = xfopen(argv[optind], "r"); | ||
3908 | opt = parse_and_run_file(input); | ||
3909 | |||
3910 | final_return: | 3920 | final_return: |
3911 | 3921 | ||
3912 | #if ENABLE_FEATURE_CLEAN_UP | 3922 | #if ENABLE_FEATURE_CLEAN_UP |
diff --git a/shell/hush_doc.txt b/shell/hush_doc.txt new file mode 100644 index 000000000..a3ead590c --- /dev/null +++ b/shell/hush_doc.txt | |||
@@ -0,0 +1,39 @@ | |||
1 | This is how hush runs commands: | ||
2 | |||
3 | /* callsite: process_command_subs */ | ||
4 | generate_stream_from_list(struct pipe *head) - handles `cmds` | ||
5 | create UNIX pipe | ||
6 | [v]fork | ||
7 | child: | ||
8 | redirect pipe output to stdout | ||
9 | _exit(run_list(head)); /* leaks memory */ | ||
10 | parent: | ||
11 | return UNIX pipe's output fd | ||
12 | /* head is freed by the caller */ | ||
13 | |||
14 | /* callsite: parse_and_run_stream */ | ||
15 | run_and_free_list(struct pipe *) | ||
16 | run_list(struct pipe *) | ||
17 | free_pipe_list(struct pipe *) | ||
18 | |||
19 | /* callsites: generate_stream_from_list, run_and_free_list, pseudo_exec, run_pipe */ | ||
20 | run_list(struct pipe *) - handles "cmd; cmd2 && cmd3", while/for/do loops | ||
21 | run_pipe - for every pipe in list | ||
22 | |||
23 | /* callsite: run_list */ | ||
24 | run_pipe - runs "cmd1 | cmd2 | cmd3 [&]" | ||
25 | run_list - used if only one cmd and it is of the form "{ cmd4; cmd5 && cmd6; }" | ||
26 | forks for every cmd if more than one cmd or if & is there | ||
27 | pseudo_exec - runs each "cmdN" (handles builtins etc) | ||
28 | |||
29 | /* callsite: run_pipe_real */ | ||
30 | pseudo_exec - runs "cmd" (handles builtins etc) | ||
31 | exec - execs external programs | ||
32 | run_list - used if cmdN is "(cmds)" or "{cmds;}" | ||
33 | /* problem: putenv's malloced strings into environ - | ||
34 | ** with vfork they will leak into parent process | ||
35 | */ | ||
36 | /* problem with ENABLE_FEATURE_SH_STANDALONE: | ||
37 | ** run_applet_no_and_exit(a, argv) uses exit - this can interfere | ||
38 | ** with vfork - switch to _exit there? | ||
39 | */ | ||
diff --git a/shell/hush_test/run-all b/shell/hush_test/run-all index c75d81e55..805f75ad5 100755 --- a/shell/hush_test/run-all +++ b/shell/hush_test/run-all | |||
@@ -1,6 +1,9 @@ | |||
1 | #!/bin/sh | 1 | #!/bin/sh |
2 | 2 | ||
3 | test -x hush || { echo "No ./hush?!"; exit; } | 3 | test -x hush || { |
4 | echo "No ./hush?! Perhaps you want to run 'ln -s ../../busybox hush'" | ||
5 | exit | ||
6 | } | ||
4 | 7 | ||
5 | PATH="$PWD:$PATH" # for hush and recho/zecho/printenv | 8 | PATH="$PWD:$PATH" # for hush and recho/zecho/printenv |
6 | export PATH | 9 | export PATH |
diff --git a/shell/hush_test/zbad2 b/shell/hush_test/zbad2 new file mode 100644 index 000000000..c30fa85a0 --- /dev/null +++ b/shell/hush_test/zbad2 | |||
@@ -0,0 +1,19 @@ | |||
1 | ## TODO: fix and add to testsuite | ||
2 | |||
3 | ## # bash zbad2 | ||
4 | ## ZVAR=z.map | ||
5 | ## *.map | ||
6 | ## # hush zbad2 | ||
7 | ## ZVAR=z.map | ||
8 | ## z.map <====== !!! | ||
9 | |||
10 | ## hush does globbing for "VAR=val" too! | ||
11 | ## it should do it only for non-assignments. | ||
12 | ## even if word looks like assignment, it can be non-assignemnt: | ||
13 | ## ZVAR=*.map /bin/echo ZVAR=*.map | ||
14 | ## ^dont_glob ^glob | ||
15 | |||
16 | >ZVAR=z.map | ||
17 | ZVAR=*.map /bin/echo ZVAR=*.map | ||
18 | ZVAR=*.map | ||
19 | echo "$ZVAR" | ||