diff options
Diffstat (limited to 'shell/hush.c')
-rw-r--r-- | shell/hush.c | 739 |
1 files changed, 480 insertions, 259 deletions
diff --git a/shell/hush.c b/shell/hush.c index 125463a56..f6da826d3 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -41,14 +41,28 @@ | |||
41 | * | 41 | * |
42 | * TODOs: | 42 | * TODOs: |
43 | * grep for "TODO" and fix (some of them are easy) | 43 | * grep for "TODO" and fix (some of them are easy) |
44 | * make complex ${var%...} constructs support optional | ||
45 | * make here documents optional | ||
44 | * special variables (done: PWD, PPID, RANDOM) | 46 | * special variables (done: PWD, PPID, RANDOM) |
47 | * follow IFS rules more precisely, including update semantics | ||
45 | * tilde expansion | 48 | * tilde expansion |
46 | * aliases | 49 | * aliases |
47 | * follow IFS rules more precisely, including update semantics | ||
48 | * builtins mandated by standards we don't support: | 50 | * builtins mandated by standards we don't support: |
49 | * [un]alias, command, fc, getopts, newgrp, readonly, times | 51 | * [un]alias, command, fc, getopts, times: |
50 | * make complex ${var%...} constructs support optional | 52 | * command -v CMD: print "/path/to/CMD" |
51 | * make here documents optional | 53 | * prints "CMD" for builtins |
54 | * prints "alias ALIAS='EXPANSION'" for aliases | ||
55 | * prints nothing and sets $? to 1 if not found | ||
56 | * command -V CMD: print "CMD is /path/CMD|a shell builtin|etc" | ||
57 | * command [-p] CMD: run CMD, even if a function CMD also exists | ||
58 | * (can use this to override standalone shell as well) | ||
59 | * -p: use default $PATH | ||
60 | * command BLTIN: disables special-ness (e.g. errors do not abort) | ||
61 | * getopts: getopt() for shells | ||
62 | * times: print getrusage(SELF/CHILDREN).ru_utime/ru_stime | ||
63 | * fc -l[nr] [BEG] [END]: list range of commands in history | ||
64 | * fc [-e EDITOR] [BEG] [END]: edit/rerun range of commands | ||
65 | * fc -s [PAT=REP] [CMD]: rerun CMD, replacing PAT with REP | ||
52 | * | 66 | * |
53 | * Bash compat TODO: | 67 | * Bash compat TODO: |
54 | * redirection of stdout+stderr: &> and >& | 68 | * redirection of stdout+stderr: &> and >& |
@@ -64,8 +78,13 @@ | |||
64 | * The EXPR is evaluated according to ARITHMETIC EVALUATION. | 78 | * The EXPR is evaluated according to ARITHMETIC EVALUATION. |
65 | * This is exactly equivalent to let "EXPR". | 79 | * This is exactly equivalent to let "EXPR". |
66 | * $[EXPR]: synonym for $((EXPR)) | 80 | * $[EXPR]: synonym for $((EXPR)) |
81 | * indirect expansion: ${!VAR} | ||
82 | * substring op on @: ${@:n:m} | ||
67 | * | 83 | * |
68 | * Won't do: | 84 | * Won't do: |
85 | * Some builtins mandated by standards: | ||
86 | * newgrp [GRP]: not a builtin in bash but a suid binary | ||
87 | * which spawns a new shell with new group ID | ||
69 | * In bash, export builtin is special, its arguments are assignments | 88 | * In bash, export builtin is special, its arguments are assignments |
70 | * and therefore expansion of them should be "one-word" expansion: | 89 | * and therefore expansion of them should be "one-word" expansion: |
71 | * $ export i=`echo 'a b'` # export has one arg: "i=a b" | 90 | * $ export i=`echo 'a b'` # export has one arg: "i=a b" |
@@ -219,6 +238,13 @@ | |||
219 | //config: help | 238 | //config: help |
220 | //config: export -n unexports variables. It is a bash extension. | 239 | //config: export -n unexports variables. It is a bash extension. |
221 | //config: | 240 | //config: |
241 | //config:config HUSH_READONLY | ||
242 | //config: bool "readonly builtin" | ||
243 | //config: default y | ||
244 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH | ||
245 | //config: help | ||
246 | //config: Enable support for read-only variables. | ||
247 | //config: | ||
222 | //config:config HUSH_KILL | 248 | //config:config HUSH_KILL |
223 | //config: bool "kill builtin (supports kill %jobspec)" | 249 | //config: bool "kill builtin (supports kill %jobspec)" |
224 | //config: default y | 250 | //config: default y |
@@ -268,17 +294,9 @@ | |||
268 | //config: bool "memleak builtin (debugging)" | 294 | //config: bool "memleak builtin (debugging)" |
269 | //config: default n | 295 | //config: default n |
270 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH | 296 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH |
271 | //config: | ||
272 | //config:config MSH | ||
273 | //config: bool "msh (deprecated: aliased to hush)" | ||
274 | //config: default n | ||
275 | //config: select HUSH | ||
276 | //config: help | ||
277 | //config: msh is deprecated and will be removed, please migrate to hush. | ||
278 | 297 | ||
279 | //applet:IF_HUSH(APPLET(hush, BB_DIR_BIN, BB_SUID_DROP)) | 298 | //applet:IF_HUSH(APPLET(hush, BB_DIR_BIN, BB_SUID_DROP)) |
280 | // APPLET_ODDNAME:name main location suid_type help | 299 | // APPLET_ODDNAME:name main location suid_type help |
281 | //applet:IF_MSH( APPLET_ODDNAME(msh, hush, BB_DIR_BIN, BB_SUID_DROP, hush)) | ||
282 | //applet:IF_SH_IS_HUSH( APPLET_ODDNAME(sh, hush, BB_DIR_BIN, BB_SUID_DROP, hush)) | 300 | //applet:IF_SH_IS_HUSH( APPLET_ODDNAME(sh, hush, BB_DIR_BIN, BB_SUID_DROP, hush)) |
283 | //applet:IF_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, BB_DIR_BIN, BB_SUID_DROP, hush)) | 301 | //applet:IF_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, BB_DIR_BIN, BB_SUID_DROP, hush)) |
284 | 302 | ||
@@ -293,7 +311,7 @@ | |||
293 | * therefore we don't show them either. | 311 | * therefore we don't show them either. |
294 | */ | 312 | */ |
295 | //usage:#define hush_trivial_usage | 313 | //usage:#define hush_trivial_usage |
296 | //usage: "[-nxl] [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]" | 314 | //usage: "[-enxl] [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]" |
297 | //usage:#define hush_full_usage "\n\n" | 315 | //usage:#define hush_full_usage "\n\n" |
298 | //usage: "Unix shell interpreter" | 316 | //usage: "Unix shell interpreter" |
299 | 317 | ||
@@ -331,9 +349,9 @@ | |||
331 | /* Separate defines document which part of code implements what */ | 349 | /* Separate defines document which part of code implements what */ |
332 | #define BASH_PATTERN_SUBST ENABLE_HUSH_BASH_COMPAT | 350 | #define BASH_PATTERN_SUBST ENABLE_HUSH_BASH_COMPAT |
333 | #define BASH_SUBSTR ENABLE_HUSH_BASH_COMPAT | 351 | #define BASH_SUBSTR ENABLE_HUSH_BASH_COMPAT |
334 | #define BASH_TEST2 ENABLE_HUSH_BASH_COMPAT | ||
335 | #define BASH_SOURCE ENABLE_HUSH_BASH_COMPAT | 352 | #define BASH_SOURCE ENABLE_HUSH_BASH_COMPAT |
336 | #define BASH_HOSTNAME_VAR ENABLE_HUSH_BASH_COMPAT | 353 | #define BASH_HOSTNAME_VAR ENABLE_HUSH_BASH_COMPAT |
354 | #define BASH_TEST2 (ENABLE_HUSH_BASH_COMPAT && ENABLE_HUSH_TEST) | ||
337 | 355 | ||
338 | 356 | ||
339 | /* Build knobs */ | 357 | /* Build knobs */ |
@@ -410,6 +428,7 @@ | |||
410 | #define debug_printf_expand(...) do {} while (0) | 428 | #define debug_printf_expand(...) do {} while (0) |
411 | #define debug_printf_varexp(...) do {} while (0) | 429 | #define debug_printf_varexp(...) do {} while (0) |
412 | #define debug_printf_glob(...) do {} while (0) | 430 | #define debug_printf_glob(...) do {} while (0) |
431 | #define debug_printf_redir(...) do {} while (0) | ||
413 | #define debug_printf_list(...) do {} while (0) | 432 | #define debug_printf_list(...) do {} while (0) |
414 | #define debug_printf_subst(...) do {} while (0) | 433 | #define debug_printf_subst(...) do {} while (0) |
415 | #define debug_printf_clean(...) do {} while (0) | 434 | #define debug_printf_clean(...) do {} while (0) |
@@ -753,6 +772,7 @@ struct function { | |||
753 | static const char o_opt_strings[] ALIGN1 = | 772 | static const char o_opt_strings[] ALIGN1 = |
754 | "pipefail\0" | 773 | "pipefail\0" |
755 | "noexec\0" | 774 | "noexec\0" |
775 | "errexit\0" | ||
756 | #if ENABLE_HUSH_MODE_X | 776 | #if ENABLE_HUSH_MODE_X |
757 | "xtrace\0" | 777 | "xtrace\0" |
758 | #endif | 778 | #endif |
@@ -760,6 +780,7 @@ static const char o_opt_strings[] ALIGN1 = | |||
760 | enum { | 780 | enum { |
761 | OPT_O_PIPEFAIL, | 781 | OPT_O_PIPEFAIL, |
762 | OPT_O_NOEXEC, | 782 | OPT_O_NOEXEC, |
783 | OPT_O_ERREXIT, | ||
763 | #if ENABLE_HUSH_MODE_X | 784 | #if ENABLE_HUSH_MODE_X |
764 | OPT_O_XTRACE, | 785 | OPT_O_XTRACE, |
765 | #endif | 786 | #endif |
@@ -816,6 +837,25 @@ struct globals { | |||
816 | #else | 837 | #else |
817 | # define G_saved_tty_pgrp 0 | 838 | # define G_saved_tty_pgrp 0 |
818 | #endif | 839 | #endif |
840 | /* How deeply are we in context where "set -e" is ignored */ | ||
841 | int errexit_depth; | ||
842 | /* "set -e" rules (do we follow them correctly?): | ||
843 | * Exit if pipe, list, or compound command exits with a non-zero status. | ||
844 | * Shell does not exit if failed command is part of condition in | ||
845 | * if/while, part of && or || list except the last command, any command | ||
846 | * in a pipe but the last, or if the command's return value is being | ||
847 | * inverted with !. If a compound command other than a subshell returns a | ||
848 | * non-zero status because a command failed while -e was being ignored, the | ||
849 | * shell does not exit. A trap on ERR, if set, is executed before the shell | ||
850 | * exits [ERR is a bashism]. | ||
851 | * | ||
852 | * If a compound command or function executes in a context where -e is | ||
853 | * ignored, none of the commands executed within are affected by the -e | ||
854 | * setting. If a compound command or function sets -e while executing in a | ||
855 | * context where -e is ignored, that setting does not have any effect until | ||
856 | * the compound command or the command containing the function call completes. | ||
857 | */ | ||
858 | |||
819 | char o_opt[NUM_OPT_O]; | 859 | char o_opt[NUM_OPT_O]; |
820 | #if ENABLE_HUSH_MODE_X | 860 | #if ENABLE_HUSH_MODE_X |
821 | # define G_x_mode (G.o_opt[OPT_O_XTRACE]) | 861 | # define G_x_mode (G.o_opt[OPT_O_XTRACE]) |
@@ -839,6 +879,7 @@ struct globals { | |||
839 | smallint exiting; /* used to prevent EXIT trap recursion */ | 879 | smallint exiting; /* used to prevent EXIT trap recursion */ |
840 | /* These four support $?, $#, and $1 */ | 880 | /* These four support $?, $#, and $1 */ |
841 | smalluint last_exitcode; | 881 | smalluint last_exitcode; |
882 | smalluint last_bg_pid_exitcode; | ||
842 | #if ENABLE_HUSH_SET | 883 | #if ENABLE_HUSH_SET |
843 | /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */ | 884 | /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */ |
844 | smalluint global_args_malloced; | 885 | smalluint global_args_malloced; |
@@ -929,6 +970,9 @@ static int builtin_exit(char **argv) FAST_FUNC; | |||
929 | #if ENABLE_HUSH_EXPORT | 970 | #if ENABLE_HUSH_EXPORT |
930 | static int builtin_export(char **argv) FAST_FUNC; | 971 | static int builtin_export(char **argv) FAST_FUNC; |
931 | #endif | 972 | #endif |
973 | #if ENABLE_HUSH_READONLY | ||
974 | static int builtin_readonly(char **argv) FAST_FUNC; | ||
975 | #endif | ||
932 | #if ENABLE_HUSH_JOB | 976 | #if ENABLE_HUSH_JOB |
933 | static int builtin_fg_bg(char **argv) FAST_FUNC; | 977 | static int builtin_fg_bg(char **argv) FAST_FUNC; |
934 | static int builtin_jobs(char **argv) FAST_FUNC; | 978 | static int builtin_jobs(char **argv) FAST_FUNC; |
@@ -1047,6 +1091,9 @@ static const struct built_in_command bltins1[] = { | |||
1047 | #if ENABLE_HUSH_READ | 1091 | #if ENABLE_HUSH_READ |
1048 | BLTIN("read" , builtin_read , "Input into variable"), | 1092 | BLTIN("read" , builtin_read , "Input into variable"), |
1049 | #endif | 1093 | #endif |
1094 | #if ENABLE_HUSH_READONLY | ||
1095 | BLTIN("readonly" , builtin_readonly, "Make variables read-only"), | ||
1096 | #endif | ||
1050 | #if ENABLE_HUSH_FUNCTIONS | 1097 | #if ENABLE_HUSH_FUNCTIONS |
1051 | BLTIN("return" , builtin_return , "Return from function"), | 1098 | BLTIN("return" , builtin_return , "Return from function"), |
1052 | #endif | 1099 | #endif |
@@ -1154,6 +1201,10 @@ static const struct built_in_command bltins2[] = { | |||
1154 | # define DEBUG_GLOB 0 | 1201 | # define DEBUG_GLOB 0 |
1155 | #endif | 1202 | #endif |
1156 | 1203 | ||
1204 | #ifndef debug_printf_redir | ||
1205 | # define debug_printf_redir(...) (indent(), fdprintf(2, __VA_ARGS__)) | ||
1206 | #endif | ||
1207 | |||
1157 | #ifndef debug_printf_list | 1208 | #ifndef debug_printf_list |
1158 | # define debug_printf_list(...) (indent(), fdprintf(2, __VA_ARGS__)) | 1209 | # define debug_printf_list(...) (indent(), fdprintf(2, __VA_ARGS__)) |
1159 | #endif | 1210 | #endif |
@@ -1389,12 +1440,30 @@ static void free_strings(char **strings) | |||
1389 | free(strings); | 1440 | free(strings); |
1390 | } | 1441 | } |
1391 | 1442 | ||
1443 | static int fcntl_F_DUPFD(int fd, int avoid_fd) | ||
1444 | { | ||
1445 | int newfd; | ||
1446 | repeat: | ||
1447 | newfd = fcntl(fd, F_DUPFD, avoid_fd + 1); | ||
1448 | if (newfd < 0) { | ||
1449 | if (errno == EBUSY) | ||
1450 | goto repeat; | ||
1451 | if (errno == EINTR) | ||
1452 | goto repeat; | ||
1453 | } | ||
1454 | return newfd; | ||
1455 | } | ||
1392 | 1456 | ||
1393 | static int xdup_and_close(int fd, int F_DUPFD_maybe_CLOEXEC) | 1457 | static int xdup_and_close(int fd, int F_DUPFD_maybe_CLOEXEC, int avoid_fd) |
1394 | { | 1458 | { |
1395 | /* We avoid taking stdio fds. Mimicking ash: use fds above 9 */ | 1459 | int newfd; |
1396 | int newfd = fcntl(fd, F_DUPFD_maybe_CLOEXEC, 10); | 1460 | repeat: |
1461 | newfd = fcntl(fd, F_DUPFD_maybe_CLOEXEC, avoid_fd + 1); | ||
1397 | if (newfd < 0) { | 1462 | if (newfd < 0) { |
1463 | if (errno == EBUSY) | ||
1464 | goto repeat; | ||
1465 | if (errno == EINTR) | ||
1466 | goto repeat; | ||
1398 | /* fd was not open? */ | 1467 | /* fd was not open? */ |
1399 | if (errno == EBADF) | 1468 | if (errno == EBADF) |
1400 | return fd; | 1469 | return fd; |
@@ -1432,13 +1501,14 @@ static void fclose_and_forget(FILE *fp) | |||
1432 | } | 1501 | } |
1433 | fclose(fp); | 1502 | fclose(fp); |
1434 | } | 1503 | } |
1435 | static int save_FILEs_on_redirect(int fd) | 1504 | static int save_FILEs_on_redirect(int fd, int avoid_fd) |
1436 | { | 1505 | { |
1437 | struct FILE_list *fl = G.FILE_list; | 1506 | struct FILE_list *fl = G.FILE_list; |
1438 | while (fl) { | 1507 | while (fl) { |
1439 | if (fd == fl->fd) { | 1508 | if (fd == fl->fd) { |
1440 | /* We use it only on script files, they are all CLOEXEC */ | 1509 | /* We use it only on script files, they are all CLOEXEC */ |
1441 | fl->fd = xdup_and_close(fd, F_DUPFD_CLOEXEC); | 1510 | fl->fd = xdup_and_close(fd, F_DUPFD_CLOEXEC, avoid_fd); |
1511 | debug_printf_redir("redirect_fd %d: matches a script fd, moving it to %d\n", fd, fl->fd); | ||
1442 | return 1; | 1512 | return 1; |
1443 | } | 1513 | } |
1444 | fl = fl->next; | 1514 | fl = fl->next; |
@@ -1451,13 +1521,14 @@ static void restore_redirected_FILEs(void) | |||
1451 | while (fl) { | 1521 | while (fl) { |
1452 | int should_be = fileno(fl->fp); | 1522 | int should_be = fileno(fl->fp); |
1453 | if (fl->fd != should_be) { | 1523 | if (fl->fd != should_be) { |
1524 | debug_printf_redir("restoring script fd from %d to %d\n", fl->fd, should_be); | ||
1454 | xmove_fd(fl->fd, should_be); | 1525 | xmove_fd(fl->fd, should_be); |
1455 | fl->fd = should_be; | 1526 | fl->fd = should_be; |
1456 | } | 1527 | } |
1457 | fl = fl->next; | 1528 | fl = fl->next; |
1458 | } | 1529 | } |
1459 | } | 1530 | } |
1460 | #if ENABLE_FEATURE_SH_STANDALONE | 1531 | #if ENABLE_FEATURE_SH_STANDALONE && BB_MMU |
1461 | static void close_all_FILE_list(void) | 1532 | static void close_all_FILE_list(void) |
1462 | { | 1533 | { |
1463 | struct FILE_list *fl = G.FILE_list; | 1534 | struct FILE_list *fl = G.FILE_list; |
@@ -1486,8 +1557,6 @@ typedef struct save_arg_t { | |||
1486 | 1557 | ||
1487 | static void save_and_replace_G_args(save_arg_t *sv, char **argv) | 1558 | static void save_and_replace_G_args(save_arg_t *sv, char **argv) |
1488 | { | 1559 | { |
1489 | int n; | ||
1490 | |||
1491 | sv->sv_argv0 = argv[0]; | 1560 | sv->sv_argv0 = argv[0]; |
1492 | sv->sv_g_argv = G.global_argv; | 1561 | sv->sv_g_argv = G.global_argv; |
1493 | sv->sv_g_argc = G.global_argc; | 1562 | sv->sv_g_argc = G.global_argc; |
@@ -1497,10 +1566,7 @@ static void save_and_replace_G_args(save_arg_t *sv, char **argv) | |||
1497 | G.global_argv = argv; | 1566 | G.global_argv = argv; |
1498 | IF_HUSH_SET(G.global_args_malloced = 0;) | 1567 | IF_HUSH_SET(G.global_args_malloced = 0;) |
1499 | 1568 | ||
1500 | n = 1; | 1569 | G.global_argc = 1 + string_array_len(argv + 1); |
1501 | while (*++argv) | ||
1502 | n++; | ||
1503 | G.global_argc = n; | ||
1504 | } | 1570 | } |
1505 | 1571 | ||
1506 | static void restore_G_args(save_arg_t *sv, char **argv) | 1572 | static void restore_G_args(save_arg_t *sv, char **argv) |
@@ -1991,32 +2057,19 @@ static const char* FAST_FUNC get_local_var_value(const char *name) | |||
1991 | 2057 | ||
1992 | /* str holds "NAME=VAL" and is expected to be malloced. | 2058 | /* str holds "NAME=VAL" and is expected to be malloced. |
1993 | * We take ownership of it. | 2059 | * We take ownership of it. |
1994 | * flg_export: | ||
1995 | * 0: do not change export flag | ||
1996 | * (if creating new variable, flag will be 0) | ||
1997 | * 1: set export flag and putenv the variable | ||
1998 | * -1: clear export flag and unsetenv the variable | ||
1999 | * flg_read_only is set only when we handle -R var=val | ||
2000 | */ | 2060 | */ |
2001 | #if !BB_MMU && ENABLE_HUSH_LOCAL | 2061 | #define SETFLAG_EXPORT (1 << 0) |
2002 | /* all params are used */ | 2062 | #define SETFLAG_UNEXPORT (1 << 1) |
2003 | #elif BB_MMU && ENABLE_HUSH_LOCAL | 2063 | #define SETFLAG_MAKE_RO (1 << 2) |
2004 | #define set_local_var(str, flg_export, local_lvl, flg_read_only) \ | 2064 | #define SETFLAG_LOCAL_SHIFT 3 |
2005 | set_local_var(str, flg_export, local_lvl) | 2065 | static int set_local_var(char *str, unsigned flags) |
2006 | #elif BB_MMU && !ENABLE_HUSH_LOCAL | ||
2007 | #define set_local_var(str, flg_export, local_lvl, flg_read_only) \ | ||
2008 | set_local_var(str, flg_export) | ||
2009 | #elif !BB_MMU && !ENABLE_HUSH_LOCAL | ||
2010 | #define set_local_var(str, flg_export, local_lvl, flg_read_only) \ | ||
2011 | set_local_var(str, flg_export, flg_read_only) | ||
2012 | #endif | ||
2013 | static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_only) | ||
2014 | { | 2066 | { |
2015 | struct variable **var_pp; | 2067 | struct variable **var_pp; |
2016 | struct variable *cur; | 2068 | struct variable *cur; |
2017 | char *free_me = NULL; | 2069 | char *free_me = NULL; |
2018 | char *eq_sign; | 2070 | char *eq_sign; |
2019 | int name_len; | 2071 | int name_len; |
2072 | IF_HUSH_LOCAL(unsigned local_lvl = (flags >> SETFLAG_LOCAL_SHIFT);) | ||
2020 | 2073 | ||
2021 | eq_sign = strchr(str, '='); | 2074 | eq_sign = strchr(str, '='); |
2022 | if (!eq_sign) { /* not expected to ever happen? */ | 2075 | if (!eq_sign) { /* not expected to ever happen? */ |
@@ -2034,14 +2087,13 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_ | |||
2034 | 2087 | ||
2035 | /* We found an existing var with this name */ | 2088 | /* We found an existing var with this name */ |
2036 | if (cur->flg_read_only) { | 2089 | if (cur->flg_read_only) { |
2037 | #if !BB_MMU | 2090 | bb_error_msg("%s: readonly variable", str); |
2038 | if (!flg_read_only) | ||
2039 | #endif | ||
2040 | bb_error_msg("%s: readonly variable", str); | ||
2041 | free(str); | 2091 | free(str); |
2092 | //NOTE: in bash, assignment in "export READONLY_VAR=Z" fails, and sets $?=1, | ||
2093 | //but export per se succeeds (does put the var in env). We don't mimic that. | ||
2042 | return -1; | 2094 | return -1; |
2043 | } | 2095 | } |
2044 | if (flg_export == -1) { // "&& cur->flg_export" ? | 2096 | if (flags & SETFLAG_UNEXPORT) { // && cur->flg_export ? |
2045 | debug_printf_env("%s: unsetenv '%s'\n", __func__, str); | 2097 | debug_printf_env("%s: unsetenv '%s'\n", __func__, str); |
2046 | *eq_sign = '\0'; | 2098 | *eq_sign = '\0'; |
2047 | unsetenv(str); | 2099 | unsetenv(str); |
@@ -2065,7 +2117,7 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_ | |||
2065 | * z=z | 2117 | * z=z |
2066 | */ | 2118 | */ |
2067 | if (cur->flg_export) | 2119 | if (cur->flg_export) |
2068 | flg_export = 1; | 2120 | flags |= SETFLAG_EXPORT; |
2069 | break; | 2121 | break; |
2070 | } | 2122 | } |
2071 | #endif | 2123 | #endif |
@@ -2096,24 +2148,24 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_ | |||
2096 | 2148 | ||
2097 | /* Not found - create new variable struct */ | 2149 | /* Not found - create new variable struct */ |
2098 | cur = xzalloc(sizeof(*cur)); | 2150 | cur = xzalloc(sizeof(*cur)); |
2099 | #if ENABLE_HUSH_LOCAL | 2151 | IF_HUSH_LOCAL(cur->func_nest_level = local_lvl;) |
2100 | cur->func_nest_level = local_lvl; | ||
2101 | #endif | ||
2102 | cur->next = *var_pp; | 2152 | cur->next = *var_pp; |
2103 | *var_pp = cur; | 2153 | *var_pp = cur; |
2104 | 2154 | ||
2105 | set_str_and_exp: | 2155 | set_str_and_exp: |
2106 | cur->varstr = str; | 2156 | cur->varstr = str; |
2107 | #if !BB_MMU | ||
2108 | cur->flg_read_only = flg_read_only; | ||
2109 | #endif | ||
2110 | exp: | 2157 | exp: |
2111 | if (flg_export == 1) | 2158 | #if !BB_MMU || ENABLE_HUSH_READONLY |
2159 | if (flags & SETFLAG_MAKE_RO) { | ||
2160 | cur->flg_read_only = 1; | ||
2161 | } | ||
2162 | #endif | ||
2163 | if (flags & SETFLAG_EXPORT) | ||
2112 | cur->flg_export = 1; | 2164 | cur->flg_export = 1; |
2113 | if (name_len == 4 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S') | 2165 | if (name_len == 4 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S') |
2114 | cmdedit_update_prompt(); | 2166 | cmdedit_update_prompt(); |
2115 | if (cur->flg_export) { | 2167 | if (cur->flg_export) { |
2116 | if (flg_export == -1) { | 2168 | if (flags & SETFLAG_UNEXPORT) { |
2117 | cur->flg_export = 0; | 2169 | cur->flg_export = 0; |
2118 | /* unsetenv was already done */ | 2170 | /* unsetenv was already done */ |
2119 | } else { | 2171 | } else { |
@@ -2130,10 +2182,9 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_ | |||
2130 | } | 2182 | } |
2131 | 2183 | ||
2132 | /* Used at startup and after each cd */ | 2184 | /* Used at startup and after each cd */ |
2133 | static void set_pwd_var(int exp) | 2185 | static void set_pwd_var(unsigned flag) |
2134 | { | 2186 | { |
2135 | set_local_var(xasprintf("PWD=%s", get_cwd(/*force:*/ 1)), | 2187 | set_local_var(xasprintf("PWD=%s", get_cwd(/*force:*/ 1)), flag); |
2136 | /*exp:*/ exp, /*lvl:*/ 0, /*ro:*/ 0); | ||
2137 | } | 2188 | } |
2138 | 2189 | ||
2139 | static int unset_local_var_len(const char *name, int name_len) | 2190 | static int unset_local_var_len(const char *name, int name_len) |
@@ -2191,7 +2242,7 @@ static void unset_vars(char **strings) | |||
2191 | static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) | 2242 | static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) |
2192 | { | 2243 | { |
2193 | char *var = xasprintf("%s=%s", name, val); | 2244 | char *var = xasprintf("%s=%s", name, val); |
2194 | set_local_var(var, /*flags:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); | 2245 | set_local_var(var, /*flag:*/ 0); |
2195 | } | 2246 | } |
2196 | #endif | 2247 | #endif |
2197 | 2248 | ||
@@ -2234,16 +2285,32 @@ static struct variable *set_vars_and_save_old(char **strings) | |||
2234 | if (eq) { | 2285 | if (eq) { |
2235 | var_pp = get_ptr_to_local_var(*s, eq - *s); | 2286 | var_pp = get_ptr_to_local_var(*s, eq - *s); |
2236 | if (var_pp) { | 2287 | if (var_pp) { |
2237 | /* Remove variable from global linked list */ | ||
2238 | var_p = *var_pp; | 2288 | var_p = *var_pp; |
2289 | if (var_p->flg_read_only) { | ||
2290 | char **p; | ||
2291 | bb_error_msg("%s: readonly variable", *s); | ||
2292 | /* | ||
2293 | * "VAR=V BLTIN" unsets VARs after BLTIN completes. | ||
2294 | * If VAR is readonly, leaving it in the list | ||
2295 | * after asssignment error (msg above) | ||
2296 | * causes doubled error message later, on unset. | ||
2297 | */ | ||
2298 | debug_printf_env("removing/freeing '%s' element\n", *s); | ||
2299 | free(*s); | ||
2300 | p = s; | ||
2301 | do { *p = p[1]; p++; } while (*p); | ||
2302 | goto next; | ||
2303 | } | ||
2304 | /* Remove variable from global linked list */ | ||
2239 | debug_printf_env("%s: removing '%s'\n", __func__, var_p->varstr); | 2305 | debug_printf_env("%s: removing '%s'\n", __func__, var_p->varstr); |
2240 | *var_pp = var_p->next; | 2306 | *var_pp = var_p->next; |
2241 | /* Add it to returned list */ | 2307 | /* Add it to returned list */ |
2242 | var_p->next = old; | 2308 | var_p->next = old; |
2243 | old = var_p; | 2309 | old = var_p; |
2244 | } | 2310 | } |
2245 | set_local_var(*s, /*exp:*/ 1, /*lvl:*/ 0, /*ro:*/ 0); | 2311 | set_local_var(*s, SETFLAG_EXPORT); |
2246 | } | 2312 | } |
2313 | next: | ||
2247 | s++; | 2314 | s++; |
2248 | } | 2315 | } |
2249 | return old; | 2316 | return old; |
@@ -3339,12 +3406,49 @@ static void done_pipe(struct parse_context *ctx, pipe_style type) | |||
3339 | debug_printf_parse("done_pipe entered, followup %d\n", type); | 3406 | debug_printf_parse("done_pipe entered, followup %d\n", type); |
3340 | /* Close previous command */ | 3407 | /* Close previous command */ |
3341 | not_null = done_command(ctx); | 3408 | not_null = done_command(ctx); |
3342 | ctx->pipe->followup = type; | ||
3343 | #if HAS_KEYWORDS | 3409 | #if HAS_KEYWORDS |
3344 | ctx->pipe->pi_inverted = ctx->ctx_inverted; | 3410 | ctx->pipe->pi_inverted = ctx->ctx_inverted; |
3345 | ctx->ctx_inverted = 0; | 3411 | ctx->ctx_inverted = 0; |
3346 | ctx->pipe->res_word = ctx->ctx_res_w; | 3412 | ctx->pipe->res_word = ctx->ctx_res_w; |
3347 | #endif | 3413 | #endif |
3414 | if (type == PIPE_BG && ctx->list_head != ctx->pipe) { | ||
3415 | /* Necessary since && and || have precedence over &: | ||
3416 | * "cmd1 && cmd2 &" must spawn both cmds, not only cmd2, | ||
3417 | * in a backgrounded subshell. | ||
3418 | */ | ||
3419 | struct pipe *pi; | ||
3420 | struct command *command; | ||
3421 | |||
3422 | /* Is this actually this construct, all pipes end with && or ||? */ | ||
3423 | pi = ctx->list_head; | ||
3424 | while (pi != ctx->pipe) { | ||
3425 | if (pi->followup != PIPE_AND && pi->followup != PIPE_OR) | ||
3426 | goto no_conv; | ||
3427 | pi = pi->next; | ||
3428 | } | ||
3429 | |||
3430 | debug_printf_parse("BG with more than one pipe, converting to { p1 &&...pN; } &\n"); | ||
3431 | pi->followup = PIPE_SEQ; /* close pN _not_ with "&"! */ | ||
3432 | pi = xzalloc(sizeof(*pi)); | ||
3433 | pi->followup = PIPE_BG; | ||
3434 | pi->num_cmds = 1; | ||
3435 | pi->cmds = xzalloc(sizeof(pi->cmds[0])); | ||
3436 | command = &pi->cmds[0]; | ||
3437 | if (CMD_NORMAL != 0) /* "if xzalloc didn't do that already" */ | ||
3438 | command->cmd_type = CMD_NORMAL; | ||
3439 | command->group = ctx->list_head; | ||
3440 | #if !BB_MMU | ||
3441 | command->group_as_string = xstrndup( | ||
3442 | ctx->as_string.data, | ||
3443 | ctx->as_string.length - 1 /* do not copy last char, "&" */ | ||
3444 | ); | ||
3445 | #endif | ||
3446 | /* Replace all pipes in ctx with one newly created */ | ||
3447 | ctx->list_head = ctx->pipe = pi; | ||
3448 | } else { | ||
3449 | no_conv: | ||
3450 | ctx->pipe->followup = type; | ||
3451 | } | ||
3348 | 3452 | ||
3349 | /* Without this check, even just <enter> on command line generates | 3453 | /* Without this check, even just <enter> on command line generates |
3350 | * tree of three NOPs (!). Which is harmless but annoying. | 3454 | * tree of three NOPs (!). Which is harmless but annoying. |
@@ -3514,9 +3618,8 @@ static int reserved_word(o_string *word, struct parse_context *ctx) | |||
3514 | if (r->flag & FLAG_START) { | 3618 | if (r->flag & FLAG_START) { |
3515 | struct parse_context *old; | 3619 | struct parse_context *old; |
3516 | 3620 | ||
3517 | old = xmalloc(sizeof(*old)); | 3621 | old = xmemdup(ctx, sizeof(*ctx)); |
3518 | debug_printf_parse("push stack %p\n", old); | 3622 | debug_printf_parse("push stack %p\n", old); |
3519 | *old = *ctx; /* physical copy */ | ||
3520 | initialize_context(ctx); | 3623 | initialize_context(ctx); |
3521 | ctx->stack = old; | 3624 | ctx->stack = old; |
3522 | } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) { | 3625 | } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) { |
@@ -4787,7 +4890,9 @@ static struct pipe *parse_stream(char **pstring, | |||
4787 | * Really, ask yourself, why | 4890 | * Really, ask yourself, why |
4788 | * "cmd && <newline>" doesn't start | 4891 | * "cmd && <newline>" doesn't start |
4789 | * cmd but waits for more input? | 4892 | * cmd but waits for more input? |
4790 | * No reason...) | 4893 | * The only reason is that it might be |
4894 | * a "cmd1 && <nl> cmd2 &" construct, | ||
4895 | * cmd1 may need to run in BG). | ||
4791 | */ | 4896 | */ |
4792 | struct pipe *pi = ctx.list_head; | 4897 | struct pipe *pi = ctx.list_head; |
4793 | if (pi->num_cmds != 0 /* check #1 */ | 4898 | if (pi->num_cmds != 0 /* check #1 */ |
@@ -5146,7 +5251,7 @@ static struct pipe *parse_stream(char **pstring, | |||
5146 | * and it will match } earlier (not here). */ | 5251 | * and it will match } earlier (not here). */ |
5147 | syntax_error_unexpected_ch(ch); | 5252 | syntax_error_unexpected_ch(ch); |
5148 | G.last_exitcode = 2; | 5253 | G.last_exitcode = 2; |
5149 | goto parse_error1; | 5254 | goto parse_error2; |
5150 | default: | 5255 | default: |
5151 | if (HUSH_DEBUG) | 5256 | if (HUSH_DEBUG) |
5152 | bb_error_msg_and_die("BUG: unexpected %c\n", ch); | 5257 | bb_error_msg_and_die("BUG: unexpected %c\n", ch); |
@@ -5155,7 +5260,7 @@ static struct pipe *parse_stream(char **pstring, | |||
5155 | 5260 | ||
5156 | parse_error: | 5261 | parse_error: |
5157 | G.last_exitcode = 1; | 5262 | G.last_exitcode = 1; |
5158 | parse_error1: | 5263 | parse_error2: |
5159 | { | 5264 | { |
5160 | struct parse_context *pctx; | 5265 | struct parse_context *pctx; |
5161 | IF_HAS_KEYWORDS(struct parse_context *p2;) | 5266 | IF_HAS_KEYWORDS(struct parse_context *p2;) |
@@ -5202,7 +5307,7 @@ static struct pipe *parse_stream(char **pstring, | |||
5202 | /*** Execution routines ***/ | 5307 | /*** Execution routines ***/ |
5203 | 5308 | ||
5204 | /* Expansion can recurse, need forward decls: */ | 5309 | /* Expansion can recurse, need forward decls: */ |
5205 | #if !BASH_PATTERN_SUBST | 5310 | #if !BASH_PATTERN_SUBST && !ENABLE_HUSH_CASE |
5206 | /* only ${var/pattern/repl} (its pattern part) needs additional mode */ | 5311 | /* only ${var/pattern/repl} (its pattern part) needs additional mode */ |
5207 | #define expand_string_to_string(str, do_unbackslash) \ | 5312 | #define expand_string_to_string(str, do_unbackslash) \ |
5208 | expand_string_to_string(str) | 5313 | expand_string_to_string(str) |
@@ -5330,6 +5435,9 @@ static int expand_on_ifs(int *ended_with_ifs, o_string *output, int n, const cha | |||
5330 | #endif | 5435 | #endif |
5331 | static char *encode_then_expand_string(const char *str, int process_bkslash, int do_unbackslash) | 5436 | static char *encode_then_expand_string(const char *str, int process_bkslash, int do_unbackslash) |
5332 | { | 5437 | { |
5438 | #if !BASH_PATTERN_SUBST | ||
5439 | const int do_unbackslash = 1; | ||
5440 | #endif | ||
5333 | char *exp_str; | 5441 | char *exp_str; |
5334 | struct in_str input; | 5442 | struct in_str input; |
5335 | o_string dest = NULL_O_STRING; | 5443 | o_string dest = NULL_O_STRING; |
@@ -5628,27 +5736,34 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
5628 | if (errmsg) | 5736 | if (errmsg) |
5629 | goto arith_err; | 5737 | goto arith_err; |
5630 | debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len); | 5738 | debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len); |
5631 | if (len >= 0) { /* bash compat: len < 0 is illegal */ | 5739 | if (beg < 0) { |
5632 | if (beg < 0) /* bash compat */ | 5740 | /* negative beg counts from the end */ |
5633 | beg = 0; | 5741 | beg = (arith_t)strlen(val) + beg; |
5634 | debug_printf_varexp("from val:'%s'\n", val); | 5742 | if (beg < 0) /* ${v: -999999} is "" */ |
5635 | if (len == 0 || !val || beg >= strlen(val)) { | 5743 | beg = len = 0; |
5744 | } | ||
5745 | debug_printf_varexp("from val:'%s'\n", val); | ||
5746 | if (len < 0) { | ||
5747 | /* in bash, len=-n means strlen()-n */ | ||
5748 | len = (arith_t)strlen(val) - beg + len; | ||
5749 | if (len < 0) /* bash compat */ | ||
5750 | die_if_script("%s: substring expression < 0", var); | ||
5751 | } | ||
5752 | if (len <= 0 || !val || beg >= strlen(val)) { | ||
5636 | arith_err: | 5753 | arith_err: |
5637 | val = NULL; | ||
5638 | } else { | ||
5639 | /* Paranoia. What if user entered 9999999999999 | ||
5640 | * which fits in arith_t but not int? */ | ||
5641 | if (len >= INT_MAX) | ||
5642 | len = INT_MAX; | ||
5643 | val = to_be_freed = xstrndup(val + beg, len); | ||
5644 | } | ||
5645 | debug_printf_varexp("val:'%s'\n", val); | ||
5646 | } else | ||
5647 | #endif /* HUSH_SUBSTR_EXPANSION && FEATURE_SH_MATH */ | ||
5648 | { | ||
5649 | die_if_script("malformed ${%s:...}", var); | ||
5650 | val = NULL; | 5754 | val = NULL; |
5755 | } else { | ||
5756 | /* Paranoia. What if user entered 9999999999999 | ||
5757 | * which fits in arith_t but not int? */ | ||
5758 | if (len >= INT_MAX) | ||
5759 | len = INT_MAX; | ||
5760 | val = to_be_freed = xstrndup(val + beg, len); | ||
5651 | } | 5761 | } |
5762 | debug_printf_varexp("val:'%s'\n", val); | ||
5763 | #else /* not (HUSH_SUBSTR_EXPANSION && FEATURE_SH_MATH) */ | ||
5764 | die_if_script("malformed ${%s:...}", var); | ||
5765 | val = NULL; | ||
5766 | #endif | ||
5652 | } else { /* one of "-=+?" */ | 5767 | } else { /* one of "-=+?" */ |
5653 | /* Standard-mandated substitution ops: | 5768 | /* Standard-mandated substitution ops: |
5654 | * ${var?word} - indicate error if unset | 5769 | * ${var?word} - indicate error if unset |
@@ -5700,7 +5815,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
5700 | val = NULL; | 5815 | val = NULL; |
5701 | } else { | 5816 | } else { |
5702 | char *new_var = xasprintf("%s=%s", var, val); | 5817 | char *new_var = xasprintf("%s=%s", var, val); |
5703 | set_local_var(new_var, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); | 5818 | set_local_var(new_var, /*flag:*/ 0); |
5704 | } | 5819 | } |
5705 | } | 5820 | } |
5706 | } | 5821 | } |
@@ -5949,7 +6064,7 @@ static char **expand_strvec_to_strvec_singleword_noglob(char **argv) | |||
5949 | */ | 6064 | */ |
5950 | static char *expand_string_to_string(const char *str, int do_unbackslash) | 6065 | static char *expand_string_to_string(const char *str, int do_unbackslash) |
5951 | { | 6066 | { |
5952 | #if !BASH_PATTERN_SUBST | 6067 | #if !BASH_PATTERN_SUBST && !ENABLE_HUSH_CASE |
5953 | const int do_unbackslash = 1; | 6068 | const int do_unbackslash = 1; |
5954 | #endif | 6069 | #endif |
5955 | char *argv[2], **list; | 6070 | char *argv[2], **list; |
@@ -5982,7 +6097,7 @@ static char *expand_string_to_string(const char *str, int do_unbackslash) | |||
5982 | return (char*)list; | 6097 | return (char*)list; |
5983 | } | 6098 | } |
5984 | 6099 | ||
5985 | /* Used for "eval" builtin */ | 6100 | /* Used for "eval" builtin and case string */ |
5986 | static char* expand_strvec_to_string(char **argv) | 6101 | static char* expand_strvec_to_string(char **argv) |
5987 | { | 6102 | { |
5988 | char **list; | 6103 | char **list; |
@@ -6524,77 +6639,108 @@ static void setup_heredoc(struct redir_struct *redir) | |||
6524 | wait(NULL); /* wait till child has died */ | 6639 | wait(NULL); /* wait till child has died */ |
6525 | } | 6640 | } |
6526 | 6641 | ||
6527 | /* fd: redirect wants this fd to be used (e.g. 3>file). | 6642 | struct squirrel { |
6528 | * Move all conflicting internally used fds, | 6643 | int orig_fd; |
6529 | * and remember them so that we can restore them later. | 6644 | int moved_to; |
6530 | */ | 6645 | /* moved_to = n: fd was moved to n; restore back to orig_fd after redir */ |
6531 | static int save_fds_on_redirect(int fd, int squirrel[3]) | 6646 | /* moved_to = -1: fd was opened by redirect; close orig_fd after redir */ |
6647 | }; | ||
6648 | |||
6649 | static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) | ||
6532 | { | 6650 | { |
6533 | if (squirrel) { | 6651 | int i = 0; |
6534 | /* Handle redirects of fds 0,1,2 */ | ||
6535 | 6652 | ||
6536 | /* If we collide with an already moved stdio fd... */ | 6653 | if (sq) while (sq[i].orig_fd >= 0) { |
6537 | if (fd == squirrel[0]) { | 6654 | /* If we collide with an already moved fd... */ |
6538 | squirrel[0] = xdup_and_close(squirrel[0], F_DUPFD); | 6655 | if (fd == sq[i].moved_to) { |
6539 | return 1; | 6656 | sq[i].moved_to = fcntl_F_DUPFD(sq[i].moved_to, avoid_fd); |
6540 | } | 6657 | debug_printf_redir("redirect_fd %d: already busy, moving to %d\n", fd, sq[i].moved_to); |
6541 | if (fd == squirrel[1]) { | 6658 | if (sq[i].moved_to < 0) /* what? */ |
6542 | squirrel[1] = xdup_and_close(squirrel[1], F_DUPFD); | ||
6543 | return 1; | ||
6544 | } | ||
6545 | if (fd == squirrel[2]) { | ||
6546 | squirrel[2] = xdup_and_close(squirrel[2], F_DUPFD); | ||
6547 | return 1; | ||
6548 | } | ||
6549 | /* If we are about to redirect stdio fd, and did not yet move it... */ | ||
6550 | if (fd <= 2 && squirrel[fd] < 0) { | ||
6551 | /* We avoid taking stdio fds */ | ||
6552 | squirrel[fd] = fcntl(fd, F_DUPFD, 10); | ||
6553 | if (squirrel[fd] < 0 && errno != EBADF) | ||
6554 | xfunc_die(); | 6659 | xfunc_die(); |
6555 | return 0; /* "we did not close fd" */ | 6660 | return sq; |
6661 | } | ||
6662 | if (fd == sq[i].orig_fd) { | ||
6663 | /* Example: echo Hello >/dev/null 1>&2 */ | ||
6664 | debug_printf_redir("redirect_fd %d: already moved\n", fd); | ||
6665 | return sq; | ||
6556 | } | 6666 | } |
6667 | i++; | ||
6557 | } | 6668 | } |
6558 | 6669 | ||
6670 | sq = xrealloc(sq, (i + 2) * sizeof(sq[0])); | ||
6671 | sq[i].orig_fd = fd; | ||
6672 | /* If this fd is open, we move and remember it; if it's closed, moved_to = -1 */ | ||
6673 | sq[i].moved_to = fcntl_F_DUPFD(fd, avoid_fd); | ||
6674 | debug_printf_redir("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, sq[i].moved_to); | ||
6675 | if (sq[i].moved_to < 0 && errno != EBADF) | ||
6676 | xfunc_die(); | ||
6677 | sq[i+1].orig_fd = -1; /* end marker */ | ||
6678 | return sq; | ||
6679 | } | ||
6680 | |||
6681 | /* fd: redirect wants this fd to be used (e.g. 3>file). | ||
6682 | * Move all conflicting internally used fds, | ||
6683 | * and remember them so that we can restore them later. | ||
6684 | */ | ||
6685 | static int save_fds_on_redirect(int fd, int avoid_fd, struct squirrel **sqp) | ||
6686 | { | ||
6687 | if (avoid_fd < 9) /* the important case here is that it can be -1 */ | ||
6688 | avoid_fd = 9; | ||
6689 | |||
6559 | #if ENABLE_HUSH_INTERACTIVE | 6690 | #if ENABLE_HUSH_INTERACTIVE |
6560 | if (fd != 0 && fd == G.interactive_fd) { | 6691 | if (fd != 0 && fd == G.interactive_fd) { |
6561 | G.interactive_fd = xdup_and_close(G.interactive_fd, F_DUPFD_CLOEXEC); | 6692 | G.interactive_fd = xdup_and_close(G.interactive_fd, F_DUPFD_CLOEXEC, avoid_fd); |
6562 | return 1; | 6693 | debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G.interactive_fd); |
6694 | return 1; /* "we closed fd" */ | ||
6563 | } | 6695 | } |
6564 | #endif | 6696 | #endif |
6565 | |||
6566 | /* Are we called from setup_redirects(squirrel==NULL)? Two cases: | 6697 | /* Are we called from setup_redirects(squirrel==NULL)? Two cases: |
6567 | * (1) Redirect in a forked child. No need to save FILEs' fds, | 6698 | * (1) Redirect in a forked child. No need to save FILEs' fds, |
6568 | * we aren't going to use them anymore, ok to trash. | 6699 | * we aren't going to use them anymore, ok to trash. |
6569 | * (2) "exec 3>FILE". Bummer. We can save FILEs' fds, | 6700 | * (2) "exec 3>FILE". Bummer. We can save script FILEs' fds, |
6570 | * but how are we doing to use them? | 6701 | * but how are we doing to restore them? |
6571 | * "fileno(fd) = new_fd" can't be done. | 6702 | * "fileno(fd) = new_fd" can't be done. |
6572 | */ | 6703 | */ |
6573 | if (!squirrel) | 6704 | if (!sqp) |
6574 | return 0; | 6705 | return 0; |
6575 | 6706 | ||
6576 | return save_FILEs_on_redirect(fd); | 6707 | /* If this one of script's fds? */ |
6708 | if (save_FILEs_on_redirect(fd, avoid_fd)) | ||
6709 | return 1; /* yes. "we closed fd" */ | ||
6710 | |||
6711 | /* Check whether it collides with any open fds (e.g. stdio), save fds as needed */ | ||
6712 | *sqp = add_squirrel(*sqp, fd, avoid_fd); | ||
6713 | return 0; /* "we did not close fd" */ | ||
6577 | } | 6714 | } |
6578 | 6715 | ||
6579 | static void restore_redirects(int squirrel[3]) | 6716 | static void restore_redirects(struct squirrel *sq) |
6580 | { | 6717 | { |
6581 | int i, fd; | 6718 | |
6582 | for (i = 0; i <= 2; i++) { | 6719 | if (sq) { |
6583 | fd = squirrel[i]; | 6720 | int i = 0; |
6584 | if (fd != -1) { | 6721 | while (sq[i].orig_fd >= 0) { |
6585 | /* We simply die on error */ | 6722 | if (sq[i].moved_to >= 0) { |
6586 | xmove_fd(fd, i); | 6723 | /* We simply die on error */ |
6724 | debug_printf_redir("restoring redirected fd from %d to %d\n", sq[i].moved_to, sq[i].orig_fd); | ||
6725 | xmove_fd(sq[i].moved_to, sq[i].orig_fd); | ||
6726 | } else { | ||
6727 | /* cmd1 9>FILE; cmd2_should_see_fd9_closed */ | ||
6728 | debug_printf_redir("restoring redirected fd %d: closing it\n", sq[i].orig_fd); | ||
6729 | close(sq[i].orig_fd); | ||
6730 | } | ||
6731 | i++; | ||
6587 | } | 6732 | } |
6733 | free(sq); | ||
6588 | } | 6734 | } |
6589 | 6735 | ||
6590 | /* Moved G.interactive_fd stays on new fd, not doing anything for it */ | 6736 | /* If moved, G.interactive_fd stays on new fd, not restoring it */ |
6591 | 6737 | ||
6592 | restore_redirected_FILEs(); | 6738 | restore_redirected_FILEs(); |
6593 | } | 6739 | } |
6594 | 6740 | ||
6595 | /* squirrel != NULL means we squirrel away copies of stdin, stdout, | 6741 | /* squirrel != NULL means we squirrel away copies of stdin, stdout, |
6596 | * and stderr if they are redirected. */ | 6742 | * and stderr if they are redirected. */ |
6597 | static int setup_redirects(struct command *prog, int squirrel[]) | 6743 | static int setup_redirects(struct command *prog, struct squirrel **sqp) |
6598 | { | 6744 | { |
6599 | int openfd, mode; | 6745 | int openfd, mode; |
6600 | struct redir_struct *redir; | 6746 | struct redir_struct *redir; |
@@ -6602,7 +6748,7 @@ static int setup_redirects(struct command *prog, int squirrel[]) | |||
6602 | for (redir = prog->redirects; redir; redir = redir->next) { | 6748 | for (redir = prog->redirects; redir; redir = redir->next) { |
6603 | if (redir->rd_type == REDIRECT_HEREDOC2) { | 6749 | if (redir->rd_type == REDIRECT_HEREDOC2) { |
6604 | /* "rd_fd<<HERE" case */ | 6750 | /* "rd_fd<<HERE" case */ |
6605 | save_fds_on_redirect(redir->rd_fd, squirrel); | 6751 | save_fds_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp); |
6606 | /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ | 6752 | /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ |
6607 | * of the heredoc */ | 6753 | * of the heredoc */ |
6608 | debug_printf_parse("set heredoc '%s'\n", | 6754 | debug_printf_parse("set heredoc '%s'\n", |
@@ -6641,7 +6787,7 @@ static int setup_redirects(struct command *prog, int squirrel[]) | |||
6641 | } | 6787 | } |
6642 | 6788 | ||
6643 | if (openfd != redir->rd_fd) { | 6789 | if (openfd != redir->rd_fd) { |
6644 | int closed = save_fds_on_redirect(redir->rd_fd, squirrel); | 6790 | int closed = save_fds_on_redirect(redir->rd_fd, /*avoid:*/ openfd, sqp); |
6645 | if (openfd == REDIRFD_CLOSE) { | 6791 | if (openfd == REDIRFD_CLOSE) { |
6646 | /* "rd_fd >&-" means "close me" */ | 6792 | /* "rd_fd >&-" means "close me" */ |
6647 | if (!closed) { | 6793 | if (!closed) { |
@@ -6817,13 +6963,11 @@ static void exec_function(char ***to_free, | |||
6817 | char **argv) | 6963 | char **argv) |
6818 | { | 6964 | { |
6819 | # if BB_MMU | 6965 | # if BB_MMU |
6820 | int n = 1; | 6966 | int n; |
6821 | 6967 | ||
6822 | argv[0] = G.global_argv[0]; | 6968 | argv[0] = G.global_argv[0]; |
6823 | G.global_argv = argv; | 6969 | G.global_argv = argv; |
6824 | while (*++argv) | 6970 | G.global_argc = n = 1 + string_array_len(argv + 1); |
6825 | n++; | ||
6826 | G.global_argc = n; | ||
6827 | /* On MMU, funcp->body is always non-NULL */ | 6971 | /* On MMU, funcp->body is always non-NULL */ |
6828 | n = run_list(funcp->body); | 6972 | n = run_list(funcp->body); |
6829 | fflush_all(); | 6973 | fflush_all(); |
@@ -7071,7 +7215,7 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
7071 | /* Do not leak open fds from opened script files etc */ | 7215 | /* Do not leak open fds from opened script files etc */ |
7072 | close_all_FILE_list(); | 7216 | close_all_FILE_list(); |
7073 | debug_printf_exec("running applet '%s'\n", argv[0]); | 7217 | debug_printf_exec("running applet '%s'\n", argv[0]); |
7074 | run_applet_no_and_exit(a, argv); | 7218 | run_applet_no_and_exit(a, argv[0], argv); |
7075 | } | 7219 | } |
7076 | # endif | 7220 | # endif |
7077 | /* Re-exec ourselves */ | 7221 | /* Re-exec ourselves */ |
@@ -7168,24 +7312,54 @@ static const char *get_cmdtext(struct pipe *pi) | |||
7168 | return pi->cmdtext; | 7312 | return pi->cmdtext; |
7169 | } | 7313 | } |
7170 | 7314 | ||
7171 | static void insert_bg_job(struct pipe *pi) | 7315 | static void remove_job_from_table(struct pipe *pi) |
7316 | { | ||
7317 | struct pipe *prev_pipe; | ||
7318 | |||
7319 | if (pi == G.job_list) { | ||
7320 | G.job_list = pi->next; | ||
7321 | } else { | ||
7322 | prev_pipe = G.job_list; | ||
7323 | while (prev_pipe->next != pi) | ||
7324 | prev_pipe = prev_pipe->next; | ||
7325 | prev_pipe->next = pi->next; | ||
7326 | } | ||
7327 | G.last_jobid = 0; | ||
7328 | if (G.job_list) | ||
7329 | G.last_jobid = G.job_list->jobid; | ||
7330 | } | ||
7331 | |||
7332 | static void delete_finished_job(struct pipe *pi) | ||
7333 | { | ||
7334 | remove_job_from_table(pi); | ||
7335 | free_pipe(pi); | ||
7336 | } | ||
7337 | |||
7338 | static void clean_up_last_dead_job(void) | ||
7339 | { | ||
7340 | if (G.job_list && !G.job_list->alive_cmds) | ||
7341 | delete_finished_job(G.job_list); | ||
7342 | } | ||
7343 | |||
7344 | static void insert_job_into_table(struct pipe *pi) | ||
7172 | { | 7345 | { |
7173 | struct pipe *job, **jobp; | 7346 | struct pipe *job, **jobp; |
7174 | int i; | 7347 | int i; |
7175 | 7348 | ||
7176 | /* Linear search for the ID of the job to use */ | 7349 | clean_up_last_dead_job(); |
7177 | pi->jobid = 1; | ||
7178 | for (job = G.job_list; job; job = job->next) | ||
7179 | if (job->jobid >= pi->jobid) | ||
7180 | pi->jobid = job->jobid + 1; | ||
7181 | 7350 | ||
7182 | /* Add job to the list of running jobs */ | 7351 | /* Find the end of the list, and find next job ID to use */ |
7352 | i = 0; | ||
7183 | jobp = &G.job_list; | 7353 | jobp = &G.job_list; |
7184 | while ((job = *jobp) != NULL) | 7354 | while ((job = *jobp) != NULL) { |
7355 | if (job->jobid > i) | ||
7356 | i = job->jobid; | ||
7185 | jobp = &job->next; | 7357 | jobp = &job->next; |
7186 | job = *jobp = xmalloc(sizeof(*job)); | 7358 | } |
7359 | pi->jobid = i + 1; | ||
7187 | 7360 | ||
7188 | *job = *pi; /* physical copy */ | 7361 | /* Create a new job struct at the end */ |
7362 | job = *jobp = xmemdup(pi, sizeof(*pi)); | ||
7189 | job->next = NULL; | 7363 | job->next = NULL; |
7190 | job->cmds = xzalloc(sizeof(pi->cmds[0]) * pi->num_cmds); | 7364 | job->cmds = xzalloc(sizeof(pi->cmds[0]) * pi->num_cmds); |
7191 | /* Cannot copy entire pi->cmds[] vector! This causes double frees */ | 7365 | /* Cannot copy entire pi->cmds[] vector! This causes double frees */ |
@@ -7199,31 +7373,6 @@ static void insert_bg_job(struct pipe *pi) | |||
7199 | printf("[%u] %u %s\n", job->jobid, (unsigned)job->cmds[0].pid, job->cmdtext); | 7373 | printf("[%u] %u %s\n", job->jobid, (unsigned)job->cmds[0].pid, job->cmdtext); |
7200 | G.last_jobid = job->jobid; | 7374 | G.last_jobid = job->jobid; |
7201 | } | 7375 | } |
7202 | |||
7203 | static void remove_bg_job(struct pipe *pi) | ||
7204 | { | ||
7205 | struct pipe *prev_pipe; | ||
7206 | |||
7207 | if (pi == G.job_list) { | ||
7208 | G.job_list = pi->next; | ||
7209 | } else { | ||
7210 | prev_pipe = G.job_list; | ||
7211 | while (prev_pipe->next != pi) | ||
7212 | prev_pipe = prev_pipe->next; | ||
7213 | prev_pipe->next = pi->next; | ||
7214 | } | ||
7215 | if (G.job_list) | ||
7216 | G.last_jobid = G.job_list->jobid; | ||
7217 | else | ||
7218 | G.last_jobid = 0; | ||
7219 | } | ||
7220 | |||
7221 | /* Remove a backgrounded job */ | ||
7222 | static void delete_finished_bg_job(struct pipe *pi) | ||
7223 | { | ||
7224 | remove_bg_job(pi); | ||
7225 | free_pipe(pi); | ||
7226 | } | ||
7227 | #endif /* JOB */ | 7376 | #endif /* JOB */ |
7228 | 7377 | ||
7229 | static int job_exited_or_stopped(struct pipe *pi) | 7378 | static int job_exited_or_stopped(struct pipe *pi) |
@@ -7310,7 +7459,7 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) | |||
7310 | if (G_interactive_fd) { | 7459 | if (G_interactive_fd) { |
7311 | #if ENABLE_HUSH_JOB | 7460 | #if ENABLE_HUSH_JOB |
7312 | if (fg_pipe->alive_cmds != 0) | 7461 | if (fg_pipe->alive_cmds != 0) |
7313 | insert_bg_job(fg_pipe); | 7462 | insert_job_into_table(fg_pipe); |
7314 | #endif | 7463 | #endif |
7315 | return rcode; | 7464 | return rcode; |
7316 | } | 7465 | } |
@@ -7339,16 +7488,31 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) | |||
7339 | found_pi_and_prognum: | 7488 | found_pi_and_prognum: |
7340 | if (dead) { | 7489 | if (dead) { |
7341 | /* child exited */ | 7490 | /* child exited */ |
7342 | pi->cmds[i].pid = 0; | 7491 | int rcode = WEXITSTATUS(status); |
7343 | pi->cmds[i].cmd_exitcode = WEXITSTATUS(status); | ||
7344 | if (WIFSIGNALED(status)) | 7492 | if (WIFSIGNALED(status)) |
7345 | pi->cmds[i].cmd_exitcode = 128 + WTERMSIG(status); | 7493 | rcode = 128 + WTERMSIG(status); |
7494 | pi->cmds[i].cmd_exitcode = rcode; | ||
7495 | if (G.last_bg_pid == pi->cmds[i].pid) | ||
7496 | G.last_bg_pid_exitcode = rcode; | ||
7497 | pi->cmds[i].pid = 0; | ||
7346 | pi->alive_cmds--; | 7498 | pi->alive_cmds--; |
7347 | if (!pi->alive_cmds) { | 7499 | if (!pi->alive_cmds) { |
7348 | if (G_interactive_fd) | 7500 | if (G_interactive_fd) { |
7349 | printf(JOB_STATUS_FORMAT, pi->jobid, | 7501 | printf(JOB_STATUS_FORMAT, pi->jobid, |
7350 | "Done", pi->cmdtext); | 7502 | "Done", pi->cmdtext); |
7351 | delete_finished_bg_job(pi); | 7503 | delete_finished_job(pi); |
7504 | } else { | ||
7505 | /* | ||
7506 | * bash deletes finished jobs from job table only in interactive mode, | ||
7507 | * after "jobs" cmd, or if pid of a new process matches one of the old ones | ||
7508 | * (see cleanup_dead_jobs(), delete_old_job(), J_NOTIFIED in bash source). | ||
7509 | * Testcase script: "(exit 3) & sleep 1; wait %1; echo $?" prints 3 in bash. | ||
7510 | * We only retain one "dead" job, if it's the single job on the list. | ||
7511 | * This covers most of real-world scenarios where this is useful. | ||
7512 | */ | ||
7513 | if (pi != G.job_list) | ||
7514 | delete_finished_job(pi); | ||
7515 | } | ||
7352 | } | 7516 | } |
7353 | } else { | 7517 | } else { |
7354 | /* child stopped */ | 7518 | /* child stopped */ |
@@ -7505,14 +7669,14 @@ static int checkjobs_and_fg_shell(struct pipe *fg_pipe) | |||
7505 | static int redirect_and_varexp_helper(char ***new_env_p, | 7669 | static int redirect_and_varexp_helper(char ***new_env_p, |
7506 | struct variable **old_vars_p, | 7670 | struct variable **old_vars_p, |
7507 | struct command *command, | 7671 | struct command *command, |
7508 | int squirrel[3], | 7672 | struct squirrel **sqp, |
7509 | char **argv_expanded) | 7673 | char **argv_expanded) |
7510 | { | 7674 | { |
7511 | /* setup_redirects acts on file descriptors, not FILEs. | 7675 | /* setup_redirects acts on file descriptors, not FILEs. |
7512 | * This is perfect for work that comes after exec(). | 7676 | * This is perfect for work that comes after exec(). |
7513 | * Is it really safe for inline use? Experimentally, | 7677 | * Is it really safe for inline use? Experimentally, |
7514 | * things seem to work. */ | 7678 | * things seem to work. */ |
7515 | int rcode = setup_redirects(command, squirrel); | 7679 | int rcode = setup_redirects(command, sqp); |
7516 | if (rcode == 0) { | 7680 | if (rcode == 0) { |
7517 | char **new_env = expand_assignments(command->argv, command->assignment_cnt); | 7681 | char **new_env = expand_assignments(command->argv, command->assignment_cnt); |
7518 | *new_env_p = new_env; | 7682 | *new_env_p = new_env; |
@@ -7532,8 +7696,7 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
7532 | struct command *command; | 7696 | struct command *command; |
7533 | char **argv_expanded; | 7697 | char **argv_expanded; |
7534 | char **argv; | 7698 | char **argv; |
7535 | /* it is not always needed, but we aim to smaller code */ | 7699 | struct squirrel *squirrel = NULL; |
7536 | int squirrel[] = { -1, -1, -1 }; | ||
7537 | int rcode; | 7700 | int rcode; |
7538 | 7701 | ||
7539 | debug_printf_exec("run_pipe start: members:%d\n", pi->num_cmds); | 7702 | debug_printf_exec("run_pipe start: members:%d\n", pi->num_cmds); |
@@ -7590,7 +7753,7 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
7590 | /* { list } */ | 7753 | /* { list } */ |
7591 | debug_printf("non-subshell group\n"); | 7754 | debug_printf("non-subshell group\n"); |
7592 | rcode = 1; /* exitcode if redir failed */ | 7755 | rcode = 1; /* exitcode if redir failed */ |
7593 | if (setup_redirects(command, squirrel) == 0) { | 7756 | if (setup_redirects(command, &squirrel) == 0) { |
7594 | debug_printf_exec(": run_list\n"); | 7757 | debug_printf_exec(": run_list\n"); |
7595 | rcode = run_list(command->group) & 0xff; | 7758 | rcode = run_list(command->group) & 0xff; |
7596 | } | 7759 | } |
@@ -7617,15 +7780,15 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
7617 | /* Ensure redirects take effect (that is, create files). | 7780 | /* Ensure redirects take effect (that is, create files). |
7618 | * Try "a=t >file" */ | 7781 | * Try "a=t >file" */ |
7619 | #if 0 /* A few cases in testsuite fail with this code. FIXME */ | 7782 | #if 0 /* A few cases in testsuite fail with this code. FIXME */ |
7620 | rcode = redirect_and_varexp_helper(&new_env, /*old_vars:*/ NULL, command, squirrel, /*argv_expanded:*/ NULL); | 7783 | rcode = redirect_and_varexp_helper(&new_env, /*old_vars:*/ NULL, command, &squirrel, /*argv_expanded:*/ NULL); |
7621 | /* Set shell variables */ | 7784 | /* Set shell variables */ |
7622 | if (new_env) { | 7785 | if (new_env) { |
7623 | argv = new_env; | 7786 | argv = new_env; |
7624 | while (*argv) { | 7787 | while (*argv) { |
7625 | set_local_var(*argv, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); | 7788 | if (set_local_var(*argv, /*flag:*/ 0)) { |
7626 | /* Do we need to flag set_local_var() errors? | 7789 | /* assignment to readonly var / putenv error? */ |
7627 | * "assignment to readonly var" and "putenv error" | 7790 | rcode = 1; |
7628 | */ | 7791 | } |
7629 | argv++; | 7792 | argv++; |
7630 | } | 7793 | } |
7631 | } | 7794 | } |
@@ -7639,7 +7802,7 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
7639 | 7802 | ||
7640 | #else /* Older, bigger, but more correct code */ | 7803 | #else /* Older, bigger, but more correct code */ |
7641 | 7804 | ||
7642 | rcode = setup_redirects(command, squirrel); | 7805 | rcode = setup_redirects(command, &squirrel); |
7643 | restore_redirects(squirrel); | 7806 | restore_redirects(squirrel); |
7644 | /* Set shell variables */ | 7807 | /* Set shell variables */ |
7645 | if (G_x_mode) | 7808 | if (G_x_mode) |
@@ -7650,10 +7813,10 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
7650 | fprintf(stderr, " %s", p); | 7813 | fprintf(stderr, " %s", p); |
7651 | debug_printf_exec("set shell var:'%s'->'%s'\n", | 7814 | debug_printf_exec("set shell var:'%s'->'%s'\n", |
7652 | *argv, p); | 7815 | *argv, p); |
7653 | set_local_var(p, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); | 7816 | if (set_local_var(p, /*flag:*/ 0)) { |
7654 | /* Do we need to flag set_local_var() errors? | 7817 | /* assignment to readonly var / putenv error? */ |
7655 | * "assignment to readonly var" and "putenv error" | 7818 | rcode = 1; |
7656 | */ | 7819 | } |
7657 | argv++; | 7820 | argv++; |
7658 | } | 7821 | } |
7659 | if (G_x_mode) | 7822 | if (G_x_mode) |
@@ -7702,7 +7865,7 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
7702 | goto clean_up_and_ret1; | 7865 | goto clean_up_and_ret1; |
7703 | } | 7866 | } |
7704 | } | 7867 | } |
7705 | rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, squirrel, argv_expanded); | 7868 | rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, &squirrel, argv_expanded); |
7706 | if (rcode == 0) { | 7869 | if (rcode == 0) { |
7707 | if (!funcp) { | 7870 | if (!funcp) { |
7708 | debug_printf_exec(": builtin '%s' '%s'...\n", | 7871 | debug_printf_exec(": builtin '%s' '%s'...\n", |
@@ -7743,7 +7906,7 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
7743 | if (ENABLE_FEATURE_SH_NOFORK) { | 7906 | if (ENABLE_FEATURE_SH_NOFORK) { |
7744 | int n = find_applet_by_name(argv_expanded[0]); | 7907 | int n = find_applet_by_name(argv_expanded[0]); |
7745 | if (n >= 0 && APPLET_IS_NOFORK(n)) { | 7908 | if (n >= 0 && APPLET_IS_NOFORK(n)) { |
7746 | rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, squirrel, argv_expanded); | 7909 | rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, &squirrel, argv_expanded); |
7747 | if (rcode == 0) { | 7910 | if (rcode == 0) { |
7748 | debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", | 7911 | debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", |
7749 | argv_expanded[0], argv_expanded[1]); | 7912 | argv_expanded[0], argv_expanded[1]); |
@@ -7968,6 +8131,7 @@ static int run_list(struct pipe *pi) | |||
7968 | /* Go through list of pipes, (maybe) executing them. */ | 8131 | /* Go through list of pipes, (maybe) executing them. */ |
7969 | for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) { | 8132 | for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) { |
7970 | int r; | 8133 | int r; |
8134 | int sv_errexit_depth; | ||
7971 | 8135 | ||
7972 | if (G.flag_SIGINT) | 8136 | if (G.flag_SIGINT) |
7973 | break; | 8137 | break; |
@@ -7977,6 +8141,13 @@ static int run_list(struct pipe *pi) | |||
7977 | IF_HAS_KEYWORDS(rword = pi->res_word;) | 8141 | IF_HAS_KEYWORDS(rword = pi->res_word;) |
7978 | debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n", | 8142 | debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n", |
7979 | rword, cond_code, last_rword); | 8143 | rword, cond_code, last_rword); |
8144 | |||
8145 | sv_errexit_depth = G.errexit_depth; | ||
8146 | if (IF_HAS_KEYWORDS(rword == RES_IF || rword == RES_ELIF ||) | ||
8147 | pi->followup != PIPE_SEQ | ||
8148 | ) { | ||
8149 | G.errexit_depth++; | ||
8150 | } | ||
7980 | #if ENABLE_HUSH_LOOPS | 8151 | #if ENABLE_HUSH_LOOPS |
7981 | if ((rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR) | 8152 | if ((rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR) |
7982 | && loop_top == NULL /* avoid bumping G.depth_of_loop twice */ | 8153 | && loop_top == NULL /* avoid bumping G.depth_of_loop twice */ |
@@ -8054,7 +8225,7 @@ static int run_list(struct pipe *pi) | |||
8054 | } | 8225 | } |
8055 | /* Insert next value from for_lcur */ | 8226 | /* Insert next value from for_lcur */ |
8056 | /* note: *for_lcur already has quotes removed, $var expanded, etc */ | 8227 | /* note: *for_lcur already has quotes removed, $var expanded, etc */ |
8057 | set_local_var(xasprintf("%s=%s", pi->cmds[0].argv[0], *for_lcur++), /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); | 8228 | set_local_var(xasprintf("%s=%s", pi->cmds[0].argv[0], *for_lcur++), /*flag:*/ 0); |
8058 | continue; | 8229 | continue; |
8059 | } | 8230 | } |
8060 | if (rword == RES_IN) { | 8231 | if (rword == RES_IN) { |
@@ -8068,6 +8239,7 @@ static int run_list(struct pipe *pi) | |||
8068 | if (rword == RES_CASE) { | 8239 | if (rword == RES_CASE) { |
8069 | debug_printf_exec("CASE cond_code:%d\n", cond_code); | 8240 | debug_printf_exec("CASE cond_code:%d\n", cond_code); |
8070 | case_word = expand_strvec_to_string(pi->cmds->argv); | 8241 | case_word = expand_strvec_to_string(pi->cmds->argv); |
8242 | unbackslash(case_word); | ||
8071 | continue; | 8243 | continue; |
8072 | } | 8244 | } |
8073 | if (rword == RES_MATCH) { | 8245 | if (rword == RES_MATCH) { |
@@ -8079,9 +8251,10 @@ static int run_list(struct pipe *pi) | |||
8079 | /* all prev words didn't match, does this one match? */ | 8251 | /* all prev words didn't match, does this one match? */ |
8080 | argv = pi->cmds->argv; | 8252 | argv = pi->cmds->argv; |
8081 | while (*argv) { | 8253 | while (*argv) { |
8082 | char *pattern = expand_string_to_string(*argv, /*unbackslash:*/ 1); | 8254 | char *pattern = expand_string_to_string(*argv, /*unbackslash:*/ 0); |
8083 | /* TODO: which FNM_xxx flags to use? */ | 8255 | /* TODO: which FNM_xxx flags to use? */ |
8084 | cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0); | 8256 | cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0); |
8257 | debug_printf_exec("fnmatch(pattern:'%s',str:'%s'):%d\n", pattern, case_word, cond_code); | ||
8085 | free(pattern); | 8258 | free(pattern); |
8086 | if (cond_code == 0) { /* match! we will execute this branch */ | 8259 | if (cond_code == 0) { /* match! we will execute this branch */ |
8087 | free(case_word); | 8260 | free(case_word); |
@@ -8162,10 +8335,11 @@ static int run_list(struct pipe *pi) | |||
8162 | * I'm NOT treating inner &'s as jobs */ | 8335 | * I'm NOT treating inner &'s as jobs */ |
8163 | #if ENABLE_HUSH_JOB | 8336 | #if ENABLE_HUSH_JOB |
8164 | if (G.run_list_level == 1) | 8337 | if (G.run_list_level == 1) |
8165 | insert_bg_job(pi); | 8338 | insert_job_into_table(pi); |
8166 | #endif | 8339 | #endif |
8167 | /* Last command's pid goes to $! */ | 8340 | /* Last command's pid goes to $! */ |
8168 | G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid; | 8341 | G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid; |
8342 | G.last_bg_pid_exitcode = 0; | ||
8169 | debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n"); | 8343 | debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n"); |
8170 | /* Check pi->pi_inverted? "! sleep 1 & echo $?": bash says 1. dash and ash says 0 */ | 8344 | /* Check pi->pi_inverted? "! sleep 1 & echo $?": bash says 1. dash and ash says 0 */ |
8171 | rcode = EXIT_SUCCESS; | 8345 | rcode = EXIT_SUCCESS; |
@@ -8187,6 +8361,14 @@ static int run_list(struct pipe *pi) | |||
8187 | check_and_run_traps(); | 8361 | check_and_run_traps(); |
8188 | } | 8362 | } |
8189 | 8363 | ||
8364 | /* Handle "set -e" */ | ||
8365 | if (rcode != 0 && G.o_opt[OPT_O_ERREXIT]) { | ||
8366 | debug_printf_exec("ERREXIT:1 errexit_depth:%d\n", G.errexit_depth); | ||
8367 | if (G.errexit_depth == 0) | ||
8368 | hush_exit(rcode); | ||
8369 | } | ||
8370 | G.errexit_depth = sv_errexit_depth; | ||
8371 | |||
8190 | /* Analyze how result affects subsequent commands */ | 8372 | /* Analyze how result affects subsequent commands */ |
8191 | #if ENABLE_HUSH_IF | 8373 | #if ENABLE_HUSH_IF |
8192 | if (rword == RES_IF || rword == RES_ELIF) | 8374 | if (rword == RES_IF || rword == RES_ELIF) |
@@ -8366,6 +8548,9 @@ static int set_mode(int state, char mode, const char *o_opt) | |||
8366 | G.o_opt[idx] = state; | 8548 | G.o_opt[idx] = state; |
8367 | break; | 8549 | break; |
8368 | } | 8550 | } |
8551 | case 'e': | ||
8552 | G.o_opt[OPT_O_ERREXIT] = state; | ||
8553 | break; | ||
8369 | default: | 8554 | default: |
8370 | return EXIT_FAILURE; | 8555 | return EXIT_FAILURE; |
8371 | } | 8556 | } |
@@ -8425,7 +8610,7 @@ int hush_main(int argc, char **argv) | |||
8425 | putenv(shell_ver->varstr); | 8610 | putenv(shell_ver->varstr); |
8426 | 8611 | ||
8427 | /* Export PWD */ | 8612 | /* Export PWD */ |
8428 | set_pwd_var(/*exp:*/ 1); | 8613 | set_pwd_var(SETFLAG_EXPORT); |
8429 | 8614 | ||
8430 | #if BASH_HOSTNAME_VAR | 8615 | #if BASH_HOSTNAME_VAR |
8431 | /* Set (but not export) HOSTNAME unless already set */ | 8616 | /* Set (but not export) HOSTNAME unless already set */ |
@@ -8492,7 +8677,7 @@ int hush_main(int argc, char **argv) | |||
8492 | flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0; | 8677 | flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0; |
8493 | builtin_argc = 0; | 8678 | builtin_argc = 0; |
8494 | while (1) { | 8679 | while (1) { |
8495 | opt = getopt(argc, argv, "+c:xinsl" | 8680 | opt = getopt(argc, argv, "+c:exinsl" |
8496 | #if !BB_MMU | 8681 | #if !BB_MMU |
8497 | "<:$:R:V:" | 8682 | "<:$:R:V:" |
8498 | # if ENABLE_HUSH_FUNCTIONS | 8683 | # if ENABLE_HUSH_FUNCTIONS |
@@ -8575,8 +8760,9 @@ int hush_main(int argc, char **argv) | |||
8575 | optarg++; | 8760 | optarg++; |
8576 | empty_trap_mask = bb_strtoull(optarg, &optarg, 16); | 8761 | empty_trap_mask = bb_strtoull(optarg, &optarg, 16); |
8577 | if (empty_trap_mask != 0) { | 8762 | if (empty_trap_mask != 0) { |
8578 | int sig; | 8763 | IF_HUSH_TRAP(int sig;) |
8579 | install_special_sighandlers(); | 8764 | install_special_sighandlers(); |
8765 | # if ENABLE_HUSH_TRAP | ||
8580 | G_traps = xzalloc(sizeof(G_traps[0]) * NSIG); | 8766 | G_traps = xzalloc(sizeof(G_traps[0]) * NSIG); |
8581 | for (sig = 1; sig < NSIG; sig++) { | 8767 | for (sig = 1; sig < NSIG; sig++) { |
8582 | if (empty_trap_mask & (1LL << sig)) { | 8768 | if (empty_trap_mask & (1LL << sig)) { |
@@ -8584,6 +8770,7 @@ int hush_main(int argc, char **argv) | |||
8584 | install_sighandler(sig, SIG_IGN); | 8770 | install_sighandler(sig, SIG_IGN); |
8585 | } | 8771 | } |
8586 | } | 8772 | } |
8773 | # endif | ||
8587 | } | 8774 | } |
8588 | # if ENABLE_HUSH_LOOPS | 8775 | # if ENABLE_HUSH_LOOPS |
8589 | optarg++; | 8776 | optarg++; |
@@ -8593,7 +8780,7 @@ int hush_main(int argc, char **argv) | |||
8593 | } | 8780 | } |
8594 | case 'R': | 8781 | case 'R': |
8595 | case 'V': | 8782 | case 'V': |
8596 | set_local_var(xstrdup(optarg), /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ opt == 'R'); | 8783 | set_local_var(xstrdup(optarg), opt == 'R' ? SETFLAG_MAKE_RO : 0); |
8597 | break; | 8784 | break; |
8598 | # if ENABLE_HUSH_FUNCTIONS | 8785 | # if ENABLE_HUSH_FUNCTIONS |
8599 | case 'F': { | 8786 | case 'F': { |
@@ -8608,6 +8795,7 @@ int hush_main(int argc, char **argv) | |||
8608 | #endif | 8795 | #endif |
8609 | case 'n': | 8796 | case 'n': |
8610 | case 'x': | 8797 | case 'x': |
8798 | case 'e': | ||
8611 | if (set_mode(1, opt, NULL) == 0) /* no error */ | 8799 | if (set_mode(1, opt, NULL) == 0) /* no error */ |
8612 | break; | 8800 | break; |
8613 | default: | 8801 | default: |
@@ -8694,7 +8882,7 @@ int hush_main(int argc, char **argv) | |||
8694 | G_saved_tty_pgrp = 0; | 8882 | G_saved_tty_pgrp = 0; |
8695 | 8883 | ||
8696 | /* try to dup stdin to high fd#, >= 255 */ | 8884 | /* try to dup stdin to high fd#, >= 255 */ |
8697 | G_interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255); | 8885 | G_interactive_fd = fcntl_F_DUPFD(STDIN_FILENO, 254); |
8698 | if (G_interactive_fd < 0) { | 8886 | if (G_interactive_fd < 0) { |
8699 | /* try to dup to any fd */ | 8887 | /* try to dup to any fd */ |
8700 | G_interactive_fd = dup(STDIN_FILENO); | 8888 | G_interactive_fd = dup(STDIN_FILENO); |
@@ -8767,7 +8955,7 @@ int hush_main(int argc, char **argv) | |||
8767 | #elif ENABLE_HUSH_INTERACTIVE | 8955 | #elif ENABLE_HUSH_INTERACTIVE |
8768 | /* No job control compiled in, only prompt/line editing */ | 8956 | /* No job control compiled in, only prompt/line editing */ |
8769 | if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { | 8957 | if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { |
8770 | G_interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255); | 8958 | G_interactive_fd = fcntl_F_DUPFD(STDIN_FILENO, 254); |
8771 | if (G_interactive_fd < 0) { | 8959 | if (G_interactive_fd < 0) { |
8772 | /* try to dup to any fd */ | 8960 | /* try to dup to any fd */ |
8773 | G_interactive_fd = dup(STDIN_FILENO); | 8961 | G_interactive_fd = dup(STDIN_FILENO); |
@@ -8806,16 +8994,6 @@ int hush_main(int argc, char **argv) | |||
8806 | } | 8994 | } |
8807 | 8995 | ||
8808 | 8996 | ||
8809 | #if ENABLE_MSH | ||
8810 | int msh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
8811 | int msh_main(int argc, char **argv) | ||
8812 | { | ||
8813 | bb_error_msg("msh is deprecated, please use hush instead"); | ||
8814 | return hush_main(argc, argv); | ||
8815 | } | ||
8816 | #endif | ||
8817 | |||
8818 | |||
8819 | /* | 8997 | /* |
8820 | * Built-ins | 8998 | * Built-ins |
8821 | */ | 8999 | */ |
@@ -8827,12 +9005,8 @@ static int FAST_FUNC builtin_true(char **argv UNUSED_PARAM) | |||
8827 | #if ENABLE_HUSH_TEST || ENABLE_HUSH_ECHO || ENABLE_HUSH_PRINTF || ENABLE_HUSH_KILL | 9005 | #if ENABLE_HUSH_TEST || ENABLE_HUSH_ECHO || ENABLE_HUSH_PRINTF || ENABLE_HUSH_KILL |
8828 | static int run_applet_main(char **argv, int (*applet_main_func)(int argc, char **argv)) | 9006 | static int run_applet_main(char **argv, int (*applet_main_func)(int argc, char **argv)) |
8829 | { | 9007 | { |
8830 | int argc = 0; | 9008 | int argc = string_array_len(argv); |
8831 | while (*argv) { | 9009 | return applet_main_func(argc, argv); |
8832 | argc++; | ||
8833 | argv++; | ||
8834 | } | ||
8835 | return applet_main_func(argc, argv - argc); | ||
8836 | } | 9010 | } |
8837 | #endif | 9011 | #endif |
8838 | #if ENABLE_HUSH_TEST || BASH_TEST2 | 9012 | #if ENABLE_HUSH_TEST || BASH_TEST2 |
@@ -8909,7 +9083,7 @@ static int FAST_FUNC builtin_cd(char **argv) | |||
8909 | * Note: do not enforce exporting. If PWD was unset or unexported, | 9083 | * Note: do not enforce exporting. If PWD was unset or unexported, |
8910 | * set it again, but do not export. bash does the same. | 9084 | * set it again, but do not export. bash does the same. |
8911 | */ | 9085 | */ |
8912 | set_pwd_var(/*exp:*/ 0); | 9086 | set_pwd_var(/*flag:*/ 0); |
8913 | return EXIT_SUCCESS; | 9087 | return EXIT_SUCCESS; |
8914 | } | 9088 | } |
8915 | 9089 | ||
@@ -9147,12 +9321,8 @@ static void print_escaped(const char *s) | |||
9147 | } | 9321 | } |
9148 | #endif | 9322 | #endif |
9149 | 9323 | ||
9150 | #if ENABLE_HUSH_EXPORT || ENABLE_HUSH_LOCAL | 9324 | #if ENABLE_HUSH_EXPORT || ENABLE_HUSH_LOCAL || ENABLE_HUSH_READONLY |
9151 | # if !ENABLE_HUSH_LOCAL | 9325 | static int helper_export_local(char **argv, unsigned flags) |
9152 | #define helper_export_local(argv, exp, lvl) \ | ||
9153 | helper_export_local(argv, exp) | ||
9154 | # endif | ||
9155 | static void helper_export_local(char **argv, int exp, int lvl) | ||
9156 | { | 9326 | { |
9157 | do { | 9327 | do { |
9158 | char *name = *argv; | 9328 | char *name = *argv; |
@@ -9166,7 +9336,7 @@ static void helper_export_local(char **argv, int exp, int lvl) | |||
9166 | vpp = get_ptr_to_local_var(name, name_end - name); | 9336 | vpp = get_ptr_to_local_var(name, name_end - name); |
9167 | var = vpp ? *vpp : NULL; | 9337 | var = vpp ? *vpp : NULL; |
9168 | 9338 | ||
9169 | if (exp == -1) { /* unexporting? */ | 9339 | if (flags & SETFLAG_UNEXPORT) { |
9170 | /* export -n NAME (without =VALUE) */ | 9340 | /* export -n NAME (without =VALUE) */ |
9171 | if (var) { | 9341 | if (var) { |
9172 | var->flg_export = 0; | 9342 | var->flg_export = 0; |
@@ -9175,7 +9345,7 @@ static void helper_export_local(char **argv, int exp, int lvl) | |||
9175 | } /* else: export -n NOT_EXISTING_VAR: no-op */ | 9345 | } /* else: export -n NOT_EXISTING_VAR: no-op */ |
9176 | continue; | 9346 | continue; |
9177 | } | 9347 | } |
9178 | if (exp == 1) { /* exporting? */ | 9348 | if (flags & SETFLAG_EXPORT) { |
9179 | /* export NAME (without =VALUE) */ | 9349 | /* export NAME (without =VALUE) */ |
9180 | if (var) { | 9350 | if (var) { |
9181 | var->flg_export = 1; | 9351 | var->flg_export = 1; |
@@ -9184,28 +9354,45 @@ static void helper_export_local(char **argv, int exp, int lvl) | |||
9184 | continue; | 9354 | continue; |
9185 | } | 9355 | } |
9186 | } | 9356 | } |
9357 | if (flags & SETFLAG_MAKE_RO) { | ||
9358 | /* readonly NAME (without =VALUE) */ | ||
9359 | if (var) { | ||
9360 | var->flg_read_only = 1; | ||
9361 | continue; | ||
9362 | } | ||
9363 | } | ||
9187 | # if ENABLE_HUSH_LOCAL | 9364 | # if ENABLE_HUSH_LOCAL |
9188 | if (exp == 0 /* local? */ | 9365 | /* Is this "local" bltin? */ |
9189 | && var && var->func_nest_level == lvl | 9366 | if (!(flags & (SETFLAG_EXPORT|SETFLAG_UNEXPORT|SETFLAG_MAKE_RO))) { |
9190 | ) { | 9367 | unsigned lvl = flags >> SETFLAG_LOCAL_SHIFT; |
9191 | /* "local x=abc; ...; local x" - ignore second local decl */ | 9368 | if (var && var->func_nest_level == lvl) { |
9192 | continue; | 9369 | /* "local x=abc; ...; local x" - ignore second local decl */ |
9370 | continue; | ||
9371 | } | ||
9193 | } | 9372 | } |
9194 | # endif | 9373 | # endif |
9195 | /* Exporting non-existing variable. | 9374 | /* Exporting non-existing variable. |
9196 | * bash does not put it in environment, | 9375 | * bash does not put it in environment, |
9197 | * but remembers that it is exported, | 9376 | * but remembers that it is exported, |
9198 | * and does put it in env when it is set later. | 9377 | * and does put it in env when it is set later. |
9199 | * We just set it to "" and export. */ | 9378 | * We just set it to "" and export. |
9379 | */ | ||
9200 | /* Or, it's "local NAME" (without =VALUE). | 9380 | /* Or, it's "local NAME" (without =VALUE). |
9201 | * bash sets the value to "". */ | 9381 | * bash sets the value to "". |
9382 | */ | ||
9383 | /* Or, it's "readonly NAME" (without =VALUE). | ||
9384 | * bash remembers NAME and disallows its creation | ||
9385 | * in the future. | ||
9386 | */ | ||
9202 | name = xasprintf("%s=", name); | 9387 | name = xasprintf("%s=", name); |
9203 | } else { | 9388 | } else { |
9204 | /* (Un)exporting/making local NAME=VALUE */ | 9389 | /* (Un)exporting/making local NAME=VALUE */ |
9205 | name = xstrdup(name); | 9390 | name = xstrdup(name); |
9206 | } | 9391 | } |
9207 | set_local_var(name, /*exp:*/ exp, /*lvl:*/ lvl, /*ro:*/ 0); | 9392 | if (set_local_var(name, flags)) |
9393 | return EXIT_FAILURE; | ||
9208 | } while (*++argv); | 9394 | } while (*++argv); |
9395 | return EXIT_SUCCESS; | ||
9209 | } | 9396 | } |
9210 | #endif | 9397 | #endif |
9211 | 9398 | ||
@@ -9251,9 +9438,7 @@ static int FAST_FUNC builtin_export(char **argv) | |||
9251 | return EXIT_SUCCESS; | 9438 | return EXIT_SUCCESS; |
9252 | } | 9439 | } |
9253 | 9440 | ||
9254 | helper_export_local(argv, (opt_unexport ? -1 : 1), 0); | 9441 | return helper_export_local(argv, opt_unexport ? SETFLAG_UNEXPORT : SETFLAG_EXPORT); |
9255 | |||
9256 | return EXIT_SUCCESS; | ||
9257 | } | 9442 | } |
9258 | #endif | 9443 | #endif |
9259 | 9444 | ||
@@ -9264,8 +9449,29 @@ static int FAST_FUNC builtin_local(char **argv) | |||
9264 | bb_error_msg("%s: not in a function", argv[0]); | 9449 | bb_error_msg("%s: not in a function", argv[0]); |
9265 | return EXIT_FAILURE; /* bash compat */ | 9450 | return EXIT_FAILURE; /* bash compat */ |
9266 | } | 9451 | } |
9267 | helper_export_local(argv, 0, G.func_nest_level); | 9452 | argv++; |
9268 | return EXIT_SUCCESS; | 9453 | return helper_export_local(argv, G.func_nest_level << SETFLAG_LOCAL_SHIFT); |
9454 | } | ||
9455 | #endif | ||
9456 | |||
9457 | #if ENABLE_HUSH_READONLY | ||
9458 | static int FAST_FUNC builtin_readonly(char **argv) | ||
9459 | { | ||
9460 | argv++; | ||
9461 | if (*argv == NULL) { | ||
9462 | /* bash: readonly [-p]: list all readonly VARs | ||
9463 | * (-p has no effect in bash) | ||
9464 | */ | ||
9465 | struct variable *e; | ||
9466 | for (e = G.top_var; e; e = e->next) { | ||
9467 | if (e->flg_read_only) { | ||
9468 | //TODO: quote value: readonly VAR='VAL' | ||
9469 | printf("readonly %s\n", e->varstr); | ||
9470 | } | ||
9471 | } | ||
9472 | return EXIT_SUCCESS; | ||
9473 | } | ||
9474 | return helper_export_local(argv, SETFLAG_MAKE_RO); | ||
9269 | } | 9475 | } |
9270 | #endif | 9476 | #endif |
9271 | 9477 | ||
@@ -9379,10 +9585,7 @@ static int FAST_FUNC builtin_set(char **argv) | |||
9379 | /* This realloc's G.global_argv */ | 9585 | /* This realloc's G.global_argv */ |
9380 | G.global_argv = pp = add_strings_to_strings(g_argv, argv, /*dup:*/ 1); | 9586 | G.global_argv = pp = add_strings_to_strings(g_argv, argv, /*dup:*/ 1); |
9381 | 9587 | ||
9382 | n = 1; | 9588 | G.global_argc = 1 + string_array_len(pp + 1); |
9383 | while (*++pp) | ||
9384 | n++; | ||
9385 | G.global_argc = n; | ||
9386 | 9589 | ||
9387 | return EXIT_SUCCESS; | 9590 | return EXIT_SUCCESS; |
9388 | 9591 | ||
@@ -9398,7 +9601,18 @@ static int FAST_FUNC builtin_shift(char **argv) | |||
9398 | int n = 1; | 9601 | int n = 1; |
9399 | argv = skip_dash_dash(argv); | 9602 | argv = skip_dash_dash(argv); |
9400 | if (argv[0]) { | 9603 | if (argv[0]) { |
9401 | n = atoi(argv[0]); | 9604 | n = bb_strtou(argv[0], NULL, 10); |
9605 | if (errno || n < 0) { | ||
9606 | /* shared string with ash.c */ | ||
9607 | bb_error_msg("Illegal number: %s", argv[0]); | ||
9608 | /* | ||
9609 | * ash aborts in this case. | ||
9610 | * bash prints error message and set $? to 1. | ||
9611 | * Interestingly, for "shift 99999" bash does not | ||
9612 | * print error message, but does set $? to 1 | ||
9613 | * (and does no shifting at all). | ||
9614 | */ | ||
9615 | } | ||
9402 | } | 9616 | } |
9403 | if (n >= 0 && n < G.global_argc) { | 9617 | if (n >= 0 && n < G.global_argc) { |
9404 | if (G_global_args_malloced) { | 9618 | if (G_global_args_malloced) { |
@@ -9511,7 +9725,7 @@ static int FAST_FUNC builtin_trap(char **argv) | |||
9511 | if (sig < 0 || sig >= NSIG) { | 9725 | if (sig < 0 || sig >= NSIG) { |
9512 | ret = EXIT_FAILURE; | 9726 | ret = EXIT_FAILURE; |
9513 | /* Mimic bash message exactly */ | 9727 | /* Mimic bash message exactly */ |
9514 | bb_perror_msg("trap: %s: invalid signal specification", argv[-1]); | 9728 | bb_error_msg("trap: %s: invalid signal specification", argv[-1]); |
9515 | continue; | 9729 | continue; |
9516 | } | 9730 | } |
9517 | 9731 | ||
@@ -9604,6 +9818,9 @@ static int FAST_FUNC builtin_jobs(char **argv UNUSED_PARAM) | |||
9604 | 9818 | ||
9605 | printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext); | 9819 | printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext); |
9606 | } | 9820 | } |
9821 | |||
9822 | clean_up_last_dead_job(); | ||
9823 | |||
9607 | return EXIT_SUCCESS; | 9824 | return EXIT_SUCCESS; |
9608 | } | 9825 | } |
9609 | 9826 | ||
@@ -9648,14 +9865,14 @@ static int FAST_FUNC builtin_fg_bg(char **argv) | |||
9648 | i = kill(- pi->pgrp, SIGCONT); | 9865 | i = kill(- pi->pgrp, SIGCONT); |
9649 | if (i < 0) { | 9866 | if (i < 0) { |
9650 | if (errno == ESRCH) { | 9867 | if (errno == ESRCH) { |
9651 | delete_finished_bg_job(pi); | 9868 | delete_finished_job(pi); |
9652 | return EXIT_SUCCESS; | 9869 | return EXIT_SUCCESS; |
9653 | } | 9870 | } |
9654 | bb_perror_msg("kill (SIGCONT)"); | 9871 | bb_perror_msg("kill (SIGCONT)"); |
9655 | } | 9872 | } |
9656 | 9873 | ||
9657 | if (argv[0][0] == 'f') { | 9874 | if (argv[0][0] == 'f') { |
9658 | remove_bg_job(pi); | 9875 | remove_job_from_table(pi); /* FG job shouldn't be in job table */ |
9659 | return checkjobs_and_fg_shell(pi); | 9876 | return checkjobs_and_fg_shell(pi); |
9660 | } | 9877 | } |
9661 | return EXIT_SUCCESS; | 9878 | return EXIT_SUCCESS; |
@@ -9847,8 +10064,12 @@ static int FAST_FUNC builtin_wait(char **argv) | |||
9847 | wait_pipe = parse_jobspec(*argv); | 10064 | wait_pipe = parse_jobspec(*argv); |
9848 | if (wait_pipe) { | 10065 | if (wait_pipe) { |
9849 | ret = job_exited_or_stopped(wait_pipe); | 10066 | ret = job_exited_or_stopped(wait_pipe); |
9850 | if (ret < 0) | 10067 | if (ret < 0) { |
9851 | ret = wait_for_child_or_signal(wait_pipe, 0); | 10068 | ret = wait_for_child_or_signal(wait_pipe, 0); |
10069 | } else { | ||
10070 | /* waiting on "last dead job" removes it */ | ||
10071 | clean_up_last_dead_job(); | ||
10072 | } | ||
9852 | } | 10073 | } |
9853 | /* else: parse_jobspec() already emitted error msg */ | 10074 | /* else: parse_jobspec() already emitted error msg */ |
9854 | continue; | 10075 | continue; |
@@ -9864,14 +10085,15 @@ static int FAST_FUNC builtin_wait(char **argv) | |||
9864 | ret = waitpid(pid, &status, WNOHANG); | 10085 | ret = waitpid(pid, &status, WNOHANG); |
9865 | if (ret < 0) { | 10086 | if (ret < 0) { |
9866 | /* No */ | 10087 | /* No */ |
10088 | ret = 127; | ||
9867 | if (errno == ECHILD) { | 10089 | if (errno == ECHILD) { |
9868 | if (G.last_bg_pid > 0 && pid == G.last_bg_pid) { | 10090 | if (pid == G.last_bg_pid) { |
9869 | /* "wait $!" but last bg task has already exited. Try: | 10091 | /* "wait $!" but last bg task has already exited. Try: |
9870 | * (sleep 1; exit 3) & sleep 2; echo $?; wait $!; echo $? | 10092 | * (sleep 1; exit 3) & sleep 2; echo $?; wait $!; echo $? |
9871 | * In bash it prints exitcode 0, then 3. | 10093 | * In bash it prints exitcode 0, then 3. |
9872 | * In dash, it is 127. | 10094 | * In dash, it is 127. |
9873 | */ | 10095 | */ |
9874 | /* ret = G.last_bg_pid_exitstatus - FIXME */ | 10096 | ret = G.last_bg_pid_exitcode; |
9875 | } else { | 10097 | } else { |
9876 | /* Example: "wait 1". mimic bash message */ | 10098 | /* Example: "wait 1". mimic bash message */ |
9877 | bb_error_msg("wait: pid %d is not a child of this shell", (int)pid); | 10099 | bb_error_msg("wait: pid %d is not a child of this shell", (int)pid); |
@@ -9880,7 +10102,6 @@ static int FAST_FUNC builtin_wait(char **argv) | |||
9880 | /* ??? */ | 10102 | /* ??? */ |
9881 | bb_perror_msg("wait %s", *argv); | 10103 | bb_perror_msg("wait %s", *argv); |
9882 | } | 10104 | } |
9883 | ret = 127; | ||
9884 | continue; /* bash checks all argv[] */ | 10105 | continue; /* bash checks all argv[] */ |
9885 | } | 10106 | } |
9886 | if (ret == 0) { | 10107 | if (ret == 0) { |