From 01f57dcc170d6207c73b3e9d7646137f599bb088 Mon Sep 17 00:00:00 2001 From: Dmitry Lyfar Date: Fri, 2 Aug 2024 18:54:16 +1200 Subject: [PATCH] fix: send EOF to master pty in interactive mode If the interactive mode is enforced, there are scenarios like: go run ./cmd/pebble exec -i -- tee < ./HACKING.md that would prompt the server to shutdown the execution earlier than the whole output from 'tee' will be read and sent to the client. Hence, instead of closing the master pty descriptor when there is no more input from the client, send Ctrl-D (EOF) to indicate to the process that there will be no more input. --- internals/overlord/cmdstate/handlers.go | 7 ++++++- internals/wsutil/wsutil_linux.go | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/internals/overlord/cmdstate/handlers.go b/internals/overlord/cmdstate/handlers.go index 4312a6542..4e657ef6a 100644 --- a/internals/overlord/cmdstate/handlers.go +++ b/internals/overlord/cmdstate/handlers.go @@ -252,7 +252,12 @@ func (e *execution) do(ctx context.Context, task *state.Task) error { // websocket and write to the PTY. go func() { <-wsutil.WebsocketRecvStream(master, ioConn) - master.Close() + // If the interactive is enforced, it is possible to finish + // reading earlier than the mirroring go routine sends all the + // output to the client. Thus, closing the master descriptor + // here will terminate mirroring prematurely. Instead, we + // should send Ctrl-D to the fd to indicate the end of input. + master.Write([]byte{byte(unix.VEOF)}) }() } else { // Non-interactive: start goroutine to receive stdin from "stdio" diff --git a/internals/wsutil/wsutil_linux.go b/internals/wsutil/wsutil_linux.go index dd2ba57ac..0536f1c7a 100644 --- a/internals/wsutil/wsutil_linux.go +++ b/internals/wsutil/wsutil_linux.go @@ -32,7 +32,7 @@ func MirrorToWebsocket(conn MessageWriter, r io.ReadCloser, exited chan struct{} for { buf, ok := <-in if !ok { - r.Close() + _ = r.Close() logger.Debugf("Sending write barrier") err := conn.WriteMessage(websocket.TextMessage, endCommandJSON) if err != nil {