tailscale/shell.nix
Kristoffer Dalby 4fd5bcc2b8 ssh/tailssh: fix exit-status ordering and improve session shutdown
Fix a race where CloseWrite (SSH_MSG_CHANNEL_EOF) could be sent before
Exit (exit-status), causing SSH clients (especially on macOS) to miss
the exit status. The root cause was a select between outputDone and
ctx.Done that allowed Exit to be called while stdout was still copying,
or after CloseWrite had already been sent.

Replace the atomic.Bool/atomic.Int32/channel synchronization with
sync.WaitGroup and guarantee the wire ordering required by RFC 4254
section 6.10: exit-status -> EOF -> CHANNEL_CLOSE.

The new ordering in run():
1. cmd.Wait() returns with exit code
2. ss.Exit(exitCode) sends exit-status
3. closeAll(childPipes) signals io.Copy goroutines to finish
4. wg.Wait() waits for stdout goroutine which calls CloseWrite (EOF)
5. defer ss.Close() sends CHANNEL_CLOSE on function return

Additional changes:
- Send SIGHUP instead of SIGKILL on session termination
- Use exit code 255 for SSH protocol/permission errors
- Use exit code 127 for command not found
- Use exit code 254 for recording failures
- Add isNotFoundOrExecutable helper

Based on tailscale/tailscale#18331.

Fixes #18256

Change-Id: Ib0c984d466c1a96c8f642e94a5dfe60d33d71fd8
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2026-05-04 11:29:41 +00:00

20 lines
788 B
Nix

# This is a shell.nix file used to describe the environment that
# tailscale needs for development.
#
# For more information about this and why this file is useful, see here:
# https://nixos.org/guides/nix-pills/developing-with-nix-shell.html
#
# Also look into direnv: https://direnv.net/, this can make it so that you can
# automatically get your environment set up when you change folders into the
# project.
(import (
let
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
in fetchTarball {
url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
sha256 = lock.nodes.flake-compat.locked.narHash; }
) {
src = ./.;
}).shellNix
# nix-direnv cache busting line: sha256-jdsGXGI6v+uaC9rWZT30WEngV9HHSHbg/kNKje4U0Uk=