fix: keep a crash log for a card read error

Any EIO was read as the window closing, so a failing SD card ended the
interface with 129 and no error.log. The error now counts as a closed
window only once the terminal itself no longer answers tcgetattr.
This commit is contained in:
Abdessamad Derraz committed 2026-10-23 22:13:42 +02:00
1 parent 76da554da9
commit bc4bcadcd3
2 files changed
+45 -6

No files matched your search

+22 -6
View File
@@ -12966,7 +12966,7 @@ def run_tui(args: argparse.Namespace) -> int:
# The result is printed in the terminal on the way out.
state.exit_code = state.code if state.code is not None else 1
except Exception as exc: # noqa: BLE001
if flags["term"] or _closed_terminal(exc):
if flags["term"] or _closed_terminal(exc, terminal.fd):
# The window closed under a screen that was changing, or the
# SIGCONT that comes with its SIGHUP put the screen back: the
# write failed before the signal was read. That is its exit.
@@ -13030,13 +13030,29 @@ def _tui_startup(args: argparse.Namespace) -> tuple[TuiState, list[tuple]] | Non
_CLOSED_TERMINAL = frozenset({errno.EIO, errno.EPIPE, errno.ENXIO})
def _closed_terminal(exc: BaseException) -> bool:
def _closed_terminal(exc: BaseException, fd: int | None = None) -> bool:
"""Whether exc is the terminal gone: an OSError, or a termios.error,
whose first argument is the errno, answering EIO, EPIPE or ENXIO."""
whose first argument is the errno, answering EIO, EPIPE or ENXIO, and,
given the terminal's fd, that terminal no longer answering. A failing
SD card answers EIO too, and that read error is kept in a crash log."""
if isinstance(exc, OSError):
return exc.errno in _CLOSED_TERMINAL
if type(exc).__name__ == "error" and type(exc).__module__ == "termios":
return bool(exc.args) and exc.args[0] in _CLOSED_TERMINAL
closed = exc.errno in _CLOSED_TERMINAL
elif type(exc).__name__ == "error" and type(exc).__module__ == "termios":
closed = bool(exc.args) and exc.args[0] in _CLOSED_TERMINAL
else:
closed = False
return closed and (fd is None or _terminal_gone(fd))
def _terminal_gone(fd: int) -> bool:
"""Whether the terminal at fd hung up: a closed window answers EIO to
tcgetattr, a live one its settings."""
import termios # noqa: PLC0415
try:
termios.tcgetattr(fd)
except (termios.error, OSError):
return True
return False
+23
View File
@@ -2557,6 +2557,29 @@ class IdleExit(unittest.TestCase):
["RetroBIOS: No button pressed, closing."])
class AReadErrorIsNotAClosedWindow(unittest.TestCase):
"""Any EIO read as the window closing: a failing SD card answers EIO
too, and that run ended 129, no error.log, no launcher message."""
def test_eio_counts_only_once_the_terminal_hung_up(self):
import pty
master, slave = pty.openpty()
try:
card = OSError(errno.EIO, "Input/output error", "/media/card/bios/x.bin")
self.assertFalse(install._closed_terminal(card, slave))
self.assertFalse(install._closed_terminal(
OSError(errno.ENOENT, "No such file"), slave))
os.close(master)
master = -1
self.assertTrue(install._closed_terminal(card, slave))
self.assertTrue(install._closed_terminal(termios.error(errno.EIO, "gone"), slave))
finally:
os.close(slave)
if master >= 0:
os.close(master)
class AuditRoundFour(unittest.TestCase):
"""tui_audit_3.md, each finding held where it was found."""