mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-09-21 16:02:20 +00:00
A remote link worked for a while, then every operation on it failed with "could not identify the remote machine: could not open a command channel: Failed to open channel (ConnectFailed)", and Try Again only made it worse. sshd was refusing the session channel: its stock MaxSessions is ten, and tty7 had left ten open on the cached connection. russh closes a channel in exactly one case. When the server sends CHANNEL_CLOSE first, the session task answers it on arrival. Dropping a `Channel` sends nothing — the one close-on-drop it has sits behind `into_stream`, which the remote link and SFTP already ride and which a command whose output is read with `wait` does not. So a command that ran and exited cost nothing, and a channel abandoned while the far side was still running it cost a session for the life of the connection. There were four ways to abandon one. The installer's `exec` returned early on a failed exec request, and more to the point was dropped mid-drain by the timeouts in `run` and `spawn_detached`: a `uname` that hangs or a daemon launch that does not answer within its budget is what those timeouts are for, and each one pinned a session. The shell and env probes broke out of their drain on EOF or at their output limit and dropped the channel. And `drive_channel`, the pane's own shell, closed only on the pane's Close: a pane whose reader had gone while the shell still ran broke out of its loop and left that shell's session held for as long as the cached connection lived. That last one is the "after some use". A command now rides a `CommandChannel`, which closes on drop: the `?` after the open, the normal return and the timeout's cancellation all queue the CHANNEL_CLOSE for the session task, the way russh's own close-on-drop does. The runtime it spawns on is taken at construction, on the runtime by definition, rather than looked up from whichever thread the drop lands on. The probes ride the same type. `drive_channel` closes after its loop on every exit; after a close the server sent first, russh has already taken the channel out of its table and the redundant EOF and CLOSE put nothing on the wire. The safety net, for a leak this change did not find: a connection whose session open comes back ConnectFailed marks itself dead, and `is_alive` is what the cache consults before handing a connection out again, so the next Connect — Try Again included — dials afresh instead of retrying a link that will refuse forever. It is not the fix: a fresh connection to a leaking client is ten operations from the same wall. The shell probe also no longer remembers a "no integration" it got from a link that refused it a channel, which would have kept integration off that host for the rest of the run. The install layer is tested against `FakeRemote`, which has no wire, so none of this was visible. An SSH server now runs in the test process — russh's server half, accepting every session up to a limit and answering `exec` as a command that exits or one that hangs — and counts the channels the client opened and closed. It shows a timed-out command closing its channel, twelve abandoned commands against a limit of ten with none refused, a finished command's close answered exactly once, a gone pane closing the shell behind it, and a refused open retiring the connection. Each was checked against the old code. What it cannot show is sshd's own accounting; the reporter did that, with a paramiko script that exec'd freely while closing each channel and was refused on the eleventh it left open. Diagnosis and reproduction by xAlisher.