package browser import ( "fmt" "os" "syscall" ) // CdpDisconnect cierra SOLO la conexion WebSocket CDP, sin tocar el proceso // Chrome. Es un alias legible de CdpClose(c, 0): usalo cuando quieras soltar la // sesion pero dejar el navegador vivo (p.ej. el navegador diario en 9222 al que // te adjuntaste, no quieres matarlo). func CdpDisconnect(c *CDPConn) error { return CdpClose(c, 0) } // CdpQuit cierra la conexion WebSocket Y mata el proceso Chrome (y su grupo de // proceso completo en Linux nativo). Es un alias legible de CdpClose(c, pid) con // pid > 0: usalo para apagar un Chrome que TU lanzaste con ChromeLaunch. func CdpQuit(c *CDPConn, pid int) error { return CdpClose(c, pid) } // CdpClose cierra la conexion WebSocket CDP y, si pid > 0, mata el proceso Chrome. // En Linux nativo mata el grupo de proceso completo (chromium lanza zygote, gpu, // renderers como hijos del mismo grupo cuando ChromeLaunch seteo Setpgid: true). // Siempre intenta cerrar la conexion aunque el kill falle, y viceversa. // Retorna el primer error encontrado. func CdpClose(c *CDPConn, pid int) error { var firstErr error if c != nil && !c.closed { c.closed = true if err := c.conn.Close(); err != nil { firstErr = fmt.Errorf("cdp close: cerrar websocket: %w", err) } } if pid > 0 { // Intentar matar el grupo de proceso completo (pid == pgid cuando Setpgid=true). // syscall.Kill con pid negativo envia la seƱal a todos los procesos del grupo. if err := syscall.Kill(-pid, syscall.SIGKILL); err != nil { // Fallback: matar solo el proceso raiz si el grupo falla // (ej: proceso ya terminado, o chrome.exe en WSL sin Setpgid). if proc, e := os.FindProcess(pid); e == nil { if killErr := proc.Kill(); killErr != nil { if firstErr == nil { firstErr = fmt.Errorf("cdp close: matar proceso %d: %w", pid, killErr) } } } else if firstErr == nil { firstErr = fmt.Errorf("cdp close: encontrar proceso %d: %w", pid, e) } } } return firstErr }