From 3d4137ae19613793693b1f64311f96d3390e9962 Mon Sep 17 00:00:00 2001 From: Viktor Liu Date: Mon, 3 Aug 2026 09:31:27 +0200 Subject: [PATCH] Wait for the Windows VNC agent to respawn instead of refusing the connection --- client/vnc/server/agent_windows.go | 38 +++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/client/vnc/server/agent_windows.go b/client/vnc/server/agent_windows.go index b726bcc37..f61cf9ab5 100644 --- a/client/vnc/server/agent_windows.go +++ b/client/vnc/server/agent_windows.go @@ -462,15 +462,41 @@ func createKillOnCloseJob() (windows.Handle, error) { // SE_TCB_NAME is missing) it surfaces a distinct error so the daemon // can reject the connection with a meaningful message instead of timing // out the proxy dial. -func (m *sessionManager) Resolve(_ context.Context) (string, string, uint32, error) { - m.mu.Lock() - defer m.mu.Unlock() - if m.socketPath == "" { - return "", "", 0, errAgentNotReady +func (m *sessionManager) Resolve(ctx context.Context) (string, string, uint32, error) { + deadline := time.NewTimer(agentResolveWait) + defer deadline.Stop() + poll := time.NewTicker(250 * time.Millisecond) + defer poll.Stop() + + for { + m.mu.Lock() + socketPath, token := m.socketPath, m.authToken + m.mu.Unlock() + if socketPath != "" { + return socketPath, token, 0, nil + } + + select { + case <-ctx.Done(): + return "", "", 0, ctx.Err() + case <-m.done: + return "", "", 0, errAgentNotReady + case <-deadline.C: + return "", "", 0, errAgentNotReady + case <-poll.C: + } } - return m.socketPath, m.authToken, 0, nil } +// agentResolveWait bounds how long a connection waits for an agent to appear. +// Signing in replaces the agent, and the manager only publishes a socket once +// the new one is listening, so there is a window with nothing to hand out. +// Failing instantly there is what made a sign-in look like "the peer cannot +// capture its screen": the wait turns it into a pause instead. Windows session +// initialisation is the slow part, so this is generous compared with the +// manager's own tick. +const agentResolveWait = 30 * time.Second + var errAgentNotReady = errors.New("VNC agent not running yet") // Stop signals the session manager to exit its polling loop and closes the