> If you're able to reproduce the problem, and modify the program to report
> the errno value, then we can discuss whether the errno value matches the
> expected behavior.

I have run the requested tests to capture errno during both a pipe EOF and
a forced SSH disconnect.

Environment
-----------
Ubuntu 22.04.5 LTS in WSL2
ncurses 6.3.20211021

Test Program
------------
#include <ncurses.h>
#include <errno.h>
#include <stdio.h>

int main() {
    FILE *f = fopen("trace.log", "w");
    initscr();
    halfdelay(5);
    for (int i = 0; i < 100; ++i) {
        errno = 0;
        int ch = getch();
        int err = errno;
        fprintf(f, "getch: %d, errno: %d\n", ch, err);
        fflush(f);
    }
    endwin();
    fclose(f);
}


Scenario 1: Pipe EOF
--------------------
$ echo "hi" | ./a.out ; cat trace.log

Results show getch: 104, 105, 10 followed by consistent getch: -1, errno: 0.


Scenario 2: SSH Disconnect
--------------------------
1. SSH into host.
2. Run ./a.out.
3. Hard-close the terminal window (sending no logout/exit).
4. Inspect log after 30 seconds.

Trace Log Result:
getch: 72, errno: 0   (H)
getch: 105, errno: 0  (i)
getch: -1, errno: 0
getch: -1, errno: 0
... [continues until loop end] ...
getch: -1, errno: 0


Scenario 3: Manually closing stdin (FD 0)
-----------------------------------------
To test if getch() would report EBADF, I added close(0) inside the loop.

Result: The loop completed instantly (ignoring the halfdelay timeout).
getch() returned -1 for every iteration, but errno remained 0.

This demonstrates that even when the file descriptor is explicitly invalid
(EBADF condition), getch() does not pass that error information through to
the caller.


Conclusion
----------
In all cases, ncurses reports ERR but the system does not report EBADF or
any other error via errno.
This suggests that the terminal disconnect is being treated as a persistent
EOF (0) rather than a file descriptor error.

On Sun, Mar 29, 2026 at 5:32 PM Thomas Dickey <[email protected]>
wrote:

> On Sun, Mar 29, 2026 at 12:15:45PM +0200, Klaas van Aarsen wrote:
> > > man read(2)
> > >       EBADF  fd is not a valid file descriptor or is not open for
> reading.
> > > Klaas is describing a condition which isn't in the manpage - ymmv.
> >
> > Regarding the suggestion of EBADF, it is my understanding that the kernel
> > must keep file descriptor 0 open (valid) even after a disconnect to
> prevent
> > immediate FD reuse.
>
> either it's closed, or it's not.
>
> ncurses doesn't reopen it.
>
> Since ncurses doesn't reopen it, I don't see any reason why the dangling
> symlink would be relevant.
>
> Checking errno after the failed call would be more useful than making
> a signal handler :-)
>
> > If read() were to return EBADF while the descriptor is still allocated to
> > the process, it would technically contradict the POSIX definition of that
> > error.
>
> If you're able to reproduce the problem, and modify the program to report
> the errno value, then we can discuss whether the errno value matches the
> expected behavior.
>
> > While the kernel could conceivably return an error like EIO or ENXIO to
> > signal a "dead" device, treating a terminal disconnect as an "end of
> > stream" (returning 0) appears to be the standard behavior for
> > pseudo-terminals across most modern Unix-like systems.
> > This ensures the stream is closed gracefully without releasing the
> > descriptor slot prematurely.
>
> ncurses is using the read directly (stream usually refers to things
> opened via fopen).
>
> initscr (used in this program) calls newterm
>
>         if (newterm(name, stdout, stdin) == NULL) {
>
> newterm copies the file descriptor:
>
>             SP_PARM->_ifd = fileno(_ifp);
>
> and after that, it ignores any buffering that the caller may have on that
> file descriptor.  ncurses never closes that file descriptor.  If you got
> EBADF, then that would indicate that the connection was closed (either
> end of a connection can close it).
>
> > I appreciate the technical dialogue on this.
> >
> >
> > On Sun, Mar 29, 2026 at 11:59 AM Thomas Dickey <
> [email protected]>
> > wrote:
> >
> > > On Sun, Mar 29, 2026 at 11:40:56AM +0200, Stian Skjelstad wrote:
> > > > > Actually what you're telling us is that there's a defect in
> (presumably
> > > > Linux)
> > > > > which should be documented in read(2).  Once you've gotten it
> > > documented
> > > > in
> > > > >the proper place (Linux manpages), then others can use the
> > > documentation.
> > > >
> > > > It is normal for read() to return 0, without an error when EOF is
> > > reached.
> > > > This is how you detect EOF/closed on sockets and files. Should TTY
> behave
> > > > differently?
> > >
> > > man read(2)
> > >
> > >        EBADF  fd is not a valid file descriptor or is not open for
> reading.
> > >
> > > Klaas is describing a condition which isn't in the manpage - ymmv.
> > >
> > > --
> > > Thomas E. Dickey <[email protected]>
> > > https://invisible-island.net
> > >
>
> --
> Thomas E. Dickey <[email protected]>
> https://invisible-island.net
>

Reply via email to