diff --git a/Cargo.lock b/Cargo.lock index 4492033..5c74d13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -270,7 +270,7 @@ checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "cm-dashboard" -version = "0.1.30" +version = "0.1.31" dependencies = [ "anyhow", "chrono", @@ -291,7 +291,7 @@ dependencies = [ [[package]] name = "cm-dashboard-agent" -version = "0.1.30" +version = "0.1.31" dependencies = [ "anyhow", "async-trait", @@ -314,7 +314,7 @@ dependencies = [ [[package]] name = "cm-dashboard-shared" -version = "0.1.30" +version = "0.1.31" dependencies = [ "chrono", "serde", diff --git a/agent/Cargo.toml b/agent/Cargo.toml index 85fe86c..3cda7d8 100644 --- a/agent/Cargo.toml +++ b/agent/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cm-dashboard-agent" -version = "0.1.31" +version = "0.1.32" edition = "2021" [dependencies] diff --git a/agent/src/collectors/systemd.rs b/agent/src/collectors/systemd.rs index 0ba69e6..6604c60 100644 --- a/agent/src/collectors/systemd.rs +++ b/agent/src/collectors/systemd.rs @@ -136,45 +136,84 @@ impl SystemdCollector { /// Auto-discover interesting services to monitor (internal version that doesn't update state) fn discover_services_internal(&self) -> Result<(Vec, std::collections::HashMap)> { debug!("Starting systemd service discovery with status caching"); - // Get all service unit files (includes services that have never been started) - let units_output = Command::new("systemctl") + + // First: Get all service unit files (includes services that have never been started) + let unit_files_output = Command::new("systemctl") .arg("list-unit-files") .arg("--type=service") .arg("--no-pager") .arg("--plain") .output()?; - if !units_output.status.success() { - return Err(anyhow::anyhow!("systemctl system command failed")); + if !unit_files_output.status.success() { + return Err(anyhow::anyhow!("systemctl list-unit-files command failed")); } - let units_str = String::from_utf8(units_output.stdout)?; + // Second: Get runtime status of all units + let units_status_output = Command::new("systemctl") + .arg("list-units") + .arg("--type=service") + .arg("--all") + .arg("--no-pager") + .arg("--plain") + .output()?; + + if !units_status_output.status.success() { + return Err(anyhow::anyhow!("systemctl list-units command failed")); + } + + let unit_files_str = String::from_utf8(unit_files_output.stdout)?; + let units_status_str = String::from_utf8(units_status_output.stdout)?; let mut services = Vec::new(); // Use configuration instead of hardcoded values let excluded_services = &self.config.excluded_services; let service_name_filters = &self.config.service_name_filters; - // Parse all services and cache their status information + // Parse all service unit files to get complete service list let mut all_service_names = std::collections::HashSet::new(); - let mut status_cache = std::collections::HashMap::new(); - for line in units_str.lines() { + for line in unit_files_str.lines() { let fields: Vec<&str> = line.split_whitespace().collect(); if fields.len() >= 2 && fields[0].ends_with(".service") { let service_name = fields[0].trim_end_matches(".service"); - let unit_file_state = fields.get(1).unwrap_or(&"unknown").to_string(); + all_service_names.insert(service_name.to_string()); + debug!("Found service unit file: {}", service_name); + } + } + + // Parse runtime status for all units + let mut status_cache = std::collections::HashMap::new(); + for line in units_status_str.lines() { + let fields: Vec<&str> = line.split_whitespace().collect(); + if fields.len() >= 4 && fields[0].ends_with(".service") { + let service_name = fields[0].trim_end_matches(".service"); - // For unit files, we don't have runtime status info yet - will be fetched individually - // Set placeholder values for status cache (actual status will be fetched when collecting metrics) + // Extract status information from systemctl list-units output + let load_state = fields.get(1).unwrap_or(&"unknown").to_string(); + let active_state = fields.get(2).unwrap_or(&"unknown").to_string(); + let sub_state = fields.get(3).unwrap_or(&"unknown").to_string(); + + // Cache the status information status_cache.insert(service_name.to_string(), ServiceStatusInfo { - load_state: "unknown".to_string(), // Will be determined when we check individual status - active_state: "unknown".to_string(), // Will be determined when we check individual status - sub_state: unit_file_state.clone(), // Use unit file state as placeholder + load_state: load_state.clone(), + active_state: active_state.clone(), + sub_state: sub_state.clone(), }); - all_service_names.insert(service_name.to_string()); - debug!("Found service unit file: {} (file_state: {})", service_name, unit_file_state); + debug!("Got runtime status for service: {} (load:{}, active:{}, sub:{})", service_name, load_state, active_state, sub_state); + } + } + + // For services found in unit files but not in runtime status, set default inactive status + for service_name in &all_service_names { + if !status_cache.contains_key(service_name) { + status_cache.insert(service_name.to_string(), ServiceStatusInfo { + load_state: "not-loaded".to_string(), + active_state: "inactive".to_string(), + sub_state: "dead".to_string(), + }); + debug!("Service {} found in unit files but not runtime - marked as inactive", service_name); } } diff --git a/dashboard/Cargo.toml b/dashboard/Cargo.toml index 6b0e762..9633806 100644 --- a/dashboard/Cargo.toml +++ b/dashboard/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cm-dashboard" -version = "0.1.31" +version = "0.1.32" edition = "2021" [dependencies] diff --git a/dashboard/src/main.rs b/dashboard/src/main.rs index fad9c77..c1639a9 100644 --- a/dashboard/src/main.rs +++ b/dashboard/src/main.rs @@ -14,7 +14,7 @@ use app::Dashboard; /// Get hardcoded version fn get_version() -> &'static str { - "v0.1.31" + "v0.1.32" } /// Check if running inside tmux session diff --git a/shared/Cargo.toml b/shared/Cargo.toml index 4eef960..d60e934 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cm-dashboard-shared" -version = "0.1.31" +version = "0.1.32" edition = "2021" [dependencies]