Bug 266627 - stty -f <file> should be non-blocking
Summary: stty -f <file> should be non-blocking
Status: Closed FIXED
Alias: None
Product: Base System
Classification: Unclassified
Component: bin (show other bugs)
Version: CURRENT
Hardware: Any Any
: --- Affects Some People
Assignee: Konstantin Belousov
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2022-09-26 12:21 UTC by Stefan Eßer
Modified: 2024-11-25 07:35 UTC (History)
2 users (show)

See Also:


Attachments
(untested) tty: do not wait for job control when doing mod ioctl on non-blocking tty fd (2.02 KB, patch)
2022-09-26 19:34 UTC, Konstantin Belousov
no flags Details | Diff
(untested) tty: do not wait for job control when doing mod ioctl on non-blocking tty fd (2.05 KB, patch)
2022-09-26 19:38 UTC, Konstantin Belousov
no flags Details | Diff
tty: do not wait for job control when doing mod ioctl on non-blocking tty fd (2.07 KB, patch)
2022-09-26 21:25 UTC, Konstantin Belousov
no flags Details | Diff
stty(1): add -i option (1.46 KB, patch)
2022-09-26 21:36 UTC, Konstantin Belousov
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Stefan Eßer freebsd_committer freebsd_triage 2022-09-26 12:21:10 UTC
Ths stty man-page describes the -f option to cause a non-blocking open of the passed file:

     -f      Open and use the terminal named by file rather than using
             standard input.  The file is opened using the O_NONBLOCK flag of
             open(), making it possible to set or display settings on a
             terminal that might otherwise block on the open.

This does work when TTY options are fetched and displayed, but not when attempting to set some parameter.

I'd expect the following command to not block, but instead to fail with a non-zero exit status:

$ stty -f /dev/tty sane &

But the command blocks, waiting for a "fg" command, and only then executes the requested operation on the TTY.

The reason seems to be that the tcsetattr() function ignores the O_NONBLOCK flag of the passed file descriptor (or rather that the ioctl() it invokes ignores it).

