diff options
-rw-r--r-- | ChangeLog | 16 | ||||
-rw-r--r-- | Doc/Zsh/expn.yo | 5 | ||||
-rw-r--r-- | Src/exec.c | 2 | ||||
-rw-r--r-- | Src/jobs.c | 59 | ||||
-rw-r--r-- | Src/signals.c | 229 |
5 files changed, 201 insertions, 110 deletions
diff --git a/ChangeLog b/ChangeLog index 46fa559db..9291ee3d1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2010-08-22 Peter Stephenson <p.w.stephenson@ntlworld.com> + + * 28179: Src/jobs.c, Src/signals.c: use WIFCONTINUED() and + WCONTINUE by exporting child handler to a function. + + * users/15314: Doc/Zsh/expn.yo: redescribe process substitution. + + * users/15310 (bits applying to process substitution with + redirection): Src/exec.c: make redirection process substitution + attach to the appropriate process group. + + * users/15200: pass foreground signals on to process + substitutions in current shell + 2010-08-22 Barton E. Schaefer <schaefer@zsh.org> * 28186: Completion/Base/Utility/_multi_parts: replace a single @@ -13550,5 +13564,5 @@ ***************************************************** * This is used by the shell to define $ZSH_PATCHLEVEL -* $Revision: 1.5061 $ +* $Revision: 1.5062 $ ***************************************************** diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo index ecb16c0d5..cacbfc4d3 100644 --- a/Doc/Zsh/expn.yo +++ b/Doc/Zsh/expn.yo @@ -390,7 +390,8 @@ Process substitutions may be used following redirection operators; in this case, the substitution must appear with no trailing string. In the case of the tt(<) or tt(>) forms, the shell runs the commands in -var(list) asynchronously. If the system supports the tt(/dev/fd) +var(list) as a subprocess of the job executing the shell command line. +If the system supports the tt(/dev/fd) mechanism, the command argument is the name of the device file corresponding to a file descriptor; otherwise, if the system supports named pipes (FIFOs), the command argument will be a named pipe. If the form with @@ -456,7 +457,7 @@ version of the example above: example(tt(paste <LPAR()cut -f1) var(file1)tt(RPAR() <LPAR()cut -f3) var(file2)tt(RPAR()) tt(> >LPAR())var(process)tt(RPAR())) (note that no tt(MULTIOS) are involved), var(process) will be run -asynchronously. The workaround is: +asynchronously as far as the parent shell is concerned. The workaround is: example(tt({ paste <LPAR()cut -f1) var(file1)tt(RPAR() <LPAR()cut -f3) var(file2)tt(RPAR() }) tt(> >LPAR())var(process)tt(RPAR())) diff --git a/Src/exec.c b/Src/exec.c index 73b4c01a8..3ab4aa90b 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -3905,7 +3905,7 @@ getpipe(char *cmd, int nullexec) addproc(pid, NULL, 1, &bgtime); return pipes[!out]; } - entersubsh(ESUB_ASYNC|ESUB_PGRP); + entersubsh(ESUB_PGRP); redup(pipes[out], out); closem(FDT_UNUSED); /* this closes pipes[!out] as well */ cmdpush(CS_CMDSUBST); diff --git a/Src/jobs.c b/Src/jobs.c index a40291a88..fd785a0e9 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -320,6 +320,36 @@ update_process(Process pn, int status) } #endif +/* + * Called when the current shell is behaving as if it received + * a interactively generated signal (sig). + * + * As we got the signal or are pretending we did, we need to pretend + * anything attached to a CURSH process got it, too. + */ +/**/ +void +check_cursh_sig(int sig) +{ + int i, j; + + if (!errflag) + return; + for (i = 1; i <= maxjob; i++) { + if ((jobtab[i].stat & (STAT_CURSH|STAT_DONE)) == + STAT_CURSH) { + for (j = 0; j < 2; j++) { + Process pn = j ? jobtab[i].auxprocs : jobtab[i].procs; + for (; pn; pn = pn->next) { + if (pn->status == SP_RUNNING) { + kill(pn->pid, sig); + } + } + } + } + } +} + /* Update status of job, possibly printing it */ /**/ @@ -331,11 +361,20 @@ update_job(Job jn) int val = 0, status = 0; int somestopped = 0, inforeground = 0; - for (pn = jn->auxprocs; pn; pn = pn->next) + for (pn = jn->auxprocs; pn; pn = pn->next) { +#ifdef WIFCONTINUED + if (WIFCONTINUED(pn->status)) + pn->status = SP_RUNNING; +#endif if (pn->status == SP_RUNNING) return; + } for (pn = jn->procs; pn; pn = pn->next) { +#ifdef WIFCONTINUED + if (WIFCONTINUED(pn->status)) + pn->status = SP_RUNNING; +#endif if (pn->status == SP_RUNNING) /* some processes in this job are running */ return; /* so no need to update job table entry */ if (WIFSTOPPED(pn->status)) /* some processes are stopped */ @@ -496,6 +535,7 @@ update_job(Job jn) breaks = loops; errflag = 1; } + check_cursh_sig(sig); } } } @@ -1793,6 +1833,14 @@ bin_fg(char *name, char **argv, Options ops, int func) } queue_signals(); + /* + * In case any processes changed state recently, wait for them. + * This updates stopped processes (but we should have been + * signalled about those, up to inevitable races), and also + * continued processes if that feature is available. + */ + wait_for_processes(); + /* If necessary, update job table. */ if (unset(NOTIFY)) scanjobs(); @@ -2216,8 +2264,11 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) job, and send the job a SIGCONT if sending it a non-stopping signal. */ if (jobtab[p].stat & STAT_STOPPED) { +#ifndef WIFCONTINUED + /* With WIFCONTINUED we find this out properly */ if (sig == SIGCONT) makerunning(jobtab + p); +#endif if (sig != SIGKILL && sig != SIGCONT && sig != SIGTSTP && sig != SIGTTOU && sig != SIGTTIN && sig != SIGSTOP) killjb(jobtab + p, SIGCONT); @@ -2230,14 +2281,18 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) if (kill(pid, sig) == -1) { zwarnnam("kill", "kill %s failed: %e", *argv, errno); returnval++; - } else if (sig == SIGCONT) { + } +#ifndef WIFCONTINUED + else if (sig == SIGCONT) { Job jn; Process pn; + /* With WIFCONTINUED we find this out properly */ if (findproc(pid, &jn, &pn, 0)) { if (WIFSTOPPED(pn->status)) pn->status = SP_RUNNING; } } +#endif } } unqueue_signals(); diff --git a/Src/signals.c b/Src/signals.c index 74aeadde7..49e5e6379 100644 --- a/Src/signals.c +++ b/Src/signals.c @@ -400,6 +400,129 @@ signal_suspend(UNUSED(int sig), int wait_cmd) /**/ int last_signal; +/* + * Wait for any processes that have changed state. + * + * The main use for this is in the SIGCHLD handler. However, + * we also use it to pick up status changes of jobs when + * updating jobs. + */ +/**/ +void +wait_for_processes(void) +{ + /* keep WAITING until no more child processes to reap */ + for (;;) { + /* save the errno, since WAIT may change it */ + int old_errno = errno; + int status; + Job jn; + Process pn; + pid_t pid; + pid_t *procsubpid = &cmdoutpid; + int *procsubval = &cmdoutval; + int cont = 0; + struct execstack *es = exstack; + + /* + * Reap the child process. + * If we want usage information, we need to use wait3. + */ +#if defined(HAVE_WAIT3) || defined(HAVE_WAITPID) +# ifdef WCONTINUED +# define WAITFLAGS (WNOHANG|WUNTRACED|WCONTINUED) +# else +# define WAITFLAGS (WNOHANG|WUNTRACED) +# endif +#endif +#ifdef HAVE_WAIT3 +# ifdef HAVE_GETRUSAGE + struct rusage ru; + + pid = wait3((void *)&status, WAITFLAGS, &ru); +# else + pid = wait3((void *)&status, WAITFLAGS, NULL); +# endif +#else +# ifdef HAVE_WAITPID + pid = waitpid(-1, &status, WAITFLAGS); +# else + pid = wait(&status); +# endif +#endif + + if (!pid) /* no more children to reap */ + break; + + /* check if child returned was from process substitution */ + for (;;) { + if (pid == *procsubpid) { + *procsubpid = 0; + if (WIFSIGNALED(status)) + *procsubval = (0200 | WTERMSIG(status)); + else + *procsubval = WEXITSTATUS(status); + use_cmdoutval = 1; + get_usage(); + cont = 1; + break; + } + if (!es) + break; + procsubpid = &es->cmdoutpid; + procsubval = &es->cmdoutval; + es = es->next; + } + if (cont) + continue; + + /* check for WAIT error */ + if (pid == -1) { + if (errno != ECHILD) + zerr("wait failed: %e", errno); + /* WAIT changed errno, so restore the original */ + errno = old_errno; + break; + } + + /* + * Find the process and job containing this pid and + * update it. + */ + pn = NULL; + if (findproc(pid, &jn, &pn, 0)) { +#if defined(HAVE_WAIT3) && defined(HAVE_GETRUSAGE) + struct timezone dummy_tz; + gettimeofday(&pn->endtime, &dummy_tz); + pn->status = status; + pn->ti = ru; +#else + update_process(pn, status); +#endif + update_job(jn); + } else if (findproc(pid, &jn, &pn, 1)) { + pn->status = status; + update_job(jn); + } else { + /* If not found, update the shell record of time spent by + * children in sub processes anyway: otherwise, this + * will get added on to the next found process that + * terminates. + */ + get_usage(); + } + /* + * Remember the status associated with $!, so we can + * wait for it even if it's exited. This value is + * only used if we can't find the PID in the job table, + * so it doesn't matter that the value we save here isn't + * useful until the process has exited. + */ + if (pn != NULL && pid == lastpid && lastpid_status != -1L) + lastpid_status = lastval2; + } +} + /* the signal handler */ /**/ @@ -458,110 +581,7 @@ zhandler(int sig) switch (sig) { case SIGCHLD: - - /* keep WAITING until no more child processes to reap */ - for (;;) { - /* save the errno, since WAIT may change it */ - int old_errno = errno; - int status; - Job jn; - Process pn; - pid_t pid; - pid_t *procsubpid = &cmdoutpid; - int *procsubval = &cmdoutval; - int cont = 0; - struct execstack *es = exstack; - - /* - * Reap the child process. - * If we want usage information, we need to use wait3. - */ -#ifdef HAVE_WAIT3 -# ifdef HAVE_GETRUSAGE - struct rusage ru; - - pid = wait3((void *)&status, WNOHANG|WUNTRACED, &ru); -# else - pid = wait3((void *)&status, WNOHANG|WUNTRACED, NULL); -# endif -#else -# ifdef HAVE_WAITPID - pid = waitpid(-1, &status, WNOHANG|WUNTRACED); -# else - pid = wait(&status); -# endif -#endif - - if (!pid) /* no more children to reap */ - break; - - /* check if child returned was from process substitution */ - for (;;) { - if (pid == *procsubpid) { - *procsubpid = 0; - if (WIFSIGNALED(status)) - *procsubval = (0200 | WTERMSIG(status)); - else - *procsubval = WEXITSTATUS(status); - use_cmdoutval = 1; - get_usage(); - cont = 1; - break; - } - if (!es) - break; - procsubpid = &es->cmdoutpid; - procsubval = &es->cmdoutval; - es = es->next; - } - if (cont) - continue; - - /* check for WAIT error */ - if (pid == -1) { - if (errno != ECHILD) - zerr("wait failed: %e", errno); - /* WAIT changed errno, so restore the original */ - errno = old_errno; - break; - } - - /* - * Find the process and job containing this pid and - * update it. - */ - pn = NULL; - if (findproc(pid, &jn, &pn, 0)) { -#if defined(HAVE_WAIT3) && defined(HAVE_GETRUSAGE) - struct timezone dummy_tz; - gettimeofday(&pn->endtime, &dummy_tz); - pn->status = status; - pn->ti = ru; -#else - update_process(pn, status); -#endif - update_job(jn); - } else if (findproc(pid, &jn, &pn, 1)) { - pn->status = status; - update_job(jn); - } else { - /* If not found, update the shell record of time spent by - * children in sub processes anyway: otherwise, this - * will get added on to the next found process that - * terminates. - */ - get_usage(); - } - /* - * Remember the status associated with $!, so we can - * wait for it even if it's exited. This value is - * only used if we can't find the PID in the job table, - * so it doesn't matter that the value we save here isn't - * useful until the process has exited. - */ - if (pn != NULL && pid == lastpid && lastpid_status != -1L) - lastpid_status = lastval2; - } + wait_for_processes(); break; case SIGHUP: @@ -580,6 +600,7 @@ zhandler(int sig) breaks = loops; errflag = 1; inerrflush(); + check_cursh_sig(SIGINT); } } break; |