From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 15959 invoked by alias); 9 Aug 2011 06:10:54 -0000 Mailing-List: contact zsh-workers-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Workers List List-Post: List-Help: X-Seq: 29660 Received: (qmail 21421 invoked from network); 9 Aug 2011 06:10:51 -0000 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received-SPF: none (ns1.primenet.com.au: domain at closedmail.com does not designate permitted sender hosts) From: Bart Schaefer Message-id: <110808231032.ZM2380@torch.brasslantern.com> Date: Mon, 08 Aug 2011 23:10:32 -0700 In-reply-to: <20110808192720.380a3ee7@pws-pc.ntlworld.com> Comments: In reply to Peter Stephenson "Re: How to misplace an entire pipeline" (Aug 8, 7:27pm) References: <110805203111.ZM32508@torch.brasslantern.com> <20110807185002.6a042cab@pws-pc.ntlworld.com> <110807144359.ZM27903@torch.brasslantern.com> <110807210507.ZM28821@torch.brasslantern.com> <20110808192720.380a3ee7@pws-pc.ntlworld.com> X-Mailer: OpenZMail Classic (0.9.2 24April2005) To: zsh-workers@zsh.org Subject: Re: How to misplace an entire pipeline MIME-version: 1.0 Content-type: text/plain; charset=us-ascii 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 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;