Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: How to misplace an entire pipeline



On Aug 8,  7:27pm, Peter Stephenson wrote:
} Subject: Re: How to misplace an entire pipeline
}
} On Sun, 07 Aug 2011 21:05:07 -0700
} Bart Schaefer <schaefer@xxxxxxxxxxxxxxxx> wrote:
} > -	jobtab[thisjob].stat |= STAT_CURSH|STAT_NOPRINT;
} > +	jobtab[thisjob].stat |= STAT_CURSH;
} > +	if (!jobtab[thisjob].procs)
} > +	    jobtab[thisjob].stat |= STAT_NOPRINT;
} 
} Looks fairly plausible, anyway.

Should that be "if (hasprocs(thisjob))" instead, do you think?

} > When "read" is the tail of the pipe, the above all happens behind the
} > scenes and then the I/O system call gets restarted, which is how the
} > shell ends up stuck.  I'm not sure how to escape from that, except
} > maybe to have zhandler() kill the shell with a different signal from
} > which the system call will not recover.
} 
} I suppose so.  I can hardly believe this ever worked.

It didn't, exactly.  This particular one behaves almost the same in
4.3.9 as it does in recent dev releases.  The main difference is that
in 4.3.9 the ^Z echoes to the terminal; in 4.3.12 there's no feedback
at all.  I haven't figured out how/why that changed.

} > The only obvious thing I can think to do here is to note in zhandler()
} > that we have STAT_CURSH but not list_pipe, and therefore SIGCONT the
} > left-hand-side immediately and return as if no signal had occurred
} > (possibly printing a warning about not being able to suspend the job,
} > which is what happens elsewhere if pipe() or fork() fails).  However,
} > that could lead to a serious busy-loop if somehow TTIN or TTOU was
} > the signal instead of TSTP.
} 
} But we can test if it's TSTP (or STOP)?

Yes, when WSTOPSIG() is defined.  The parent shell never gets the
original signal, it only gets the SIGCHLD and can then examine the
return value from wait3() or whatever system call was available.

SIGSTOP can't be caught or blocked so in that case it's likely the
parent shell has also stopped.  I'm not sure it'd be good behavior
for the shell to auto-resume a process that was hit with STOP, even
if it could.

So, some more about what's going on ...

Near the end of execpline2() there's this snippet:

	/* if another execpline() is invoked because the command is *
	 * a list it must know that we're already in a pipeline     */
	cmdpush(CS_PIPE);
	list_pipe = 1;
	execpline2(state, *state->pc++, how, pipes[0], output, last1);
	list_pipe = old_list_pipe;
	cmdpop();

Somewhere in the call stack beyond this execpline2(), execbuiltin() is
called.  If that returns immediately we climb out of execpline2() and
clear list_pipe and wait for the pipeline at execpline() line 1500.
However, in the case of "read", execbuiltin() blocks on the I/O, so
list_pipe remains true, and when the TSTP is received, update_job()
proceeds as if execpline() is going to fork.  

So the following hack does the right thing in the case of piping to
true where execbuiltin() has returned, but does not do the right thing
in the case of read.  What needs to be tested in place of (!list_pipe)
to determine that the tail of the current pipeline is a simple shell
builtin?

In fact even that may not be enough, maybe this needs to know if the
current job is a simple shell builtin -- it might be blocked on a
"while read; do ..." or on a "wait" in the middle of a loop, etc.

--- ../zsh-forge/current/Src/signals.c	2011-08-06 11:13:23.000000000 -0700
+++ Src/signals.c	2011-08-08 22:28:11.000000000 -0700
@@ -490,14 +490,20 @@
 	 * update it.
 	 */
 	if (findproc(pid, &jn, &pn, 0)) {
+	    if (!list_pipe && (jn->stat & STAT_CURSH) &&
+		WIFSTOPPED(status) && WSTOPSIG(status) == SIGTSTP) {
+		killjb(jn, SIGCONT);
+		zwarn("job can't be suspended");
+	    } else {
 #if defined(HAVE_WAIT3) && defined(HAVE_GETRUSAGE)
-	    struct timezone dummy_tz;
-	    gettimeofday(&pn->endtime, &dummy_tz);
-	    pn->status = status;
-	    pn->ti = ru;
+		struct timezone dummy_tz;
+		gettimeofday(&pn->endtime, &dummy_tz);
+		pn->status = status;
+		pn->ti = ru;
 #else
-	    update_process(pn, status);
+		update_process(pn, status);
 #endif
+	    }
 	    update_job(jn);
 	} else if (findproc(pid, &jn, &pn, 1)) {
 	    pn->status = status;



Messages sorted by: Reverse Date, Date, Thread, Author