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) { |
