diff options
Diffstat (limited to 'Src/jobs.c')
-rw-r--r-- | Src/jobs.c | 131 |
1 files changed, 102 insertions, 29 deletions
diff --git a/Src/jobs.c b/Src/jobs.c index db2e87ec1..ed9f81f26 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -30,6 +30,27 @@ #include "zsh.mdh" #include "jobs.pro" +/* + * Job control in zsh + * ================== + * + * A 'job' represents a pipeline; see the section JOBS in zshmisc(1)) for an + * introduction. The 'struct job's are allocated in the array 'jobtab' which + * has 'jobtabsize' elements. The job whose processes we are currently + * preparing to execute is identified by the global variable 'thisjob'. + * + * A 'superjob' is a job that represents a complex shell construct that has been + * backgrounded. For example, if one runs '() { vi; echo }', a job is created + * for the pipeline 'vi'. If one then backgrounds vi (with ^Z / SIGTSTP), + * the shell forks; the parent shell returns to the interactive prompt and + * the child shell becomes a new job in the parent shell. The job representing + * the child shell to the parent shell is a superjob (STAT_SUPERJOB); the 'vi' + * job is marked as a subjob (STAT_SUBJOB) in the parent shell. When the child + * shell is resumed (with fg / SIGCONT), it forwards the signal to vi and, + * after vi exits, continues executing the remainder of the function. + * (See workers/43565.) + */ + /* the process group of the shell at startup (equal to mypgprp, except when we started without being process group leader */ @@ -40,18 +61,23 @@ mod_export pid_t origpgrp; /**/ mod_export pid_t mypgrp; + +/* the last process group to attach to the terminal */ + +/**/ +pid_t last_attached_pgrp; -/* the job we are working on */ +/* the job we are working on, or -1 if none */ /**/ mod_export int thisjob; -/* the current job (+) */ +/* the current job (%+) */ /**/ mod_export int curjob; -/* the previous job (-) */ +/* the previous job (%-) */ /**/ mod_export int prevjob; @@ -454,19 +480,42 @@ update_job(Job jn) jn->ty = (struct ttyinfo *) zalloc(sizeof(struct ttyinfo)); gettyinfo(jn->ty); } - if (jn->stat & STAT_STOPPED) { - if (jn->stat & STAT_SUBJOB) { - /* If we have `cat foo|while read a; grep $a bar;done' - * and have hit ^Z, the sub-job is stopped, but the - * super-job may still be running, waiting to be stopped - * or to exit. So we have to send it a SIGTSTP. */ - int i; - - if ((i = super_job(job))) - killpg(jobtab[i].gleader, SIGTSTP); + if (jn->stat & STAT_SUBJOB) { + /* If we have `cat foo|while read a; grep $a bar;done' + * and have hit ^Z, the sub-job is stopped, but the + * super-job may still be running, waiting to be stopped + * or to exit. So we have to send it a SIGTSTP. */ + int i; + + jn->stat |= STAT_CHANGED | STAT_STOPPED; + if ((i = super_job(job))) { + Job sjn = &jobtab[i]; + killpg(sjn->gleader, SIGTSTP); + /* + * Job may already be stopped if it consists of only the + * forked shell waiting for the subjob -- so mark as + * stopped immediately. This ensures we send it (and, + * crucially, the subjob, as the visible job used with + * fg/bg is the superjob) a SIGCONT if we need it. + */ + sjn->stat |= STAT_CHANGED | STAT_STOPPED; + if (isset(NOTIFY) && (sjn->stat & STAT_LOCKED) && + !(sjn->stat & STAT_NOPRINT)) { + /* + * Print the subjob state, which we don't usually + * do, so the user knows something has stopped. + * So as not to be confusing, we actually output + * the user-visible superjob. + */ + if (printjob(sjn, !!isset(LONGLISTJOBS), 0) && + zleactive) + zleentry(ZLE_CMD_REFRESH); + } } return; } + if (jn->stat & STAT_STOPPED) + return; } { /* job is done or stopped, remember return value */ lastval2 = val; @@ -1020,15 +1069,30 @@ printjob(Job jn, int lng, int synch) "bogus job number, jn = %L, jobtab = %L, oldjobtab = %L", (long)jn, (long)jobtab, (long)oldjobtab); - if (jn->stat & STAT_NOPRINT) { + if (jn->stat & STAT_NOPRINT) skip_print = 1; - } if (lng < 0) { conted = 1; lng = !!isset(LONGLISTJOBS); } + if (jn->stat & STAT_SUPERJOB && + jn->other) + { + Job sjn = &jobtab[jn->other]; + if (sjn->procs || sjn->auxprocs) + { + /* + * A subjob still has process, which must finish before + * further excution of the superjob, which the user wants to + * know about. So report the status of the subjob as if it + * were the user-visible superjob. + */ + jn = sjn; + } + } + /* find length of longest signame, check to see */ /* if we really need to print this job */ @@ -1405,6 +1469,11 @@ addproc(pid_t pid, char *text, int aux, struct timeval *bgtime, jobtab[thisjob].gleader = gleader; if (list_pipe_job_used != -1) jobtab[list_pipe_job_used].gleader = gleader; + /* + * Record here this is the latest process group to grab the + * terminal as attachtty() was run in the subshell. + */ + last_attached_pgrp = gleader; } else if (!jobtab[thisjob].gleader) jobtab[thisjob].gleader = pid; /* attach this process to end of process list of current job */ @@ -1559,10 +1628,8 @@ zwaitjob(int job, int wait_cmd) errflag = 0; */ - if (subsh) { + if (subsh) killjb(jn, SIGCONT); - jn->stat &= ~STAT_STOPPED; - } if (jn->stat & STAT_SUPERJOB) if (handle_sub(jn - jobtab, 1)) break; @@ -1580,6 +1647,17 @@ zwaitjob(int job, int wait_cmd) return 0; } +static void waitonejob(Job jn) +{ + if (jn->procs || jn->auxprocs) + zwaitjob(jn - jobtab, 0); + else { + deletejob(jn, 0); + pipestats[0] = lastval; + numpipestats = 1; + } +} + /* wait for running job to finish */ /**/ @@ -1589,13 +1667,11 @@ waitjobs(void) Job jn = jobtab + thisjob; DPUTS(thisjob == -1, "No valid job in waitjobs."); - if (jn->procs || jn->auxprocs) - zwaitjob(thisjob, 0); - else { - deletejob(jn, 0); - pipestats[0] = lastval; - numpipestats = 1; - } + /* If there's a subjob, it should finish first. */ + if (jn->stat & STAT_SUPERJOB) + waitonejob(jobtab + jn->other); + waitonejob(jn); + thisjob = -1; } @@ -2284,11 +2360,8 @@ bin_fg(char *name, char **argv, Options ops, int func) Process p; if (findproc(pid, &j, &p, 0)) { - if (j->stat & STAT_STOPPED) { + if (j->stat & STAT_STOPPED) retval = (killjb(j, SIGCONT) != 0); - if (retval == 0) - makerunning(j); - } if (retval == 0) { /* * returns 0 for normal exit, else signal+128 |