This appears to be a deviation from documented behavior (and breaks a script I'm working on which wants to reset the TTY on exit, but now blocks exiting from the script when it has been put into the background by the user).
Comment 1 Konstantin Belousov freebsd_committer freebsd_triage 2022-09-26 19:33:52 UTC
What would you expect in this case to happen? TIOCSETA override the job
control rules?  I do not think this is wise.  Or TIOCSETA returning EAGAIN
for instance.  The later may be reasonable, but I am not sure how much code
would break.

Anyway, the later option probably could be done by the attached change.  Do you
know what e.g. Linux does in this case?
Comment 2 Konstantin Belousov freebsd_committer freebsd_triage 2022-09-26 19:34:29 UTC
Created attachment 236840 [details]
(untested) tty: do not wait for job control when doing mod ioctl on non-blocking tty fd
Comment 3 Konstantin Belousov freebsd_committer freebsd_triage 2022-09-26 19:38:14 UTC
Created attachment 236841 [details]
(untested) tty: do not wait for job control when doing mod ioctl on non-blocking tty fd

Some corrections to the patch
Comment 4 Konstantin Belousov freebsd_committer freebsd_triage 2022-09-26 19:42:02 UTC
And error should be EWOULDBLOCK, I changed that locally.
Comment 5 Stefan Eßer freebsd_committer freebsd_triage 2022-09-26 20:59:49 UTC
(In reply to Konstantin Belousov from comment #3)

Thank you for providing the patch. Sadly, it does only address part of the issue:

 $ stty -f /dev/tty sane &
 [1] 3355
 $ fg
 stty -f /dev/tty sane
 stty: tcsetattr: Interrupted system call
 $

The error code is returned, but the system call is still blocking.
Comment 6 Stefan Eßer freebsd_committer freebsd_triage 2022-09-26 21:19:40 UTC
(In reply to Konstantin Belousov from comment #1)

> What would you expect in this case to happen? TIOCSETA override the job
> control rules?  I do not think this is wise.  Or TIOCSETA returning EAGAIN
> for instance.  The later may be reasonable, but I am not sure how much code
> would break.

Yes, I'm also afraid that making "stty sane &" return with an error code instead of waiting for the terminal to be controlled by the stty process might cause a violation of POLA.

A program that runs in the background must not change the setting of a terminal in use by the foreground process. Both, blocking until the stty program has control of the terminal, or exiting with an error code seem to be possible approaches.

But I can imagine that programs that want to set the terminal mode to "raw" may rather want to wait for access to the terminal when started in the background, but this would still work if the terminal is not opened with O_NONBLOCK.

According to the documentation "stty sane" does not imply O_NONBLOCK, thus the blocking behavior would still be available.

If there is a way that a shell script can determine whether "stty sane" would block, I could skip that call in the script running in the background.
But I do not know of a reliable method to detect that case.

> Anyway, the later option probably could be done by the attached change.  Do you
> know what e.g. Linux does in this case?

Linux blocks the system call in the same way as FreeBSD:

$ stty sane &
[1] 8
$ fg
stty sane
$
Comment 7 Konstantin Belousov freebsd_committer freebsd_triage 2022-09-26 21:24:11 UTC
(In reply to Stefan Eßer from comment #5)
I found a Linux box to test, and so far the behavior seems to be identical
to ours.  As I said, the patch would break existing software most likely.

[kostik@fc ~]$ stty -F /dev/tty sane&
[1] 1309
[kostik@fc ~]$ 

[1]+  Stopped                 stty -F /dev/tty sane
[kostik@fc ~]$

The hang you see is due to SIGTTOU sent before return from tty_wait_background().
This means that the patch is to make things work to your liking is even less
likely to be reasonable.

I attached the updated version that should work for you, but it is arguably
not committable.
Comment 8 Konstantin Belousov freebsd_committer freebsd_triage 2022-09-26 21:25:12 UTC
Created attachment 236843 [details]
tty: do not wait for job control when doing mod ioctl on non-blocking tty fd
Comment 9 Konstantin Belousov freebsd_committer freebsd_triage 2022-09-26 21:34:00 UTC
I think I have an idea that makes your case workable:

root@:/ # stty -i -f /dev/tty sane &
root@:/ # 

[1]   Done                    stty -i -f /dev/tty sane
root@:/ # 

Note the new '-i' option, which makes stty not block when in background,
without kernel patch.  In fact it would not block even if using blocking fd.
Comment 10 Konstantin Belousov freebsd_committer freebsd_triage 2022-09-26 21:36:32 UTC
Created attachment 236847 [details]
stty(1): add -i option
Comment 11 Jilles Tjoelker freebsd_committer freebsd_triage 2022-09-27 21:13:03 UTC
A similar effect could be obtained without patching any code using a command like

    (trap '' TTOU; stty -f /dev/tty sane)

Note that with both this example and the proposed -i option, both the tcsetattr() call and any writes are affected (i.e. error messages or -a output will be written regardless of the TOSTOP setting).

I agree that patching the kernel for this is a bad idea. The O_NONBLOCK flag is intended to allow non-blocking reads and writes such as for ssh(1), not to interfere with the normal functioning of job control.
Comment 12 commit-hook freebsd_committer freebsd_triage 2022-10-02 17:31:49 UTC
A commit in branch main references this bug:

URL: https://cgit.FreeBSD.org/src/commit/?id=a7eac018437e592a575f46a59151eedc7a742fa7

commit a7eac018437e592a575f46a59151eedc7a742fa7
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2022-09-26 21:34:22 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2022-10-02 17:29:53 +0000

    stty(1): provide details about interaction with job control

    Describe a shell trick to do non-blocking modification of the terminal
    settings, by ignoring job control signals with trap built-in.

    PR:     266627
    With input from:        jilles
    Reviewed by:    pauamma
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D36745

 bin/stty/stty.1 | 28 +++++++++++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)
Comment 13 commit-hook freebsd_committer freebsd_triage 2022-10-09 02:23:46 UTC
A commit in branch stable/13 references this bug:

URL: https://cgit.FreeBSD.org/src/commit/?id=298d7504ae5b83cb61816b5ef7ae987a153f9f49

commit 298d7504ae5b83cb61816b5ef7ae987a153f9f49
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2022-09-26 21:34:22 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2022-10-09 02:23:04 +0000

    stty(1): provide details about interaction with job control

    PR:     266627

    (cherry picked from commit a7eac018437e592a575f46a59151eedc7a742fa7)

 bin/stty/stty.1 | 28 +++++++++++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)