Skip to content

ssh_executor: Execute commands without creating stdin file - #67

Merged
robertbaldyga merged 1 commit into
Open-CAS:masterfrom
robertbaldyga:ssh-exec-no-stdin
Oct 21, 2026
Merged

robertbaldyga merged 1 commit into
Open-CAS:masterfrom
robertbaldyga:ssh-exec-no-stdin

Conversation

@robertbaldyga

Copy link
Copy Markdown
Member

SSHClient.exec_command() returns a stdin file object, which _execute() and is_active() never use. When it is garbage collected, its close() calls Channel.shutdown_write(), which builds the EOF message under the channel lock but sends it only after releasing the lock. If the transport thread handles the server's channel CLOSE in that window, it sends EOF and CLOSE of its own, and our EOF reaches the server after the CLOSE. OpenSSH sshd (seen with 10.2) treats this as a protocol error:

channel_by_id: 1: bad id: channel free
Disconnecting user root ...: ieof packet referred to nonexistent channel 1

and drops the whole connection, killing every command running on it. With several threads sharing one connection, this shows up as random commands failing with exit code -1.

Open the session channel directly instead, run the command on it and read only stdout and stderr, so no stdin file object is ever created. Close the channel explicitly once the command completes; Channel.close() sends EOF and CLOSE together, in order, and only if the channel is not closed already.

SSHClient.exec_command() returns a stdin file object, which _execute()
and is_active() never use. When it is garbage collected, its close()
calls Channel.shutdown_write(), which builds the EOF message under the
channel lock but sends it only after releasing the lock. If the
transport thread handles the server's channel CLOSE in that window, it
sends EOF and CLOSE of its own, and our EOF reaches the server after the
CLOSE. OpenSSH sshd (seen with 10.2) treats this as a protocol error:

  channel_by_id: 1: bad id: channel free
  Disconnecting user root ...: ieof packet referred to nonexistent channel 1

and drops the whole connection, killing every command running on it.
With several threads sharing one connection, this shows up as random
commands failing with exit code -1.

Open the session channel directly instead, run the command on it and
read only stdout and stderr, so no stdin file object is ever created.
Close the channel explicitly once the command completes; Channel.close()
sends EOF and CLOSE together, in order, and only if the channel is not
closed already.

Signed-off-by: Robert Baldyga <robert.baldyga@unvertical.com>
@robertbaldyga
robertbaldyga merged commit 8b7af57 into Open-CAS:master Oct 21, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant