diff options
author | Ron Yorston <rmy@pobox.com> | 2012-03-22 13:15:08 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2012-03-22 13:15:08 +0000 |
commit | c0d4367d6b581eb5989c02815880cf0fa2851ae8 (patch) | |
tree | 868c266e627e2d7f65ba5a4d5f98a1c421453181 /shell | |
parent | f6bad5ef766b0447158e3de2f55c35f1f6cecb58 (diff) | |
parent | da4441c44f6efccb6f7b7588404d9c6bfb7b6af8 (diff) | |
download | busybox-w32-c0d4367d6b581eb5989c02815880cf0fa2851ae8.tar.gz busybox-w32-c0d4367d6b581eb5989c02815880cf0fa2851ae8.tar.bz2 busybox-w32-c0d4367d6b581eb5989c02815880cf0fa2851ae8.zip |
Merge commit 'da4441c44f6efccb6f7b7588404d9c6bfb7b6af8' into merge
Conflicts:
libbb/vfork_daemon_rexec.c
networking/wget.c
procps/ps.c
Diffstat (limited to 'shell')
-rw-r--r-- | shell/Config.src | 9 | ||||
-rw-r--r-- | shell/ash.c | 122 | ||||
-rw-r--r-- | shell/ash_test/ash-misc/echo_write_error.right | 2 | ||||
-rw-r--r-- | shell/ash_test/ash-misc/echo_write_error.tests | 7 | ||||
-rw-r--r-- | shell/ash_test/ash-redir/redir.right | 1 | ||||
-rw-r--r-- | shell/ash_test/ash-signals/sigint1.right | 1 | ||||
-rwxr-xr-x | shell/ash_test/ash-signals/sigint1.tests | 41 | ||||
-rw-r--r-- | shell/hush.c | 27 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/echo_write_error.right | 2 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/echo_write_error.tests | 7 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/sigint1.right | 1 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/sigint1.tests | 41 | ||||
-rw-r--r-- | shell/shell_common.c | 6 |
13 files changed, 220 insertions, 47 deletions
diff --git a/shell/Config.src b/shell/Config.src index c9c2439e7..e96c21620 100644 --- a/shell/Config.src +++ b/shell/Config.src | |||
@@ -123,9 +123,9 @@ config FEATURE_SH_NOFORK | |||
123 | default n | 123 | default n |
124 | depends on (HUSH || ASH) && FEATURE_PREFER_APPLETS | 124 | depends on (HUSH || ASH) && FEATURE_PREFER_APPLETS |
125 | help | 125 | help |
126 | This option causes busybox shells [currently only ash] | 126 | This option causes busybox shells to not execute typical |
127 | to not execute typical fork/exec/wait sequence, but call <applet>_main | 127 | fork/exec/wait sequence, but call <applet>_main directly, |
128 | directly, if possible. (Sometimes it is not possible: for example, | 128 | if possible. (Sometimes it is not possible: for example, |
129 | this is not possible in pipes). | 129 | this is not possible in pipes). |
130 | 130 | ||
131 | This will be done only for some applets (those which are marked | 131 | This will be done only for some applets (those which are marked |
@@ -133,6 +133,7 @@ config FEATURE_SH_NOFORK | |||
133 | 133 | ||
134 | This may significantly speed up some shell scripts. | 134 | This may significantly speed up some shell scripts. |
135 | 135 | ||
136 | This feature is relatively new. Use with care. | 136 | This feature is relatively new. Use with care. Report bugs |
137 | to project mailing list. | ||
137 | 138 | ||
138 | endmenu | 139 | endmenu |
diff --git a/shell/ash.c b/shell/ash.c index 28f988698..fd9141661 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -111,6 +111,13 @@ | |||
111 | //config: help | 111 | //config: help |
112 | //config: Enable bash-compatible extensions. | 112 | //config: Enable bash-compatible extensions. |
113 | //config: | 113 | //config: |
114 | //config:config ASH_IDLE_TIMEOUT | ||
115 | //config: bool "Idle timeout variable" | ||
116 | //config: default n | ||
117 | //config: depends on ASH | ||
118 | //config: help | ||
119 | //config: Enables bash-like auto-logout after $TMOUT seconds of idle time. | ||
120 | //config: | ||
114 | //config:config ASH_JOB_CONTROL | 121 | //config:config ASH_JOB_CONTROL |
115 | //config: bool "Job control" | 122 | //config: bool "Job control" |
116 | //config: default y | 123 | //config: default y |
@@ -119,7 +126,7 @@ | |||
119 | //config: Enable job control in the ash shell. | 126 | //config: Enable job control in the ash shell. |
120 | //config: | 127 | //config: |
121 | //config:config ASH_ALIAS | 128 | //config:config ASH_ALIAS |
122 | //config: bool "alias support" | 129 | //config: bool "Alias support" |
123 | //config: default y | 130 | //config: default y |
124 | //config: depends on ASH | 131 | //config: depends on ASH |
125 | //config: help | 132 | //config: help |
@@ -130,28 +137,28 @@ | |||
130 | //config: default y | 137 | //config: default y |
131 | //config: depends on ASH | 138 | //config: depends on ASH |
132 | //config: help | 139 | //config: help |
133 | //config: Enable getopts builtin in the ash shell. | 140 | //config: Enable support for getopts builtin in ash. |
134 | //config: | 141 | //config: |
135 | //config:config ASH_BUILTIN_ECHO | 142 | //config:config ASH_BUILTIN_ECHO |
136 | //config: bool "Builtin version of 'echo'" | 143 | //config: bool "Builtin version of 'echo'" |
137 | //config: default y | 144 | //config: default y |
138 | //config: depends on ASH | 145 | //config: depends on ASH |
139 | //config: help | 146 | //config: help |
140 | //config: Enable support for echo, builtin to ash. | 147 | //config: Enable support for echo builtin in ash. |
141 | //config: | 148 | //config: |
142 | //config:config ASH_BUILTIN_PRINTF | 149 | //config:config ASH_BUILTIN_PRINTF |
143 | //config: bool "Builtin version of 'printf'" | 150 | //config: bool "Builtin version of 'printf'" |
144 | //config: default y | 151 | //config: default y |
145 | //config: depends on ASH | 152 | //config: depends on ASH |
146 | //config: help | 153 | //config: help |
147 | //config: Enable support for printf, builtin to ash. | 154 | //config: Enable support for printf builtin in ash. |
148 | //config: | 155 | //config: |
149 | //config:config ASH_BUILTIN_TEST | 156 | //config:config ASH_BUILTIN_TEST |
150 | //config: bool "Builtin version of 'test'" | 157 | //config: bool "Builtin version of 'test'" |
151 | //config: default y | 158 | //config: default y |
152 | //config: depends on ASH | 159 | //config: depends on ASH |
153 | //config: help | 160 | //config: help |
154 | //config: Enable support for test, builtin to ash. | 161 | //config: Enable support for test builtin in ash. |
155 | //config: | 162 | //config: |
156 | //config:config ASH_CMDCMD | 163 | //config:config ASH_CMDCMD |
157 | //config: bool "'command' command to override shell builtins" | 164 | //config: bool "'command' command to override shell builtins" |
@@ -167,7 +174,7 @@ | |||
167 | //config: default n | 174 | //config: default n |
168 | //config: depends on ASH | 175 | //config: depends on ASH |
169 | //config: help | 176 | //config: help |
170 | //config: Enable "check for new mail" in the ash shell. | 177 | //config: Enable "check for new mail" function in the ash shell. |
171 | //config: | 178 | //config: |
172 | //config:config ASH_OPTIMIZE_FOR_SIZE | 179 | //config:config ASH_OPTIMIZE_FOR_SIZE |
173 | //config: bool "Optimize for size instead of speed" | 180 | //config: bool "Optimize for size instead of speed" |
@@ -449,6 +456,9 @@ static const char *var_end(const char *var) | |||
449 | 456 | ||
450 | 457 | ||
451 | /* ============ Interrupts / exceptions */ | 458 | /* ============ Interrupts / exceptions */ |
459 | |||
460 | static void exitshell(void) NORETURN; | ||
461 | |||
452 | /* | 462 | /* |
453 | * These macros allow the user to suspend the handling of interrupt signals | 463 | * These macros allow the user to suspend the handling of interrupt signals |
454 | * over a period of time. This is similar to SIGHOLD or to sigblock, but | 464 | * over a period of time. This is similar to SIGHOLD or to sigblock, but |
@@ -1929,7 +1939,9 @@ change_lc_ctype(const char *value) | |||
1929 | #endif | 1939 | #endif |
1930 | #if ENABLE_ASH_MAIL | 1940 | #if ENABLE_ASH_MAIL |
1931 | static void chkmail(void); | 1941 | static void chkmail(void); |
1932 | static void changemail(const char *) FAST_FUNC; | 1942 | static void changemail(const char *var_value) FAST_FUNC; |
1943 | #else | ||
1944 | # define chkmail() ((void)0) | ||
1933 | #endif | 1945 | #endif |
1934 | static void changepath(const char *) FAST_FUNC; | 1946 | static void changepath(const char *) FAST_FUNC; |
1935 | #if ENABLE_ASH_RANDOM_SUPPORT | 1947 | #if ENABLE_ASH_RANDOM_SUPPORT |
@@ -3937,18 +3949,51 @@ setjobctl(int on) | |||
3937 | static int FAST_FUNC | 3949 | static int FAST_FUNC |
3938 | killcmd(int argc, char **argv) | 3950 | killcmd(int argc, char **argv) |
3939 | { | 3951 | { |
3940 | int i = 1; | ||
3941 | if (argv[1] && strcmp(argv[1], "-l") != 0) { | 3952 | if (argv[1] && strcmp(argv[1], "-l") != 0) { |
3953 | int i = 1; | ||
3942 | do { | 3954 | do { |
3943 | if (argv[i][0] == '%') { | 3955 | if (argv[i][0] == '%') { |
3944 | struct job *jp = getjob(argv[i], 0); | 3956 | /* |
3945 | unsigned pid = jp->ps[0].ps_pid; | 3957 | * "kill %N" - job kill |
3946 | /* Enough space for ' -NNN<nul>' */ | 3958 | * Converting to pgrp / pid kill |
3947 | argv[i] = alloca(sizeof(int)*3 + 3); | 3959 | */ |
3948 | /* kill_main has matching code to expect | 3960 | struct job *jp; |
3949 | * leading space. Needed to not confuse | 3961 | char *dst; |
3950 | * negative pids with "kill -SIGNAL_NO" syntax */ | 3962 | int j, n; |
3951 | sprintf(argv[i], " -%u", pid); | 3963 | |
3964 | jp = getjob(argv[i], 0); | ||
3965 | /* | ||
3966 | * In jobs started under job control, we signal | ||
3967 | * entire process group by kill -PGRP_ID. | ||
3968 | * This happens, f.e., in interactive shell. | ||
3969 | * | ||
3970 | * Otherwise, we signal each child via | ||
3971 | * kill PID1 PID2 PID3. | ||
3972 | * Testcases: | ||
3973 | * sh -c 'sleep 1|sleep 1 & kill %1' | ||
3974 | * sh -c 'true|sleep 2 & sleep 1; kill %1' | ||
3975 | * sh -c 'true|sleep 1 & sleep 2; kill %1' | ||
3976 | */ | ||
3977 | n = jp->nprocs; /* can't be 0 (I hope) */ | ||
3978 | if (jp->jobctl) | ||
3979 | n = 1; | ||
3980 | dst = alloca(n * sizeof(int)*4); | ||
3981 | argv[i] = dst; | ||
3982 | for (j = 0; j < n; j++) { | ||
3983 | struct procstat *ps = &jp->ps[j]; | ||
3984 | /* Skip non-running and not-stopped members | ||
3985 | * (i.e. dead members) of the job | ||
3986 | */ | ||
3987 | if (ps->ps_status != -1 && !WIFSTOPPED(ps->ps_status)) | ||
3988 | continue; | ||
3989 | /* | ||
3990 | * kill_main has matching code to expect | ||
3991 | * leading space. Needed to not confuse | ||
3992 | * negative pids with "kill -SIGNAL_NO" syntax | ||
3993 | */ | ||
3994 | dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid); | ||
3995 | } | ||
3996 | *dst = '\0'; | ||
3952 | } | 3997 | } |
3953 | } while (argv[++i]); | 3998 | } while (argv[++i]); |
3954 | } | 3999 | } |
@@ -4046,6 +4091,7 @@ sprint_status(char *s, int status, int sigonly) | |||
4046 | #endif | 4091 | #endif |
4047 | } | 4092 | } |
4048 | st &= 0x7f; | 4093 | st &= 0x7f; |
4094 | //TODO: use bbox's get_signame? strsignal adds ~600 bytes to text+rodata | ||
4049 | col = fmtstr(s, 32, strsignal(st)); | 4095 | col = fmtstr(s, 32, strsignal(st)); |
4050 | if (WCOREDUMP(status)) { | 4096 | if (WCOREDUMP(status)) { |
4051 | col += fmtstr(s + col, 16, " (core dumped)"); | 4097 | col += fmtstr(s + col, 16, " (core dumped)"); |
@@ -4462,8 +4508,9 @@ waitcmd(int argc UNUSED_PARAM, char **argv) | |||
4462 | break; | 4508 | break; |
4463 | job = job->prev_job; | 4509 | job = job->prev_job; |
4464 | } | 4510 | } |
4465 | } else | 4511 | } else { |
4466 | job = getjob(*argv, 0); | 4512 | job = getjob(*argv, 0); |
4513 | } | ||
4467 | /* loop until process terminated or stopped */ | 4514 | /* loop until process terminated or stopped */ |
4468 | while (job->state == JOBRUNNING) | 4515 | while (job->state == JOBRUNNING) |
4469 | blocking_wait_with_raise_on_sig(); | 4516 | blocking_wait_with_raise_on_sig(); |
@@ -4959,7 +5006,7 @@ forkchild(struct job *jp, union node *n, int mode) | |||
4959 | #if JOBS | 5006 | #if JOBS |
4960 | /* do job control only in root shell */ | 5007 | /* do job control only in root shell */ |
4961 | doing_jobctl = 0; | 5008 | doing_jobctl = 0; |
4962 | if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) { | 5009 | if (mode != FORK_NOJOB && jp->jobctl && oldlvl == 0) { |
4963 | pid_t pgrp; | 5010 | pid_t pgrp; |
4964 | 5011 | ||
4965 | if (jp->nprocs == 0) | 5012 | if (jp->nprocs == 0) |
@@ -4985,7 +5032,7 @@ forkchild(struct job *jp, union node *n, int mode) | |||
4985 | ash_msg_and_raise_error("can't open '%s'", bb_dev_null); | 5032 | ash_msg_and_raise_error("can't open '%s'", bb_dev_null); |
4986 | } | 5033 | } |
4987 | } | 5034 | } |
4988 | if (!oldlvl) { | 5035 | if (oldlvl == 0) { |
4989 | if (iflag) { /* why if iflag only? */ | 5036 | if (iflag) { /* why if iflag only? */ |
4990 | setsignal(SIGINT); | 5037 | setsignal(SIGINT); |
4991 | setsignal(SIGTERM); | 5038 | setsignal(SIGTERM); |
@@ -10065,10 +10112,21 @@ preadfd(void) | |||
10065 | if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) | 10112 | if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) |
10066 | nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); | 10113 | nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); |
10067 | else { | 10114 | else { |
10068 | #if ENABLE_FEATURE_TAB_COMPLETION | 10115 | int timeout = -1; |
10116 | # if ENABLE_ASH_IDLE_TIMEOUT | ||
10117 | if (iflag) { | ||
10118 | const char *tmout_var = lookupvar("TMOUT"); | ||
10119 | if (tmout_var) { | ||
10120 | timeout = atoi(tmout_var) * 1000; | ||
10121 | if (timeout <= 0) | ||
10122 | timeout = -1; | ||
10123 | } | ||
10124 | } | ||
10125 | # endif | ||
10126 | # if ENABLE_FEATURE_TAB_COMPLETION | ||
10069 | line_input_state->path_lookup = pathval(); | 10127 | line_input_state->path_lookup = pathval(); |
10070 | #endif | 10128 | # endif |
10071 | nr = read_line_input(cmdedit_prompt, buf, IBUFSIZ, line_input_state); | 10129 | nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ, timeout); |
10072 | if (nr == 0) { | 10130 | if (nr == 0) { |
10073 | /* Ctrl+C pressed */ | 10131 | /* Ctrl+C pressed */ |
10074 | if (trap[SIGINT]) { | 10132 | if (trap[SIGINT]) { |
@@ -10079,17 +10137,24 @@ preadfd(void) | |||
10079 | } | 10137 | } |
10080 | goto retry; | 10138 | goto retry; |
10081 | } | 10139 | } |
10082 | if (nr < 0 && errno == 0) { | 10140 | if (nr < 0) { |
10083 | /* Ctrl+D pressed */ | 10141 | if (errno == 0) { |
10084 | nr = 0; | 10142 | /* Ctrl+D pressed */ |
10143 | nr = 0; | ||
10144 | } | ||
10145 | # if ENABLE_ASH_IDLE_TIMEOUT | ||
10146 | else if (errno == EAGAIN && timeout > 0) { | ||
10147 | printf("\007timed out waiting for input: auto-logout\n"); | ||
10148 | exitshell(); | ||
10149 | } | ||
10150 | # endif | ||
10085 | } | 10151 | } |
10086 | } | 10152 | } |
10087 | #else | 10153 | #else |
10088 | nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); | 10154 | nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); |
10089 | #endif | 10155 | #endif |
10090 | 10156 | ||
10091 | #if 0 | 10157 | #if 0 /* disabled: nonblock_safe_read() handles this problem */ |
10092 | /* nonblock_safe_read() handles this problem */ | ||
10093 | if (nr < 0) { | 10158 | if (nr < 0) { |
10094 | if (parsefile->fd == 0 && errno == EWOULDBLOCK) { | 10159 | if (parsefile->fd == 0 && errno == EWOULDBLOCK) { |
10095 | int flags = fcntl(0, F_GETFL); | 10160 | int flags = fcntl(0, F_GETFL); |
@@ -12574,9 +12639,7 @@ cmdloop(int top) | |||
12574 | inter = 0; | 12639 | inter = 0; |
12575 | if (iflag && top) { | 12640 | if (iflag && top) { |
12576 | inter++; | 12641 | inter++; |
12577 | #if ENABLE_ASH_MAIL | ||
12578 | chkmail(); | 12642 | chkmail(); |
12579 | #endif | ||
12580 | } | 12643 | } |
12581 | n = parsecmd(inter); | 12644 | n = parsecmd(inter); |
12582 | #if DEBUG | 12645 | #if DEBUG |
@@ -13346,7 +13409,6 @@ ulimitcmd(int argc UNUSED_PARAM, char **argv) | |||
13346 | /* | 13409 | /* |
13347 | * Called to exit the shell. | 13410 | * Called to exit the shell. |
13348 | */ | 13411 | */ |
13349 | static void exitshell(void) NORETURN; | ||
13350 | static void | 13412 | static void |
13351 | exitshell(void) | 13413 | exitshell(void) |
13352 | { | 13414 | { |
diff --git a/shell/ash_test/ash-misc/echo_write_error.right b/shell/ash_test/ash-misc/echo_write_error.right new file mode 100644 index 000000000..3e91a13d3 --- /dev/null +++ b/shell/ash_test/ash-misc/echo_write_error.right | |||
@@ -0,0 +1,2 @@ | |||
1 | ash: write error: Broken pipe | ||
2 | Ok: 1 | ||
diff --git a/shell/ash_test/ash-misc/echo_write_error.tests b/shell/ash_test/ash-misc/echo_write_error.tests new file mode 100644 index 000000000..0a40c9ff7 --- /dev/null +++ b/shell/ash_test/ash-misc/echo_write_error.tests | |||
@@ -0,0 +1,7 @@ | |||
1 | trap "" PIPE | ||
2 | |||
3 | { | ||
4 | sleep 1 | ||
5 | echo Cant write this - get EPIPE | ||
6 | echo Ok: $? >&2 | ||
7 | } | { true; } | ||
diff --git a/shell/ash_test/ash-redir/redir.right b/shell/ash_test/ash-redir/redir.right index 2a02d41ce..c1a6e729a 100644 --- a/shell/ash_test/ash-redir/redir.right +++ b/shell/ash_test/ash-redir/redir.right | |||
@@ -1 +1,2 @@ | |||
1 | ash: write error: Bad file descriptor | ||
1 | TEST | 2 | TEST |
diff --git a/shell/ash_test/ash-signals/sigint1.right b/shell/ash_test/ash-signals/sigint1.right new file mode 100644 index 000000000..a9094b056 --- /dev/null +++ b/shell/ash_test/ash-signals/sigint1.right | |||
@@ -0,0 +1 @@ | |||
Sending SIGINT to main shell PID | |||
diff --git a/shell/ash_test/ash-signals/sigint1.tests b/shell/ash_test/ash-signals/sigint1.tests new file mode 100755 index 000000000..3d483d32a --- /dev/null +++ b/shell/ash_test/ash-signals/sigint1.tests | |||
@@ -0,0 +1,41 @@ | |||
1 | # What should happen if non-interactive shell gets SIGINT? | ||
2 | |||
3 | (sleep 1; echo Sending SIGINT to main shell PID; exec kill -INT $$) & | ||
4 | |||
5 | # We create a child which exits with 0 even on SIGINT | ||
6 | # (The complex command is necessary only if SIGINT is generated by ^C, | ||
7 | # in this testcase even bare "sleep 2" would do because | ||
8 | # in the testcase we don't send SIGINT *to the child*...) | ||
9 | $THIS_SH -c 'trap "exit 0" SIGINT; sleep 2' | ||
10 | |||
11 | # In one second, we (main shell) get SIGINT here. | ||
12 | # The question is whether we should, or should not, exit. | ||
13 | |||
14 | # bash will not stop here. It will execute next command(s). | ||
15 | |||
16 | # The rationale for this is described here: | ||
17 | # http://www.cons.org/cracauer/sigint.html | ||
18 | # | ||
19 | # Basically, bash will not exit on SIGINT immediately if it waits | ||
20 | # for a child. It will wait for the child to exit. | ||
21 | # If child exits NOT by dying on SIGINT, then bash will not exit. | ||
22 | # | ||
23 | # The idea is that the following script: | ||
24 | # | emacs file.txt | ||
25 | # | more cmds | ||
26 | # User may use ^C to interrupt editor's ops like search. But then | ||
27 | # emacs exits normally. User expects that script doesn't stop. | ||
28 | # | ||
29 | # This is a nice idea, but detecting "did process really exit | ||
30 | # with SIGINT?" is racy. Consider: | ||
31 | # | bash -c 'while true; do /bin/true; done' | ||
32 | # When ^C is pressed while bash waits for /bin/true to exit, | ||
33 | # it may happen that /bin/true exits with exitcode 0 before | ||
34 | # ^C is delivered to it as SIGINT. bash will see SIGINT, then | ||
35 | # it will see that child exited with 0, and bash will NOT EXIT. | ||
36 | |||
37 | # Therefore we do not implement bash behavior. | ||
38 | # I'd say that emacs need to put itself into a separate pgrp | ||
39 | # to isolate shell from getting stray SIGINTs from ^C. | ||
40 | |||
41 | echo Next command after SIGINT was executed | ||
diff --git a/shell/hush.c b/shell/hush.c index 1709fd9d1..4d9e5f8c7 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -1418,6 +1418,7 @@ static void sigexit(int sig) | |||
1418 | static void hush_exit(int exitcode) NORETURN; | 1418 | static void hush_exit(int exitcode) NORETURN; |
1419 | static void hush_exit(int exitcode) | 1419 | static void hush_exit(int exitcode) |
1420 | { | 1420 | { |
1421 | fflush_all(); | ||
1421 | if (G.exiting <= 0 && G.traps && G.traps[0] && G.traps[0][0]) { | 1422 | if (G.exiting <= 0 && G.traps && G.traps[0] && G.traps[0][0]) { |
1422 | /* Prevent recursion: | 1423 | /* Prevent recursion: |
1423 | * trap "echo Hi; exit" EXIT; exit | 1424 | * trap "echo Hi; exit" EXIT; exit |
@@ -1901,7 +1902,7 @@ static void get_user_input(struct in_str *i) | |||
1901 | G.flag_SIGINT = 0; | 1902 | G.flag_SIGINT = 0; |
1902 | /* buglet: SIGINT will not make new prompt to appear _at once_, | 1903 | /* buglet: SIGINT will not make new prompt to appear _at once_, |
1903 | * only after <Enter>. (^C will work) */ | 1904 | * only after <Enter>. (^C will work) */ |
1904 | r = read_line_input(prompt_str, G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, G.line_input_state); | 1905 | r = read_line_input(G.line_input_state, prompt_str, G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, /*timeout*/ -1); |
1905 | /* catch *SIGINT* etc (^C is handled by read_line_input) */ | 1906 | /* catch *SIGINT* etc (^C is handled by read_line_input) */ |
1906 | check_and_run_traps(0); | 1907 | check_and_run_traps(0); |
1907 | } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */ | 1908 | } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */ |
@@ -6105,10 +6106,13 @@ static void exec_builtin(char ***to_free, | |||
6105 | char **argv) | 6106 | char **argv) |
6106 | { | 6107 | { |
6107 | #if BB_MMU | 6108 | #if BB_MMU |
6108 | int rcode = x->b_function(argv); | 6109 | int rcode; |
6110 | fflush_all(); | ||
6111 | rcode = x->b_function(argv); | ||
6109 | fflush_all(); | 6112 | fflush_all(); |
6110 | _exit(rcode); | 6113 | _exit(rcode); |
6111 | #else | 6114 | #else |
6115 | fflush_all(); | ||
6112 | /* On NOMMU, we must never block! | 6116 | /* On NOMMU, we must never block! |
6113 | * Example: { sleep 99 | read line; } & echo Ok | 6117 | * Example: { sleep 99 | read line; } & echo Ok |
6114 | */ | 6118 | */ |
@@ -6500,13 +6504,15 @@ static int checkjobs(struct pipe *fg_pipe) | |||
6500 | fg_pipe->alive_cmds--; | 6504 | fg_pipe->alive_cmds--; |
6501 | ex = WEXITSTATUS(status); | 6505 | ex = WEXITSTATUS(status); |
6502 | /* bash prints killer signal's name for *last* | 6506 | /* bash prints killer signal's name for *last* |
6503 | * process in pipe (prints just newline for SIGINT). | 6507 | * process in pipe (prints just newline for SIGINT/SIGPIPE). |
6504 | * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT) | 6508 | * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT) |
6505 | */ | 6509 | */ |
6506 | if (WIFSIGNALED(status)) { | 6510 | if (WIFSIGNALED(status)) { |
6507 | int sig = WTERMSIG(status); | 6511 | int sig = WTERMSIG(status); |
6508 | if (i == fg_pipe->num_cmds-1) | 6512 | if (i == fg_pipe->num_cmds-1) |
6509 | printf("%s\n", sig == SIGINT ? "" : get_signame(sig)); | 6513 | /* TODO: use strsignal() instead for bash compat? but that's bloat... */ |
6514 | printf("%s\n", sig == SIGINT || sig == SIGPIPE ? "" : get_signame(sig)); | ||
6515 | /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */ | ||
6510 | /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here? | 6516 | /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here? |
6511 | * Maybe we need to use sig | 128? */ | 6517 | * Maybe we need to use sig | 128? */ |
6512 | ex = sig + 128; | 6518 | ex = sig + 128; |
@@ -6615,7 +6621,7 @@ static int checkjobs_and_fg_shell(struct pipe *fg_pipe) | |||
6615 | * cmd ; ... { list } ; ... | 6621 | * cmd ; ... { list } ; ... |
6616 | * cmd && ... { list } && ... | 6622 | * cmd && ... { list } && ... |
6617 | * cmd || ... { list } || ... | 6623 | * cmd || ... { list } || ... |
6618 | * If it is, then we can run cmd as a builtin, NOFORK [do we do this?], | 6624 | * If it is, then we can run cmd as a builtin, NOFORK, |
6619 | * or (if SH_STANDALONE) an applet, and we can run the { list } | 6625 | * or (if SH_STANDALONE) an applet, and we can run the { list } |
6620 | * with run_list. If it isn't one of these, we fork and exec cmd. | 6626 | * with run_list. If it isn't one of these, we fork and exec cmd. |
6621 | * | 6627 | * |
@@ -6797,13 +6803,12 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
6797 | } | 6803 | } |
6798 | 6804 | ||
6799 | /* Expand the rest into (possibly) many strings each */ | 6805 | /* Expand the rest into (possibly) many strings each */ |
6800 | if (0) {} | ||
6801 | #if ENABLE_HUSH_BASH_COMPAT | 6806 | #if ENABLE_HUSH_BASH_COMPAT |
6802 | else if (command->cmd_type == CMD_SINGLEWORD_NOGLOB) { | 6807 | if (command->cmd_type == CMD_SINGLEWORD_NOGLOB) { |
6803 | argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt); | 6808 | argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt); |
6804 | } | 6809 | } else |
6805 | #endif | 6810 | #endif |
6806 | else { | 6811 | { |
6807 | argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt); | 6812 | argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt); |
6808 | } | 6813 | } |
6809 | 6814 | ||
@@ -6833,6 +6838,7 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
6833 | if (!funcp) { | 6838 | if (!funcp) { |
6834 | debug_printf_exec(": builtin '%s' '%s'...\n", | 6839 | debug_printf_exec(": builtin '%s' '%s'...\n", |
6835 | x->b_cmd, argv_expanded[1]); | 6840 | x->b_cmd, argv_expanded[1]); |
6841 | fflush_all(); | ||
6836 | rcode = x->b_function(argv_expanded) & 0xff; | 6842 | rcode = x->b_function(argv_expanded) & 0xff; |
6837 | fflush_all(); | 6843 | fflush_all(); |
6838 | } | 6844 | } |
@@ -6865,7 +6871,7 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
6865 | return rcode; | 6871 | return rcode; |
6866 | } | 6872 | } |
6867 | 6873 | ||
6868 | if (ENABLE_FEATURE_SH_STANDALONE) { | 6874 | if (ENABLE_FEATURE_SH_NOFORK) { |
6869 | int n = find_applet_by_name(argv_expanded[0]); | 6875 | int n = find_applet_by_name(argv_expanded[0]); |
6870 | if (n >= 0 && APPLET_IS_NOFORK(n)) { | 6876 | if (n >= 0 && APPLET_IS_NOFORK(n)) { |
6871 | rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, squirrel, argv_expanded); | 6877 | rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, squirrel, argv_expanded); |
@@ -7642,6 +7648,7 @@ int hush_main(int argc, char **argv) | |||
7642 | G.global_argc -= builtin_argc; /* skip [BARGV...] "" */ | 7648 | G.global_argc -= builtin_argc; /* skip [BARGV...] "" */ |
7643 | G.global_argv += builtin_argc; | 7649 | G.global_argv += builtin_argc; |
7644 | G.global_argv[-1] = NULL; /* replace "" */ | 7650 | G.global_argv[-1] = NULL; /* replace "" */ |
7651 | fflush_all(); | ||
7645 | G.last_exitcode = x->b_function(argv + optind - 1); | 7652 | G.last_exitcode = x->b_function(argv + optind - 1); |
7646 | } | 7653 | } |
7647 | goto final_return; | 7654 | goto final_return; |
diff --git a/shell/hush_test/hush-misc/echo_write_error.right b/shell/hush_test/hush-misc/echo_write_error.right new file mode 100644 index 000000000..ddcad4363 --- /dev/null +++ b/shell/hush_test/hush-misc/echo_write_error.right | |||
@@ -0,0 +1,2 @@ | |||
1 | hush: write error: Broken pipe | ||
2 | Ok: 1 | ||
diff --git a/shell/hush_test/hush-misc/echo_write_error.tests b/shell/hush_test/hush-misc/echo_write_error.tests new file mode 100755 index 000000000..0a40c9ff7 --- /dev/null +++ b/shell/hush_test/hush-misc/echo_write_error.tests | |||
@@ -0,0 +1,7 @@ | |||
1 | trap "" PIPE | ||
2 | |||
3 | { | ||
4 | sleep 1 | ||
5 | echo Cant write this - get EPIPE | ||
6 | echo Ok: $? >&2 | ||
7 | } | { true; } | ||
diff --git a/shell/hush_test/hush-misc/sigint1.right b/shell/hush_test/hush-misc/sigint1.right new file mode 100644 index 000000000..a9094b056 --- /dev/null +++ b/shell/hush_test/hush-misc/sigint1.right | |||
@@ -0,0 +1 @@ | |||
Sending SIGINT to main shell PID | |||
diff --git a/shell/hush_test/hush-misc/sigint1.tests b/shell/hush_test/hush-misc/sigint1.tests new file mode 100755 index 000000000..3d483d32a --- /dev/null +++ b/shell/hush_test/hush-misc/sigint1.tests | |||
@@ -0,0 +1,41 @@ | |||
1 | # What should happen if non-interactive shell gets SIGINT? | ||
2 | |||
3 | (sleep 1; echo Sending SIGINT to main shell PID; exec kill -INT $$) & | ||
4 | |||
5 | # We create a child which exits with 0 even on SIGINT | ||
6 | # (The complex command is necessary only if SIGINT is generated by ^C, | ||
7 | # in this testcase even bare "sleep 2" would do because | ||
8 | # in the testcase we don't send SIGINT *to the child*...) | ||
9 | $THIS_SH -c 'trap "exit 0" SIGINT; sleep 2' | ||
10 | |||
11 | # In one second, we (main shell) get SIGINT here. | ||
12 | # The question is whether we should, or should not, exit. | ||
13 | |||
14 | # bash will not stop here. It will execute next command(s). | ||
15 | |||
16 | # The rationale for this is described here: | ||
17 | # http://www.cons.org/cracauer/sigint.html | ||
18 | # | ||
19 | # Basically, bash will not exit on SIGINT immediately if it waits | ||
20 | # for a child. It will wait for the child to exit. | ||
21 | # If child exits NOT by dying on SIGINT, then bash will not exit. | ||
22 | # | ||
23 | # The idea is that the following script: | ||
24 | # | emacs file.txt | ||
25 | # | more cmds | ||
26 | # User may use ^C to interrupt editor's ops like search. But then | ||
27 | # emacs exits normally. User expects that script doesn't stop. | ||
28 | # | ||
29 | # This is a nice idea, but detecting "did process really exit | ||
30 | # with SIGINT?" is racy. Consider: | ||
31 | # | bash -c 'while true; do /bin/true; done' | ||
32 | # When ^C is pressed while bash waits for /bin/true to exit, | ||
33 | # it may happen that /bin/true exits with exitcode 0 before | ||
34 | # ^C is delivered to it as SIGINT. bash will see SIGINT, then | ||
35 | # it will see that child exited with 0, and bash will NOT EXIT. | ||
36 | |||
37 | # Therefore we do not implement bash behavior. | ||
38 | # I'd say that emacs need to put itself into a separate pgrp | ||
39 | # to isolate shell from getting stray SIGINTs from ^C. | ||
40 | |||
41 | echo Next command after SIGINT was executed | ||
diff --git a/shell/shell_common.c b/shell/shell_common.c index 2d5c0db0f..c7b5b6122 100644 --- a/shell/shell_common.c +++ b/shell/shell_common.c | |||
@@ -375,9 +375,9 @@ shell_builtin_ulimit(char **argv) | |||
375 | #endif | 375 | #endif |
376 | /* optarg = NULL; opterr = 0; optopt = 0; - do we need this?? */ | 376 | /* optarg = NULL; opterr = 0; optopt = 0; - do we need this?? */ |
377 | 377 | ||
378 | argc = 1; | 378 | argc = 1; |
379 | while (argv[argc]) | 379 | while (argv[argc]) |
380 | argc++; | 380 | argc++; |
381 | 381 | ||
382 | opts = 0; | 382 | opts = 0; |
383 | while (1) { | 383 | while (1) { |