MCPcopy Index your code
hub / github.com/coder/coder / doAttach

Method doAttach

agent/reconnectingpty/screen.go:218–329  ·  view source on GitHub ↗

doAttach spawns the screen client and starts the heartbeat. It exists separately only so we can defer the mutex unlock which is not possible in Attach since it blocks.

(ctx context.Context, conn net.Conn, height, width uint16, logger slog.Logger)

Source from the content-addressed store, hash-verified

216// separately only so we can defer the mutex unlock which is not possible in
217// Attach since it blocks.
218func (rpty *screenReconnectingPTY) doAttach(ctx context.Context, conn net.Conn, height, width uint16, logger slog.Logger) (pty.PTYCmd, pty.Process, error) {
219 // Ensure another attach does not come in and spawn a duplicate session.
220 rpty.mutex.Lock()
221 defer rpty.mutex.Unlock()
222
223 logger.Debug(ctx, "spawning screen client", slog.F("screen_id", rpty.id))
224
225 // Wrap the command with screen and tie it to the connection's context.
226 cmd := rpty.execer.PTYCommandContext(ctx, "screen", append([]string{
227 // -S is for setting the session's name.
228 "-S", rpty.id,
229 // -U tells screen to use UTF-8 encoding.
230 // -x allows attaching to an already attached session.
231 // -RR reattaches to the daemon or creates the session daemon if missing.
232 // -q disables the "New screen..." message that appears for five seconds
233 // when creating a new session with -RR.
234 // -c is the flag for the config file.
235 "-UxRRqc", rpty.configFile,
236 rpty.command.Path,
237 // pty.Cmd duplicates Path as the first argument so remove it.
238 }, rpty.command.Args[1:]...)...)
239 cmd.Env = withTerminalEnv(rpty.command.Env)
240 cmd.Dir = rpty.command.Dir
241 ptty, process, err := pty.Start(cmd, pty.WithPTYOption(
242 pty.WithSSHRequest(ssh.Pty{
243 Window: ssh.Window{
244 // Make sure to spawn at the right size because if we resize afterward it
245 // leaves confusing padding (screen will resize such that the screen
246 // contents are aligned to the bottom).
247 Height: int(height),
248 Width: int(width),
249 },
250 }),
251 ))
252 if err != nil {
253 rpty.metrics.WithLabelValues("screen_spawn").Add(1)
254 return nil, nil, err
255 }
256
257 // This context lets us abort the version command if the process dies.
258 versionCtx, versionCancel := context.WithCancel(ctx)
259 defer versionCancel()
260
261 // Pipe pty -> conn and close the connection when the process exits.
262 // We do not need to separately monitor for the process exiting. When it
263 // exits, our ptty.OutputReader() will return EOF after reading all process
264 // output.
265 go func() {
266 defer versionCancel()
267 defer func() {
268 err := conn.Close()
269 if err != nil {
270 // Log only for debugging since the connection might have already closed
271 // on its own.
272 logger.Debug(ctx, "closed connection with error", slog.Error(err))
273 }
274 }()
275 buffer := make([]byte, 1024)

Callers 1

AttachMethod · 0.95

Calls 15

sendCommandMethod · 0.95
StartFunction · 0.92
WithPTYOptionFunction · 0.92
WithSSHRequestFunction · 0.92
withTerminalEnvFunction · 0.85
WithLabelValuesMethod · 0.80
PTYCommandContextMethod · 0.65
AddMethod · 0.65
CloseMethod · 0.65
ReadMethod · 0.65
OutputReaderMethod · 0.65
WriteMethod · 0.65

Tested by

no test coverage detected