Wait waits for the command to finish and cleans up PTY resources.
()
| 236 | |
| 237 | // Wait waits for the command to finish and cleans up PTY resources. |
| 238 | func (h *PTYHandle) Wait() error { |
| 239 | // Wait for the command to finish |
| 240 | cmdErr := h.cmd.Wait() |
| 241 | |
| 242 | // Restore terminal state before any other cleanup |
| 243 | if h.oldTermState != nil { |
| 244 | if restoreErr := term.Restore(int(os.Stdin.Fd()), h.oldTermState); restoreErr != nil { |
| 245 | h.logger.Debug("failed to restore terminal state", zap.Error(restoreErr)) |
| 246 | } |
| 247 | } |
| 248 | |
| 249 | // Use stopOnce to ensure cleanup only happens once (Wait and StopPTYCommand may both be called) |
| 250 | h.stopOnce.Do(func() { |
| 251 | // Stop listening for resize signals |
| 252 | signal.Stop(h.resizeCh) |
| 253 | // Drain any pending signals |
| 254 | select { |
| 255 | case <-h.resizeCh: |
| 256 | default: |
| 257 | } |
| 258 | close(h.resizeCh) |
| 259 | |
| 260 | // Set closing flag and close PTY under mutex to prevent race with resize handler |
| 261 | h.ptmxMu.Lock() |
| 262 | h.closing = true |
| 263 | h.closeOnce.Do(func() { |
| 264 | if closeErr := h.ptmx.Close(); closeErr != nil { |
| 265 | h.logger.Debug("failed to close PTY", zap.Error(closeErr)) |
| 266 | } |
| 267 | }) |
| 268 | h.ptmxMu.Unlock() |
| 269 | }) |
| 270 | |
| 271 | // Wait for background goroutines (resize, stdout copy, stdin copy) to finish |
| 272 | h.wg.Wait() |
| 273 | |
| 274 | return cmdErr |
| 275 | } |
| 276 | |
| 277 | // GetProcess returns the underlying process for signal handling |
| 278 | func (h *PTYHandle) GetProcess() *os.Process { |