diff options
Diffstat (limited to '')
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 | ||
173 | config FEATURE_SH_EMBEDDED_SCRIPTS | 174 | config 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 */ |
465 | static const char *const optletters_optnames[] ALIGN_PTR = { | 465 | static 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 | |||
1826 | struct globals_memstack { | 1825 | struct 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 |
2368 | static void FAST_FUNC | 2365 | static 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 | |||
3993 | static struct alias **atab; // [ATABSIZE]; | 3988 | static 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 | |||
3999 | static struct alias ** | 3993 | static 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 */ |
9910 | static void *funcblock; /* block to allocate function from */ | 9901 | static 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 | |||
14777 | static const char * | 14766 | static const char * |
14778 | expandstr(const char *ps, int syntax_type) | 14767 | expandstr(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 | ||| | ||
8 | Whitespace 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 @@ | |||
1 | echo "X:Y:Z:" | (IFS=": " read x y; echo "|$x|$y|") | ||
2 | echo "X:Y:Z" | (IFS=": " read x y; echo "|$x|$y|") | ||
3 | echo "X:Y:" | (IFS=": " read x y; echo "|$x|$y|") | ||
4 | echo "X:Y" | (IFS=": " read x y; echo "|$x|$y|") | ||
5 | echo "X:" | (IFS=": " read x y; echo "|$x|$y|") | ||
6 | echo "X" | (IFS=": " read x y; echo "|$x|$y|") | ||
7 | echo "" | (IFS=": " read x y; echo "|$x|$y|") | ||
8 | echo Whitespace should be trimmed too: | ||
9 | echo "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 @@ | |||
31 | extern char **environ; | 31 | extern char **environ; |
32 | 32 | ||
33 | int | 33 | int |
34 | main (argc, argv) | 34 | main (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 | ||
30 | void strprint(); | 30 | void strprint(char *); |
31 | 31 | ||
32 | int main(int argc, char **argv) | 32 | int 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) | |||
1352 | static void *xxmalloc(int lineno, size_t size) | 1360 | static 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 | } | ||
1366 | static 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 | } |
1358 | static void *xxrealloc(int lineno, void *ptr, size_t size) | 1372 | static 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 | } | ||
1380 | static 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 | } |
1364 | static char *xxstrdup(int lineno, const char *str) | 1388 | static 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 | } | ||
1394 | static 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 | } | ||
1400 | static 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 | } |
1370 | static void xxfree(void *ptr) | 1411 | static 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 | ||
2005 | static void hush_exit(int exitcode) NORETURN; | ||
2006 | |||
2007 | static void restore_ttypgrp_and__exit(void) NORETURN; | ||
2008 | static 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 | */ |
2031 | static void fflush_and__exit(void) NORETURN; | 2063 | static NORETURN void fflush_and__exit(void) |
2032 | static 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 | */ |
2049 | static void sigexit(int sig) NORETURN; | 2076 | static NORETURN void restore_ttypgrp_and_killsig_or__exit(int sig) |
2050 | static 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 | |||
2095 | static 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. */ | 2141 | static const char* FAST_FUNC get_local_var_value(const char *name); |
2103 | static void hush_exit(int exitcode) | 2142 | |
2143 | /* Self-explanatory. | ||
2144 | * Restores tty foreground process group too. | ||
2145 | */ | ||
2146 | static 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 */ | ||
7370 | void re_execute_shell(char ***to_free, const char *s, | ||
7371 | char *g_argv0, char **g_argv, | ||
7372 | char **builtin_argv) NORETURN; | ||
7373 | |||
7374 | static void reset_traps_to_defaults(void) | 7435 | static 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 | ||
7423 | static void re_execute_shell(char ***to_free, const char *s, | 7484 | static 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; | ||
7426 | static 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 |
8279 | static const char * FAST_FUNC hush_command_name(int i) | 8344 | static 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 |
8444 | static void exec_function(char ***to_free, | 8509 | static NORETURN void exec_function( |
8445 | const struct function *funcp, | 8510 | char * *volatile *to_free, |
8446 | char **argv) NORETURN; | ||
8447 | static 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 |
8543 | static void exec_builtin(char ***to_free, | 8606 | static NORETURN void exec_builtin( |
8544 | const struct built_in_command *x, | 8607 | char * *volatile *to_free, |
8545 | char **argv) NORETURN; | ||
8546 | static 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 | ||
8569 | static void execvp_or_die(char **argv) NORETURN; | 8630 | static NORETURN void execvp_or_die(char **argv) |
8570 | static 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 | */ |
8691 | static void pseudo_exec_argv(nommu_save_t *nommu_save, | 8751 | static NORETURN NOINLINE void pseudo_exec_argv( |
8692 | char **argv, int assignment_cnt, | 8752 | volatile nommu_save_t *nommu_save, |
8693 | char **argv_expanded) NORETURN; | ||
8694 | static 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 | */ |
8847 | static void pseudo_exec(nommu_save_t *nommu_save, | 8906 | static NORETURN void pseudo_exec( |
8848 | struct command *command, | 8907 | volatile nommu_save_t *nommu_save, |
8849 | char **argv_expanded) NORETURN; | ||
8850 | static 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 | ||
10255 | static 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 | |||
10198 | static void install_sighandlers(unsigned mask) | 10301 | static 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) | |||
10333 | int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 10436 | int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
10334 | int hush_main(int argc, char **argv) | 10437 | int 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 | ||
8 | grep -v free "$output" >"$output.leaked" | 8 | grep -v free "$output" >"$output.leaked" |
9 | 9 | ||
10 | i=8 | 10 | i=16 |
11 | list= | 11 | list= |
12 | for freed in $freelist; do | 12 | for 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= |
20 | done | 20 | done |
21 | if test "$list"; then | 21 | if 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" |
25 | fi | 25 | fi |
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. | ||
34 | cat 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 @@ | |||
1 | echo << | ||
2 | echo 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 @@ | |||
1 | echo << > | ||
2 | echo 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 | ||
2 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
3 | |||
1 | exec 2>&1 | 4 | exec 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 | ||
2 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
3 | |||
1 | sleep 2 & sleep 1 & wait $! | 4 | sleep 2 & sleep 1 & wait $! |
2 | echo $? | 5 | echo $? |
3 | jobs | 6 | jobs |
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 | ||
2 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
3 | |||
1 | sleep 3 & sleep 2 & sleep 1 | 4 | sleep 3 & sleep 2 & sleep 1 |
2 | wait $! | 5 | wait $! |
3 | echo $? | 6 | echo $? |
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 | ||
2 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
3 | |||
1 | sleep 2 & (sleep 1;exit 3) & wait $! | 4 | sleep 2 & (sleep 1;exit 3) & wait $! |
2 | echo $? | 5 | echo $? |
3 | jobs | 6 | jobs |
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 | ||
2 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
3 | |||
1 | sleep 1 | (sleep 1;exit 3) & wait %1 | 4 | sleep 1 | (sleep 1;exit 3) & wait %1 |
2 | echo Three:$? | 5 | echo 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 | ||
2 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
3 | |||
1 | sleep 0 | (sleep 0;exit 3) & | 4 | sleep 0 | (sleep 0;exit 3) & |
2 | sleep 1 | 5 | sleep 1 |
3 | echo Zero:$? | 6 | echo 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 | ||
2 | test "`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) | ||
2 | test "`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 |
2 | trap ":" USR1 USR2 | 5 | trap ":" 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 | ||
2 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
3 | |||
1 | trap "echo got signal" USR1 | 4 | trap "echo got signal" USR1 |
2 | 5 | ||
3 | for try in 1 2 3 4 5; do | 6 | for 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 | ||
2 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
3 | |||
1 | "$THIS_SH" -c ' | 4 | "$THIS_SH" -c ' |
2 | exit_func() { | 5 | exit_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) | ||
2 | test "`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 $$) & |
3 | while true; do | 6 | while 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) | ||
4 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
5 | |||
3 | # HUP is special in interactive shells | 6 | # HUP is special in interactive shells |
4 | trap '' HUP | 7 | trap '' 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 | |||
55 | shell_builtin_read(struct builtin_read_params *params) | 55 | shell_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 | } |