From b19cb39219ec6f18ae542aa6ad8c03cc5a24033d Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Thu, 16 Jan 2025 22:24:35 -0800 Subject: [PATCH 1/3] feat: add basic wsl nowsh implementation --- pkg/blockcontroller/blockcontroller.go | 20 +++++++++++++++++--- pkg/shellexec/shellexec.go | 21 +++++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/pkg/blockcontroller/blockcontroller.go b/pkg/blockcontroller/blockcontroller.go index 38238ebbf6..ccdeec7a03 100644 --- a/pkg/blockcontroller/blockcontroller.go +++ b/pkg/blockcontroller/blockcontroller.go @@ -388,9 +388,23 @@ func (bc *BlockController) setupAndStartShellProcess(logCtx context.Context, rc swapToken.Env[wshutil.WaveJwtTokenVarName] = jwtStr cmdOpts.Env[wshutil.WaveJwtTokenVarName] = jwtStr } - shellProc, err = shellexec.StartWslShellProc(ctx, rc.TermSize, cmdStr, cmdOpts, wslConn) - if err != nil { - return nil, err + if !wslConn.WshEnabled.Load() { + shellProc, err = shellexec.StartWslShellProcNoWsh(ctx, rc.TermSize, cmdStr, cmdOpts, wslConn) + if err != nil { + return nil, err + } + } else { + shellProc, err = shellexec.StartWslShellProc(ctx, rc.TermSize, cmdStr, cmdOpts, wslConn) + if err != nil { + wslConn.SetWshError(err) + wslConn.WshEnabled.Store(false) + log.Printf("error starting wsl shell proc with wsh: %v", err) + log.Print("attempting install without wsh") + shellProc, err = shellexec.StartWslShellProcNoWsh(ctx, rc.TermSize, cmdStr, cmdOpts, wslConn) + if err != nil { + return nil, err + } + } } } else if remoteName != "" { credentialCtx, cancelFunc := context.WithTimeout(context.Background(), 60*time.Second) diff --git a/pkg/shellexec/shellexec.go b/pkg/shellexec/shellexec.go index 81ce89a360..394f2fbe7b 100644 --- a/pkg/shellexec/shellexec.go +++ b/pkg/shellexec/shellexec.go @@ -150,6 +150,27 @@ func (pp *PipePty) WriteString(s string) (n int, err error) { return pp.Write([]byte(s)) } +func StartWslShellProcNoWsh(ctx context.Context, termSize waveobj.TermSize, cmdStr string, cmdOpts CommandOptsType, conn *wslconn.WslConn) (*ShellProc, error) { + client := conn.GetClient() + conn.Infof(ctx, "WSL-NEWSESSION (StartWslShellProcNoWsh)") + + ecmd := exec.Command("wsl.exe", "~", "-d", client.Name()) + + if termSize.Rows == 0 || termSize.Cols == 0 { + termSize.Rows = shellutil.DefaultTermRows + termSize.Cols = shellutil.DefaultTermCols + } + if termSize.Rows <= 0 || termSize.Cols <= 0 { + return nil, fmt.Errorf("invalid term size: %v", termSize) + } + cmdPty, err := pty.StartWithSize(ecmd, &pty.Winsize{Rows: uint16(termSize.Rows), Cols: uint16(termSize.Cols)}) + if err != nil { + return nil, err + } + cmdWrap := MakeCmdWrap(ecmd, cmdPty) + return &ShellProc{Cmd: cmdWrap, ConnName: conn.GetName(), CloseOnce: &sync.Once{}, DoneCh: make(chan any)}, nil +} + func StartWslShellProc(ctx context.Context, termSize waveobj.TermSize, cmdStr string, cmdOpts CommandOptsType, conn *wslconn.WslConn) (*ShellProc, error) { client := conn.GetClient() conn.Infof(ctx, "WSL-NEWSESSION (StartWslShellProc)") From ba9f902584d9fb34f73f4c2d4c882bc77cc75d26 Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Fri, 17 Jan 2025 14:29:00 -0800 Subject: [PATCH 2/3] fix: persist no wsh errors to frontend on wsl --- pkg/wslconn/wslconn.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pkg/wslconn/wslconn.go b/pkg/wslconn/wslconn.go index 1f561c8269..42ec611189 100644 --- a/pkg/wslconn/wslconn.go +++ b/pkg/wslconn/wslconn.go @@ -101,11 +101,14 @@ func (conn *WslConn) DeriveConnStatus() wshrpc.ConnStatus { return wshrpc.ConnStatus{ Status: conn.Status, Connected: conn.Status == Status_Connected, - WshEnabled: true, // always use wsh for wsl connections (temporary) + WshEnabled: conn.WshEnabled.Load(), Connection: conn.GetName(), HasConnected: (conn.LastConnectTime > 0), ActiveConnNum: conn.ActiveConnNum, Error: conn.Error, + WshError: conn.WshError, + NoWshReason: conn.NoWshReason, + WshVersion: conn.WshVersion, } } @@ -702,6 +705,9 @@ func (conn *WslConn) waitForDisconnect() { log.Printf("wait for disconnect in %+#v", conn) defer conn.FireConnChangeEvent() defer conn.HasWaiter.Store(false) + if conn.ConnController == nil { + return + } err := conn.ConnController.Wait() conn.WithLock(func() { // disconnects happen for a variety of reasons (like network, etc. and are typically transient) From 5c76320500b5716df6d7aaef1223f8fa7d89928e Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Fri, 17 Jan 2025 14:42:56 -0800 Subject: [PATCH 3/3] feat: dismissable wsh error for wsl --- pkg/wshrpc/wshserver/wshserver.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pkg/wshrpc/wshserver/wshserver.go b/pkg/wshrpc/wshserver/wshserver.go index 13618bfac1..dce043fcde 100644 --- a/pkg/wshrpc/wshserver/wshserver.go +++ b/pkg/wshrpc/wshserver/wshserver.go @@ -783,6 +783,16 @@ func (ws *WshServer) WslDefaultDistroCommand(ctx context.Context) (string, error * Dismisses the WshFail Command in runtime memory on the backend */ func (ws *WshServer) DismissWshFailCommand(ctx context.Context, connName string) error { + if strings.HasPrefix(connName, "wsl://") { + distroName := strings.TrimPrefix(connName, "wsl://") + conn := wslconn.GetWslConn(ctx, distroName, false) + if conn == nil { + return fmt.Errorf("connection not found: %s", connName) + } + conn.ClearWshError() + conn.FireConnChangeEvent() + return nil + } opts, err := remote.ParseOpts(connName) if err != nil { return err