monitorChild waits for a child process to exit and handles cleanup. This should be called in a goroutine after spawnChild.
(child *ChildProcess)
| 234 | |
| 235 | // monitorChild waits for a child process to exit and handles cleanup. |
| 236 | // This should be called in a goroutine after spawnChild. |
| 237 | func (s *Server) monitorChild(child *ChildProcess) { |
| 238 | // Wait for the child process to exit |
| 239 | err := child.Cmd.Wait() |
| 240 | |
| 241 | // Get exit code |
| 242 | exitCode := 0 |
| 243 | if err != nil { |
| 244 | if exitErr, ok := err.(*exec.ExitError); ok { |
| 245 | exitCode = exitErr.ExitCode() |
| 246 | } else { |
| 247 | exitCode = ExitError |
| 248 | } |
| 249 | } |
| 250 | child.exitCode = exitCode |
| 251 | |
| 252 | duration := time.Since(child.StartTime) |
| 253 | |
| 254 | // Handle auth failure exit code |
| 255 | if exitCode == ExitAuthFailure { |
| 256 | // Record failed auth for rate limiting |
| 257 | // We need to parse the remote address to get a net.Addr |
| 258 | addr, err := net.ResolveTCPAddr("tcp", child.RemoteAddr) |
| 259 | if err == nil { |
| 260 | banned := s.rateLimiter.RecordFailedAuth(addr) |
| 261 | if banned { |
| 262 | slog.Warn("IP banned after child auth failure", "remote_addr", child.RemoteAddr) |
| 263 | } |
| 264 | } |
| 265 | slog.Warn("Child process exited with auth failure", |
| 266 | "pid", child.PID, |
| 267 | "username", child.Username, |
| 268 | "remote_addr", child.RemoteAddr, |
| 269 | "duration", duration, |
| 270 | ) |
| 271 | } else if exitCode != ExitSuccess { |
| 272 | slog.Error("Child process exited with error", |
| 273 | "pid", child.PID, |
| 274 | "username", child.Username, |
| 275 | "remote_addr", child.RemoteAddr, |
| 276 | "exit_code", exitCode, |
| 277 | "duration", duration, |
| 278 | ) |
| 279 | } else { |
| 280 | slog.Info("Child process exited cleanly", |
| 281 | "pid", child.PID, |
| 282 | "username", child.Username, |
| 283 | "remote_addr", child.RemoteAddr, |
| 284 | "duration", duration, |
| 285 | ) |
| 286 | } |
| 287 | |
| 288 | // Remove from tracker |
| 289 | s.childTracker.Remove(child.PID) |
| 290 | |
| 291 | // Signal that this child is done |
| 292 | close(child.done) |
| 293 | } |
no test coverage detected