Bug 171427 - sh(1): Hitting ^Z doesn't suspend jobs like expected
Summary: sh(1): Hitting ^Z doesn't suspend jobs like expected
Status: Open
Alias: None
Product: Base System
Classification: Unclassified
Component: bin (show other bugs)
Version: 7.4-RELEASE
Hardware: Any Any
: Normal Affects Only Me
Assignee: freebsd-bugs (Nobody)
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2012-09-08 00:10 UTC by Enji Cooper
Modified: 2018-01-03 05:12 UTC (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Enji Cooper freebsd_committer freebsd_triage 2012-09-08 00:10:01 UTC
Running some netperf tests in a while loop like so under /bin/sh, I'm running into
cases where the while loop continues to loop, even after I mash on ^C (which I would expect to kill netperf eventually -- works on 9.1-RC1, but not 7.4-STABLE) or ^Z (I would expect job control to stop spawning jobs, but it doesn't in either version):

bf049# exec sh


wf048# exec sh
$ while : ; do netperf -cCjt TCP_STREAM -H 10.7.187.52 -l 10 -- -D; done

..

^ZTCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.7.187.56 () port 0 AF_INET : nodelay : histogram : interval : dirty data : demo
^ZTCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.7.187.56 () port 0 AF_INET : nodelay : histogram : interval : dirty data : demo
^ZTCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.7.187.56 () port 0 AF_INET : nodelay : histogram : interval : dirty data : demo
^ZTCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.7.187.56 () port 0 AF_INET : nodelay : histogram : interval : dirty data : demo
^ZTCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.7.187.56 () port 0 AF_INET : nodelay : histogram : interval : dirty data : demo
^ZTCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.7.187.56 () port 0 AF_INET : nodelay : histogram : interval : dirty data : demo
^ZTCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.7.187.56 () port 0 AF_INET : nodelay : histogram : interval : dirty data : demo
^ZTCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.7.187.56 () port 0 AF_INET : nodelay : histogram : interval : dirty data : demo
^ZTCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.7.187.56 () port 0 AF_INET : nodelay : histogram : interval : dirty data : demo
^ZTCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.7.187.56 () port 0 AF_INET : nodelay : histogram : interval : dirty data : demo
^ZTCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.7.187.56 () port 0 AF_INET : nodelay : histogram : interval : dirty data : demo
^ZTCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.7.187.56 () port 0 AF_INET : nodelay : histogram : interval : dirty data : demo
^ZTCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.7.187.56 () port 0 AF_INET : nodelay : histogram : interval : dirty data : demo
^ZTCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.7.187.56 () port 0 AF_INET : nodelay : histogram : interval : dirty data : demo
^ZTCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.7.187.56 () port 0 AF_INET : nodelay : histogram : interval : dirty data : demo
^ZTCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.7.187.56 () port 0 AF_INET : nodelay : histogram : interval : dirty data : demo
^ZTCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.7.187.56 () port 0 AF_INET : nodelay : histogram : interval : dirty data : demo
^ZTCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.7.187.56 () port 0 AF_INET : nodelay : histogram : interval : dirty data : demo
^ZTCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.7.187.56 () port 0 AF_INET : nodelay : histogram : interval : dirty data : demo
^ZTCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.7.187.56 () port 0 AF_INET : nodelay : histogram : interval : dirty data : demo
Recv   Send    Send                          Utilization       Service Demand
Socket Socket  Message  Elapsed              Send     Recv     Send    Recv
Size   Size    Size     Time     Throughput  local    remote   local   remote
bytes  bytes   bytes    secs.    10^6bits/s  % C      % C      us/KB   us/KB

 65536  32768  32768    10.01       810.28   8.05     7.52     3.254   6.080  
TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.7.187.56 () port 0 AF_INET : nodelay : histogram : interval : dirty data : demo

Bash on the other hand does stop the code snippet:

$ while : ; do netperf -cCjt TCP_STREAM -H 10.7.187.52 -l 10 -- -D; done
TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.7.187.52 () port 0 AF_INET : nodelay : histogram : interval : dirty data : demo
^Z
[1]+  Stopped                 netperf -cCjt TCP_STREAM -H 10.7.187.52 -l 10 -- -D

The expected behavior isn't documented in sh(1) and in the opengroup sh(1) manpage.

How-To-Repeat: exec sh
while : ; do netperf -cCjt TCP_STREAM -H $IP -l 10 -- -D; done

You could probably replace the second command (to netperf) with something that blocks.
Comment 1 Jilles Tjoelker freebsd_committer freebsd_triage 2012-09-16 22:11:33 UTC
> [while : ; do netperf -cCjt TCP_STREAM -H 10.7.187.52 -l 10 -- -D; done
> works strangely with ^C/^Z]

For the case with ^C, I indeed deliberately fixed that for 9.0.

For the case with ^Z, this is indeed a caveat that is complained about
more frequently, and different shells handle it differently because
POSIX leaves it vague.

POSIX discusses suspending jobs (process groups), but a sequence or
while loop is not a job.

One workaround is to make it a job by placing parentheses around the
loop. With most shells, this creates a new process group and job that
can be suspended normally. An exception is ksh93 which attempts to
execute the subshell in the same process; I do not like this but I think
POSIX permits it (and in non-interactive or no-job-control mode it is
fine to execute a subshell in that way).

What sh does is return status 146 (128+SIGTSTP) and continue (so while
netperf; do :; done will abort if ^Z'ed).

What bash appears to do is break loops if something was ^Z'ed.

What zsh appears to do is fork and continue the loop after the suspended
process terminates. However, it gets $? wrong. Also, it needs a fair bit
of code and changes to the shell environment after the suspension do not
affect the parent.

-- 
Jilles Tjoelker
Comment 2 Eitan Adler freebsd_committer freebsd_triage 2017-12-31 07:59:52 UTC
For bugs matching the following criteria:

Status: In Progress Changed: (is less than) 2014-06-01

Reset to default assignee and clear in-progress tags.

Mail being skipped