Re: dash 0.5.11.2, busybox sh 1.32.0, FreeBSD 12.2 sh: spring TTOU but should not i think

2021-01-10 Thread Harald van Dijk

On 23/12/2020 20:18, Harald van Dijk wrote:

On 21/12/2020 16:24, Jilles Tjoelker wrote:

Tradition is for job control shells to be a process group leader, but I
don't really know why. Changing this will not fix the issue entirely
anyway since the shell must perform tcsetpgrp() from the background when
a foreground job has terminated.

[...]
This should, in my opinion, *only* happen for interactive shells, not 
for job-control-enabled non-interactive shells. [...]

Consider also an extra difficulty arising from this process group switching:

  : | dash -m

When a job-control-enabled shell terminates, or when job control is 
disabled via set +m, it attempts to re-join its initial process group 
and set that as the foreground process group. This can fail if the 
process group has ceased to exist after the shell left it, as it does 
here, resulting in:


  $ : | dash -m
  dash: 0: Cannot set tty process group (No such process)

In theory, because of PID reuse, this may even result in some random 
unrelated process group temporarily becoming the foreground process group.


I am leaning towards saying that once the shell has committed to 
becoming a process group leader, it should remain one. By basing this on 
the shell being interactive rather than on job control being enabled, 
this is easier to handle, as as far as POSIX is concerned "interactive" 
is a property that cannot be changed once the shell has started: set -i 
and set +i are extensions not required by POSIX, and if they are 
nonetheless supported it is easy to defend them not being fully 
equivalent to specifying or leaving out -i on the shell invocation.


Cheers,
Harald van Dijk


Re: dash 0.5.11.2, busybox sh 1.32.0, FreeBSD 12.2 sh: spring TTOU but should not i think

2021-01-05 Thread Herbert Xu
Jilles Tjoelker  wrote:
>
> This seems a good approach, but certain writes to the terminal may need
> the same treatment, for example the error message when fork fails for
> the second and further elements of a pipeline. This also makes me wonder
> why SIGTTOU is ignored at all by default.

I think the approach of blocking all signals should be able to
resolve this too if anyone cares enough about this case.

Thanks,
-- 
Email: Herbert Xu 
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt


Re: dash 0.5.11.2, busybox sh 1.32.0, FreeBSD 12.2 sh: spring TTOU but should not i think

2020-12-24 Thread Jilles Tjoelker
On Wed, Dec 23, 2020 at 08:18:24PM +, Harald van Dijk wrote:
> On 21/12/2020 16:24, Jilles Tjoelker wrote:
> > Also, simply entering the command
> >  trap "echo TTOU" TTOU
> > in an interactive shell makes it behave strangely. Created jobs
> > immediately stop, "fg" works once but after that the shell tends to get
> > stuck quickly.

> Good catch, there is some work to be done there too.

> > This seems a good approach, but certain writes to the terminal may need
> > the same treatment, for example the error message when fork fails for
> > the second and further elements of a pipeline. This also makes me wonder
> > why SIGTTOU is ignored at all by default.

> True. This is hard to create a reliable test case for, but there is only
> limited code that can get executed while a job-control-enabled shell may not
> be in the foreground process group.

An rlimit (ulimit -u) will cause fork to fail after a number of
processes, but will not work reliably if other code is executing
concurrently with the same UID.

> If fork fails halfway through a pipeline, it may also be a problem that some
> of the commands in the pipeline may still run.

This can be handled much like the case where an element in the pipeline
fails immediately such as because a utility cannot be found. I am not
sure how well this currently works.

> > In mksh, the issue is resolved slightly differently: setting a trap on
> > TTOU pretends to work but the signal disposition remains set to ignored.

> zsh also rejects traps on TTOU, but does so explicitly:

>   zsh: can't trap SIGTTOU in interactive shells

> Amusingly, it prints this in any shell where job control is enabled,
> regardless of whether the shell is interactive.

The rejection makes sense for any shell instance that has job control
and uses tcsetpgrp(), whether interactive or not.

