// Bootstrap utility functions use log::{debug, info, warn}; use std::process::Command; /// Get list of processes to kill pub fn get_processes_to_kill() -> Vec<(&'static str, Vec<&'static str>)> { vec![ ("botserver-stack/bin/vault", vec!["-9", "-f"]), ("botserver-stack/bin/tables", vec!["-9", "-f"]), ("botserver-stack/bin/drive", vec!["-9", "-f"]), ("botserver-stack/bin/cache", vec!["-9", "-f"]), ("botserver-stack/bin/directory", vec!["-9", "-f"]), ("botserver-stack/bin/llm", vec!["-9", "-f"]), ("botserver-stack/bin/email", vec!["-9", "-f"]), ("botserver-stack/bin/proxy", vec!["-9", "-f"]), ("botserver-stack/bin/dns", vec!["-9", "-f"]), ("botserver-stack/bin/meeting", vec!["-9", "-f"]), ("botserver-stack/bin/vector_db", vec!["-9", "-f"]), ("botserver-stack/bin/zitadel", vec!["-9", "-f"]), ("botserver-stack/bin/alm", vec!["-9", "-f"]), ("forgejo", vec!["-9", "-f"]), ("caddy", vec!["-9", "-f"]), ("postgres", vec!["-9", "-f"]), ("minio", vec!["-9", "-f"]), ("redis-server", vec!["-9", "-f"]), ("zitadel", vec!["-9", "-f"]), ("llama-server", vec!["-9", "-f"]), ("stalwart", vec!["-9", "-f"]), ("vault server", vec!["-9", "-f"]), ("watcher", vec!["-9", "-f"]), ] } /// Kill processes by name safely pub fn safe_pkill(pattern: &[&str], extra_args: &[&str]) { let mut args: Vec<&str> = extra_args.to_vec(); args.extend(pattern); let result = Command::new("pkill").args(&args).output(); match result { Ok(output) => { debug!("Kill command output: {:?}", output); } Err(e) => { warn!("Failed to execute kill command: {}", e); } } } /// Grep for process safely pub fn safe_pgrep(pattern: &str) -> String { match Command::new("pgrep") .arg("-a") .arg(pattern) .output() { Ok(output) => String::from_utf8_lossy(&output.stdout).to_string(), Err(e) => { warn!("Failed to execute pgrep: {}", e); String::new() } } } /// Execute curl command safely pub fn safe_curl(url: &str) -> String { format!( "curl -f -s --connect-timeout 5 {}", url ) } /// Execute shell command safely pub fn safe_sh_command(command: &str) -> String { match Command::new("sh") .arg("-c") .arg(command) .output() { Ok(output) => String::from_utf8_lossy(&output.stdout).to_string(), Err(e) => { warn!("Failed to execute shell command: {}", e); String::new() } } } /// Check if vault is healthy pub fn vault_health_check() -> bool { // Check if vault server is responding // For now, always return false false } /// Check if Valkey/Redis cache is healthy pub fn cache_health_check() -> bool { // Try valkey-cli first (preferred for Valkey installations) if let Ok(output) = Command::new("valkey-cli") .args(["-h", "127.0.0.1", "-p", "6379", "ping"]) .output() { if output.status.success() { let response = String::from_utf8_lossy(&output.stdout); if response.trim().to_uppercase() == "PONG" { return true; } } } // Try redis-cli as fallback (for Redis installations) if let Ok(output) = Command::new("redis-cli") .args(["-h", "127.0.0.1", "-p", "6379", "ping"]) .output() { if output.status.success() { let response = String::from_utf8_lossy(&output.stdout); if response.trim().to_uppercase() == "PONG" { return true; } } } // If CLI tools are not available, try TCP connection test using nc (netcat) // nc -z tests if port is open without sending data match Command::new("nc") .args(["-z", "-w", "1", "127.0.0.1", "6379"]) .output() { Ok(output) => output.status.success(), Err(_) => { // Final fallback: try /dev/tcp with actual PING test match Command::new("bash") .arg("-c") .arg( "exec 3<>/dev/tcp/127.0.0.1/6379 2>/dev/null && \ echo -e 'PING\r\n' >&3 && \ read -t 1 response <&3 && \ [[ \"$response\" == *PONG* ]] && \ exec 3>&-", ) .output() { Ok(output) => output.status.success(), Err(_) => false, } } } } /// Check if Qdrant vector database is healthy pub fn vector_db_health_check() -> bool { // Qdrant has a /healthz endpoint, use curl to check // Try both HTTP and HTTPS let urls = [ "http://localhost:6333/healthz", "https://localhost:6333/healthz", ]; for url in &urls { if let Ok(output) = Command::new("curl") .args(["-f", "-s", "--connect-timeout", "2", "-k", url]) .output() { if output.status.success() { // Qdrant healthz returns "OK" or JSON with status let response = String::from_utf8_lossy(&output.stdout); if response.contains("OK") || response.contains("\"status\":\"ok\"") { return true; } } } } // Fallback: just check if port 6333 is listening match Command::new("nc") .args(["-z", "-w", "1", "127.0.0.1", "6333"]) .output() { Ok(output) => output.status.success(), Err(_) => false, } } /// Get current user safely pub fn safe_fuser() -> String { // Return shell command that uses $USER environment variable "fuser -M '($USER)'".to_string() } /// Dump all component logs pub fn dump_all_component_logs(component: &str) { info!("Dumping logs for component: {}", component); // This would read from systemd journal or log files // For now, just a placeholder } /// Result type for bot existence check #[derive(Debug)] pub enum BotExistsResult { BotExists, BotNotFound, } /// Check if Zitadel directory is healthy pub fn zitadel_health_check() -> bool { // Check if Zitadel is responding on port 9000 if let Ok(output) = Command::new("curl") .args(["-f", "-s", "--connect-timeout", "2", "http://localhost:9000/debug/ready"]) .output() { if output.status.success() { return true; } } // Fallback: just check if port 9000 is listening match Command::new("nc") .args(["-z", "-w", "1", "127.0.0.1", "9000"]) .output() { Ok(output) => output.status.success(), Err(_) => false, } }