2026-02-12 21:09:30 +00:00
|
|
|
// Bootstrap utility functions
|
2026-02-13 22:30:57 +00:00
|
|
|
use log::{debug, info, warn};
|
2026-02-12 21:09:30 +00:00
|
|
|
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"]),
|
|
|
|
|
("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);
|
|
|
|
|
|
2026-02-13 22:30:57 +00:00
|
|
|
let result = Command::new("pkill").args(&args).output();
|
2026-02-12 21:09:30 +00:00
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-15 11:26:27 +00:00
|
|
|
/// Check if Valkey/Redis cache is healthy
|
|
|
|
|
pub fn cache_health_check() -> bool {
|
2026-02-15 11:48:02 +00:00
|
|
|
// Try valkey-cli first (preferred for Valkey installations)
|
|
|
|
|
if let Ok(output) = Command::new("valkey-cli")
|
2026-02-15 11:26:27 +00:00
|
|
|
.args(["-h", "127.0.0.1", "-p", "6379", "ping"])
|
|
|
|
|
.output()
|
|
|
|
|
{
|
2026-02-15 11:48:02 +00:00
|
|
|
if output.status.success() {
|
|
|
|
|
let response = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
if response.trim().to_uppercase() == "PONG" {
|
|
|
|
|
return true;
|
2026-02-15 11:26:27 +00:00
|
|
|
}
|
|
|
|
|
}
|
2026-02-15 11:48:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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;
|
2026-02-15 11:26:27 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-02-15 11:48:02 +00:00
|
|
|
|
2026-02-15 12:02:23 +00:00
|
|
|
// 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"])
|
2026-02-15 11:48:02 +00:00
|
|
|
.output()
|
|
|
|
|
{
|
|
|
|
|
Ok(output) => output.status.success(),
|
2026-02-15 12:02:23 +00:00
|
|
|
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,
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-02-15 11:48:02 +00:00
|
|
|
}
|
2026-02-15 11:26:27 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-15 14:54:17 +00:00
|
|
|
/// 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,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-12 21:09:30 +00:00
|
|
|
/// 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,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|