diff options
-rw-r--r-- | shell/ash.c | 57 |
1 files changed, 31 insertions, 26 deletions
diff --git a/shell/ash.c b/shell/ash.c index fe11245..fc1b5d9 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -296,8 +296,7 @@ struct globals_misc { volatile int suppress_int; /* counter */ volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */ volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */ - /* last pending signal */ - volatile /*sig_atomic_t*/ smallint pending_sig; + volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */ smallint exception_type; /* kind of exception (0..5) */ /* exceptions */ #define EXINT 0 /* SIGINT received */ @@ -3515,11 +3514,6 @@ setsignal(int signo) #define CUR_RUNNING 1 #define CUR_STOPPED 0 -/* mode flags for dowait */ -#define DOWAIT_NONBLOCK 0 -#define DOWAIT_BLOCK 1 -#define DOWAIT_BLOCK_OR_SIG 2 - #if JOBS /* pgrp of shell on invocation */ static int initialpgrp; //references:2 @@ -3940,24 +3934,30 @@ sprint_status48(char *s, int status, int sigonly) } static int -wait_block_or_sig(int *status, int wait_flags) +wait_block_or_sig(int *status) { - sigset_t mask; int pid; do { /* Poll all children for changes in their state */ got_sigchld = 0; - pid = waitpid(-1, status, wait_flags | WNOHANG); + /* if job control is active, accept stopped processes too */ + pid = waitpid(-1, status, doing_jobctl ? (WNOHANG|WUNTRACED) : WNOHANG); if (pid != 0) - break; /* Error (e.g. EINTR) or pid */ + break; /* Error (e.g. EINTR, ECHILD) or pid */ - /* No child is ready. Sleep until interesting signal is received */ + /* Children exist, but none are ready. Sleep until interesting signal */ +#if 0 /* dash does this */ + sigset_t mask; sigfillset(&mask); sigprocmask(SIG_SETMASK, &mask, &mask); while (!got_sigchld && !pending_sig) sigsuspend(&mask); sigprocmask(SIG_SETMASK, &mask, NULL); +#else + while (!got_sigchld && !pending_sig) + pause(); +#endif /* If it was SIGCHLD, poll children again */ } while (got_sigchld); @@ -3965,11 +3965,13 @@ wait_block_or_sig(int *status, int wait_flags) return pid; } +#define DOWAIT_NONBLOCK 0 +#define DOWAIT_BLOCK 1 +#define DOWAIT_BLOCK_OR_SIG 2 static int dowait(int block, struct job *job) { - int wait_flags; int pid; int status; struct job *jp; @@ -3977,13 +3979,6 @@ dowait(int block, struct job *job) TRACE(("dowait(0x%x) called\n", block)); - wait_flags = 0; - if (block == DOWAIT_NONBLOCK) - wait_flags = WNOHANG; - /* If job control is compiled in, we accept stopped processes too. */ - if (doing_jobctl) - wait_flags |= WUNTRACED; - /* It's wrong to call waitpid() outside of INT_OFF region: * signal can arrive just after syscall return and handler can * longjmp away, losing stop/exit notification processing. @@ -3994,17 +3989,27 @@ dowait(int block, struct job *job) * in INT_OFF region: "wait" needs to wait for any running job * to change state, but should exit on any trap too. * In INT_OFF region, a signal just before syscall entry can set - * pending_sig valiables, but we can't check them, and we would + * pending_sig variables, but we can't check them, and we would * either enter a sleeping waitpid() (BUG), or need to busy-loop. + * * Because of this, we run inside INT_OFF, but use a special routine - * which combines waitpid() and sigsuspend(). + * which combines waitpid() and pause(). + * This is the reason why we need to have a handler for SIGCHLD: + * SIG_DFL handler does not wake pause(). */ INT_OFF; - if (block == DOWAIT_BLOCK_OR_SIG) - pid = wait_block_or_sig(&status, wait_flags); - else - /* NB: _not_ safe_waitpid, we need to detect EINTR. */ + if (block == DOWAIT_BLOCK_OR_SIG) { + pid = wait_block_or_sig(&status); + } else { + int wait_flags = 0; + if (block == DOWAIT_NONBLOCK) + wait_flags = WNOHANG; + /* if job control is active, accept stopped processes too */ + if (doing_jobctl) + wait_flags |= WUNTRACED; + /* NB: _not_ safe_waitpid, we need to detect EINTR */ pid = waitpid(-1, &status, wait_flags); + } TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n", pid, status, errno, strerror(errno))); if (pid <= 0) |