diff options
-rw-r--r-- | shell/ash.c | 83 |
1 files changed, 70 insertions, 13 deletions
diff --git a/shell/ash.c b/shell/ash.c index b060d227a..1ef02b8be 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -295,6 +295,7 @@ struct globals_misc { | |||
295 | 295 | ||
296 | volatile int suppress_int; /* counter */ | 296 | volatile int suppress_int; /* counter */ |
297 | volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */ | 297 | volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */ |
298 | volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */ | ||
298 | /* last pending signal */ | 299 | /* last pending signal */ |
299 | volatile /*sig_atomic_t*/ smallint pending_sig; | 300 | volatile /*sig_atomic_t*/ smallint pending_sig; |
300 | smallint exception_type; /* kind of exception (0..5) */ | 301 | smallint exception_type; /* kind of exception (0..5) */ |
@@ -370,6 +371,7 @@ extern struct globals_misc *const ash_ptr_to_globals_misc; | |||
370 | #define exception_type (G_misc.exception_type ) | 371 | #define exception_type (G_misc.exception_type ) |
371 | #define suppress_int (G_misc.suppress_int ) | 372 | #define suppress_int (G_misc.suppress_int ) |
372 | #define pending_int (G_misc.pending_int ) | 373 | #define pending_int (G_misc.pending_int ) |
374 | #define got_sigchld (G_misc.got_sigchld ) | ||
373 | #define pending_sig (G_misc.pending_sig ) | 375 | #define pending_sig (G_misc.pending_sig ) |
374 | #define isloginsh (G_misc.isloginsh ) | 376 | #define isloginsh (G_misc.isloginsh ) |
375 | #define nullstr (G_misc.nullstr ) | 377 | #define nullstr (G_misc.nullstr ) |
@@ -3388,7 +3390,14 @@ ignoresig(int signo) | |||
3388 | static void | 3390 | static void |
3389 | signal_handler(int signo) | 3391 | signal_handler(int signo) |
3390 | { | 3392 | { |
3393 | if (signo == SIGCHLD) { | ||
3394 | got_sigchld = 1; | ||
3395 | if (!trap[SIGCHLD]) | ||
3396 | return; | ||
3397 | } | ||
3398 | |||
3391 | gotsig[signo - 1] = 1; | 3399 | gotsig[signo - 1] = 1; |
3400 | pending_sig = signo; | ||
3392 | 3401 | ||
3393 | if (signo == SIGINT && !trap[SIGINT]) { | 3402 | if (signo == SIGINT && !trap[SIGINT]) { |
3394 | if (!suppress_int) { | 3403 | if (!suppress_int) { |
@@ -3396,8 +3405,6 @@ signal_handler(int signo) | |||
3396 | raise_interrupt(); /* does not return */ | 3405 | raise_interrupt(); /* does not return */ |
3397 | } | 3406 | } |
3398 | pending_int = 1; | 3407 | pending_int = 1; |
3399 | } else { | ||
3400 | pending_sig = signo; | ||
3401 | } | 3408 | } |
3402 | } | 3409 | } |
3403 | 3410 | ||
@@ -3455,6 +3462,9 @@ setsignal(int signo) | |||
3455 | //whereas we have to restore it to what shell got on entry | 3462 | //whereas we have to restore it to what shell got on entry |
3456 | //from the parent. See comment above | 3463 | //from the parent. See comment above |
3457 | 3464 | ||
3465 | if (signo == SIGCHLD) | ||
3466 | new_act = S_CATCH; | ||
3467 | |||
3458 | t = &sigmode[signo - 1]; | 3468 | t = &sigmode[signo - 1]; |
3459 | cur_act = *t; | 3469 | cur_act = *t; |
3460 | if (cur_act == 0) { | 3470 | if (cur_act == 0) { |
@@ -3507,6 +3517,7 @@ setsignal(int signo) | |||
3507 | /* mode flags for dowait */ | 3517 | /* mode flags for dowait */ |
3508 | #define DOWAIT_NONBLOCK 0 | 3518 | #define DOWAIT_NONBLOCK 0 |
3509 | #define DOWAIT_BLOCK 1 | 3519 | #define DOWAIT_BLOCK 1 |
3520 | #define DOWAIT_BLOCK_OR_SIG 2 | ||
3510 | 3521 | ||
3511 | #if JOBS | 3522 | #if JOBS |
3512 | /* pgrp of shell on invocation */ | 3523 | /* pgrp of shell on invocation */ |
@@ -3928,32 +3939,76 @@ sprint_status48(char *s, int status, int sigonly) | |||
3928 | } | 3939 | } |
3929 | 3940 | ||
3930 | static int | 3941 | static int |
3942 | wait_block_or_sig(int *status, int wait_flags) | ||
3943 | { | ||
3944 | sigset_t mask; | ||
3945 | int pid; | ||
3946 | |||
3947 | do { | ||
3948 | /* Poll all children for changes in their state */ | ||
3949 | got_sigchld = 0; | ||
3950 | pid = waitpid(-1, status, wait_flags | WNOHANG); | ||
3951 | if (pid != 0) | ||
3952 | break; /* Error (e.g. EINTR) or pid */ | ||
3953 | |||
3954 | /* No child is ready. Sleep until interesting signal is received */ | ||
3955 | sigfillset(&mask); | ||
3956 | sigprocmask(SIG_SETMASK, &mask, &mask); | ||
3957 | while (!got_sigchld && !pending_sig) | ||
3958 | sigsuspend(&mask); | ||
3959 | sigprocmask(SIG_SETMASK, &mask, NULL); | ||
3960 | |||
3961 | /* If it was SIGCHLD, poll children again */ | ||
3962 | } while (got_sigchld); | ||
3963 | |||
3964 | return pid; | ||
3965 | } | ||
3966 | |||
3967 | |||
3968 | static int | ||
3931 | dowait(int block, struct job *job) | 3969 | dowait(int block, struct job *job) |
3932 | { | 3970 | { |
3933 | int wait_flags; | 3971 | int wait_flags; |
3934 | int pid; | 3972 | int pid; |
3935 | int status; | 3973 | int status; |
3936 | struct job *jp; | 3974 | struct job *jp; |
3937 | struct job *thisjob; | 3975 | struct job *thisjob = NULL; |
3938 | 3976 | ||
3939 | TRACE(("dowait(0x%x) called\n", block)); | 3977 | TRACE(("dowait(0x%x) called\n", block)); |
3940 | 3978 | ||
3941 | /* Do a wait system call. If job control is compiled in, we accept | ||
3942 | * stopped processes. | ||
3943 | * NB: _not_ safe_waitpid, we need to detect EINTR. | ||
3944 | */ | ||
3945 | wait_flags = 0; | 3979 | wait_flags = 0; |
3946 | if (block == DOWAIT_NONBLOCK) | 3980 | if (block == DOWAIT_NONBLOCK) |
3947 | wait_flags = WNOHANG; | 3981 | wait_flags = WNOHANG; |
3982 | /* If job control is compiled in, we accept stopped processes too. */ | ||
3948 | if (doing_jobctl) | 3983 | if (doing_jobctl) |
3949 | wait_flags |= WUNTRACED; | 3984 | wait_flags |= WUNTRACED; |
3950 | pid = waitpid(-1, &status, wait_flags); | 3985 | |
3986 | /* It's wrong to call waitpid() outside of INT_OFF region: | ||
3987 | * signal can arrive just after syscall return and handler can | ||
3988 | * longjmp away, losing stop/exit notification processing. | ||
3989 | * Thus, for "jobs" builtin, and for waiting for a fg job, | ||
3990 | * we call waitpid() (blocking or non-blocking) inside INT_OFF. | ||
3991 | * | ||
3992 | * However, for "wait" builtin it is wrong to simply call waitpid() | ||
3993 | * in INT_OFF region: "wait" needs to wait for any running job | ||
3994 | * to change state, but should exit on any trap too. | ||
3995 | * In INT_OFF region, a signal just before syscall entry can set | ||
3996 | * pending_sig valiables, but we can't check them, and we would | ||
3997 | * either enter a sleeping waitpid() (BUG), or need to busy-loop. | ||
3998 | * Because of this, we run inside INT_OFF, but use a special routine | ||
3999 | * which combines waitpid() and sigsuspend(). | ||
4000 | */ | ||
4001 | INT_OFF; | ||
4002 | if (block == DOWAIT_BLOCK_OR_SIG) | ||
4003 | pid = wait_block_or_sig(&status, wait_flags); | ||
4004 | else | ||
4005 | /* NB: _not_ safe_waitpid, we need to detect EINTR. */ | ||
4006 | pid = waitpid(-1, &status, wait_flags); | ||
3951 | TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n", | 4007 | TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n", |
3952 | pid, status, errno, strerror(errno))); | 4008 | pid, status, errno, strerror(errno))); |
3953 | if (pid <= 0) | 4009 | if (pid <= 0) |
3954 | return pid; | 4010 | goto out; |
3955 | 4011 | ||
3956 | INT_OFF; | ||
3957 | thisjob = NULL; | 4012 | thisjob = NULL; |
3958 | for (jp = curjob; jp; jp = jp->prev_job) { | 4013 | for (jp = curjob; jp; jp = jp->prev_job) { |
3959 | int jobstate; | 4014 | int jobstate; |
@@ -4217,7 +4272,7 @@ waitcmd(int argc UNUSED_PARAM, char **argv) | |||
4217 | * with an exit status greater than 128, immediately after which | 4272 | * with an exit status greater than 128, immediately after which |
4218 | * the trap is executed." | 4273 | * the trap is executed." |
4219 | */ | 4274 | */ |
4220 | dowait(DOWAIT_BLOCK, NULL); ///DOWAIT_WAITCMD | 4275 | dowait(DOWAIT_BLOCK_OR_SIG, NULL); |
4221 | /* if child sends us a signal *and immediately exits*, | 4276 | /* if child sends us a signal *and immediately exits*, |
4222 | * dowait() returns pid > 0. Check this case, | 4277 | * dowait() returns pid > 0. Check this case, |
4223 | * not "if (dowait() < 0)"! | 4278 | * not "if (dowait() < 0)"! |
@@ -4244,7 +4299,7 @@ waitcmd(int argc UNUSED_PARAM, char **argv) | |||
4244 | } | 4299 | } |
4245 | /* loop until process terminated or stopped */ | 4300 | /* loop until process terminated or stopped */ |
4246 | while (job->state == JOBRUNNING) { | 4301 | while (job->state == JOBRUNNING) { |
4247 | dowait(DOWAIT_BLOCK, NULL); ///DOWAIT_WAITCMD | 4302 | dowait(DOWAIT_BLOCK_OR_SIG, NULL); |
4248 | if (pending_sig) | 4303 | if (pending_sig) |
4249 | goto sigout; | 4304 | goto sigout; |
4250 | } | 4305 | } |
@@ -13113,7 +13168,9 @@ init(void) | |||
13113 | /* we will never free this */ | 13168 | /* we will never free this */ |
13114 | basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ); | 13169 | basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ); |
13115 | 13170 | ||
13116 | signal(SIGCHLD, SIG_DFL); | 13171 | sigmode[SIGCHLD - 1] = S_DFL; |
13172 | setsignal(SIGCHLD); | ||
13173 | |||
13117 | /* bash re-enables SIGHUP which is SIG_IGNed on entry. | 13174 | /* bash re-enables SIGHUP which is SIG_IGNed on entry. |
13118 | * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$" | 13175 | * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$" |
13119 | */ | 13176 | */ |