Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion crates/cardwire-daemon/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ zbus.workspace = true
serde.workspace = true
log.workspace = true
thiserror.workspace = true
anyhow.workspace = true
env_logger.workspace = true
toml.workspace = true
serde_json.workspace = true
Expand Down
19 changes: 9 additions & 10 deletions crates/cardwire-daemon/src/analyzer/models.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
use crate::{
Result, analyzer::{
dynamic_analysis::{check_env, get_app_id_wayland_with_retry, get_steam_app_id}, helpers::{
comm_to_string, get_real_process_name, is_proc_still_alive, normalized_candidates
}, static_analysis::{self, AppMetadata, watch_fdo_folders}
}, file::{DbusAppMetadata, GpuPolicy}, interface::{LogEntry, LoggerInterfaceSignals, SmartPolicyInterface}
};
use aya::maps::{HashMap as AyaHashMap, RingBuf};
use aya_log::EbpfLogger;
use cardwire_ebpf_userspace::EbpfBlocker;
Expand All @@ -9,14 +16,6 @@ use tokio::{
io::{Interest, unix::AsyncFd}, sync::{Mutex, RwLock, Semaphore, mpsc, oneshot}, task, time::Instant
};
use zbus::object_server::SignalEmitter;

use crate::{
analyzer::{
dynamic_analysis::{check_env, get_app_id_wayland_with_retry, get_steam_app_id}, helpers::{
comm_to_string, get_real_process_name, is_proc_still_alive, normalized_candidates
}, static_analysis::{self, AppMetadata, watch_fdo_folders}
}, file::{DbusAppMetadata, GpuPolicy}, interface::{LogEntry, LoggerInterfaceSignals, SmartPolicyInterface}
};
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ExecEvent {
Expand Down Expand Up @@ -71,7 +70,7 @@ impl CardwireAnalyzer {
db_cache: Arc<RwLock<HashMap<String, GpuPolicy>>>,
db_tx: mpsc::Sender<(String, AppMetadata, oneshot::Sender<bool>)>,
new_app_signal: Arc<OnceLock<SignalEmitter<'static>>>,
) -> anyhow::Result<CardwireAnalyzer> {
) -> Result<CardwireAnalyzer> {
let mut blocker = blocker.write().await;
let exec_ring = blocker.get_exec_ring()?;
let report_ring = blocker.get_report_ring()?;
Expand Down Expand Up @@ -112,7 +111,7 @@ impl CardwireAnalyzer {
new_app_signal,
})
}
pub async fn run(self) -> anyhow::Result<()> {
pub async fn run(self) -> Result<()> {
// Clone the Arcs and Sender to move into the background task
let exec_arc = self.exec_ring.clone();
let logger_arc = self.ebpf_logger.clone();
Expand Down
3 changes: 2 additions & 1 deletion crates/cardwire-daemon/src/analyzer/static_analysis.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Functions for static analysis, contains:
//! - FDO desktop entries analysis
use crate::Result;
use freedesktop_desktop_entry::{DesktopEntry, get_languages_from_env};
use inotify::{EventMask, Inotify, StreamExt, WatchDescriptor, WatchMask};
use log::error;
Expand All @@ -17,7 +18,7 @@ pub struct AppMetadata {
}

/// Return a list of fdo apps present in the system
pub async fn get_fdo_apps() -> anyhow::Result<(HashMap<String, AppMetadata>, Vec<PathBuf>)> {
pub async fn get_fdo_apps() -> Result<(HashMap<String, AppMetadata>, Vec<PathBuf>)> {
let mut app_directories: Vec<PathBuf> = Vec::new();
// get from ENV
let xdg_dir = BaseDirectories::new();
Expand Down
50 changes: 40 additions & 10 deletions crates/cardwire-daemon/src/core/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,22 @@ use std::{io, path};
use thiserror::Error;

#[derive(Error, Debug)]
pub enum Error {
pub enum CardwireError {
#[error("IO Error: {0}")]
Io(#[from] io::Error),

#[error("ebpf error: {0}")]
CardwireEbpfError(#[from] cardwire_ebpf_userspace::CardwireEbpfError),

#[error("zbus error: {0}")]
ZbusError(#[from] zbus::Error),

#[error("fdo error: {0}")]
FdoError(#[from] zbus::fdo::Error),

#[error("rustqlite error: {0}")]
RusqliteError(#[from] rusqlite::Error),

#[error("parse int error: {0}")]
ParseInt(#[from] std::num::ParseIntError),

Expand All @@ -19,58 +28,79 @@ pub enum Error {
#[error("Missing 'devices' directory in group path: {0}")]
MissingDevicesDir(path::PathBuf),

// Config Error
#[error("Couldn't create the default var folder: {0}")]
VarFolderError(io::Error),

#[error("Couldn't generate default config: {0}")]
DefaultConfigError(toml::ser::Error),

#[error("Couldn't generate default json state: {0}")]
DefaultStateError(serde_json::Error),

#[error("Error with cardwire.toml: {0}")]
CardwireConfigError(io::Error),

#[error("Error with state_file {0}: {1}")]
CardwireStateError(String, serde_json::Error),

// Mode errors
#[error("unknown mode: {0}")]
UnknownMode(u32),

#[error("{0}")]
Other(String),
}

impl From<&str> for Error {
impl From<&str> for CardwireError {
fn from(s: &str) -> Self {
Error::Other(s.to_string())
CardwireError::Other(s.to_string())
}
}
pub type Result<T, E = CardwireError> = core::result::Result<T, E>;

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_error_from_str() {
let err = Error::from("something went wrong");
let err = CardwireError::from("something went wrong");
match err {
Error::Other(msg) => assert_eq!(msg, "something went wrong"),
CardwireError::Other(msg) => assert_eq!(msg, "something went wrong"),
_ => panic!("expected Error::Other"),
}
}

#[test]
fn test_error_display_iommu_not_enabled() {
let err = Error::IommuNotEnabled;
let err = CardwireError::IommuNotEnabled;
assert_eq!(err.to_string(), "IOMMU Not Enabled");
}

#[test]
fn test_error_display_missing_devices_dir() {
let err = Error::MissingDevicesDir(std::path::PathBuf::from("/sys/test"));
let err = CardwireError::MissingDevicesDir(std::path::PathBuf::from("/sys/test"));
assert!(err.to_string().contains("/sys/test"));
}

#[test]
fn test_error_display_io_error() {
let io_err = io::Error::new(io::ErrorKind::NotFound, "file not found");
let err = Error::Io(io_err);
let err = CardwireError::Io(io_err);
assert!(err.to_string().contains("file not found"));
}

#[test]
fn test_error_display_parse_int() {
let parse_err = "abc".parse::<u32>().unwrap_err();
let err = Error::ParseInt(parse_err);
let err = CardwireError::ParseInt(parse_err);
assert!(err.to_string().contains("parse"));
}

#[test]
fn test_error_display_other() {
let err = Error::Other("custom error".to_string());
let err = CardwireError::Other("custom error".to_string());
assert_eq!(err.to_string(), "custom error");
}
}
2 changes: 1 addition & 1 deletion crates/cardwire-daemon/src/core/inode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{
collections::BTreeMap, fs::{self}, os::unix::fs::MetadataExt, path::Path
};

use anyhow::Result;
use crate::Result;
use log::{error, warn};

use cardwire_ebpf_userspace::InodeKey;
Expand Down
6 changes: 4 additions & 2 deletions crates/cardwire-daemon/src/core/pci/iommu.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use crate::core::{errors::Error as CardwireError, pci::IommuGroup};
use crate::{
Result, core::{errors::CardwireError, pci::IommuGroup}
};
use log::error;
use std::{collections::BTreeMap, fs, path::Path};

pub fn read_iommu_groups() -> Result<BTreeMap<usize, IommuGroup>, CardwireError> {
pub fn read_iommu_groups() -> Result<BTreeMap<usize, IommuGroup>> {
let base_path = Path::new("/sys/kernel/iommu_groups");
let mut dir_iter = base_path.read_dir().map_err(|e| {
error!(
Expand Down
6 changes: 4 additions & 2 deletions crates/cardwire-daemon/src/core/pci/pci_device.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::core::{
errors::Error as CardwireError, pci::{PciDevice, is_iommu_enabled, read_iommu_groups}
use crate::{
Result, core::{
errors::CardwireError, pci::{PciDevice, is_iommu_enabled, read_iommu_groups}
}
};
use log::{error, info, warn};
use std::{
Expand Down
5 changes: 2 additions & 3 deletions crates/cardwire-daemon/src/daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ mod manager;
mod tasks;
pub mod types;

use crate::{manager::DaemonManager, tasks::watch_power_state};
use anyhow::Result;
use crate::{core::errors::Result, manager::DaemonManager, tasks::watch_power_state};
use env_logger::Env;
use log::info;
use std::{future::pending, sync::Arc};
Expand Down Expand Up @@ -128,7 +127,7 @@ async fn main() -> Result<()> {
async fn spawn_dbus_api(
object_server: &zbus::ObjectServer,
daemon: &mut DaemonManager,
) -> anyhow::Result<()> {
) -> Result<()> {
let path = "/org/opengamingcollective/cardwire";

let gpu_interfaces = daemon.inner.gpu_list.read().await;
Expand Down
41 changes: 21 additions & 20 deletions crates/cardwire-daemon/src/file/common.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
//! helper to manage cardwired configs, include the user config .toml, and the .json states like
//! gpu, mode or pci
use crate::file::{CardwireConfig, CardwireGpuUnit, CardwireModeState};
use anyhow::{Context, Ok};
use crate::{
Result, core::errors::CardwireError, file::{CardwireConfig, CardwireGpuUnit, CardwireModeState}
};
use std::{collections::BTreeMap, fs, io};

#[allow(dead_code)]
Expand All @@ -12,55 +13,55 @@ pub enum FileKind {
}

/// Create all folders cardwire need
pub fn create_default_folder(kind: FileKind) -> anyhow::Result<()> {
pub fn create_default_folder(kind: FileKind) -> Result<(), io::Error> {
let directory = match kind {
FileKind::Config => crate::CONFIG_PATH,
_ => crate::STATE_PATH,
};
// fs error that should make the daemon exit
if let Err(e) = fs::create_dir_all(directory) {
let _ = match e.kind() {
io::ErrorKind::PermissionDenied => return Err(e.into()),
io::ErrorKind::ReadOnlyFilesystem => return Err(e.into()),
io::ErrorKind::NotADirectory => return Err(e.into()),
_ => Ok(()),
match e.kind() {
io::ErrorKind::PermissionDenied => return Err(e),
io::ErrorKind::ReadOnlyFilesystem => return Err(e),
io::ErrorKind::NotADirectory => return Err(e),
_ => {}
};
}
Comment thread
luytan marked this conversation as resolved.
Ok(())
}
/// Helper function to create default file, used for all config struct
pub fn create_default_file(kind: FileKind) -> anyhow::Result<()> {
pub fn create_default_file(kind: FileKind) -> Result<()> {
let result = match kind {
FileKind::Config => {
create_default_folder(FileKind::Config)
.context("could not create default folder for cardwire.toml")?;
create_default_folder(FileKind::Config).map_err(CardwireError::VarFolderError)?;
// Default config for cardwire
let default_config = toml::to_string_pretty(&CardwireConfig::default())?;
let default_config = toml::to_string_pretty(&CardwireConfig::default())
.map_err(CardwireError::DefaultConfigError)?;
// write
fs::write(
format!("{}/cardwire.toml", crate::CONFIG_PATH),
default_config,
)
}
FileKind::GpuState => {
create_default_folder(FileKind::GpuState)
.context("could not create default folder for gpu_state.json")?;
create_default_folder(FileKind::GpuState).map_err(CardwireError::VarFolderError)?;
// Default gpu_state for cardwire
let mut gpu_hash: BTreeMap<String, CardwireGpuUnit> = BTreeMap::new();
gpu_hash.insert("Null".to_string(), CardwireGpuUnit::default());
let default_gpu_state = serde_json::to_string_pretty(&gpu_hash)?;
let default_gpu_state = serde_json::to_string_pretty(&gpu_hash)
.map_err(CardwireError::DefaultStateError)?;
// write
fs::write(
format!("{}/gpu_state.json", crate::STATE_PATH),
default_gpu_state,
)
}
FileKind::ModeState => {
create_default_folder(FileKind::ModeState)
.context("could not create default folder for mode.json")?;
create_default_folder(FileKind::ModeState).map_err(CardwireError::VarFolderError)?;
// Default mode for cardwire
let default_state = CardwireModeState::default();
let default_mode_state = serde_json::to_string_pretty(&default_state)?;
let default_mode_state = serde_json::to_string_pretty(&default_state)
.map_err(CardwireError::DefaultStateError)?;
// write
fs::write(
format!("{}/mode.json", crate::STATE_PATH),
Expand All @@ -69,7 +70,7 @@ pub fn create_default_file(kind: FileKind) -> anyhow::Result<()> {
}
};
// Handle the fs error here
let result: anyhow::Result<()> = match result {
let result: Result<()> = match result {
std::result::Result::Ok(()) => Ok(()),
std::result::Result::Err(e) => match e.kind() {
io::ErrorKind::PermissionDenied => return Err(e.into()),
Expand All @@ -81,7 +82,7 @@ pub fn create_default_file(kind: FileKind) -> anyhow::Result<()> {
// ignore this one
io::ErrorKind::AlreadyExists => Ok(()),
// if directory not found, try to create again
io::ErrorKind::NotFound => create_default_folder(kind),
io::ErrorKind::NotFound => create_default_folder(kind).map_err(CardwireError::Io),
_ => Ok(()),
},
};
Expand Down
12 changes: 5 additions & 7 deletions crates/cardwire-daemon/src/file/config.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
//! helper to manage cardwired configs, include the user config .toml, and the .json states like
//! gpu, mode or pci
use crate::{
file::common::{FileKind, create_default_file}, types::Modes
Result, core::errors::CardwireError::CardwireConfigError, file::common::{FileKind, create_default_file}, types::Modes
};
use anyhow::Context;
use log::warn;
use tokio::io::AsyncWriteExt;

Expand Down Expand Up @@ -50,17 +49,16 @@ impl CardwireConfig {
}
}
/// Read TOML config file and return it's settings as a struct
pub fn build() -> anyhow::Result<CardwireConfig> {
pub fn build() -> Result<CardwireConfig> {
let config_file = format!("{}/cardwire.toml", crate::CONFIG_PATH);
// create the config if it doesnt exist
if !(fs::exists(&config_file)?) {
Self::create_default_config().context("Could not create default dir for config")?;
Self::create_default_config()?;
}
// remove leftover temp files from a save interrupted by a crash
Self::cleanup_stale_tmp_files();
// read the config into a string and parse it
let config_content =
fs::read_to_string(&config_file).context("Could not read cardwire.toml")?;
let config_content = fs::read_to_string(&config_file).map_err(CardwireConfigError)?;
Ok(Self::parse_or_default(&config_content))
}
/// Remove leftover cardwire.toml.*.tmp files from a save interrupted by a crash
Expand Down Expand Up @@ -90,7 +88,7 @@ impl CardwireConfig {
}
}
/// Create a default cardwire.toml if not present
fn create_default_config() -> anyhow::Result<()> {
fn create_default_config() -> Result<()> {
create_default_file(FileKind::Config)?;
Ok(())
}
Expand Down
Loading