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 /shell | |
| 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)
Diffstat (limited to 'shell')
| -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 |
4 files changed, 163 insertions, 92 deletions
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" | ||