I am not entirely happy with the rejection idea, though, since the check
can be bypassed by temporarily disabling job control:
set +m; trap 'echo TTOU' TTOU; set -m
and running external utilities then fails with:
zsh: can't set tty pgrp: interrupt

> > Tradition is for job control shells to be a process group leader, but I
> > don't really know why. Changing this will not fix the issue entirely
> > anyway since the shell must perform tcsetpgrp() from the background when
> > a foreground job has terminated.

> I've been thinking more about this, and I suspect it's a another conflation
> between interactive mode and job control. In an interactive shell that's not
> executing any external program, you want any Ctrl-C to only send SIGINT to
> that shell, not to any other process. In order for that to work, it needs to
> be its own process group.

> This should, in my opinion, *only* happen for interactive shells, not for
> job-control-enabled non-interactive shells. Consider

>   sh -c 'sh -mc "while :; do :; done"; echo bye'

> The behaviour I would want is that Ctrl-C kills the parent shell, so that
> this does not print "bye". Different shells behave differently.

I think the main effect of the -m option is that it places jobs in
separate process groups, which may also be useful in scripts. After
something like:

set -m
foo &
foopid=$!
set +m

it is possible to  kill -- "-$foopid"  .

Although non-portable kernel features such as cgroups (Linux) or reaper
(FreeBSD) might be more useful for tracking processes like this, the
work to define how to use them in a shell has not been done.

In FreeBSD sh, I extended the possibilities here by allowing job control
without an accessible tty in non-interactive mode. This applies both if
there is no controlling terminal at all and if the shell is in the
background.

In the example 
sh -c 'sh -mc "while :; do :; done"; echo bye'
the -m flag seems to have no effect since that shell only runs builtins.

Changing it to
sh -mc 'sh -c "while :; do :; done"; echo bye'
the desired Ctrl+C behaviour can still work because the outer shell
exits when it notices the inner shell has terminated because of SIGINT.

-- 
Jilles Tjoelker


Re: dash 0.5.11.2, busybox sh 1.32.0, FreeBSD 12.2 sh: spring TTOU but should not i think

2020-12-23 Thread Harald van Dijk

On 21/12/2020 16:24, Jilles Tjoelker wrote:

Also, simply entering the command
 trap "echo TTOU" TTOU
in an interactive shell makes it behave strangely. Created jobs
immediately stop, "fg" works once but after that the shell tends to get
stuck quickly.


Good catch, there is some work to be done there too.


This seems a good approach, but certain writes to the terminal may need
the same treatment, for example the error message when fork fails for
the second and further elements of a pipeline. This also makes me wonder
why SIGTTOU is ignored at all by default.


True. This is hard to create a reliable test case for, but there is only 
limited code that can get executed while a job-control-enabled shell may 
not be in the foreground process group.


If fork fails halfway through a pipeline, it may also be a problem that 
some of the commands in the pipeline may still run.



In mksh, the issue is resolved slightly differently: setting a trap on
TTOU pretends to work but the signal disposition remains set to ignored.


zsh also rejects traps on TTOU, but does so explicitly:

  zsh: can't trap SIGTTOU in interactive shells

Amusingly, it prints this in any shell where job control is enabled, 
regardless of whether the shell is interactive.



Tradition is for job control shells to be a process group leader, but I
don't really know why. Changing this will not fix the issue entirely
anyway since the shell must perform tcsetpgrp() from the background when
a foreground job has terminated.


I've been thinking more about this, and I suspect it's a another 
conflation between interactive mode and job control. In an interactive 
shell that's not executing any external program, you want any Ctrl-C to 
only send SIGINT to that shell, not to any other process. In order for 
that to work, it needs to be its own process group.


This should, in my opinion, *only* happen for interactive shells, not 
for job-control-enabled non-interactive shells. Consider


  sh -c 'sh -mc "while :; do :; done"; echo bye'

The behaviour I would want is that Ctrl-C kills the parent shell, so 
that this does not print "bye". Different shells behave differently.



What is definitely required, though, is that the shell not read input or
alter terminal settings if it is started in the background (possibly
unless the script explicitly ignored SIGTTOU). This is what the loop
with tcgetpgrp() does.


