summaryrefslogtreecommitdiff
path: root/Src/jobs.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/jobs.c')
-rw-r--r--Src/jobs.c131
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