use crate::cloudhv::errors::{CloudHypervisorError, Result};
use std::fs;
use std::path::PathBuf;
#[derive(Debug, Clone)]
pub struct ProcessInfo {
pub pid: u32,
pub vm_name: String,
pub api_socket: Option<PathBuf>,
pub cmdline: String,
}
pub fn find_vm_process(vm_name: &str) -> Result<ProcessInfo> {
let proc_dir = fs::read_dir("/proc").map_err(|e| {
CloudHypervisorError::Process(format!("Failed to read /proc directory: {}", e))
})?;
for entry in proc_dir {
let entry = match entry {
Ok(e) => e,
Err(_) => continue,
};
let file_name = entry.file_name();
let pid_str = match file_name.to_str() {
Some(s) => s,
None => continue,
};
let pid: u32 = match pid_str.parse() {
Ok(p) => p,
Err(_) => continue,
};
let cmdline_path = entry.path().join("cmdline");
let cmdline = match fs::read_to_string(&cmdline_path) {
Ok(c) => c,
Err(_) => continue,
};
if !cmdline.contains("cloud-hypervisor") {
continue;
}
let args: Vec<&str> = cmdline.split('\0').collect();
let mut api_socket = None;
for i in 0..args.len() {
if args[i] == "--api-socket" && i + 1 < args.len() {
api_socket = Some(PathBuf::from(args[i + 1]));
break;
}
}
let matches = if let Some(ref socket) = api_socket {
socket.to_string_lossy().contains(vm_name)
} else {
false
};
if matches {
return Ok(ProcessInfo {
pid,
vm_name: vm_name.to_string(),
api_socket,
cmdline: cmdline.replace('\0', " "),
});
}
}
Err(CloudHypervisorError::VmNotFound(format!(
"No Cloud Hypervisor process found for VM: {}",
vm_name
)))
}
pub fn find_all_vm_processes() -> Result<Vec<ProcessInfo>> {
let proc_dir = fs::read_dir("/proc").map_err(|e| {
CloudHypervisorError::Process(format!("Failed to read /proc directory: {}", e))
})?;
let mut processes = Vec::new();
for entry in proc_dir {
let entry = match entry {
Ok(e) => e,
Err(_) => continue,
};
let file_name = entry.file_name();
let pid_str = match file_name.to_str() {
Some(s) => s,
None => continue,
};
let pid: u32 = match pid_str.parse() {
Ok(p) => p,
Err(_) => continue,
};
let cmdline_path = entry.path().join("cmdline");
let cmdline = match fs::read_to_string(&cmdline_path) {
Ok(c) => c,
Err(_) => continue,
};
if !cmdline.contains("cloud-hypervisor") {
continue;
}
let args: Vec<&str> = cmdline.split('\0').collect();
let mut api_socket = None;
for i in 0..args.len() {
if args[i] == "--api-socket" && i + 1 < args.len() {
api_socket = Some(PathBuf::from(args[i + 1]));
break;
}
}
let vm_name = if let Some(ref socket) = api_socket {
socket
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("unknown")
.to_string()
} else {
format!("pid-{}", pid)
};
processes.push(ProcessInfo {
pid,
vm_name,
api_socket,
cmdline: cmdline.replace('\0', " "),
});
}
Ok(processes)
}
pub fn is_process_running(pid: u32) -> bool {
let proc_path = format!("/proc/{}", pid);
PathBuf::from(proc_path).exists()
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(target_os = "linux")]
#[test]
fn test_is_process_running() {
assert!(is_process_running(1));
assert!(!is_process_running(9999999));
}
#[cfg(target_os = "linux")]
#[test]
fn test_find_all_vm_processes() {
let result = find_all_vm_processes();
assert!(result.is_ok());
}
#[cfg(target_os = "linux")]
#[test]
fn test_find_vm_process_not_found() {
let result = find_vm_process("nonexistent-vm-12345");
assert!(result.is_err());
if let Err(CloudHypervisorError::VmNotFound(msg)) = result {
assert!(msg.contains("nonexistent-vm-12345"));
} else {
panic!("Expected VmNotFound error");
}
}
}