Definitely.

Cheers,
Harald van Dijk


Re: dash 0.5.11.2, busybox sh 1.32.0, FreeBSD 12.2 sh: spring TTOU but should not i think

2020-12-21 Thread Steffen Nurpmeso
Jilles Tjoelker wrote in
 <20201221162442.ga26...@stack.nl>:
 |On Sat, Dec 19, 2020 at 11:52:31PM +, Harald van Dijk wrote:
 |> On 19/12/2020 22:21, Steffen Nurpmeso wrote:
 |>> Steffen Nurpmeso wrote in
 |>>   <20201219172838.1b-wb%stef...@sdaoden.eu>:
 |>>|Long story short, after falsely accusing BSD make of not working
 |
 |>> After dinner i shortened it a bit more, and attach it again, ok?
 |>> It is terrible, but now less redundant than before.
 |>> Sorry for being so terse, that problem crosses my head for about
 |>> a week, and i was totally mislead and if you bang your head
 |>> against the wall so many hours bugs or misbehaviours in a handful
 |>> of other programs is not the expected outcome.
 |
 |> I think a minimal test case is simply
 |
 |> all:
 |> $(SHELL) -c 'trap "echo TTOU" TTOU; set -m; echo all good'
 |
 |> unless I accidentally oversimplified.
 |
 |Yes, and it can be simplified a bit more to remove make from the
 |picture:
 |(SHELL -c 'trap "echo TTOU" TTOU; set -m; echo all good'; :)

Cool, .. but it all seems to depend on the outer shell

  #$ bash -c  "( /bin/dash -c 'trap echo\ TTOU TTOU; set -m; echo all good' )"
  /bin/dash: 1: set: Cannot set tty process group (Interrupted system call)
  #$ mksh -c  "( /bin/dash -c 'trap echo\ TTOU TTOU; set -m; echo all good' )"
  /bin/dash: 1: set: Cannot set tty process group (Interrupted system call)
  #$ dash -c  "( /bin/dash -c 'trap echo\ TTOU TTOU; set -m; echo all good' )"
  all good

which results in the funny experience

  #?0|kent:tmp$ smake -f t.mk SHELL=/bin/bash
  .../bin/bash -c 'trap "echo TTOU" TTOU; set -m; echo all good'
  all good
  #?0|kent:tmp$ smake -f t.mk SHELL=/bin/mksh
  .../bin/mksh -c 'trap "echo TTOU" TTOU; set -m; echo all good'
  all good
  #?0|kent:tmp$ smake -f t.mk SHELL=/bin/dash
  .../bin/dash -c 'trap "echo TTOU" TTOU; set -m; echo all good'
  /bin/dash: 1: set: Cannot set tty process group (Interrupted system call)
  smake: *** Code 2 (No such file or directory) from command line for target 
'all'.
  smake: Couldn't make 'all'.

Ah, it is a Monday ..

--steffen
|
|Der Kragenbaer,The moon bear,
|der holt sich munter   he cheerfully and one by one
|einen nach dem anderen runter  wa.ks himself off
|(By Robert Gernhardt)


Re: dash 0.5.11.2, busybox sh 1.32.0, FreeBSD 12.2 sh: spring TTOU but should not i think

2020-12-21 Thread Jilles Tjoelker
On Sat, Dec 19, 2020 at 11:52:31PM +, Harald van Dijk wrote:
> On 19/12/2020 22:21, Steffen Nurpmeso wrote:
> > Steffen Nurpmeso wrote in
> >   <20201219172838.1b-wb%stef...@sdaoden.eu>:
> >   |Long story short, after falsely accusing BSD make of not working

> > After dinner i shortened it a bit more, and attach it again, ok?
> > It is terrible, but now less redundant than before.
> > Sorry for being so terse, that problem crosses my head for about
> > a week, and i was totally mislead and if you bang your head
> > against the wall so many hours bugs or misbehaviours in a handful
> > of other programs is not the expected outcome.

> I think a minimal test case is simply

