From f7c3fa972cdfb6dbc96c3b4840cdf5d880880751 Mon Sep 17 00:00:00 2001 From: Augustin Cavalier Date: Wed, 3 Sep 2025 18:37:47 -0400 Subject: [PATCH] tty: Notify disconnections properly, and with B_SELECT_DISCONNECTED. The tty_notify_select_event() at the end of tty_close_cookie() checked if the other_tty's open_count was > 0 before notifying. But in the case where the master is closing all children, it will drop to 0 and leave things still in the select pool. So we should notify unconditionally here. Additionally, use B_SELECT_DISCONNECTED (i.e. POLLHUP). This matches what Linux seems to do. Fixes #19714. --- src/add-ons/kernel/generic/tty/tty.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/add-ons/kernel/generic/tty/tty.cpp b/src/add-ons/kernel/generic/tty/tty.cpp index dea650b6e8..d142198e20 100644 --- a/src/add-ons/kernel/generic/tty/tty.cpp +++ b/src/add-ons/kernel/generic/tty/tty.cpp @@ -1528,11 +1528,8 @@ tty_close_cookie(tty_cookie* cookie) requestLocker.Unlock(); - // notify a select read and write event on the other tty, if we've closed this tty - if (cookie->other_tty->open_count > 0) { - tty_notify_select_event(cookie->other_tty, B_SELECT_WRITE); - tty_notify_select_event(cookie->other_tty, B_SELECT_READ); - } + // notify a select event on the other tty, if we've closed this tty + tty_notify_select_event(cookie->other_tty, B_SELECT_DISCONNECTED); cookie->tty->is_exclusive = false; } @@ -1998,7 +1995,7 @@ tty_select(tty_cookie* cookie, uint8 event, uint32 ref, selectsync* sync) "%p)\n", cookie, event, ref, sync)); // we don't support all kinds of events - if (event < B_SELECT_READ || event > B_SELECT_ERROR) + if (event < B_SELECT_READ || (event > B_SELECT_ERROR && event != B_SELECT_DISCONNECTED)) return B_BAD_VALUE; // if the TTY is already closed, we notify immediately @@ -2006,7 +2003,8 @@ tty_select(tty_cookie* cookie, uint8 event, uint32 ref, selectsync* sync) if (!ttyReference.IsLocked()) { TRACE(("tty_select() done: cookie %p already closed\n", cookie)); - notify_select_event(sync, event); + if (event == B_SELECT_DISCONNECTED) + notify_select_event(sync, event); return B_OK; } @@ -2042,10 +2040,8 @@ tty_select(tty_cookie* cookie, uint8 event, uint32 ref, selectsync* sync) case B_SELECT_WRITE: { // writes go to the other TTY - if (!otherTTY) { - notify_select_event(sync, event); + if (otherTTY == NULL) break; - } // In case input is echoed, we have to check, whether we can // currently can write to our TTY as well. @@ -2063,6 +2059,11 @@ tty_select(tty_cookie* cookie, uint8 event, uint32 ref, selectsync* sync) break; } + case B_SELECT_DISCONNECTED: + if (otherTTY == NULL) + notify_select_event(sync, event); + break; + case B_SELECT_ERROR: default: break; @@ -2081,7 +2082,7 @@ tty_deselect(tty_cookie* cookie, uint8 event, selectsync* sync) sync)); // we don't support all kinds of events - if (event < B_SELECT_READ || event > B_SELECT_ERROR) + if (event < B_SELECT_READ || (event > B_SELECT_ERROR && event != B_SELECT_DISCONNECTED)) return B_BAD_VALUE; // lock the TTY (guards the select sync pool, among other things)