aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--shell/ash.c15
-rw-r--r--shell/hush.c463
-rwxr-xr-xshell/hush_leaktool.sh18
-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
-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
16 files changed, 302 insertions, 237 deletions
diff --git a/shell/ash.c b/shell/ash.c
index 0038aa1e9..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
@@ -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 */
@@ -16170,7 +16158,6 @@ init(void)
16170 } 16158 }
16171} 16159}
16172 16160
16173
16174//usage:#define ash_trivial_usage 16161//usage:#define ash_trivial_usage
16175//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]"
16176//////// comes from ^^^^^^^^^^optletters 16163//////// comes from ^^^^^^^^^^optletters
diff --git a/shell/hush.c b/shell/hush.c
index d1f687f9d..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.
@@ -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? */
@@ -2101,11 +2140,15 @@ static sighandler_t pick_sighandler(unsigned sig)
2101 2140
2102static const char* FAST_FUNC get_local_var_value(const char *name); 2141static const char* FAST_FUNC get_local_var_value(const char *name);
2103 2142
2104/* Restores tty foreground process group, and exits. */ 2143/* Self-explanatory.
2105static void hush_exit(int exitcode) 2144 * Restores tty foreground process group too.
2145 */
2146static NORETURN void save_history_run_exit_trap_and_exit(int exitcode)
2106{ 2147{
2107#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 2148#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
2108 if (G.line_input_state) { 2149 if (G.line_input_state
2150 && getpid() == G.root_pid /* exits in subshells do not save history */
2151 ) {
2109 const char *hp; 2152 const char *hp;
2110# if ENABLE_FEATURE_SH_HISTFILESIZE 2153# if ENABLE_FEATURE_SH_HISTFILESIZE
2111// in bash: 2154// in bash:
@@ -2155,7 +2198,7 @@ static void hush_exit(int exitcode)
2155 2198
2156 fflush_all(); 2199 fflush_all();
2157#if ENABLE_HUSH_JOB 2200#if ENABLE_HUSH_JOB
2158 sigexit(- (exitcode & 0xff)); 2201 restore_ttypgrp_and_killsig_or__exit(- (exitcode & 0xff));
2159#else 2202#else
2160 _exit(exitcode); 2203 _exit(exitcode);
2161#endif 2204#endif
@@ -2240,7 +2283,7 @@ static int check_and_run_traps(void)
2240 } 2283 }
2241 } 2284 }
2242 /* this restores tty pgrp, then kills us with SIGHUP */ 2285 /* this restores tty pgrp, then kills us with SIGHUP */
2243 sigexit(SIGHUP); 2286 restore_ttypgrp_and_killsig_or__exit(SIGHUP);
2244 } 2287 }
2245#endif 2288#endif
2246#if ENABLE_HUSH_FAST 2289#if ENABLE_HUSH_FAST
@@ -7389,11 +7432,6 @@ static void switch_off_special_sigs(unsigned mask)
7389} 7432}
7390 7433
7391#if BB_MMU 7434#if BB_MMU
7392/* never called */
7393void re_execute_shell(char ***to_free, const char *s,
7394 char *g_argv0, char **g_argv,
7395 char **builtin_argv) NORETURN;
7396
7397static void reset_traps_to_defaults(void) 7435static void reset_traps_to_defaults(void)
7398{ 7436{
7399 /* This function is always called in a child shell 7437 /* This function is always called in a child shell
@@ -7443,10 +7481,8 @@ static void reset_traps_to_defaults(void)
7443 7481
7444#else /* !BB_MMU */ 7482#else /* !BB_MMU */
7445 7483
7446static void re_execute_shell(char ***to_free, const char *s, 7484static NORETURN void re_execute_shell(
7447 char *g_argv0, char **g_argv, 7485 char * *volatile * to_free, const char *s,
7448 char **builtin_argv) NORETURN;
7449static void re_execute_shell(char ***to_free, const char *s,
7450 char *g_argv0, char **g_argv, 7486 char *g_argv0, char **g_argv,
7451 char **builtin_argv) 7487 char **builtin_argv)
7452{ 7488{
@@ -7676,7 +7712,13 @@ static int generate_stream_from_string(const char *s, pid_t *pid_p)
7676 pid_t pid; 7712 pid_t pid;
7677 int channel[2]; 7713 int channel[2];
7678# if !BB_MMU 7714# if !BB_MMU
7679 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;
7680# endif 7722# endif
7681 7723
7682 xpipe(channel); 7724 xpipe(channel);
@@ -7823,7 +7865,7 @@ static void setup_heredoc(struct redir_struct *redir)
7823 const char *heredoc = redir->rd_filename; 7865 const char *heredoc = redir->rd_filename;
7824 char *expanded; 7866 char *expanded;
7825#if !BB_MMU 7867#if !BB_MMU
7826 char **to_free; 7868 char * *volatile to_free;
7827#endif 7869#endif
7828 7870
7829 expanded = NULL; 7871 expanded = NULL;
@@ -8298,7 +8340,7 @@ static const struct built_in_command *find_builtin(const char *name)
8298 return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]); 8340 return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]);
8299} 8341}
8300 8342
8301#if ENABLE_HUSH_JOB && ENABLE_FEATURE_TAB_COMPLETION 8343#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_TAB_COMPLETION
8302static const char * FAST_FUNC hush_command_name(int i) 8344static const char * FAST_FUNC hush_command_name(int i)
8303{ 8345{
8304 if (/*i >= 0 && */ i < ARRAY_SIZE(bltins1)) { 8346 if (/*i >= 0 && */ i < ARRAY_SIZE(bltins1)) {
@@ -8464,10 +8506,8 @@ static void unset_func(const char *name)
8464#define exec_function(to_free, funcp, argv) \ 8506#define exec_function(to_free, funcp, argv) \
8465 exec_function(funcp, argv) 8507 exec_function(funcp, argv)
8466# endif 8508# endif
8467static void exec_function(char ***to_free, 8509static NORETURN void exec_function(
8468 const struct function *funcp, 8510 char * *volatile *to_free,
8469 char **argv) NORETURN;
8470static void exec_function(char ***to_free,
8471 const struct function *funcp, 8511 const struct function *funcp,
8472 char **argv) 8512 char **argv)
8473{ 8513{
@@ -8563,10 +8603,8 @@ static int run_function(const struct function *funcp, char **argv)
8563#define exec_builtin(to_free, x, argv) \ 8603#define exec_builtin(to_free, x, argv) \
8564 exec_builtin(to_free, argv) 8604 exec_builtin(to_free, argv)
8565#endif 8605#endif
8566static void exec_builtin(char ***to_free, 8606static NORETURN void exec_builtin(
8567 const struct built_in_command *x, 8607 char * *volatile *to_free,
8568 char **argv) NORETURN;
8569static void exec_builtin(char ***to_free,
8570 const struct built_in_command *x, 8608 const struct built_in_command *x,
8571 char **argv) 8609 char **argv)
8572{ 8610{
@@ -8589,8 +8627,7 @@ static void exec_builtin(char ***to_free,
8589#endif 8627#endif
8590} 8628}
8591 8629
8592static void execvp_or_die(char **argv) NORETURN; 8630static NORETURN void execvp_or_die(char **argv)
8593static void execvp_or_die(char **argv)
8594{ 8631{
8595 int e; 8632 int e;
8596 debug_printf_exec("execing '%s'\n", argv[0]); 8633 debug_printf_exec("execing '%s'\n", argv[0]);
@@ -8711,10 +8748,8 @@ static void if_command_vV_print_and_exit(char opt_vV, char *cmd, const char *exp
8711 * The at_exit handlers apparently confuse the calling process, 8748 * The at_exit handlers apparently confuse the calling process,
8712 * in particular stdin handling. Not sure why? -- because of vfork! (vda) 8749 * in particular stdin handling. Not sure why? -- because of vfork! (vda)
8713 */ 8750 */
8714static void pseudo_exec_argv(nommu_save_t *nommu_save, 8751static NORETURN NOINLINE void pseudo_exec_argv(
8715 char **argv, int assignment_cnt, 8752 volatile nommu_save_t *nommu_save,
8716 char **argv_expanded) NORETURN;
8717static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
8718 char **argv, int assignment_cnt, 8753 char **argv, int assignment_cnt,
8719 char **argv_expanded) 8754 char **argv_expanded)
8720{ 8755{
@@ -8740,7 +8775,8 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
8740#if BB_MMU 8775#if BB_MMU
8741 G.shadowed_vars_pp = NULL; /* "don't save, free them instead" */ 8776 G.shadowed_vars_pp = NULL; /* "don't save, free them instead" */
8742#else 8777#else
8743 G.shadowed_vars_pp = &nommu_save->old_vars; 8778 /* cast away volatility */
8779 G.shadowed_vars_pp = (struct variable **)&nommu_save->old_vars;
8744 G.var_nest_level++; 8780 G.var_nest_level++;
8745#endif 8781#endif
8746 set_vars_and_save_old(new_env); 8782 set_vars_and_save_old(new_env);
@@ -8867,10 +8903,8 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
8867 8903
8868/* Called after [v]fork() in run_pipe 8904/* Called after [v]fork() in run_pipe
8869 */ 8905 */
8870static void pseudo_exec(nommu_save_t *nommu_save, 8906static NORETURN void pseudo_exec(
8871 struct command *command, 8907 volatile nommu_save_t *nommu_save,
8872 char **argv_expanded) NORETURN;
8873static void pseudo_exec(nommu_save_t *nommu_save,
8874 struct command *command, 8908 struct command *command,
8875 char **argv_expanded) 8909 char **argv_expanded)
8876{ 8910{
@@ -9755,8 +9789,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
9755 9789
9756 /* Stores to nommu_save list of env vars putenv'ed 9790 /* Stores to nommu_save list of env vars putenv'ed
9757 * (NOMMU, on MMU we don't need that) */ 9791 * (NOMMU, on MMU we don't need that) */
9758 /* cast away volatility... */ 9792 pseudo_exec(&nommu_save, command, argv_expanded);
9759 pseudo_exec((nommu_save_t*) &nommu_save, command, argv_expanded);
9760 /* pseudo_exec() does not return */ 9793 /* pseudo_exec() does not return */
9761 } 9794 }
9762 9795
@@ -10144,7 +10177,7 @@ static int run_list(struct pipe *pi)
10144 if (rcode != 0 && G.o_opt[OPT_O_ERREXIT]) { 10177 if (rcode != 0 && G.o_opt[OPT_O_ERREXIT]) {
10145 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);
10146 if (G.errexit_depth == 0) 10179 if (G.errexit_depth == 0)
10147 hush_exit(rcode); 10180 save_history_run_exit_trap_and_exit(rcode);
10148 } 10181 }
10149 G.errexit_depth = sv_errexit_depth; 10182 G.errexit_depth = sv_errexit_depth;
10150 10183
@@ -10218,6 +10251,53 @@ static int run_and_free_list(struct pipe *pi)
10218/* 10251/*
10219 * Initialization and main 10252 * Initialization and main
10220 */ 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
10221static void install_sighandlers(unsigned mask) 10301static void install_sighandlers(unsigned mask)
10222{ 10302{
10223 sighandler_t old_handler; 10303 sighandler_t old_handler;
@@ -10356,7 +10436,6 @@ static int set_mode(int state, char mode, const char *o_opt)
10356int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 10436int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
10357int hush_main(int argc, char **argv) 10437int hush_main(int argc, char **argv)
10358{ 10438{
10359 pid_t cached_getpid;
10360 enum { 10439 enum {
10361 OPT_login = (1 << 0), 10440 OPT_login = (1 << 0),
10362 }; 10441 };
@@ -10369,6 +10448,11 @@ int hush_main(int argc, char **argv)
10369 struct variable *shell_ver; 10448 struct variable *shell_ver;
10370 10449
10371 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
10372 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 */
10373 G.last_exitcode = EXIT_SUCCESS; 10457 G.last_exitcode = EXIT_SUCCESS;
10374#if !BB_MMU 10458#if !BB_MMU
@@ -10384,9 +10468,6 @@ int hush_main(int argc, char **argv)
10384 _exit(0); 10468 _exit(0);
10385 } 10469 }
10386 G.argv0_for_re_execing = argv[0]; 10470 G.argv0_for_re_execing = argv[0];
10387 if (G.argv0_for_re_execing[0] == '-')
10388 /* reexeced hush should never be a login shell */
10389 G.argv0_for_re_execing++;
10390#endif 10471#endif
10391#if ENABLE_HUSH_TRAP 10472#if ENABLE_HUSH_TRAP
10392# if ENABLE_HUSH_FUNCTIONS 10473# if ENABLE_HUSH_FUNCTIONS
@@ -10399,9 +10480,8 @@ int hush_main(int argc, char **argv)
10399 G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ 10480 G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
10400#endif 10481#endif
10401 10482
10402 cached_getpid = getpid(); /* for tcsetpgrp() during init */ 10483 G.root_pid = getpid(); /* for $PID (NOMMU can override via -$HEXPID:HEXPPID:...) */
10403 G.root_pid = cached_getpid; /* for $PID (NOMMU can override via -$HEXPID:HEXPPID:...) */ 10484 G.root_ppid = getppid(); /* for $PPID (NOMMU can override) */
10404 G.root_ppid = getppid(); /* for $PPID (NOMMU can override) */
10405 10485
10406 /* Deal with HUSH_VERSION */ 10486 /* Deal with HUSH_VERSION */
10407 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION"); 10487 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION");
@@ -10484,27 +10564,24 @@ int hush_main(int argc, char **argv)
10484 * PS4='+ ' 10564 * PS4='+ '
10485 */ 10565 */
10486 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
10487#if NUM_SCRIPTS > 0 10575#if NUM_SCRIPTS > 0
10488 if (argc < 0) { 10576 if (argc < 0) {
10489 char *script = get_script_content(-argc - 1); 10577 char *script = get_script_content(-argc - 1);
10490 G.global_argv = argv; 10578 G.global_argv = argv;
10491 G.global_argc = string_array_len(argv); 10579 G.global_argc = string_array_len(argv);
10492 //install_special_sighandlers(); - needed?
10493 parse_and_run_string(script); 10580 parse_and_run_string(script);
10494 goto final_return; 10581 goto final_return;
10495 } 10582 }
10496#endif 10583#endif
10497 10584
10498 /* Initialize some more globals to non-zero values */
10499 die_func = restore_ttypgrp_and__exit;
10500
10501 /* Shell is non-interactive at first. We need to call
10502 * install_special_sighandlers() if we are going to execute "sh <script>",
10503 * "sh -c <cmds>" or login shell's /etc/profile and friends.
10504 * If we later decide that we are interactive, we run install_special_sighandlers()
10505 * in order to intercept (more) signals.
10506 */
10507
10508 /* Parse options */ 10585 /* Parse options */
10509 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */ 10586 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */
10510 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0; 10587 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0;
@@ -10568,6 +10645,7 @@ int hush_main(int argc, char **argv)
10568 case '$': { 10645 case '$': {
10569 unsigned long long empty_trap_mask; 10646 unsigned long long empty_trap_mask;
10570 10647
10648 G.reexeced_on_NOMMU = 1;
10571 G.root_pid = bb_strtou(optarg, &optarg, 16); 10649 G.root_pid = bb_strtou(optarg, &optarg, 16);
10572 optarg++; 10650 optarg++;
10573 G.root_ppid = bb_strtou(optarg, &optarg, 16); 10651 G.root_ppid = bb_strtou(optarg, &optarg, 16);
@@ -10581,7 +10659,6 @@ int hush_main(int argc, char **argv)
10581 empty_trap_mask = bb_strtoull(optarg, &optarg, 16); 10659 empty_trap_mask = bb_strtoull(optarg, &optarg, 16);
10582 if (empty_trap_mask != 0) { 10660 if (empty_trap_mask != 0) {
10583 IF_HUSH_TRAP(int sig;) 10661 IF_HUSH_TRAP(int sig;)
10584 install_special_sighandlers();
10585# if ENABLE_HUSH_TRAP 10662# if ENABLE_HUSH_TRAP
10586 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG); 10663 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
10587 for (sig = 1; sig < NSIG; sig++) { 10664 for (sig = 1; sig < NSIG; sig++) {
@@ -10647,7 +10724,9 @@ int hush_main(int argc, char **argv)
10647 G.global_argv[0] = argv[0]; 10724 G.global_argv[0] = argv[0];
10648 10725
10649 /* If we are login shell... */ 10726 /* If we are login shell... */
10650 if (flags & OPT_login) { 10727 if (!G_reexeced_on_NOMMU /* reexeced hush should never be a login shell */
10728 && (flags & OPT_login)
10729 ) {
10651 const char *hp = NULL; 10730 const char *hp = NULL;
10652 HFILE *input; 10731 HFILE *input;
10653 10732
@@ -10655,7 +10734,6 @@ int hush_main(int argc, char **argv)
10655 input = hfopen("/etc/profile"); 10734 input = hfopen("/etc/profile");
10656 run_profile: 10735 run_profile:
10657 if (input != NULL) { 10736 if (input != NULL) {
10658 install_special_sighandlers();
10659 parse_and_run_file(input); 10737 parse_and_run_file(input);
10660 hfclose(input); 10738 hfclose(input);
10661 } 10739 }
@@ -10692,8 +10770,6 @@ int hush_main(int argc, char **argv)
10692 */ 10770 */
10693 char *script; 10771 char *script;
10694 10772
10695 install_special_sighandlers();
10696
10697 G.global_argc--; 10773 G.global_argc--;
10698 G.global_argv++; 10774 G.global_argv++;
10699#if !BB_MMU 10775#if !BB_MMU
@@ -10744,7 +10820,6 @@ int hush_main(int argc, char **argv)
10744 bb_simple_perror_msg_and_die(G.global_argv[0]); 10820 bb_simple_perror_msg_and_die(G.global_argv[0]);
10745 } 10821 }
10746 xfunc_error_retval = 1; 10822 xfunc_error_retval = 1;
10747 install_special_sighandlers();
10748 parse_and_run_file(input); 10823 parse_and_run_file(input);
10749#if ENABLE_FEATURE_CLEAN_UP 10824#if ENABLE_FEATURE_CLEAN_UP
10750 hfclose(input); 10825 hfclose(input);
@@ -10760,138 +10835,86 @@ int hush_main(int argc, char **argv)
10760 10835
10761 /* A shell is interactive if the '-i' flag was given, 10836 /* A shell is interactive if the '-i' flag was given,
10762 * or if all of the following conditions are met: 10837 * or if all of the following conditions are met:
10763 * no -c command 10838 * not -c 'CMD'
10764 * no arguments remaining or the -s flag given 10839 * not running a script (no arguments remaining, or -s flag given)
10765 * standard input is a terminal 10840 * standard input is a terminal
10766 * standard output is a terminal 10841 * standard output is a terminal
10767 * Refer to Posix.2, the description of the 'sh' utility. 10842 * Refer to Posix.2, the description of the 'sh' utility.
10768 */ 10843 */
10769#if ENABLE_HUSH_JOB 10844#if ENABLE_HUSH_INTERACTIVE
10770 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { 10845 if (!G_reexeced_on_NOMMU
10771 G_saved_tty_pgrp = tcgetpgrp(STDIN_FILENO); 10846 && isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)
10772 debug_printf("saved_tty_pgrp:%d\n", G_saved_tty_pgrp); 10847 ) {
10773 if (G_saved_tty_pgrp < 0) 10848 /* Try to dup stdin to high fd#, >= 255 */
10774 G_saved_tty_pgrp = 0;
10775
10776 /* try to dup stdin to high fd#, >= 255 */
10777 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254); 10849 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254);
10778 if (G_interactive_fd < 0) { 10850 if (G_interactive_fd < 0) {
10779 /* try to dup to any fd */ 10851 /* Try to dup to any fd */
10780 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1); 10852 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1);
10781 if (G_interactive_fd < 0) { 10853 if (G_interactive_fd < 0)
10782 /* give up */ 10854 /* Give up */
10783 G_interactive_fd = 0; 10855 G_interactive_fd = 0;
10784 G_saved_tty_pgrp = 0;
10785 }
10786 } 10856 }
10787 } 10857 debug_printf("interactive_fd:%d\n", G_interactive_fd);
10788 debug_printf("interactive_fd:%d\n", G_interactive_fd); 10858 if (G_interactive_fd) {
10789 if (G_interactive_fd) { 10859// TODO? bash:
10790 if (G_saved_tty_pgrp) { 10860// if interactive but not a login shell, sources ~/.bashrc
10791 /* If we were run as 'hush &', sleep until we are 10861// (--norc turns this off, --rcfile <file> overrides)
10792 * in the foreground (tty pgrp == our pgrp). 10862# if ENABLE_HUSH_JOB
10793 * If we get started under a job aware app (like bash), 10863 /* Can we do job control? */
10794 * make sure we are now in charge so we don't fight over 10864 G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd);
10795 * who gets the foreground */ 10865 debug_printf("saved_tty_pgrp:%d\n", G_saved_tty_pgrp);
10796 while (1) { 10866 if (G_saved_tty_pgrp < 0)
10797 pid_t shell_pgrp = getpgrp(); 10867 G_saved_tty_pgrp = 0; /* no */
10798 G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd); 10868 if (G_saved_tty_pgrp) {
10799 if (G_saved_tty_pgrp == shell_pgrp) 10869 /* If we were run as 'hush &', sleep until we are
10800 break; 10870 * in the foreground (tty pgrp == our pgrp).
10801 /* send TTIN to ourself (should stop us) */ 10871 * If we get started under a job aware app (like bash),
10802 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 }
10803 } 10890 }
10804 }
10805
10806 /* Install more signal handlers */
10807 install_special_sighandlers();
10808
10809 if (G_saved_tty_pgrp) {
10810 /* Set other signals to restore saved_tty_pgrp */
10811 install_fatal_sighandlers();
10812 /* Put ourselves in our own process group
10813 * (bash, too, does this only if ctty is available) */
10814 bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
10815 /* Grab control of the terminal */
10816 tcsetpgrp(G_interactive_fd, cached_getpid);
10817 }
10818 enable_restore_tty_pgrp_on_exit();
10819
10820# if ENABLE_FEATURE_EDITING
10821 G.line_input_state = new_line_input_t(FOR_SHELL);
10822# if ENABLE_FEATURE_TAB_COMPLETION
10823 G.line_input_state->get_exe_name = hush_command_name;
10824# endif
10825# if EDITING_HAS_sh_get_var
10826 G.line_input_state->sh_get_var = get_local_var_value;
10827# endif
10828# endif 10891# endif
10829# if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0 10892 /* Install more signal handlers */
10830 { 10893 install_special_sighandlers();
10831 const char *hp = get_local_var_value("HISTFILE"); 10894# if ENABLE_HUSH_JOB
10832 if (!hp) { 10895 if (G_saved_tty_pgrp) {
10833 hp = get_local_var_value("HOME"); 10896 /* Set fatal signals to restore saved_tty_pgrp */
10834 if (hp) { 10897 install_fatal_sighandlers();
10835 hp = concat_path_file(hp, ".hush_history"); 10898 /* (The if() is an optimization: can avoid two redundant syscalls) */
10836 /* Make HISTFILE set on exit (else history won't be saved) */ 10899 if (G_saved_tty_pgrp != G.root_pid) {
10837 set_local_var_from_halves("HISTFILE", hp); 10900 /* Put ourselves in our own process group
10901 * (bash, too, does this only if ctty is available) */
10902 bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
10903 /* Grab control of the terminal */
10904 tcsetpgrp(G_interactive_fd, G.root_pid);
10838 } 10905 }
10839 } else {
10840 hp = xstrdup(hp);
10841 } 10906 }
10842 if (hp) {
10843 G.line_input_state->hist_file = hp;
10844 }
10845# if ENABLE_FEATURE_SH_HISTFILESIZE
10846 hp = get_local_var_value("HISTSIZE");
10847 /* Using HISTFILESIZE above to limit max_history would be WRONG:
10848 * users may set HISTFILESIZE=0 in their profile scripts
10849 * to prevent _saving_ of history files, but still want to have
10850 * non-zero history limit for in-memory list.
10851 */
10852// in bash, runtime history size is controlled by HISTSIZE (0=no history),
10853// HISTFILESIZE controls on-disk history file size (in lines, 0=no history):
10854 G.line_input_state->max_history = size_from_HISTFILESIZE(hp);
10855// HISTFILESIZE: "The shell sets the default value to the value of HISTSIZE after reading any startup files."
10856// HISTSIZE: "The shell sets the default value to 500 after reading any startup files."
10857// (meaning: if the value wasn't set after startup files, the default value is set as described above)
10858# endif
10859 }
10860# endif 10907# endif
10861 } else { 10908# if ENABLE_FEATURE_EDITING_FANCY_PROMPT
10862 install_special_sighandlers(); 10909 /* Set (but not export) PS1/2 unless already set */
10863 } 10910 if (!get_local_var_value("PS1"))
10864#elif ENABLE_HUSH_INTERACTIVE 10911 set_local_var_from_halves("PS1", "\\w \\$ ");
10865 /* No job control compiled in, only prompt/line editing */ 10912 if (!get_local_var_value("PS2"))
10866 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { 10913 set_local_var_from_halves("PS2", "> ");
10867 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254); 10914# endif
10868 if (G_interactive_fd < 0) { 10915 init_line_editing();
10869 /* try to dup to any fd */
10870 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1);
10871 if (G_interactive_fd < 0)
10872 /* give up */
10873 G_interactive_fd = 0;
10874 }
10875 }
10876 install_special_sighandlers();
10877#else
10878 /* We have interactiveness code disabled */
10879 install_special_sighandlers();
10880#endif
10881 /* bash:
10882 * if interactive but not a login shell, sources ~/.bashrc
10883 * (--norc turns this off, --rcfile <file> overrides)
10884 */
10885 10916
10886 if (G_interactive_fd) { 10917# if !ENABLE_FEATURE_SH_EXTRA_QUIET
10887#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT
10888 /* Set (but not export) PS1/2 unless already set */
10889 if (!get_local_var_value("PS1"))
10890 set_local_var_from_halves("PS1", "\\w \\$ ");
10891 if (!get_local_var_value("PS2"))
10892 set_local_var_from_halves("PS2", "> ");
10893#endif
10894 if (!ENABLE_FEATURE_SH_EXTRA_QUIET) {
10895 /* note: ash and hush share this string */ 10918 /* note: ash and hush share this string */
10896 printf("\n\n%s %s\n" 10919 printf("\n\n%s %s\n"
10897 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")
@@ -10899,13 +10922,15 @@ int hush_main(int argc, char **argv)
10899 bb_banner, 10922 bb_banner,
10900 "hush - the humble shell" 10923 "hush - the humble shell"
10901 ); 10924 );
10902 } 10925# endif
10903 } 10926 } /* if become interactive */
10927 } /* if on tty */
10928#endif /* if INTERACTIVE is allowed by build config */
10904 10929
10905 parse_and_run_file(hfopen(NULL)); /* stdin */ 10930 parse_and_run_file(hfopen(NULL)); /* stdin */
10906 10931
10907 final_return: 10932 final_return:
10908 hush_exit(G.last_exitcode); 10933 save_history_run_exit_trap_and_exit(G.last_exitcode);
10909} 10934}
10910 10935
10911/* 10936/*
@@ -11091,19 +11116,19 @@ static int FAST_FUNC builtin_exit(char **argv)
11091 * 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"
11092 */ 11117 */
11093 11118
11094 /* note: EXIT trap is run by hush_exit */ 11119 /* note: EXIT trap is run by save_history_run_exit_trap_and_exit */
11095 argv = skip_dash_dash(argv); 11120 argv = skip_dash_dash(argv);
11096 if (argv[0] == NULL) { 11121 if (argv[0] == NULL) {
11097#if ENABLE_HUSH_TRAP 11122#if ENABLE_HUSH_TRAP
11098 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 */
11099 hush_exit(G.pre_trap_exitcode); 11124 save_history_run_exit_trap_and_exit(G.pre_trap_exitcode);
11100#endif 11125#endif
11101 hush_exit(G.last_exitcode); 11126 save_history_run_exit_trap_and_exit(G.last_exitcode);
11102 } 11127 }
11103 /* mimic bash: exit 123abc == exit 255 + error msg */ 11128 /* mimic bash: exit 123abc == exit 255 + error msg */
11104 xfunc_error_retval = 255; 11129 xfunc_error_retval = 255;
11105 /* bash: exit -2 == exit 254, no error msg */ 11130 /* bash: exit -2 == exit 254, no error msg */
11106 hush_exit(xatoi(argv[0]) & 0xff); 11131 save_history_run_exit_trap_and_exit(xatoi(argv[0]) & 0xff);
11107} 11132}
11108 11133
11109#if ENABLE_HUSH_TYPE 11134#if ENABLE_HUSH_TYPE
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-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-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" \