> all:
> $(SHELL) -c 'trap "echo TTOU" TTOU; set -m; echo all good'

> unless I accidentally oversimplified.

Yes, and it can be simplified a bit more to remove make from the
picture:
(SHELL -c 'trap "echo TTOU" TTOU; set -m; echo all good'; :)

The idea is to start the shell without being a process group leader, set
a trap for SIGTTOU and enable job control.

Also, simply entering the command
trap "echo TTOU" TTOU
in an interactive shell makes it behave strangely. Created jobs
immediately stop, "fg" works once but after that the shell tends to get
stuck quickly.

> The SIGTTOU is caused by setjobctl's xtcsetpgrp(fd, pgrp) call to make its
> newly started process group the foreground process group when job control is
> enabled, where xtcsetpgrp is a wrapper for tcsetpgrp. (That's in dash, the
> other variants may have some small differences.) tcsetpgrp has this little
> bit in its specification:

>Attempts to use tcsetpgrp() from a process which is a member of
>a background process group on a fildes associated with its con‐
>trolling  terminal  shall  cause the process group to be sent a
>SIGTTOU signal. If the calling thread is blocking SIGTTOU  sig‐
>nals  or  the  process is ignoring SIGTTOU signals, the process
>shall be allowed to perform the operation,  and  no  signal  is
>sent.

> Ordinarily, when job control is enabled, SIGTTOU is ignored. However, when a
> trap action is specified for SIGTTOU, the signal is not ignored, and there
> is no blocking in place either, so the tcsetpgrp() call is not allowed.

Right.

> The lowest impact change to make here, the one that otherwise preserves the
> existing shell behaviour, is to block signals before calling tcsetpgrp and
> unblocking them afterwards. This ensures SIGTTOU does not get raised here,
> but also ensures that if SIGTTOU is sent to the shell for another reason,
> there is no window where it gets silently ignored.

This seems a good approach, but certain writes to the terminal may need
the same treatment, for example the error message when fork fails for
the second and further elements of a pipeline. This also makes me wonder
why SIGTTOU is ignored at all by default.

In mksh, the issue is resolved slightly differently: setting a trap on
TTOU pretends to work but the signal disposition remains set to ignored.

> Another way to fix this is by not trying to make the shell start a new
> process group, or at least not make it the foreground process group. Most
> other shells appear to not try to do this.

Tradition is for job control shells to be a process group leader, but I
don't really know why. Changing this will not fix the issue entirely
anyway since the shell must perform tcsetpgrp() from the background when
a foreground job has terminated.

What is definitely required, though, is that the shell not read input or
alter terminal settings if it is started in the background (possibly
unless the script explicitly ignored SIGTTOU). This is what the loop
with tcgetpgrp() does.

-- 
Jilles Tjoelker


Re: dash 0.5.11.2, busybox sh 1.32.0, FreeBSD 12.2 sh: spring TTOU but should not i think

2020-12-19 Thread Harald van Dijk

On 19/12/2020 22:21, Steffen Nurpmeso wrote:

Steffen Nurpmeso wrote in
  <20201219172838.1b-wb%stef...@sdaoden.eu>:
  |Long story short, after falsely accusing BSD make of not working

After dinner i shortened it a bit more, and attach it again, ok?
It is terrible, but now less redundant than before.
Sorry for being so terse, that problem crosses my head for about
a week, and i was totally mislead and if you bang your head
against the wall so many hours bugs or misbehaviours in a handful
of other programs is not the expected outcome.


I think a minimal test case is simply

all:
$(SHELL) -c 'trap "echo TTOU" TTOU; set -m; echo all good'

unless I accidentally oversimplified.

The SIGTTOU is caused by setjobctl's xtcsetpgrp(fd, pgrp) call to make 
its newly started process group the foreground process group when job 
control is enabled, where xtcsetpgrp is a wrapper for tcsetpgrp. (That's 
in dash, the other variants may have some small differences.) tcsetpgrp 
has this little bit in its specification:


   Attempts to use tcsetpgrp() from a process which is a member of
   a background process group on a fildes associated with its con‐
   trolling  terminal  shall  cause the process group to be sent a
   SIGTTOU signal. If the calling thread is blocking SIGTTOU  sig‐
   nals  or  the  process is ignoring SIGTTOU signals, the process
   shall be allowed to perform the operation,  and  no  signal  is
   sent.

