feat(session): surface exec exit-status / exit-signal via SshSession.exitInfo#232
Conversation
…p probe (#58) Phase 3 hit an upstream wall worth documenting: sshlib runs exec but drops the RFC 4254 §6.10 exit-status/exit-signal reports, so ExecResult.exitStatus cannot be produced honestly — and run_command automations branch on exit codes. Upstreamed as connectbot/cbssh#232 (SshSession.exitInfo: Deferred<SessionExit?>, flow + OpenSSH-9.9p2 integration tests, 1170 upstream tests green). Haven side, landable today: ExecContractTest — the engine-agnostic exec contract (stdout, stderr, exit codes, 1 MiB output, timeout shape) against MINA with an in-process scripted CommandFactory, run by JschExecContractTest as the behaviour pin; plus a spike GAP probe asserting the released sshlib has no exit API — it fails when a release carries #232, which is the signal to add SshlibExecContractTest and the engine-aware exec routing.
|
Some context on where this fits, @kruton, for whenever you next have review bandwidth (CI is green across JDK 17/21/25 + CodeQL). This closes the last gap for adopting sshlib as a second SSH engine in Haven — the migration you kindly kicked off back in GlassHaven/Haven#58 when you reached out about ssh-proto. Since then the exec (#95), subsystem (#98), SFTP (#112) and FIDO2/SK (#146) work all landed upstream, and Haven now has the per-engine building blocks built and tested against real servers:
The one thing still missing is a truthful command exit status. Haven runs Happy to reshape the API if you'd prefer something other than an exposed |
478277a to
0c4163e
Compare
…exitInfo The kaitai schemas already parse the RFC 4254 §6.10 exit-status and exit-signal channel requests, but the connection's packet loop dropped them after logging, so exec consumers had no way to learn a command's exit code. Adds a sealed SessionExit (Status / Signal) and a Deferred exitInfo on SshSession, completed from the packet loop when the server reports termination and resolved to null when the channel closes without a report (servers are not required to send one). First report wins per the RFC. Tests: four FakeSshServer flow tests (status, signal, close-without- report resolves null, first-report-wins) plus two Testcontainers integration tests against OpenSSH 9.9p2 (sh -c 'exit 42' and a KILL-terminated command). Metalava signature updated; metalavaCheckCompatibility passes.
0c4163e to
d961d41
Compare
What
Adds
SshSession.exitInfo: Deferred<SessionExit?>— the RFC 4254 §6.10exit-status/exit-signalreports, which the packet loop previously parsed (the kaitai types were already wired intoSshMsgChannelRequest) but dropped after logging. Without this, an exec consumer can read stdout/stderr to EOF but never learn how the command terminated.SessionExitsealed interface:Status(code)/Signal(signalName, coreDumped, errorMessage)nullwhen the channel closes without a report (servers aren't required to send one), so waiters are never left suspendedmetalavaCheckCompatibilitypassesWhy
Haven is adopting sshlib as its second SSH engine (context in GlassHaven/Haven#58); the next slice is remote command execution, where automations branch on exit codes. This was the one missing piece for exec parity — happy to adjust the API shape (e.g. a suspend accessor instead of the exposed
Deferred) if you'd prefer.Tests
FakeSshServerflow tests: exit-status completesexitInfo; exit-signal completes it; close-without-report resolves null; first report wins over a later onesh -c 'exit 42'→Status(42); a KILL-terminated command →Signal("KILL", …):sshlib:testsuite: 1170 tests, 0 failures (unit + integration, Docker available)