diff --git a/app/src/control.rs b/app/src/control.rs index 0967408..8bf2621 100644 --- a/app/src/control.rs +++ b/app/src/control.rs @@ -2,33 +2,66 @@ // SPDX-License-Identifier: MIT use intel_crashlog::prelude::*; +use intel_crashlog::source::Capability; pub fn rearm(sources: Vec) -> Result<(), Error> { - control_command(sources, CrashLogSource::rearm) + control_command(sources, Capability::Rearm, CrashLogSource::rearm) } pub fn trigger(sources: Vec) -> Result<(), Error> { - control_command(sources, CrashLogSource::trigger) + control_command(sources, Capability::Trigger, CrashLogSource::trigger) } pub fn clear(sources: Vec) -> Result<(), Error> { - control_command(sources, CrashLogSource::clear) + control_command(sources, Capability::Clear, CrashLogSource::clear) } pub fn enable(sources: Vec) -> Result<(), Error> { - control_command(sources, CrashLogSource::enable) + control_command(sources, Capability::EnableDisable, CrashLogSource::enable) } pub fn disable(sources: Vec) -> Result<(), Error> { - control_command(sources, CrashLogSource::disable) + control_command(sources, Capability::EnableDisable, CrashLogSource::disable) } -fn control_command(sources: Vec, control: F) -> Result<(), Error> +fn control_command( + sources: Vec, + capability: Capability, + control: F, +) -> Result<(), Error> where F: Fn(&CrashLogSource) -> Result<(), Error>, { - for source in sources { - control(&source)?; + let mut cmd_success = false; + let mut first_error = None; + + let control_sources = if sources.is_empty() { + CrashLogSource::discover_distinct() + .into_iter() + .filter(|src| src.capabilities().contains(&capability)) + .collect() + } else { + sources + }; + + if control_sources.is_empty() { + return Err(Error::NoCrashLogSourceFound); + } + + for source in control_sources { + match control(&source) { + Ok(()) => cmd_success = true, + Err(err) => { + log::warn!("Error while running {capability} command on {source}: {err}"); + if first_error.is_none() { + first_error = Some(err); + } + } + } + } + + if !cmd_success { + return Err(first_error.unwrap_or(Error::InternalError)); } Ok(()) diff --git a/lib/src/source.rs b/lib/src/source.rs index ab1c9f9..b68c9e8 100644 --- a/lib/src/source.rs +++ b/lib/src/source.rs @@ -134,6 +134,18 @@ impl FromStr for CrashLogSource { impl CrashLogSource { /// Returns all the Crash Log sources that are available in the platform pub fn discover() -> Vec { + Self::discover_sources() + } + + /// Returns all the Crash Log sources that are available in the platform without duplicates + pub fn discover_distinct() -> Vec { + Self::discover_sources() + .into_iter() + .filter(|src| src.is_distinct()) + .collect() + } + + fn discover_sources() -> Vec { let mut sources = Vec::new(); if Acpi::default().is_available() { @@ -155,6 +167,16 @@ impl CrashLogSource { sources } + /// Returns `true` if the Crash log source is distinct, e. g. it is not mirrored by another + /// Crash Log source in the system + pub fn is_distinct(&self) -> bool { + match self { + Self::Acpi => true, + Self::EventLog => true, + Self::PmtDevice(dev) => Pmt::default().is_distinct(dev), + } + } + /// Returns the Crash Log extracted from the platform using the current Crash Log source #[cfg(feature = "extraction")] pub fn extract(&self) -> Result, Error> { diff --git a/lib/src/source/pmt.rs b/lib/src/source/pmt.rs index 8b9ecff..742517e 100644 --- a/lib/src/source/pmt.rs +++ b/lib/src/source/pmt.rs @@ -170,4 +170,14 @@ impl Pmt { PmtDeviceId::Bdf(bdf) => format!("PMT endpoints for PCI device {bdf}"), } } + + #[cfg(target_os = "linux")] + pub fn is_distinct(&self, dev: &PmtDeviceId) -> bool { + self.sysfs.is_distinct(dev) + } + + #[cfg(not(target_os = "linux"))] + pub fn is_distinct(&self, _dev: &PmtDeviceId) -> bool { + true + } } diff --git a/lib/src/source/pmt/sysfs.rs b/lib/src/source/pmt/sysfs.rs index 3c77372..62d19aa 100644 --- a/lib/src/source/pmt/sysfs.rs +++ b/lib/src/source/pmt/sysfs.rs @@ -129,9 +129,17 @@ impl PmtSysFs { devices } + pub(super) fn is_distinct(&self, dev: &PmtDeviceId) -> bool { + match dev { + PmtDeviceId::Name(_name) => true, + PmtDeviceId::Bdf(_bdf) => false, + } + } + pub fn get_all_endpoints(&self) -> Vec { self.discover() .into_iter() + .filter(|dev| self.is_distinct(dev)) .flat_map(|devid| self.get_endpoints(&devid)) .collect() }