refactor: comprehensive codebase improvements and documentation
- Enhanced error handling with expanded GhostError variants and From impls - Fixed race conditions in TUI (ui.rs unwrap calls) - Added comprehensive module documentation with doc comments - Improved type safety with proper validation in DetectionConfig - Implemented Linux process enumeration via procfs - Refactored TUI for better state management and removed emojis - Enhanced CLI with proper logging initialization - Added example configuration file (examples/ghost.toml) - Updated README with complete feature documentation - Added performance optimizations (saturating arithmetic, reduced clones) - Improved testing framework with proper struct initialization - Added validation and preset modes to DetectionConfig
This commit is contained in:
@@ -1,15 +1,44 @@
|
||||
//! Process enumeration and information retrieval.
|
||||
//!
|
||||
//! This module provides cross-platform process enumeration capabilities,
|
||||
//! allowing the detection engine to gather information about running processes.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
/// Information about a running process.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct ProcessInfo {
|
||||
/// Process identifier.
|
||||
pub pid: u32,
|
||||
/// Parent process identifier.
|
||||
pub ppid: u32,
|
||||
/// Process name (executable name).
|
||||
pub name: String,
|
||||
/// Full path to the executable, if available.
|
||||
pub path: Option<String>,
|
||||
/// Number of threads in the process.
|
||||
pub thread_count: u32,
|
||||
}
|
||||
|
||||
impl ProcessInfo {
|
||||
/// Creates a new ProcessInfo instance.
|
||||
pub fn new(pid: u32, ppid: u32, name: String) -> Self {
|
||||
Self {
|
||||
pid,
|
||||
ppid,
|
||||
name,
|
||||
path: None,
|
||||
thread_count: 1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if this is likely a system process.
|
||||
pub fn is_system_process(&self) -> bool {
|
||||
self.pid == 0 || self.pid == 4 || self.name == "System" || self.name == "Idle"
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ProcessInfo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "[{}] {}", self.pid, self.name)
|
||||
@@ -92,17 +121,119 @@ mod platform {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
#[cfg(target_os = "linux")]
|
||||
mod platform {
|
||||
use super::ProcessInfo;
|
||||
use anyhow::{Context, Result};
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
pub fn enumerate_processes() -> Result<Vec<ProcessInfo>> {
|
||||
let mut processes = Vec::new();
|
||||
|
||||
let proc_dir = Path::new("/proc");
|
||||
if !proc_dir.exists() {
|
||||
return Err(anyhow::anyhow!("procfs not available"));
|
||||
}
|
||||
|
||||
for entry in fs::read_dir(proc_dir).context("Failed to read /proc directory")? {
|
||||
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,
|
||||
};
|
||||
|
||||
if let Ok(info) = get_process_info(pid) {
|
||||
processes.push(info);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(processes)
|
||||
}
|
||||
|
||||
fn get_process_info(pid: u32) -> Result<ProcessInfo> {
|
||||
let stat_path = format!("/proc/{}/stat", pid);
|
||||
let stat_content =
|
||||
fs::read_to_string(&stat_path).context("Failed to read process stat")?;
|
||||
|
||||
let (name, ppid, thread_count) = parse_stat(&stat_content)?;
|
||||
|
||||
let exe_path = format!("/proc/{}/exe", pid);
|
||||
let path = fs::read_link(&exe_path).ok().map(|p| p.to_string_lossy().into_owned());
|
||||
|
||||
Ok(ProcessInfo {
|
||||
pid,
|
||||
ppid,
|
||||
name,
|
||||
path,
|
||||
thread_count,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_stat(stat: &str) -> Result<(String, u32, u32)> {
|
||||
let open_paren = stat.find('(').context("Invalid stat format")?;
|
||||
let close_paren = stat.rfind(')').context("Invalid stat format")?;
|
||||
|
||||
let name = stat[open_paren + 1..close_paren].to_string();
|
||||
let rest = &stat[close_paren + 2..];
|
||||
let fields: Vec<&str> = rest.split_whitespace().collect();
|
||||
|
||||
if fields.len() < 18 {
|
||||
return Err(anyhow::anyhow!("Insufficient fields in stat"));
|
||||
}
|
||||
|
||||
let ppid: u32 = fields[1].parse().context("Failed to parse PPID")?;
|
||||
let thread_count: u32 = fields[17].parse().unwrap_or(1);
|
||||
|
||||
Ok((name, ppid, thread_count))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
mod platform {
|
||||
use super::ProcessInfo;
|
||||
use anyhow::Result;
|
||||
|
||||
pub fn enumerate_processes() -> Result<Vec<ProcessInfo>> {
|
||||
// TODO: Implement Linux/macOS enumeration
|
||||
// macOS implementation would use libproc or sysctl
|
||||
// For now, return empty to indicate platform support is partial
|
||||
log::warn!("macOS process enumeration not yet fully implemented");
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(windows, target_os = "linux", target_os = "macos")))]
|
||||
mod platform {
|
||||
use super::ProcessInfo;
|
||||
use anyhow::Result;
|
||||
|
||||
pub fn enumerate_processes() -> Result<Vec<ProcessInfo>> {
|
||||
Err(anyhow::anyhow!("Process enumeration not supported on this platform"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Enumerates all running processes on the system.
|
||||
///
|
||||
/// # Platform Support
|
||||
///
|
||||
/// - **Windows**: Uses the ToolHelp API to enumerate processes.
|
||||
/// - **Linux**: Reads from the /proc filesystem.
|
||||
/// - **macOS**: Partial support (not yet implemented).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if process enumeration fails due to insufficient
|
||||
/// privileges or platform limitations.
|
||||
pub fn enumerate_processes() -> anyhow::Result<Vec<ProcessInfo>> {
|
||||
platform::enumerate_processes()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user