Ordinarily, when job control is enabled, SIGTTOU is ignored. However, 
when a trap action is specified for SIGTTOU, the signal is not ignored, 
and there is no blocking in place either, so the tcsetpgrp() call is not 
allowed.


The lowest impact change to make here, the one that otherwise preserves 
the existing shell behaviour, is to block signals before calling 
tcsetpgrp and unblocking them afterwards. This ensures SIGTTOU does not 
get raised here, but also ensures that if SIGTTOU is sent to the shell 
for another reason, there is no window where it gets silently ignored.


Another way to fix this is by not trying to make the shell start a new 
process group, or at least not make it the foreground process group. 
Most other shells appear to not try to do this.


Cheers,
Harald van Dijk


Re: dash 0.5.11.2, busybox sh 1.32.0, FreeBSD 12.2 sh: spring TTOU but should not i think

2020-12-19 Thread Steffen Nurpmeso
Steffen Nurpmeso wrote in
 <20201219172838.1b-wb%stef...@sdaoden.eu>:
 |Long story short, after falsely accusing BSD make of not working

After dinner i shortened it a bit more, and attach it again, ok?
It is terrible, but now less redundant than before.
Sorry for being so terse, that problem crosses my head for about
a week, and i was totally mislead and if you bang your head
against the wall so many hours bugs or misbehaviours in a handful
of other programs is not the expected outcome.

  #?0|kent:tmp$ SHELL=/bin/bash make -f jobo.mk
  ./mx-test.sh
  Starting job reaper (timeout of 2 seconds)
  .. waiting for job reaper to come up
  PRE T1 CHEK
  PREWAIT
  CHECK 10
  ls: cannot access '/NONEXISTENT': No such file or directory
  CHECK /bin/bash 50
  allexport   off
  braceexpand on
  emacs   off
  errexit off
  errtraceoff
  functrace   off
  hashall on
  histexpand  off
  history off
  ignoreeof   off
  interactive-commentson
  keyword off
  monitor on
  noclobber   off
  noexec  off
  noglob  off
  nolog   off
  notify  off
  nounset off
  onecmd  off
  physicaloff
  pipefailoff
  posix   off
  privileged  off
  verbose off
  vi  off
  xtrace  off
  CHECK 23
  CHECK 100
  AFTER T1 CHEK
  Stopping job reaper
  make: *** [jobo.mk:2: test] Error 1

That is how it "should look" ;), monitor is on in there. 

  #?2|kent:tmp$ SHELL=/bin/dash make -f jobo.mk
  ./mx-test.sh
  Starting job reaper (timeout of 2 seconds)
  .. waiting for job reaper to come up
  TTOU
  Cleaning up running jobs
  Stopping job reaper
  make: *** [jobo.mk:2: test] Error 2

Somehow refuses to work when supervised by (any) make (i tried).
Works as desired when ridden directly.

  #?2|kent:tmp$ SHELL=/bin/mksh make -f jobo.mk
  ./mx-test.sh
  Starting job reaper (timeout of 2 seconds)
  .. waiting for job reaper to come up
  PRE T1 CHEK
  CHECK 10
  PREWAIT
  ls: cannot access '/NONEXISTENT': No such file or directory
  PREWAIT
  PREWAIT
  PREWAIT
  PREWAIT
  PREWAIT
  PREWAIT
  PREWAIT
  TIMEOUT IN PARENT
  
  !! Timeout: reaped job 1 [X_errexit]
  Stopping job reaper

And that condition is very strange, with or without supervision.

Ciao!

--steffen
|
|Der Kragenbaer,The moon bear,
|der holt sich munter   he cheerfully and one by one
|einen nach dem anderen runter  wa.ks himself off
|(By Robert Gernhardt)


mx-test.sh
Description: Bourne shell script
test:
./mx-test.sh