aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--shell/Config.src7
-rw-r--r--shell/ash.c48
-rw-r--r--shell/ash_test/ash-read/read_ifs2.right9
-rwxr-xr-xshell/ash_test/ash-read/read_ifs2.tests9
-rw-r--r--shell/ash_test/ash-read/read_t.right8
-rwxr-xr-xshell/ash_test/ash-read/read_t.tests18
-rw-r--r--shell/ash_test/printenv.c4
-rw-r--r--shell/ash_test/recho.c2
-rw-r--r--shell/hush.c486
-rwxr-xr-xshell/hush_leaktool.sh18
-rw-r--r--shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.right1
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.tests2
-rw-r--r--shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.right1
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.tests2
-rwxr-xr-xshell/hush_test/hush-misc/sig_exitcode.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait1.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait2.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait3.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait4.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait5.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait6.tests3
-rw-r--r--shell/hush_test/hush-read/read_t.right8
-rwxr-xr-xshell/hush_test/hush-read/read_t.tests18
-rwxr-xr-xshell/hush_test/hush-signals/catch.tests3
-rwxr-xr-xshell/hush_test/hush-signals/signal1.tests3
-rwxr-xr-xshell/hush_test/hush-signals/signal8.tests3
-rwxr-xr-xshell/hush_test/hush-signals/signal_read2.tests3
-rwxr-xr-xshell/hush_test/hush-signals/subshell.tests3
-rwxr-xr-xshell/hush_test/run-all7
-rw-r--r--shell/shell_common.c22
30 files changed, 427 insertions, 279 deletions
diff --git a/shell/Config.src b/shell/Config.src
index 5efbf9995..5b3fe08f3 100644
--- a/shell/Config.src
+++ b/shell/Config.src
@@ -166,9 +166,10 @@ config FEATURE_SH_HISTFILESIZE
166 default y 166 default y
167 depends on SHELL_ASH || SHELL_HUSH 167 depends on SHELL_ASH || SHELL_HUSH
168 help 168 help
169 This option makes busybox shells to use $HISTFILESIZE variable 169 This option makes busybox shells to use $HISTSIZE and
170 to set shell history size. Note that its max value is capped 170 $HISTFILESIZE variables to set shell history size.
171 by "History size" setting in library tuning section. 171 Note that its max value is capped by "History size" setting
172 in library tuning section.
172 173
173config FEATURE_SH_EMBEDDED_SCRIPTS 174config FEATURE_SH_EMBEDDED_SCRIPTS
174 bool "Embed scripts in the binary" 175 bool "Embed scripts in the binary"
diff --git a/shell/ash.c b/shell/ash.c
index 99fbf6053..605215e41 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -461,7 +461,7 @@ static void forkshell_print(FILE *fp0, struct forkshell *fs, const char **notes)
461 461
462/* ============ Shell options */ 462/* ============ Shell options */
463 463
464/* If you add/change options hare, update --help text too */ 464/* If you add/change options here, update --help text too */
465static const char *const optletters_optnames[] ALIGN_PTR = { 465static const char *const optletters_optnames[] ALIGN_PTR = {
466 "e" "errexit", 466 "e" "errexit",
467 "f" "noglob", 467 "f" "noglob",
@@ -1822,7 +1822,6 @@ struct stackmark {
1822 size_t stacknleft; 1822 size_t stacknleft;
1823}; 1823};
1824 1824
1825
1826struct globals_memstack { 1825struct globals_memstack {
1827 struct stack_block *g_stackp; // = &stackbase; 1826 struct stack_block *g_stackp; // = &stackbase;
1828 char *g_stacknxt; // = stackbase.space; 1827 char *g_stacknxt; // = stackbase.space;
@@ -1845,7 +1844,6 @@ extern struct globals_memstack *BB_GLOBAL_CONST ash_ptr_to_globals_memstack;
1845 sstrend = stackbase.space + MINSIZE; \ 1844 sstrend = stackbase.space + MINSIZE; \
1846} while (0) 1845} while (0)
1847 1846
1848
1849#define stackblock() ((void *)g_stacknxt) 1847#define stackblock() ((void *)g_stacknxt)
1850#define stackblocksize() g_stacknleft 1848#define stackblocksize() g_stacknleft
1851 1849
@@ -2362,7 +2360,6 @@ struct localvar {
2362# define VIMPORT 0x400 /* variable was imported from environment */ 2360# define VIMPORT 0x400 /* variable was imported from environment */
2363#endif 2361#endif
2364 2362
2365
2366/* Need to be before varinit_data[] */ 2363/* Need to be before varinit_data[] */
2367#if ENABLE_LOCALE_SUPPORT 2364#if ENABLE_LOCALE_SUPPORT
2368static void FAST_FUNC 2365static void FAST_FUNC
@@ -3582,7 +3579,6 @@ pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3582 3579
3583/* ============ ... */ 3580/* ============ ... */
3584 3581
3585
3586#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024) 3582#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
3587 3583
3588/* Syntax classes */ 3584/* Syntax classes */
@@ -3989,13 +3985,11 @@ struct alias {
3989 int flag; 3985 int flag;
3990}; 3986};
3991 3987
3992
3993static struct alias **atab; // [ATABSIZE]; 3988static struct alias **atab; // [ATABSIZE];
3994#define INIT_G_alias() do { \ 3989#define INIT_G_alias() do { \
3995 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \ 3990 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3996} while (0) 3991} while (0)
3997 3992
3998
3999static struct alias ** 3993static struct alias **
4000__lookupalias(const char *name) 3994__lookupalias(const char *name)
4001{ 3995{
@@ -4177,7 +4171,6 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
4177 4171
4178#endif /* ASH_ALIAS */ 4172#endif /* ASH_ALIAS */
4179 4173
4180
4181/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ 4174/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
4182#define FORK_FG 0 4175#define FORK_FG 0
4183#define FORK_BG 1 4176#define FORK_BG 1
@@ -4275,7 +4268,7 @@ signal_handler(int signo)
4275 return; 4268 return;
4276 } 4269 }
4277#if ENABLE_FEATURE_EDITING 4270#if ENABLE_FEATURE_EDITING
4278 bb_got_signal = signo; /* for read_line_input: "we got a signal" */ 4271 bb_got_signal = signo; /* for read_line_input / read builtin: "we got a signal" */
4279#endif 4272#endif
4280 gotsig[signo - 1] = 1; 4273 gotsig[signo - 1] = 1;
4281 pending_sig = signo; 4274 pending_sig = signo;
@@ -6194,7 +6187,6 @@ stoppedjobs(void)
6194} 6187}
6195#endif 6188#endif
6196 6189
6197
6198/* 6190/*
6199 * Code for dealing with input/output redirection. 6191 * Code for dealing with input/output redirection.
6200 */ 6192 */
@@ -9904,7 +9896,6 @@ commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
9904} 9896}
9905#endif 9897#endif
9906 9898
9907
9908/*static int funcblocksize; // size of structures in function */ 9899/*static int funcblocksize; // size of structures in function */
9909/*static int funcstringsize; // size of strings in node */ 9900/*static int funcstringsize; // size of strings in node */
9910static void *funcblock; /* block to allocate function from */ 9901static void *funcblock; /* block to allocate function from */
@@ -11920,7 +11911,6 @@ goodname(const char *p)
11920 return endofname(p)[0] == '\0'; 11911 return endofname(p)[0] == '\0';
11921} 11912}
11922 11913
11923
11924/* 11914/*
11925 * Search for a command. This is called before we fork so that the 11915 * Search for a command. This is called before we fork so that the
11926 * location of the command will be available in the parent as well as 11916 * location of the command will be available in the parent as well as
@@ -14773,7 +14763,6 @@ parseheredoc(void)
14773 } 14763 }
14774} 14764}
14775 14765
14776
14777static const char * 14766static const char *
14778expandstr(const char *ps, int syntax_type) 14767expandstr(const char *ps, int syntax_type)
14779{ 14768{
@@ -15403,7 +15392,6 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
15403 entry->u = cmdp->param; 15392 entry->u = cmdp->param;
15404} 15393}
15405 15394
15406
15407/* 15395/*
15408 * The trap builtin. 15396 * The trap builtin.
15409 */ 15397 */
@@ -15821,6 +15809,11 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
15821 if (pending_sig == 0) 15809 if (pending_sig == 0)
15822 goto again; 15810 goto again;
15823 } 15811 }
15812
15813 if ((uintptr_t)r == 2) /* -t SEC timeout? */
15814 /* bash: "The exit status is greater than 128 if the timeout is exceeded." */
15815 /* The actual value observed with bash 5.2.15: */
15816 return 128 + SIGALRM;
15824#else /* ENABLE_PLATFORM_MINGW32 */ 15817#else /* ENABLE_PLATFORM_MINGW32 */
15825 if ((uintptr_t)r == 2) { 15818 if ((uintptr_t)r == 2) {
15826 /* Timeout, return 128 + SIGALRM */ 15819 /* Timeout, return 128 + SIGALRM */
@@ -15973,8 +15966,25 @@ exitshell(void)
15973 char *p; 15966 char *p;
15974 15967
15975#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 15968#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
15976 save_history(line_input_state); /* may be NULL */ 15969 if (line_input_state) {
15970 const char *hp;
15971# if ENABLE_FEATURE_SH_HISTFILESIZE
15972// in bash:
15973// HISTFILESIZE controls the on-disk history file size (in lines, 0=no history):
15974// "When this variable is assigned a value, the history file is truncated, if necessary"
15975// but we do it only at exit, not on assignment:
15976 /* Use HISTFILESIZE to limit file size */
15977 hp = lookupvar("HISTFILESIZE");
15978 if (hp)
15979 line_input_state->max_history = size_from_HISTFILESIZE(hp);
15980# endif
15981 /* HISTFILE: "If unset, the command history is not saved when a shell exits." */
15982 hp = lookupvar("HISTFILE");
15983 line_input_state->hist_file = hp;
15984 save_history(line_input_state); /* no-op if hist_file is NULL or "" */
15985 }
15977#endif 15986#endif
15987
15978 savestatus = exitstatus; 15988 savestatus = exitstatus;
15979 TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus)); 15989 TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus));
15980 if (setjmp(loc.loc)) 15990 if (setjmp(loc.loc))
@@ -16148,7 +16158,6 @@ init(void)
16148 } 16158 }
16149} 16159}
16150 16160
16151
16152//usage:#define ash_trivial_usage 16161//usage:#define ash_trivial_usage
16153//usage: "[-il] [-|+Cabefmnuvx] [-|+o OPT]... [-c 'SCRIPT' [ARG0 ARGS] | FILE ARGS | -s ARGS]" 16162//usage: "[-il] [-|+Cabefmnuvx] [-|+o OPT]... [-c 'SCRIPT' [ARG0 ARGS] | FILE ARGS | -s ARGS]"
16154//////// comes from ^^^^^^^^^^optletters 16163//////// comes from ^^^^^^^^^^optletters
@@ -16478,7 +16487,12 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
16478 if (hp) 16487 if (hp)
16479 line_input_state->hist_file = xstrdup(hp); 16488 line_input_state->hist_file = xstrdup(hp);
16480# if ENABLE_FEATURE_SH_HISTFILESIZE 16489# if ENABLE_FEATURE_SH_HISTFILESIZE
16481 hp = lookupvar("HISTFILESIZE"); 16490 hp = lookupvar("HISTSIZE");
16491 /* Using HISTFILESIZE above to limit max_history would be WRONG:
16492 * users may set HISTFILESIZE=0 in their profile scripts
16493 * to prevent _saving_ of history files, but still want to have
16494 * non-zero history limit for in-memory list.
16495 */
16482 line_input_state->max_history = size_from_HISTFILESIZE(hp); 16496 line_input_state->max_history = size_from_HISTFILESIZE(hp);
16483# endif 16497# endif
16484 } 16498 }
diff --git a/shell/ash_test/ash-read/read_ifs2.right b/shell/ash_test/ash-read/read_ifs2.right
new file mode 100644
index 000000000..797137dae
--- /dev/null
+++ b/shell/ash_test/ash-read/read_ifs2.right
@@ -0,0 +1,9 @@
1|X|Y:Z:|
2|X|Y:Z|
3|X|Y|
4|X|Y|
5|X||
6|X||
7|||
8Whitespace should be trimmed too:
9|X|Y|
diff --git a/shell/ash_test/ash-read/read_ifs2.tests b/shell/ash_test/ash-read/read_ifs2.tests
new file mode 100755
index 000000000..f01a68978
--- /dev/null
+++ b/shell/ash_test/ash-read/read_ifs2.tests
@@ -0,0 +1,9 @@
1echo "X:Y:Z:" | (IFS=": " read x y; echo "|$x|$y|")
2echo "X:Y:Z" | (IFS=": " read x y; echo "|$x|$y|")
3echo "X:Y:" | (IFS=": " read x y; echo "|$x|$y|")
4echo "X:Y" | (IFS=": " read x y; echo "|$x|$y|")
5echo "X:" | (IFS=": " read x y; echo "|$x|$y|")
6echo "X" | (IFS=": " read x y; echo "|$x|$y|")
7echo "" | (IFS=": " read x y; echo "|$x|$y|")
8echo Whitespace should be trimmed too:
9echo "X:Y : " | (IFS=": " read x y; echo "|$x|$y|")
diff --git a/shell/ash_test/ash-read/read_t.right b/shell/ash_test/ash-read/read_t.right
index 04126cbe6..3eedae275 100644
--- a/shell/ash_test/ash-read/read_t.right
+++ b/shell/ash_test/ash-read/read_t.right
@@ -1,4 +1,4 @@
1>< 1>te:142<
2>< 2>:142<
3>test< 3>test:0<
4>test< 4>test:0<
diff --git a/shell/ash_test/ash-read/read_t.tests b/shell/ash_test/ash-read/read_t.tests
index d65f1aeaa..9fbeec517 100755
--- a/shell/ash_test/ash-read/read_t.tests
+++ b/shell/ash_test/ash-read/read_t.tests
@@ -1,10 +1,10 @@
1# bash 3.2 outputs: 1# bash 5.2 outputs:
2 2
3# >< 3# >te:142<
4{ echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply<") 4{ echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply:$?<")
5# >< 5# >:142<
6{ sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply<") 6{ sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply:$?<")
7# >test< 7# >test:0<
8{ echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply<") 8{ echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply:$?<")
9# >test< 9# >test:0<
10{ sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply<") 10{ sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply:$?<")
diff --git a/shell/ash_test/printenv.c b/shell/ash_test/printenv.c
index c86308d3b..f0f41984d 100644
--- a/shell/ash_test/printenv.c
+++ b/shell/ash_test/printenv.c
@@ -31,9 +31,7 @@
31extern char **environ; 31extern char **environ;
32 32
33int 33int
34main (argc, argv) 34main (int argc, char **argv)
35 int argc;
36 char **argv;
37{ 35{
38 register char **envp, *eval; 36 register char **envp, *eval;
39 int len; 37 int len;
diff --git a/shell/ash_test/recho.c b/shell/ash_test/recho.c
index 42a5feafd..7e96b14cc 100644
--- a/shell/ash_test/recho.c
+++ b/shell/ash_test/recho.c
@@ -27,7 +27,7 @@
27#include <stdio.h> 27#include <stdio.h>
28#include <stdlib.h> 28#include <stdlib.h>
29 29
30void strprint(); 30void strprint(char *);
31 31
32int main(int argc, char **argv) 32int main(int argc, char **argv)
33{ 33{
diff --git a/shell/hush.c b/shell/hush.c
index 4a97293cc..09ab6ebc0 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -392,6 +392,8 @@
392 392
393/* Build knobs */ 393/* Build knobs */
394#define LEAK_HUNTING 0 394#define LEAK_HUNTING 0
395#define LEAK_PRINTF(...) fdprintf(__VA_ARGS__)
396//#define LEAK_PRINTF(...) do { if (ptr_to_globals && G.root_pid == getpid()) fdprintf(__VA_ARGS__); } while (0)
395#define BUILD_AS_NOMMU 0 397#define BUILD_AS_NOMMU 0
396/* Enable/disable sanity checks. Ok to enable in production, 398/* Enable/disable sanity checks. Ok to enable in production,
397 * only adds a bit of bloat. Set to >1 to get non-production level verbosity. 399 * only adds a bit of bloat. Set to >1 to get non-production level verbosity.
@@ -930,6 +932,12 @@ struct globals {
930# define G_flag_return_in_progress 0 932# define G_flag_return_in_progress 0
931#endif 933#endif
932 smallint exiting; /* used to prevent EXIT trap recursion */ 934 smallint exiting; /* used to prevent EXIT trap recursion */
935#if !BB_MMU
936 smallint reexeced_on_NOMMU;
937# define G_reexeced_on_NOMMU (G.reexeced_on_NOMMU)
938#else
939# define G_reexeced_on_NOMMU 0
940#endif
933 /* These support $? */ 941 /* These support $? */
934 smalluint last_exitcode; 942 smalluint last_exitcode;
935 smalluint expand_exitcode; 943 smalluint expand_exitcode;
@@ -1352,30 +1360,67 @@ static void debug_print_strings(const char *prefix, char **vv)
1352static void *xxmalloc(int lineno, size_t size) 1360static void *xxmalloc(int lineno, size_t size)
1353{ 1361{
1354 void *ptr = xmalloc((size + 0xff) & ~0xff); 1362 void *ptr = xmalloc((size + 0xff) & ~0xff);
1355 fdprintf(2, "line %d: malloc %p\n", lineno, ptr); 1363 LEAK_PRINTF(2, "line %d: malloc %p\n", lineno, ptr);
1364 return ptr;
1365}
1366static void *xxzalloc(int lineno, size_t size)
1367{
1368 void *ptr = xzalloc((size + 0xff) & ~0xff);
1369 LEAK_PRINTF(2, "line %d: zalloc %p\n", lineno, ptr);
1356 return ptr; 1370 return ptr;
1357} 1371}
1358static void *xxrealloc(int lineno, void *ptr, size_t size) 1372static void *xxrealloc(int lineno, void *ptr, size_t size)
1359{ 1373{
1374 char *p = ptr;
1360 ptr = xrealloc(ptr, (size + 0xff) & ~0xff); 1375 ptr = xrealloc(ptr, (size + 0xff) & ~0xff);
1361 fdprintf(2, "line %d: realloc %p\n", lineno, ptr); 1376 if (p != ptr)
1377 LEAK_PRINTF(2, "line %d: realloc %p\n", lineno, ptr);
1378 return ptr;
1379}
1380static void *xxrealloc_getcwd_or_warn(int lineno, char *ptr)
1381{
1382 char *p = ptr;
1383 ptr = xrealloc_getcwd_or_warn(ptr);
1384 if (p != ptr)
1385 LEAK_PRINTF(2, "line %d: xrealloc_getcwd_or_warn %p\n", lineno, ptr);
1362 return ptr; 1386 return ptr;
1363} 1387}
1364static char *xxstrdup(int lineno, const char *str) 1388static char *xxstrdup(int lineno, const char *str)
1365{ 1389{
1366 char *ptr = xstrdup(str); 1390 char *ptr = xstrdup(str);
1367 fdprintf(2, "line %d: strdup %p\n", lineno, ptr); 1391 LEAK_PRINTF(2, "line %d: strdup %p\n", lineno, ptr);
1392 return ptr;
1393}
1394static char *xxstrndup(int lineno, const char *str, size_t n)
1395{
1396 char *ptr = xstrndup(str, n);
1397 LEAK_PRINTF(2, "line %d: strndup %p\n", lineno, ptr);
1398 return ptr;
1399}
1400static char *xxasprintf(int lineno, const char *f, ...)
1401{
1402 char *ptr;
1403 va_list args;
1404 va_start(args, f);
1405 if (vasprintf(&ptr, f, args) < 0)
1406 bb_die_memory_exhausted();
1407 va_end(args);
1408 LEAK_PRINTF(2, "line %d: xasprintf %p\n", lineno, ptr);
1368 return ptr; 1409 return ptr;
1369} 1410}
1370static void xxfree(void *ptr) 1411static void xxfree(void *ptr)
1371{ 1412{
1372 fdprintf(2, "free %p\n", ptr); 1413 LEAK_PRINTF(2, "free %p\n", ptr);
1373 free(ptr); 1414 free(ptr);
1374} 1415}
1375# define xmalloc(s) xxmalloc(__LINE__, s) 1416# define xmalloc(s) xxmalloc(__LINE__, s)
1376# define xrealloc(p, s) xxrealloc(__LINE__, p, s) 1417# define xzalloc(s) xxzalloc(__LINE__, s)
1377# define xstrdup(s) xxstrdup(__LINE__, s) 1418# define xrealloc(p, s) xxrealloc(__LINE__, p, s)
1378# define free(p) xxfree(p) 1419# define xrealloc_getcwd_or_warn(p) xxrealloc_getcwd_or_warn(__LINE__, p)
1420# define xstrdup(s) xxstrdup(__LINE__, s)
1421# define xstrndup(s, n) xxstrndup(__LINE__, s, n)
1422# define xasprintf(f, ...) xxasprintf(__LINE__, f, __VA_ARGS__)
1423# define free(p) xxfree(p)
1379#endif 1424#endif
1380 1425
1381/* 1426/*
@@ -1929,7 +1974,7 @@ static void restore_G_args(save_arg_t *sv, char **argv)
1929 * "trap - SIGxxx": 1974 * "trap - SIGxxx":
1930 * if sig is in special_sig_mask, set handler back to: 1975 * if sig is in special_sig_mask, set handler back to:
1931 * record_pending_signo, or to IGN if it's a tty stop signal 1976 * record_pending_signo, or to IGN if it's a tty stop signal
1932 * if sig is in fatal_sig_mask, set handler back to sigexit. 1977 * if sig is in fatal_sig_mask, set handler back to restore_ttypgrp_and_killsig_or__exit.
1933 * else: set handler back to SIG_DFL 1978 * else: set handler back to SIG_DFL
1934 * "trap 'cmd' SIGxxx": 1979 * "trap 'cmd' SIGxxx":
1935 * set handler to record_pending_signo. 1980 * set handler to record_pending_signo.
@@ -1973,7 +2018,7 @@ static void record_pending_signo(int sig)
1973 || (G_traps && G_traps[SIGCHLD] && G_traps[SIGCHLD][0]) 2018 || (G_traps && G_traps[SIGCHLD] && G_traps[SIGCHLD][0])
1974 /* ^^^ if SIGCHLD, interrupt line reading only if it has a trap */ 2019 /* ^^^ if SIGCHLD, interrupt line reading only if it has a trap */
1975 ) { 2020 ) {
1976 bb_got_signal = sig; /* for read_line_input: "we got a signal" */ 2021 bb_got_signal = sig; /* for read_line_input / read builtin: "we got a signal" */
1977 } 2022 }
1978#endif 2023#endif
1979#if ENABLE_HUSH_FAST 2024#if ENABLE_HUSH_FAST
@@ -2002,19 +2047,6 @@ static sighandler_t install_sighandler(int sig, sighandler_t handler)
2002 return old_sa.sa_handler; 2047 return old_sa.sa_handler;
2003} 2048}
2004 2049
2005static void hush_exit(int exitcode) NORETURN;
2006
2007static void restore_ttypgrp_and__exit(void) NORETURN;
2008static void restore_ttypgrp_and__exit(void)
2009{
2010 /* xfunc has failed! die die die */
2011 /* no EXIT traps, this is an escape hatch! */
2012 G.exiting = 1;
2013 hush_exit(xfunc_error_retval);
2014}
2015
2016#if ENABLE_HUSH_JOB
2017
2018/* Needed only on some libc: 2050/* Needed only on some libc:
2019 * It was observed that on exit(), fgetc'ed buffered data 2051 * It was observed that on exit(), fgetc'ed buffered data
2020 * gets "unwound" via lseek(fd, -NUM, SEEK_CUR). 2052 * gets "unwound" via lseek(fd, -NUM, SEEK_CUR).
@@ -2028,26 +2060,20 @@ static void restore_ttypgrp_and__exit(void)
2028 * and in `cmd` handling. 2060 * and in `cmd` handling.
2029 * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit(): 2061 * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit():
2030 */ 2062 */
2031static void fflush_and__exit(void) NORETURN; 2063static NORETURN void fflush_and__exit(void)
2032static void fflush_and__exit(void)
2033{ 2064{
2034 fflush_all(); 2065 fflush_all();
2035 _exit(xfunc_error_retval); 2066 _exit(xfunc_error_retval);
2036} 2067}
2037 2068
2038/* After [v]fork, in child: do not restore tty pgrp on xfunc death */ 2069#if ENABLE_HUSH_JOB
2039# define disable_restore_tty_pgrp_on_exit() (die_func = fflush_and__exit)
2040/* After [v]fork, in parent: restore tty pgrp on xfunc death */
2041# define enable_restore_tty_pgrp_on_exit() (die_func = restore_ttypgrp_and__exit)
2042
2043/* Restores tty foreground process group, and exits. 2070/* Restores tty foreground process group, and exits.
2044 * May be called as signal handler for fatal signal 2071 * May be called as signal handler for fatal signal
2045 * (will resend signal to itself, producing correct exit state) 2072 * (will resend signal to itself, producing correct exit state)
2046 * or called directly with -EXITCODE. 2073 * or called directly with -EXITCODE.
2047 * We also call it if xfunc is exiting. 2074 * We also call it if xfunc is exiting.
2048 */ 2075 */
2049static void sigexit(int sig) NORETURN; 2076static NORETURN void restore_ttypgrp_and_killsig_or__exit(int sig)
2050static void sigexit(int sig)
2051{ 2077{
2052 /* Careful: we can end up here after [v]fork. Do not restore 2078 /* Careful: we can end up here after [v]fork. Do not restore
2053 * tty pgrp then, only top-level shell process does that */ 2079 * tty pgrp then, only top-level shell process does that */
@@ -2065,6 +2091,19 @@ static void sigexit(int sig)
2065 2091
2066 kill_myself_with_sig(sig); /* does not return */ 2092 kill_myself_with_sig(sig); /* does not return */
2067} 2093}
2094
2095static NORETURN void fflush_restore_ttypgrp_and__exit(void)
2096{
2097 /* xfunc has failed! die die die */
2098 fflush_all();
2099 restore_ttypgrp_and_killsig_or__exit(- xfunc_error_retval);
2100}
2101
2102/* After [v]fork, in child: do not restore tty pgrp on xfunc death */
2103# define disable_restore_tty_pgrp_on_exit() (die_func = fflush_and__exit)
2104/* After [v]fork, in parent: restore tty pgrp on xfunc death */
2105# define enable_restore_tty_pgrp_on_exit() (die_func = fflush_restore_ttypgrp_and__exit)
2106
2068#else 2107#else
2069 2108
2070# define disable_restore_tty_pgrp_on_exit() ((void)0) 2109# define disable_restore_tty_pgrp_on_exit() ((void)0)
@@ -2081,7 +2120,7 @@ static sighandler_t pick_sighandler(unsigned sig)
2081#if ENABLE_HUSH_JOB 2120#if ENABLE_HUSH_JOB
2082 /* is sig fatal? */ 2121 /* is sig fatal? */
2083 if (G_fatal_sig_mask & sigmask) 2122 if (G_fatal_sig_mask & sigmask)
2084 handler = sigexit; 2123 handler = restore_ttypgrp_and_killsig_or__exit;
2085 else 2124 else
2086#endif 2125#endif
2087 /* sig has special handling? */ 2126 /* sig has special handling? */
@@ -2099,11 +2138,33 @@ static sighandler_t pick_sighandler(unsigned sig)
2099 return handler; 2138 return handler;
2100} 2139}
2101 2140
2102/* Restores tty foreground process group, and exits. */ 2141static const char* FAST_FUNC get_local_var_value(const char *name);
2103static void hush_exit(int exitcode) 2142
2143/* Self-explanatory.
2144 * Restores tty foreground process group too.
2145 */
2146static NORETURN void save_history_run_exit_trap_and_exit(int exitcode)
2104{ 2147{
2105#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 2148#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
2106 save_history(G.line_input_state); /* may be NULL */ 2149 if (G.line_input_state
2150 && getpid() == G.root_pid /* exits in subshells do not save history */
2151 ) {
2152 const char *hp;
2153# if ENABLE_FEATURE_SH_HISTFILESIZE
2154// in bash:
2155// HISTFILESIZE controls the on-disk history file size (in lines, 0=no history):
2156// "When this variable is assigned a value, the history file is truncated, if necessary"
2157// but we do it only at exit, not on every assignment:
2158 /* Use HISTFILESIZE to limit file size */
2159 hp = get_local_var_value("HISTFILESIZE");
2160 if (hp)
2161 G.line_input_state->max_history = size_from_HISTFILESIZE(hp);
2162# endif
2163 /* HISTFILE: "If unset, the command history is not saved when a shell exits." */
2164 hp = get_local_var_value("HISTFILE");
2165 G.line_input_state->hist_file = hp;
2166 save_history(G.line_input_state); /* no-op if hist_file is NULL or "" */
2167 }
2107#endif 2168#endif
2108 2169
2109 fflush_all(); 2170 fflush_all();
@@ -2137,7 +2198,7 @@ static void hush_exit(int exitcode)
2137 2198
2138 fflush_all(); 2199 fflush_all();
2139#if ENABLE_HUSH_JOB 2200#if ENABLE_HUSH_JOB
2140 sigexit(- (exitcode & 0xff)); 2201 restore_ttypgrp_and_killsig_or__exit(- (exitcode & 0xff));
2141#else 2202#else
2142 _exit(exitcode); 2203 _exit(exitcode);
2143#endif 2204#endif
@@ -2222,7 +2283,7 @@ static int check_and_run_traps(void)
2222 } 2283 }
2223 } 2284 }
2224 /* this restores tty pgrp, then kills us with SIGHUP */ 2285 /* this restores tty pgrp, then kills us with SIGHUP */
2225 sigexit(SIGHUP); 2286 restore_ttypgrp_and_killsig_or__exit(SIGHUP);
2226 } 2287 }
2227#endif 2288#endif
2228#if ENABLE_HUSH_FAST 2289#if ENABLE_HUSH_FAST
@@ -4607,6 +4668,11 @@ static int fetch_heredocs(o_string *as_string, struct pipe *pi, int heredoc_cnt,
4607 4668
4608 redir->rd_type = REDIRECT_HEREDOC2; 4669 redir->rd_type = REDIRECT_HEREDOC2;
4609 /* redir->rd_dup is (ab)used to indicate <<- */ 4670 /* redir->rd_dup is (ab)used to indicate <<- */
4671 if (!redir->rd_filename) {
4672 /* examples: "echo <<", "echo <<<", "echo <<>" */
4673 syntax_error("missing here document terminator");
4674 return -1;
4675 }
4610 p = fetch_till_str(as_string, input, 4676 p = fetch_till_str(as_string, input,
4611 redir->rd_filename, redir->rd_dup); 4677 redir->rd_filename, redir->rd_dup);
4612 if (!p) { 4678 if (!p) {
@@ -7366,11 +7432,6 @@ static void switch_off_special_sigs(unsigned mask)
7366} 7432}
7367 7433
7368#if BB_MMU 7434#if BB_MMU
7369/* never called */
7370void re_execute_shell(char ***to_free, const char *s,
7371 char *g_argv0, char **g_argv,
7372 char **builtin_argv) NORETURN;
7373
7374static void reset_traps_to_defaults(void) 7435static void reset_traps_to_defaults(void)
7375{ 7436{
7376 /* This function is always called in a child shell 7437 /* This function is always called in a child shell
@@ -7420,10 +7481,8 @@ static void reset_traps_to_defaults(void)
7420 7481
7421#else /* !BB_MMU */ 7482#else /* !BB_MMU */
7422 7483
7423static void re_execute_shell(char ***to_free, const char *s, 7484static NORETURN void re_execute_shell(
7424 char *g_argv0, char **g_argv, 7485 char * *volatile * to_free, const char *s,
7425 char **builtin_argv) NORETURN;
7426static void re_execute_shell(char ***to_free, const char *s,
7427 char *g_argv0, char **g_argv, 7486 char *g_argv0, char **g_argv,
7428 char **builtin_argv) 7487 char **builtin_argv)
7429{ 7488{
@@ -7653,7 +7712,13 @@ static int generate_stream_from_string(const char *s, pid_t *pid_p)
7653 pid_t pid; 7712 pid_t pid;
7654 int channel[2]; 7713 int channel[2];
7655# if !BB_MMU 7714# if !BB_MMU
7656 char **to_free = NULL; 7715 /* _volatile_ pointer to "char*".
7716 * Or else compiler can peek from inside re_execute_shell()
7717 * and see that this pointer is a local var (i.e. not globally visible),
7718 * and decide to optimize out the store to it. Yes,
7719 * it was seen in the wild.
7720 */
7721 char * *volatile to_free = NULL;
7657# endif 7722# endif
7658 7723
7659 xpipe(channel); 7724 xpipe(channel);
@@ -7800,7 +7865,7 @@ static void setup_heredoc(struct redir_struct *redir)
7800 const char *heredoc = redir->rd_filename; 7865 const char *heredoc = redir->rd_filename;
7801 char *expanded; 7866 char *expanded;
7802#if !BB_MMU 7867#if !BB_MMU
7803 char **to_free; 7868 char * *volatile to_free;
7804#endif 7869#endif
7805 7870
7806 expanded = NULL; 7871 expanded = NULL;
@@ -8275,7 +8340,7 @@ static const struct built_in_command *find_builtin(const char *name)
8275 return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]); 8340 return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]);
8276} 8341}
8277 8342
8278#if ENABLE_HUSH_JOB && ENABLE_FEATURE_TAB_COMPLETION 8343#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_TAB_COMPLETION
8279static const char * FAST_FUNC hush_command_name(int i) 8344static const char * FAST_FUNC hush_command_name(int i)
8280{ 8345{
8281 if (/*i >= 0 && */ i < ARRAY_SIZE(bltins1)) { 8346 if (/*i >= 0 && */ i < ARRAY_SIZE(bltins1)) {
@@ -8441,10 +8506,8 @@ static void unset_func(const char *name)
8441#define exec_function(to_free, funcp, argv) \ 8506#define exec_function(to_free, funcp, argv) \
8442 exec_function(funcp, argv) 8507 exec_function(funcp, argv)
8443# endif 8508# endif
8444static void exec_function(char ***to_free, 8509static NORETURN void exec_function(
8445 const struct function *funcp, 8510 char * *volatile *to_free,
8446 char **argv) NORETURN;
8447static void exec_function(char ***to_free,
8448 const struct function *funcp, 8511 const struct function *funcp,
8449 char **argv) 8512 char **argv)
8450{ 8513{
@@ -8540,10 +8603,8 @@ static int run_function(const struct function *funcp, char **argv)
8540#define exec_builtin(to_free, x, argv) \ 8603#define exec_builtin(to_free, x, argv) \
8541 exec_builtin(to_free, argv) 8604 exec_builtin(to_free, argv)
8542#endif 8605#endif
8543static void exec_builtin(char ***to_free, 8606static NORETURN void exec_builtin(
8544 const struct built_in_command *x, 8607 char * *volatile *to_free,
8545 char **argv) NORETURN;
8546static void exec_builtin(char ***to_free,
8547 const struct built_in_command *x, 8608 const struct built_in_command *x,
8548 char **argv) 8609 char **argv)
8549{ 8610{
@@ -8566,8 +8627,7 @@ static void exec_builtin(char ***to_free,
8566#endif 8627#endif
8567} 8628}
8568 8629
8569static void execvp_or_die(char **argv) NORETURN; 8630static NORETURN void execvp_or_die(char **argv)
8570static void execvp_or_die(char **argv)
8571{ 8631{
8572 int e; 8632 int e;
8573 debug_printf_exec("execing '%s'\n", argv[0]); 8633 debug_printf_exec("execing '%s'\n", argv[0]);
@@ -8688,10 +8748,8 @@ static void if_command_vV_print_and_exit(char opt_vV, char *cmd, const char *exp
8688 * The at_exit handlers apparently confuse the calling process, 8748 * The at_exit handlers apparently confuse the calling process,
8689 * in particular stdin handling. Not sure why? -- because of vfork! (vda) 8749 * in particular stdin handling. Not sure why? -- because of vfork! (vda)
8690 */ 8750 */
8691static void pseudo_exec_argv(nommu_save_t *nommu_save, 8751static NORETURN NOINLINE void pseudo_exec_argv(
8692 char **argv, int assignment_cnt, 8752 volatile nommu_save_t *nommu_save,
8693 char **argv_expanded) NORETURN;
8694static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
8695 char **argv, int assignment_cnt, 8753 char **argv, int assignment_cnt,
8696 char **argv_expanded) 8754 char **argv_expanded)
8697{ 8755{
@@ -8717,7 +8775,8 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
8717#if BB_MMU 8775#if BB_MMU
8718 G.shadowed_vars_pp = NULL; /* "don't save, free them instead" */ 8776 G.shadowed_vars_pp = NULL; /* "don't save, free them instead" */
8719#else 8777#else
8720 G.shadowed_vars_pp = &nommu_save->old_vars; 8778 /* cast away volatility */
8779 G.shadowed_vars_pp = (struct variable **)&nommu_save->old_vars;
8721 G.var_nest_level++; 8780 G.var_nest_level++;
8722#endif 8781#endif
8723 set_vars_and_save_old(new_env); 8782 set_vars_and_save_old(new_env);
@@ -8844,10 +8903,8 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
8844 8903
8845/* Called after [v]fork() in run_pipe 8904/* Called after [v]fork() in run_pipe
8846 */ 8905 */
8847static void pseudo_exec(nommu_save_t *nommu_save, 8906static NORETURN void pseudo_exec(
8848 struct command *command, 8907 volatile nommu_save_t *nommu_save,
8849 char **argv_expanded) NORETURN;
8850static void pseudo_exec(nommu_save_t *nommu_save,
8851 struct command *command, 8908 struct command *command,
8852 char **argv_expanded) 8909 char **argv_expanded)
8853{ 8910{
@@ -9732,8 +9789,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
9732 9789
9733 /* Stores to nommu_save list of env vars putenv'ed 9790 /* Stores to nommu_save list of env vars putenv'ed
9734 * (NOMMU, on MMU we don't need that) */ 9791 * (NOMMU, on MMU we don't need that) */
9735 /* cast away volatility... */ 9792 pseudo_exec(&nommu_save, command, argv_expanded);
9736 pseudo_exec((nommu_save_t*) &nommu_save, command, argv_expanded);
9737 /* pseudo_exec() does not return */ 9793 /* pseudo_exec() does not return */
9738 } 9794 }
9739 9795
@@ -10121,7 +10177,7 @@ static int run_list(struct pipe *pi)
10121 if (rcode != 0 && G.o_opt[OPT_O_ERREXIT]) { 10177 if (rcode != 0 && G.o_opt[OPT_O_ERREXIT]) {
10122 debug_printf_exec("ERREXIT:1 errexit_depth:%d\n", G.errexit_depth); 10178 debug_printf_exec("ERREXIT:1 errexit_depth:%d\n", G.errexit_depth);
10123 if (G.errexit_depth == 0) 10179 if (G.errexit_depth == 0)
10124 hush_exit(rcode); 10180 save_history_run_exit_trap_and_exit(rcode);
10125 } 10181 }
10126 G.errexit_depth = sv_errexit_depth; 10182 G.errexit_depth = sv_errexit_depth;
10127 10183
@@ -10195,6 +10251,53 @@ static int run_and_free_list(struct pipe *pi)
10195/* 10251/*
10196 * Initialization and main 10252 * Initialization and main
10197 */ 10253 */
10254#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING
10255static void init_line_editing(void)
10256{
10257 G.line_input_state = new_line_input_t(FOR_SHELL);
10258# if ENABLE_FEATURE_TAB_COMPLETION
10259 G.line_input_state->get_exe_name = hush_command_name;
10260# endif
10261# if EDITING_HAS_sh_get_var
10262 G.line_input_state->sh_get_var = get_local_var_value;
10263# endif
10264# if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0
10265 {
10266 const char *hp = get_local_var_value("HISTFILE");
10267 if (!hp) {
10268 hp = get_local_var_value("HOME");
10269 if (hp) {
10270 hp = concat_path_file(hp, ".hush_history");
10271 /* Make HISTFILE set on exit (else history won't be saved) */
10272 set_local_var_from_halves("HISTFILE", hp);
10273 }
10274 } else {
10275 hp = xstrdup(hp);
10276 }
10277 if (hp) {
10278 G.line_input_state->hist_file = hp;
10279 }
10280# if ENABLE_FEATURE_SH_HISTFILESIZE
10281 hp = get_local_var_value("HISTSIZE");
10282 /* Using HISTFILESIZE above to limit max_history would be WRONG:
10283 * users may set HISTFILESIZE=0 in their profile scripts
10284 * to prevent _saving_ of history files, but still want to have
10285 * non-zero history limit for in-memory list.
10286 */
10287// in bash, runtime history size is controlled by HISTSIZE (0=no history),
10288// HISTFILESIZE controls on-disk history file size (in lines, 0=no history):
10289 G.line_input_state->max_history = size_from_HISTFILESIZE(hp);
10290// HISTFILESIZE: "The shell sets the default value to the value of HISTSIZE after reading any startup files."
10291// HISTSIZE: "The shell sets the default value to 500 after reading any startup files."
10292// (meaning: if the value wasn't set after startup files, the default value is set as described above)
10293# endif
10294 }
10295# endif
10296}
10297#else
10298# define init_line_editing() ((void)0)
10299#endif
10300
10198static void install_sighandlers(unsigned mask) 10301static void install_sighandlers(unsigned mask)
10199{ 10302{
10200 sighandler_t old_handler; 10303 sighandler_t old_handler;
@@ -10333,7 +10436,6 @@ static int set_mode(int state, char mode, const char *o_opt)
10333int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 10436int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
10334int hush_main(int argc, char **argv) 10437int hush_main(int argc, char **argv)
10335{ 10438{
10336 pid_t cached_getpid;
10337 enum { 10439 enum {
10338 OPT_login = (1 << 0), 10440 OPT_login = (1 << 0),
10339 }; 10441 };
@@ -10346,6 +10448,11 @@ int hush_main(int argc, char **argv)
10346 struct variable *shell_ver; 10448 struct variable *shell_ver;
10347 10449
10348 INIT_G(); 10450 INIT_G();
10451#if ENABLE_HUSH_JOB
10452 die_func = fflush_restore_ttypgrp_and__exit;
10453#else
10454 die_func = fflush_and__exit;
10455#endif
10349 if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */ 10456 if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */
10350 G.last_exitcode = EXIT_SUCCESS; 10457 G.last_exitcode = EXIT_SUCCESS;
10351#if !BB_MMU 10458#if !BB_MMU
@@ -10361,9 +10468,6 @@ int hush_main(int argc, char **argv)
10361 _exit(0); 10468 _exit(0);
10362 } 10469 }
10363 G.argv0_for_re_execing = argv[0]; 10470 G.argv0_for_re_execing = argv[0];
10364 if (G.argv0_for_re_execing[0] == '-')
10365 /* reexeced hush should never be a login shell */
10366 G.argv0_for_re_execing++;
10367#endif 10471#endif
10368#if ENABLE_HUSH_TRAP 10472#if ENABLE_HUSH_TRAP
10369# if ENABLE_HUSH_FUNCTIONS 10473# if ENABLE_HUSH_FUNCTIONS
@@ -10376,9 +10480,8 @@ int hush_main(int argc, char **argv)
10376 G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ 10480 G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
10377#endif 10481#endif
10378 10482
10379 cached_getpid = getpid(); /* for tcsetpgrp() during init */ 10483 G.root_pid = getpid(); /* for $PID (NOMMU can override via -$HEXPID:HEXPPID:...) */
10380 G.root_pid = cached_getpid; /* for $PID (NOMMU can override via -$HEXPID:HEXPPID:...) */ 10484 G.root_ppid = getppid(); /* for $PPID (NOMMU can override) */
10381 G.root_ppid = getppid(); /* for $PPID (NOMMU can override) */
10382 10485
10383 /* Deal with HUSH_VERSION */ 10486 /* Deal with HUSH_VERSION */
10384 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION"); 10487 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION");
@@ -10427,7 +10530,7 @@ int hush_main(int argc, char **argv)
10427 if (!get_local_var_value("PATH")) 10530 if (!get_local_var_value("PATH"))
10428 set_local_var_from_halves("PATH", bb_default_root_path); 10531 set_local_var_from_halves("PATH", bb_default_root_path);
10429 10532
10430 /* PS1/PS2 are set later, if we determine that we are interactive */ 10533 /* PS1/PS2/HISTFILE are set later, if we determine that we are interactive */
10431 10534
10432 /* bash also exports SHLVL and _, 10535 /* bash also exports SHLVL and _,
10433 * and sets (but doesn't export) the following variables: 10536 * and sets (but doesn't export) the following variables:
@@ -10449,7 +10552,6 @@ int hush_main(int argc, char **argv)
10449 * BASH_SOURCE=() 10552 * BASH_SOURCE=()
10450 * DIRSTACK=() 10553 * DIRSTACK=()
10451 * PIPESTATUS=([0]="0") 10554 * PIPESTATUS=([0]="0")
10452 * HISTFILE=/<xxx>/.bash_history
10453 * HISTFILESIZE=500 10555 * HISTFILESIZE=500
10454 * HISTSIZE=500 10556 * HISTSIZE=500
10455 * MAILCHECK=60 10557 * MAILCHECK=60
@@ -10462,27 +10564,24 @@ int hush_main(int argc, char **argv)
10462 * PS4='+ ' 10564 * PS4='+ '
10463 */ 10565 */
10464 10566
10567 /* Shell is non-interactive at first. We need to call
10568 * install_special_sighandlers() if we are going to execute "sh <script>",
10569 * "sh -c <cmds>" or login shell's /etc/profile and friends.
10570 * If we later decide that we are interactive, we run
10571 * install_special_sighandlers() in order to intercept more signals.
10572 */
10573 install_special_sighandlers();
10574
10465#if NUM_SCRIPTS > 0 10575#if NUM_SCRIPTS > 0
10466 if (argc < 0) { 10576 if (argc < 0) {
10467 char *script = get_script_content(-argc - 1); 10577 char *script = get_script_content(-argc - 1);
10468 G.global_argv = argv; 10578 G.global_argv = argv;
10469 G.global_argc = string_array_len(argv); 10579 G.global_argc = string_array_len(argv);
10470 //install_special_sighandlers(); - needed?
10471 parse_and_run_string(script); 10580 parse_and_run_string(script);
10472 goto final_return; 10581 goto final_return;
10473 } 10582 }
10474#endif 10583#endif
10475 10584
10476 /* Initialize some more globals to non-zero values */
10477 die_func = restore_ttypgrp_and__exit;
10478
10479 /* Shell is non-interactive at first. We need to call
10480 * install_special_sighandlers() if we are going to execute "sh <script>",
10481 * "sh -c <cmds>" or login shell's /etc/profile and friends.
10482 * If we later decide that we are interactive, we run install_special_sighandlers()
10483 * in order to intercept (more) signals.
10484 */
10485
10486 /* Parse options */ 10585 /* Parse options */
10487 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */ 10586 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */
10488 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0; 10587 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0;
@@ -10546,6 +10645,7 @@ int hush_main(int argc, char **argv)
10546 case '$': { 10645 case '$': {
10547 unsigned long long empty_trap_mask; 10646 unsigned long long empty_trap_mask;
10548 10647
10648 G.reexeced_on_NOMMU = 1;
10549 G.root_pid = bb_strtou(optarg, &optarg, 16); 10649 G.root_pid = bb_strtou(optarg, &optarg, 16);
10550 optarg++; 10650 optarg++;
10551 G.root_ppid = bb_strtou(optarg, &optarg, 16); 10651 G.root_ppid = bb_strtou(optarg, &optarg, 16);
@@ -10559,7 +10659,6 @@ int hush_main(int argc, char **argv)
10559 empty_trap_mask = bb_strtoull(optarg, &optarg, 16); 10659 empty_trap_mask = bb_strtoull(optarg, &optarg, 16);
10560 if (empty_trap_mask != 0) { 10660 if (empty_trap_mask != 0) {
10561 IF_HUSH_TRAP(int sig;) 10661 IF_HUSH_TRAP(int sig;)
10562 install_special_sighandlers();
10563# if ENABLE_HUSH_TRAP 10662# if ENABLE_HUSH_TRAP
10564 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG); 10663 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
10565 for (sig = 1; sig < NSIG; sig++) { 10664 for (sig = 1; sig < NSIG; sig++) {
@@ -10625,7 +10724,9 @@ int hush_main(int argc, char **argv)
10625 G.global_argv[0] = argv[0]; 10724 G.global_argv[0] = argv[0];
10626 10725
10627 /* If we are login shell... */ 10726 /* If we are login shell... */
10628 if (flags & OPT_login) { 10727 if (!G_reexeced_on_NOMMU /* reexeced hush should never be a login shell */
10728 && (flags & OPT_login)
10729 ) {
10629 const char *hp = NULL; 10730 const char *hp = NULL;
10630 HFILE *input; 10731 HFILE *input;
10631 10732
@@ -10633,7 +10734,6 @@ int hush_main(int argc, char **argv)
10633 input = hfopen("/etc/profile"); 10734 input = hfopen("/etc/profile");
10634 run_profile: 10735 run_profile:
10635 if (input != NULL) { 10736 if (input != NULL) {
10636 install_special_sighandlers();
10637 parse_and_run_file(input); 10737 parse_and_run_file(input);
10638 hfclose(input); 10738 hfclose(input);
10639 } 10739 }
@@ -10670,8 +10770,6 @@ int hush_main(int argc, char **argv)
10670 */ 10770 */
10671 char *script; 10771 char *script;
10672 10772
10673 install_special_sighandlers();
10674
10675 G.global_argc--; 10773 G.global_argc--;
10676 G.global_argv++; 10774 G.global_argv++;
10677#if !BB_MMU 10775#if !BB_MMU
@@ -10722,7 +10820,6 @@ int hush_main(int argc, char **argv)
10722 bb_simple_perror_msg_and_die(G.global_argv[0]); 10820 bb_simple_perror_msg_and_die(G.global_argv[0]);
10723 } 10821 }
10724 xfunc_error_retval = 1; 10822 xfunc_error_retval = 1;
10725 install_special_sighandlers();
10726 parse_and_run_file(input); 10823 parse_and_run_file(input);
10727#if ENABLE_FEATURE_CLEAN_UP 10824#if ENABLE_FEATURE_CLEAN_UP
10728 hfclose(input); 10825 hfclose(input);
@@ -10738,126 +10835,86 @@ int hush_main(int argc, char **argv)
10738 10835
10739 /* A shell is interactive if the '-i' flag was given, 10836 /* A shell is interactive if the '-i' flag was given,
10740 * or if all of the following conditions are met: 10837 * or if all of the following conditions are met:
10741 * no -c command 10838 * not -c 'CMD'
10742 * no arguments remaining or the -s flag given 10839 * not running a script (no arguments remaining, or -s flag given)
10743 * standard input is a terminal 10840 * standard input is a terminal
10744 * standard output is a terminal 10841 * standard output is a terminal
10745 * Refer to Posix.2, the description of the 'sh' utility. 10842 * Refer to Posix.2, the description of the 'sh' utility.
10746 */ 10843 */
10747#if ENABLE_HUSH_JOB 10844#if ENABLE_HUSH_INTERACTIVE
10748 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { 10845 if (!G_reexeced_on_NOMMU
10749 G_saved_tty_pgrp = tcgetpgrp(STDIN_FILENO); 10846 && isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)
10750 debug_printf("saved_tty_pgrp:%d\n", G_saved_tty_pgrp); 10847 ) {
10751 if (G_saved_tty_pgrp < 0) 10848 /* Try to dup stdin to high fd#, >= 255 */
10752 G_saved_tty_pgrp = 0;
10753
10754 /* try to dup stdin to high fd#, >= 255 */
10755 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254); 10849 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254);
10756 if (G_interactive_fd < 0) { 10850 if (G_interactive_fd < 0) {
10757 /* try to dup to any fd */ 10851 /* Try to dup to any fd */
10758 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1); 10852 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1);
10759 if (G_interactive_fd < 0) { 10853 if (G_interactive_fd < 0)
10760 /* give up */ 10854 /* Give up */
10761 G_interactive_fd = 0; 10855 G_interactive_fd = 0;
10762 G_saved_tty_pgrp = 0;
10763 }
10764 } 10856 }
10765 } 10857 debug_printf("interactive_fd:%d\n", G_interactive_fd);
10766 debug_printf("interactive_fd:%d\n", G_interactive_fd); 10858 if (G_interactive_fd) {
10767 if (G_interactive_fd) { 10859// TODO? bash:
10768 if (G_saved_tty_pgrp) { 10860// if interactive but not a login shell, sources ~/.bashrc
10769 /* If we were run as 'hush &', sleep until we are 10861// (--norc turns this off, --rcfile <file> overrides)
10770 * in the foreground (tty pgrp == our pgrp). 10862# if ENABLE_HUSH_JOB
10771 * If we get started under a job aware app (like bash), 10863 /* Can we do job control? */
10772 * make sure we are now in charge so we don't fight over 10864 G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd);
10773 * who gets the foreground */ 10865 debug_printf("saved_tty_pgrp:%d\n", G_saved_tty_pgrp);
10774 while (1) { 10866 if (G_saved_tty_pgrp < 0)
10775 pid_t shell_pgrp = getpgrp(); 10867 G_saved_tty_pgrp = 0; /* no */
10776 G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd); 10868 if (G_saved_tty_pgrp) {
10777 if (G_saved_tty_pgrp == shell_pgrp) 10869 /* If we were run as 'hush &', sleep until we are
10778 break; 10870 * in the foreground (tty pgrp == our pgrp).
10779 /* send TTIN to ourself (should stop us) */ 10871 * If we get started under a job aware app (like bash),
10780 kill(- shell_pgrp, SIGTTIN); 10872 * make sure we are now in charge so we don't fight over
10873 * who gets the foreground */
10874 while (1) {
10875 pid_t shell_pgrp = getpgrp();
10876 if (G_saved_tty_pgrp == shell_pgrp) {
10877/* Often both pgrps here are set to our pid - but not always!
10878 * Example: sh -c 'echo $$; hush; echo FIN'
10879 * Here, the parent shell is not interactive, so it does NOT set up
10880 * a separate process group for its children, and we (hush) initially
10881 * run in parent's process group (until we set up our own a few lines down).
10882 */
10883 //bb_error_msg("process groups tty:%d hush:%d", G_saved_tty_pgrp, shell_pgrp);
10884 break;
10885 }
10886 /* Send TTIN to ourself (should stop us) */
10887 kill(- shell_pgrp, SIGTTIN);
10888 G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd);
10889 }
10781 } 10890 }
10782 }
10783
10784 /* Install more signal handlers */
10785 install_special_sighandlers();
10786
10787 if (G_saved_tty_pgrp) {
10788 /* Set other signals to restore saved_tty_pgrp */
10789 install_fatal_sighandlers();
10790 /* Put ourselves in our own process group
10791 * (bash, too, does this only if ctty is available) */
10792 bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
10793 /* Grab control of the terminal */
10794 tcsetpgrp(G_interactive_fd, cached_getpid);
10795 }
10796 enable_restore_tty_pgrp_on_exit();
10797
10798# if ENABLE_FEATURE_EDITING
10799 G.line_input_state = new_line_input_t(FOR_SHELL);
10800# if ENABLE_FEATURE_TAB_COMPLETION
10801 G.line_input_state->get_exe_name = hush_command_name;
10802# endif
10803# if EDITING_HAS_sh_get_var
10804 G.line_input_state->sh_get_var = get_local_var_value;
10805# endif
10806# endif 10891# endif
10807# if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0 10892 /* Install more signal handlers */
10808 { 10893 install_special_sighandlers();
10809 const char *hp = get_local_var_value("HISTFILE"); 10894# if ENABLE_HUSH_JOB
10810 if (!hp) { 10895 if (G_saved_tty_pgrp) {
10811 hp = get_local_var_value("HOME"); 10896 /* Set fatal signals to restore saved_tty_pgrp */
10812 if (hp) 10897 install_fatal_sighandlers();
10813 hp = concat_path_file(hp, ".hush_history"); 10898 /* (The if() is an optimization: can avoid two redundant syscalls) */
10814 } else { 10899 if (G_saved_tty_pgrp != G.root_pid) {
10815 hp = xstrdup(hp); 10900 /* Put ourselves in our own process group
10816 } 10901 * (bash, too, does this only if ctty is available) */
10817 if (hp) { 10902 bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
10818 G.line_input_state->hist_file = hp; 10903 /* Grab control of the terminal */
10819 //set_local_var(xasprintf("HISTFILE=%s", ...)); 10904 tcsetpgrp(G_interactive_fd, G.root_pid);
10905 }
10820 } 10906 }
10821# if ENABLE_FEATURE_SH_HISTFILESIZE
10822 hp = get_local_var_value("HISTFILESIZE");
10823 G.line_input_state->max_history = size_from_HISTFILESIZE(hp);
10824# endif
10825 }
10826# endif 10907# endif
10827 } else { 10908# if ENABLE_FEATURE_EDITING_FANCY_PROMPT
10828 install_special_sighandlers(); 10909 /* Set (but not export) PS1/2 unless already set */
10829 } 10910 if (!get_local_var_value("PS1"))
10830#elif ENABLE_HUSH_INTERACTIVE 10911 set_local_var_from_halves("PS1", "\\w \\$ ");
10831 /* No job control compiled in, only prompt/line editing */ 10912 if (!get_local_var_value("PS2"))
10832 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { 10913 set_local_var_from_halves("PS2", "> ");
10833 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254); 10914# endif
10834 if (G_interactive_fd < 0) { 10915 init_line_editing();
10835 /* try to dup to any fd */
10836 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1);
10837 if (G_interactive_fd < 0)
10838 /* give up */
10839 G_interactive_fd = 0;
10840 }
10841 }
10842 install_special_sighandlers();
10843#else
10844 /* We have interactiveness code disabled */
10845 install_special_sighandlers();
10846#endif
10847 /* bash:
10848 * if interactive but not a login shell, sources ~/.bashrc
10849 * (--norc turns this off, --rcfile <file> overrides)
10850 */
10851 10916
10852 if (G_interactive_fd) { 10917# if !ENABLE_FEATURE_SH_EXTRA_QUIET
10853#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT
10854 /* Set (but not export) PS1/2 unless already set */
10855 if (!get_local_var_value("PS1"))
10856 set_local_var_from_halves("PS1", "\\w \\$ ");
10857 if (!get_local_var_value("PS2"))
10858 set_local_var_from_halves("PS2", "> ");
10859#endif
10860 if (!ENABLE_FEATURE_SH_EXTRA_QUIET) {
10861 /* note: ash and hush share this string */ 10918 /* note: ash and hush share this string */
10862 printf("\n\n%s %s\n" 10919 printf("\n\n%s %s\n"
10863 IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n") 10920 IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n")
@@ -10865,13 +10922,15 @@ int hush_main(int argc, char **argv)
10865 bb_banner, 10922 bb_banner,
10866 "hush - the humble shell" 10923 "hush - the humble shell"
10867 ); 10924 );
10868 } 10925# endif
10869 } 10926 } /* if become interactive */
10927 } /* if on tty */
10928#endif /* if INTERACTIVE is allowed by build config */
10870 10929
10871 parse_and_run_file(hfopen(NULL)); /* stdin */ 10930 parse_and_run_file(hfopen(NULL)); /* stdin */
10872 10931
10873 final_return: 10932 final_return:
10874 hush_exit(G.last_exitcode); 10933 save_history_run_exit_trap_and_exit(G.last_exitcode);
10875} 10934}
10876 10935
10877/* 10936/*
@@ -11057,19 +11116,19 @@ static int FAST_FUNC builtin_exit(char **argv)
11057 * TODO: we can use G.exiting = -1 as indicator "last cmd was exit" 11116 * TODO: we can use G.exiting = -1 as indicator "last cmd was exit"
11058 */ 11117 */
11059 11118
11060 /* note: EXIT trap is run by hush_exit */ 11119 /* note: EXIT trap is run by save_history_run_exit_trap_and_exit */
11061 argv = skip_dash_dash(argv); 11120 argv = skip_dash_dash(argv);
11062 if (argv[0] == NULL) { 11121 if (argv[0] == NULL) {
11063#if ENABLE_HUSH_TRAP 11122#if ENABLE_HUSH_TRAP
11064 if (G.pre_trap_exitcode >= 0) /* "exit" in trap uses $? from before the trap */ 11123 if (G.pre_trap_exitcode >= 0) /* "exit" in trap uses $? from before the trap */
11065 hush_exit(G.pre_trap_exitcode); 11124 save_history_run_exit_trap_and_exit(G.pre_trap_exitcode);
11066#endif 11125#endif
11067 hush_exit(G.last_exitcode); 11126 save_history_run_exit_trap_and_exit(G.last_exitcode);
11068 } 11127 }
11069 /* mimic bash: exit 123abc == exit 255 + error msg */ 11128 /* mimic bash: exit 123abc == exit 255 + error msg */
11070 xfunc_error_retval = 255; 11129 xfunc_error_retval = 255;
11071 /* bash: exit -2 == exit 254, no error msg */ 11130 /* bash: exit -2 == exit 254, no error msg */
11072 hush_exit(xatoi(argv[0]) & 0xff); 11131 save_history_run_exit_trap_and_exit(xatoi(argv[0]) & 0xff);
11073} 11132}
11074 11133
11075#if ENABLE_HUSH_TYPE 11134#if ENABLE_HUSH_TYPE
@@ -11175,6 +11234,11 @@ static int FAST_FUNC builtin_read(char **argv)
11175 goto again; 11234 goto again;
11176 } 11235 }
11177 11236
11237 if ((uintptr_t)r == 2) /* -t SEC timeout? */
11238 /* bash: "The exit status is greater than 128 if the timeout is exceeded." */
11239 /* The actual value observed with bash 5.2.15: */
11240 return 128 + SIGALRM;
11241
11178 if ((uintptr_t)r > 1) { 11242 if ((uintptr_t)r > 1) {
11179 bb_simple_error_msg(r); 11243 bb_simple_error_msg(r);
11180 r = (char*)(uintptr_t)1; 11244 r = (char*)(uintptr_t)1;
diff --git a/shell/hush_leaktool.sh b/shell/hush_leaktool.sh
index ca35ec144..3edd3df61 100755
--- a/shell/hush_leaktool.sh
+++ b/shell/hush_leaktool.sh
@@ -7,7 +7,7 @@ freelist=`grep 'free 0x' "$output" | cut -d' ' -f2 | sort | uniq | xargs`
7 7
8grep -v free "$output" >"$output.leaked" 8grep -v free "$output" >"$output.leaked"
9 9
10i=8 10i=16
11list= 11list=
12for freed in $freelist; do 12for freed in $freelist; do
13 list="$list -e $freed" 13 list="$list -e $freed"
@@ -15,7 +15,7 @@ for freed in $freelist; do
15 echo Dropping $list 15 echo Dropping $list
16 grep -F -v $list <"$output.leaked" >"$output.temp" 16 grep -F -v $list <"$output.leaked" >"$output.temp"
17 mv "$output.temp" "$output.leaked" 17 mv "$output.temp" "$output.leaked"
18 i=8 18 i=16
19 list= 19 list=
20done 20done
21if test "$list"; then 21if test "$list"; then
@@ -23,3 +23,17 @@ if test "$list"; then
23 grep -F -v $list <"$output.leaked" >"$output.temp" 23 grep -F -v $list <"$output.leaked" >"$output.temp"
24 mv "$output.temp" "$output.leaked" 24 mv "$output.temp" "$output.leaked"
25fi 25fi
26
27# All remaining allocations are on addresses which were never freed.
28# * Sort them by line, grouping together allocations which allocated the same address.
29# A leaky allocation will give many different addresses (because it's never freed,
30# the address can not be reused).
31# * Remove the address (field #4).
32# * Count the allocations per every unique source line and alloc type.
33# * Show largest counts on top.
34cat output.leaked \
35 | sort -u \
36 | cut -d' ' -f1-3 \
37 | uniq -c \
38 | sort -rn \
39>output.leaked.counted_uniq_alloc_address
diff --git a/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.right b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.right
new file mode 100644
index 000000000..7af73557a
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.right
@@ -0,0 +1 @@
hush: syntax error: missing here document terminator
diff --git a/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.tests b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.tests
new file mode 100755
index 000000000..33ccde26b
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.tests
@@ -0,0 +1,2 @@
1echo <<
2echo Done:
diff --git a/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.right b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.right
new file mode 100644
index 000000000..7af73557a
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.right
@@ -0,0 +1 @@
hush: syntax error: missing here document terminator
diff --git a/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.tests b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.tests
new file mode 100755
index 000000000..fcda11045
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.tests
@@ -0,0 +1,2 @@
1echo << >
2echo Done:
diff --git a/shell/hush_test/hush-misc/sig_exitcode.tests b/shell/hush_test/hush-misc/sig_exitcode.tests
index 7879dc854..67b4500f4 100755
--- a/shell/hush_test/hush-misc/sig_exitcode.tests
+++ b/shell/hush_test/hush-misc/sig_exitcode.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1exec 2>&1 4exec 2>&1
2 5
3$THIS_SH -c 'kill -9 $$' 6$THIS_SH -c 'kill -9 $$'
diff --git a/shell/hush_test/hush-misc/wait1.tests b/shell/hush_test/hush-misc/wait1.tests
index f9cf6d48c..54120319b 100755
--- a/shell/hush_test/hush-misc/wait1.tests
+++ b/shell/hush_test/hush-misc/wait1.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1sleep 2 & sleep 1 & wait $! 4sleep 2 & sleep 1 & wait $!
2echo $? 5echo $?
3jobs 6jobs
diff --git a/shell/hush_test/hush-misc/wait2.tests b/shell/hush_test/hush-misc/wait2.tests
index be20f95a5..60f382c9f 100755
--- a/shell/hush_test/hush-misc/wait2.tests
+++ b/shell/hush_test/hush-misc/wait2.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1sleep 3 & sleep 2 & sleep 1 4sleep 3 & sleep 2 & sleep 1
2wait $! 5wait $!
3echo $? 6echo $?
diff --git a/shell/hush_test/hush-misc/wait3.tests b/shell/hush_test/hush-misc/wait3.tests
index ac541c3fc..aceed1126 100755
--- a/shell/hush_test/hush-misc/wait3.tests
+++ b/shell/hush_test/hush-misc/wait3.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1sleep 2 & (sleep 1;exit 3) & wait $! 4sleep 2 & (sleep 1;exit 3) & wait $!
2echo $? 5echo $?
3jobs 6jobs
diff --git a/shell/hush_test/hush-misc/wait4.tests b/shell/hush_test/hush-misc/wait4.tests
index cc34059ac..c979a38b6 100755
--- a/shell/hush_test/hush-misc/wait4.tests
+++ b/shell/hush_test/hush-misc/wait4.tests
@@ -1,2 +1,5 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1sleep 1 | (sleep 1;exit 3) & wait %1 4sleep 1 | (sleep 1;exit 3) & wait %1
2echo Three:$? 5echo Three:$?
diff --git a/shell/hush_test/hush-misc/wait5.tests b/shell/hush_test/hush-misc/wait5.tests
index 1b4762d89..e0ac8c251 100755
--- a/shell/hush_test/hush-misc/wait5.tests
+++ b/shell/hush_test/hush-misc/wait5.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1sleep 0 | (sleep 0;exit 3) & 4sleep 0 | (sleep 0;exit 3) &
2sleep 1 5sleep 1
3echo Zero:$? 6echo Zero:$?
diff --git a/shell/hush_test/hush-misc/wait6.tests b/shell/hush_test/hush-misc/wait6.tests
index c23713199..c09002ab0 100755
--- a/shell/hush_test/hush-misc/wait6.tests
+++ b/shell/hush_test/hush-misc/wait6.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1# In bash, "wait $!" extracts correct exitcode even if bg task has already exited 4# In bash, "wait $!" extracts correct exitcode even if bg task has already exited
2# It prints 0, then 3: 5# It prints 0, then 3:
3(sleep 0; exit 3) & sleep 1 6(sleep 0; exit 3) & sleep 1
diff --git a/shell/hush_test/hush-read/read_t.right b/shell/hush_test/hush-read/read_t.right
index 04126cbe6..3eedae275 100644
--- a/shell/hush_test/hush-read/read_t.right
+++ b/shell/hush_test/hush-read/read_t.right
@@ -1,4 +1,4 @@
1>< 1>te:142<
2>< 2>:142<
3>test< 3>test:0<
4>test< 4>test:0<
diff --git a/shell/hush_test/hush-read/read_t.tests b/shell/hush_test/hush-read/read_t.tests
index d65f1aeaa..9fbeec517 100755
--- a/shell/hush_test/hush-read/read_t.tests
+++ b/shell/hush_test/hush-read/read_t.tests
@@ -1,10 +1,10 @@
1# bash 3.2 outputs: 1# bash 5.2 outputs:
2 2
3# >< 3# >te:142<
4{ echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply<") 4{ echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply:$?<")
5# >< 5# >:142<
6{ sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply<") 6{ sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply:$?<")
7# >test< 7# >test:0<
8{ echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply<") 8{ echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply:$?<")
9# >test< 9# >test:0<
10{ sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply<") 10{ sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply:$?<")
diff --git a/shell/hush_test/hush-signals/catch.tests b/shell/hush_test/hush-signals/catch.tests
index d2a21d17e..4b1a08e8f 100755
--- a/shell/hush_test/hush-signals/catch.tests
+++ b/shell/hush_test/hush-signals/catch.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test ("User defined signal 2" text not emitted)
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1# avoid ugly warnings about signals not being caught 4# avoid ugly warnings about signals not being caught
2trap ":" USR1 USR2 5trap ":" USR1 USR2
3 6
diff --git a/shell/hush_test/hush-signals/signal1.tests b/shell/hush_test/hush-signals/signal1.tests
index 61943467a..c83fa1254 100755
--- a/shell/hush_test/hush-signals/signal1.tests
+++ b/shell/hush_test/hush-signals/signal1.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1trap "echo got signal" USR1 4trap "echo got signal" USR1
2 5
3for try in 1 2 3 4 5; do 6for try in 1 2 3 4 5; do
diff --git a/shell/hush_test/hush-signals/signal8.tests b/shell/hush_test/hush-signals/signal8.tests
index 731af7477..cd5790164 100755
--- a/shell/hush_test/hush-signals/signal8.tests
+++ b/shell/hush_test/hush-signals/signal8.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1"$THIS_SH" -c ' 4"$THIS_SH" -c '
2exit_func() { 5exit_func() {
3 echo "Removing traps" 6 echo "Removing traps"
diff --git a/shell/hush_test/hush-signals/signal_read2.tests b/shell/hush_test/hush-signals/signal_read2.tests
index eab5b9b5b..11accd5ab 100755
--- a/shell/hush_test/hush-signals/signal_read2.tests
+++ b/shell/hush_test/hush-signals/signal_read2.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test ("Hangup" not emitted)
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1$THIS_SH -c ' 4$THIS_SH -c '
2(sleep 1; kill -HUP $$) & 5(sleep 1; kill -HUP $$) &
3while true; do 6while true; do
diff --git a/shell/hush_test/hush-signals/subshell.tests b/shell/hush_test/hush-signals/subshell.tests
index d877f2b82..856c922d3 100755
--- a/shell/hush_test/hush-signals/subshell.tests
+++ b/shell/hush_test/hush-signals/subshell.tests
@@ -1,5 +1,8 @@
1# Non-empty traps should be reset in subshell 1# Non-empty traps should be reset in subshell
2 2
3# If job control is disabled, skip the test ("Terminated" text not emitted)
4test "`type jobs`" = "jobs is a shell builtin" || exit 77
5
3# HUP is special in interactive shells 6# HUP is special in interactive shells
4trap '' HUP 7trap '' HUP
5# QUIT is always special 8# QUIT is always special
diff --git a/shell/hush_test/run-all b/shell/hush_test/run-all
index 7345fee43..ff29ca0b1 100755
--- a/shell/hush_test/run-all
+++ b/shell/hush_test/run-all
@@ -67,9 +67,12 @@ do_test()
67# echo Running test: "$x" 67# echo Running test: "$x"
68 echo -n "$1/$x:" 68 echo -n "$1/$x:"
69 ( 69 (
70 "$THIS_SH" "./$x" 2>&1 | \ 70 "$THIS_SH" "./$x" >"$name.xx" 2>&1
71 grep -va "^hush: using fallback suid method$" >"$name.xx"
72 r=$? 71 r=$?
72 # filter !FEATURE_SUID_CONFIG_QUIET message
73 sed -i \
74 -e "/^hush: using fallback suid method$/d" \
75 "$name.xx"
73 # filter C library differences 76 # filter C library differences
74 sed -i \ 77 sed -i \
75 -e "/: invalid option /s:'::g" \ 78 -e "/: invalid option /s:'::g" \
diff --git a/shell/shell_common.c b/shell/shell_common.c
index 2702ef98a..657f0df8f 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -55,7 +55,7 @@ const char* FAST_FUNC
55shell_builtin_read(struct builtin_read_params *params) 55shell_builtin_read(struct builtin_read_params *params)
56{ 56{
57 struct pollfd pfd[1]; 57 struct pollfd pfd[1];
58#define fd (pfd[0].fd) /* -u FD */ 58#define fd (pfd->fd) /* -u FD */
59 unsigned err; 59 unsigned err;
60 unsigned end_ms; /* -t TIMEOUT */ 60 unsigned end_ms; /* -t TIMEOUT */
61 int nchars; /* -n NUM */ 61 int nchars; /* -n NUM */
@@ -144,7 +144,7 @@ shell_builtin_read(struct builtin_read_params *params)
144 * bash seems to ignore -p PROMPT for this use case. 144 * bash seems to ignore -p PROMPT for this use case.
145 */ 145 */
146 int r; 146 int r;
147 pfd[0].events = POLLIN; 147 pfd->events = POLLIN;
148 r = poll(pfd, 1, /*timeout:*/ 0); 148 r = poll(pfd, 1, /*timeout:*/ 0);
149 /* Return 0 only if poll returns 1 ("one fd ready"), else return 1: */ 149 /* Return 0 only if poll returns 1 ("one fd ready"), else return 1: */
150 return (const char *)(uintptr_t)(r <= 0); 150 return (const char *)(uintptr_t)(r <= 0);
@@ -209,13 +209,8 @@ shell_builtin_read(struct builtin_read_params *params)
209 * 32-bit unix time wrapped (year 2038+). 209 * 32-bit unix time wrapped (year 2038+).
210 */ 210 */
211 if (timeout <= 0) { /* already late? */ 211 if (timeout <= 0) { /* already late? */
212#if ENABLE_PLATFORM_MINGW32
213 retval = (const char *)(uintptr_t)2; 212 retval = (const char *)(uintptr_t)2;
214 break; 213 break;
215#else
216 retval = (const char *)(uintptr_t)1;
217 goto ret;
218#endif
219 } 214 }
220 } 215 }
221 216
@@ -224,8 +219,8 @@ shell_builtin_read(struct builtin_read_params *params)
224 * regardless of SA_RESTART-ness of that signal! 219 * regardless of SA_RESTART-ness of that signal!
225 */ 220 */
226 errno = 0; 221 errno = 0;
227 pfd[0].events = POLLIN; 222 pfd->events = POLLIN;
228//TODO race with a signal arriving just before the poll! 223
229#if ENABLE_PLATFORM_MINGW32 224#if ENABLE_PLATFORM_MINGW32
230 /* Don't poll if timeout is -1, it hurts performance. The 225 /* Don't poll if timeout is -1, it hurts performance. The
231 * caution above about interrupts isn't relevant on Windows 226 * caution above about interrupts isn't relevant on Windows
@@ -233,15 +228,14 @@ shell_builtin_read(struct builtin_read_params *params)
233 */ 228 */
234 if (timeout >= 0) 229 if (timeout >= 0)
235#endif 230#endif
236 if (poll(pfd, 1, timeout) <= 0) { 231 /* test bb_got_signal, then poll(), atomically wrt signals */
237 /* timed out, or EINTR */ 232 if (check_got_signal_and_poll(pfd, timeout) <= 0) {
233 /* timed out, or some error */
238 err = errno; 234 err = errno;
239#if ENABLE_PLATFORM_MINGW32 235 if (!err) { /* timed out */
240 if (!err) {
241 retval = (const char *)(uintptr_t)2; 236 retval = (const char *)(uintptr_t)2;
242 break; 237 break;
243 } 238 }
244#endif
245 retval = (const char *)(uintptr_t)1; 239 retval = (const char *)(uintptr_t)1;
246 goto ret; 240 goto ret;
247 } 241 }