From 2aad95428e11cc7c7c6d71cf75b4da74b09515ee Mon Sep 17 00:00:00 2001 From: Artie Poole Date: Fri, 22 May 2026 14:29:42 +0100 Subject: [PATCH] softeners: dfx-mgr - share logic to find dfx-mgr-client binary between dbus method and run_dfx_mgr both dfx_mgr dbus method and run_dfx_mgr function inside the softener now share the path resolution to find dfx-mgr-client to reduce maintenance burden run_dfx_mgr called from within the dbus method Signed-off-by: Artie Poole --- daemon/src/comm/dbus/control_interface.rs | 57 ++------- daemon/src/error.rs | 14 --- daemon/src/softeners/xilinx_dfx_mgr.rs | 40 +------ .../xilinx_dfx_mgr/xilinx_dfx_mgr_helpers.rs | 108 ++++++++++++++++++ 4 files changed, 119 insertions(+), 100 deletions(-) diff --git a/daemon/src/comm/dbus/control_interface.rs b/daemon/src/comm/dbus/control_interface.rs index eed14d55..0023c159 100644 --- a/daemon/src/comm/dbus/control_interface.rs +++ b/daemon/src/comm/dbus/control_interface.rs @@ -17,15 +17,13 @@ //! use crate::comm::dbus::validate_device_handle; -use crate::error::map_error_io_to_fdo; use crate::platforms::platform::{platform_for_known_platform, platform_from_compat_or_device}; use crate::platforms::universal::universal_write_handler; -use crate::softeners::error::FpgadSoftenerError; +#[cfg(feature = "xilinx-dfx-mgr")] +use crate::softeners::xilinx_dfx_mgr::xilinx_dfx_mgr_helpers::run_dfx_mgr; use log::{info, trace}; -use std::env; use std::path::Path; use std::sync::Arc; -use tokio::process::Command; use tokio::sync::{Mutex, MutexGuard, OnceCell}; use zbus::{fdo, interface}; @@ -340,51 +338,16 @@ impl ControlInterface { /// ``` async fn dfx_mgr(&self, cmd_string: &str) -> Result { if cfg!(feature = "xilinx-dfx-mgr") { - let snap_env = env::var("SNAP").unwrap_or("".to_string()); + let args: Vec<&str> = cmd_string.split_whitespace().collect(); - let dfx_mgr_client_path = format!("{}/usr/bin/dfx-mgr-client", snap_env); - - // Check if dfx-mgr-client exists - if !Path::new(&dfx_mgr_client_path).exists() { - return Err(FpgadSoftenerError::DfxMgr(format!( - "dfx-mgr-client not detected.\n\ - If using snap, please install the dfx-mgr component with \n\ - `[sudo] snap install fpgad+dfx-mgr [options]` \n\ - otherwise ensure that dfx-mgr-client exists at `{dfx_mgr_client_path}`" - )) - .into()); - } - - let output = Command::new(&dfx_mgr_client_path) - .args(cmd_string.split_whitespace()) - .output() - .await - .map_err(|e| { - map_error_io_to_fdo("dfx-mgr-client call failed to produce any output", e) - })?; - - // Exit status - match output.status.success() { - true => { - info!("Command ran successfully!"); - Ok(format!( - "dfx-mgr called with args {}.\nExit status: {}\nStdout:\n{}\nStderr:\n{}", - cmd_string, - output.status, - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr), - )) + match run_dfx_mgr(&args) { + Ok(output) => { + info!("dfx-mgr command ran successfully!"); + Ok(output) } - false => { - info!("Command failed with code: {:#?}", output.status.code()); - Err(FpgadSoftenerError::DfxMgr(format!( - "dfx-mgr called with args {}.\nExit status: {}\nStdout:\n{}\nStderr:\n{}", - cmd_string, - output.status, - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr), - )) - .into()) + Err(e) => { + info!("dfx-mgr command failed: {}", e); + Err(e.into()) } } } else { diff --git a/daemon/src/error.rs b/daemon/src/error.rs index 0a09bf0b..fd649d20 100644 --- a/daemon/src/error.rs +++ b/daemon/src/error.rs @@ -131,17 +131,3 @@ impl From for fdo::Error { } } } - -/// Converter for tokio::io::Error into zbus::fdo::Error types, with added "custom_msg" in order -/// to provide additional context. -/// This allows for "?" on e.g., tokio::io::Command calls -/// -/// # Arguments -/// -/// * `custom_msg`: String to prepend the error - use to add context e.g. "failed when calling nproc" -/// * `err`: the tokio::io::error from the failing function -/// -/// returns: zbus::fdo::Error type suitable for dbus transmission -pub(crate) fn map_error_io_to_fdo(custom_msg: &str, err: impl std::fmt::Display) -> fdo::Error { - fdo::Error::Failed(format!("{custom_msg}:\n{err}")) -} diff --git a/daemon/src/softeners/xilinx_dfx_mgr.rs b/daemon/src/softeners/xilinx_dfx_mgr.rs index 571f2c0f..88964ba2 100644 --- a/daemon/src/softeners/xilinx_dfx_mgr.rs +++ b/daemon/src/softeners/xilinx_dfx_mgr.rs @@ -55,7 +55,6 @@ //! # } //! ``` -use std::env; use std::path::Path; use std::sync::OnceLock; @@ -65,6 +64,7 @@ use crate::softeners::error::FpgadSoftenerError; use fpgad_macros::platform; use log::trace; use xilinx_dfx_mgr_fpga::XilinxDfxMgrFPGA; +use xilinx_dfx_mgr_helpers::run_dfx_mgr; use xilinx_dfx_mgr_overlay_handler::XilinxDfxMgrOverlayHandler; // Marked as public so that the members are published in docs @@ -217,41 +217,3 @@ pub fn load_overlay(bitstream_path: &Path, dtbo_path: &Path) -> Result Result { - let prefix = if let Ok(snap_env) = env::var("SNAP_COMPONENTS") { - snap_env + "/dfx-mgr" - } else { - "".to_string() - }; - - let dfx_mgr_client_path = format!("{}/usr/bin/dfx-mgr-client", prefix); - - // Check if dfx-mgr-client exists before trying to run it - if !Path::new(&dfx_mgr_client_path).exists() { - return Err(FpgadSoftenerError::DfxMgr(format!( - "dfx-mgr-client not found at '{}'. Install the dfx-mgr component with: snap install fpgad+dfx-mgr.comp", - dfx_mgr_client_path - ))); - } - - trace!("Calling dfx-mgr with args {:#?}", args); - let output = std::process::Command::new(&dfx_mgr_client_path) - .args(args) - .output() - .map_err(|e| { - FpgadSoftenerError::DfxMgr(format!("dfx-mgr-client failed to produce output:\n{e}")) - })?; - if output.status.success() { - Ok(String::from_utf8_lossy(&output.stdout).to_string()) - } else { - // output.status usually looks like `exit status: 255` - Err(FpgadSoftenerError::DfxMgr(format!( - "dfx-mgr-client failed.\n{}\nStdout:\n{:?}\nStderr:\n{:?}", - output.status, - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr) - ))) - } -} diff --git a/daemon/src/softeners/xilinx_dfx_mgr/xilinx_dfx_mgr_helpers.rs b/daemon/src/softeners/xilinx_dfx_mgr/xilinx_dfx_mgr_helpers.rs index cba678db..fda81fcf 100644 --- a/daemon/src/softeners/xilinx_dfx_mgr/xilinx_dfx_mgr_helpers.rs +++ b/daemon/src/softeners/xilinx_dfx_mgr/xilinx_dfx_mgr_helpers.rs @@ -17,6 +17,8 @@ //! //! # Key Functions //! +//! - [`get_dfx_mgr_client_path`] - Locates and validates the dfx-mgr-client binary path +//! - [`run_dfx_mgr`] - Executes dfx-mgr-client synchronously with given arguments //! - [`extract_firmware_name`] - Parses .dtbo files to extract the firmware-name property //! //! # Device Tree Parsing @@ -40,8 +42,114 @@ use crate::error::FpgadError; use crate::softeners::error::FpgadSoftenerError; use log::trace; +use std::env; use std::fs; use std::path::Path; +use std::process::Command; + +/// Get the path to the dfx-mgr-client binary and verify it exists. +/// +/// Checks for dfx-mgr-client in the following order: +/// 1. In snap component: `$SNAP_COMPONENTS/dfx-mgr/usr/bin/dfx-mgr-client` +/// 2. In snap itself: `$SNAP/usr/bin/dfx-mgr-client` +/// +/// # Returns +/// * `Ok(String)` - The path to dfx-mgr-client if it exists +/// * `Err(FpgadSoftenerError)` - If dfx-mgr-client cannot be found or doesn't exist +/// +/// # Examples +/// +/// ```rust,no_run +/// # use daemon::softeners::xilinx_dfx_mgr::xilinx_dfx_mgr_helpers::get_dfx_mgr_client_path; +/// # fn example() -> Result<(), daemon::softeners::error::FpgadSoftenerError> { +/// let path = get_dfx_mgr_client_path()?; +/// println!("dfx-mgr-client is at: {}", path); +/// # Ok(()) +/// # } +/// ``` +pub fn get_dfx_mgr_client_path() -> Result { + // Check for dfx-mgr-client in snap component first, then fall back to snap itself + let dfx_mgr_client_path = if let Ok(snap_components) = env::var("SNAP_COMPONENTS") { + let component_path = format!("{}/dfx-mgr/usr/bin/dfx-mgr-client", snap_components); + if Path::new(&component_path).exists() { + component_path + } else if let Ok(snap_env) = env::var("SNAP") { + // Fall back to snap path + format!("{}/usr/bin/dfx-mgr-client", snap_env) + } else { + return Err(FpgadSoftenerError::DfxMgr( + "SNAP_COMPONENTS and SNAP environment variables not set. \ + Not running in a snap environment?" + .to_string(), + )); + } + } else if let Ok(snap_env) = env::var("SNAP") { + // Not using components, use snap path + format!("{}/usr/bin/dfx-mgr-client", snap_env) + } else { + return Err(FpgadSoftenerError::DfxMgr( + "SNAP environment variable not set. Not running in a snap environment?".to_string(), + )); + }; + + // Check if dfx-mgr-client exists + if !Path::new(&dfx_mgr_client_path).exists() { + return Err(FpgadSoftenerError::DfxMgr(format!( + "dfx-mgr-client not found at '{}'.\n\n\ + To enable Xilinx DFX Manager support, install the dfx-mgr component:\n\n\ + sudo snap install fpgad+dfx-mgr.comp --dangerous\n\n\ + Or run the CLI using the `universal` platform (no dfx-mgr required):\n\n\ + fpgad --platform=universal \n\n\ + If you are calling the daemon over DBus, set the platform_string to `universal` instead.", + dfx_mgr_client_path + ))); + } + + Ok(dfx_mgr_client_path) +} + +/// Run dfx-mgr-client with the given arguments (synchronous version). +/// +/// # Arguments +/// +/// * `args` - Command line arguments to pass to dfx-mgr-client +/// +/// # Returns +/// * `Ok(String)` - stdout from dfx-mgr-client on success +/// * `Err(FpgadSoftenerError)` - If dfx-mgr-client cannot be found or execution fails +/// +/// # Examples +/// +/// ```rust,no_run +/// # use daemon::softeners::xilinx_dfx_mgr::xilinx_dfx_mgr_helpers::run_dfx_mgr; +/// # fn example() -> Result<(), daemon::softeners::error::FpgadSoftenerError> { +/// let output = run_dfx_mgr(&["-listPackage"])?; +/// println!("Packages: {}", output); +/// # Ok(()) +/// # } +/// ``` +pub fn run_dfx_mgr(args: &[&str]) -> Result { + let dfx_mgr_client_path = get_dfx_mgr_client_path()?; + + trace!("Calling dfx-mgr-client with args {:#?}", args); + let output = Command::new(&dfx_mgr_client_path) + .args(args) + .output() + .map_err(|e| { + FpgadSoftenerError::DfxMgr(format!("dfx-mgr-client failed to produce output:\n{e}")) + })?; + + if output.status.success() { + Ok(String::from_utf8_lossy(&output.stdout).to_string()) + } else { + Err(FpgadSoftenerError::DfxMgr(format!( + "dfx-mgr-client failed.\n{}\nStdout:\n{:?}\nStderr:\n{:?}", + output.status, + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ))) + } +} /// Extract the firmware-name property from a device tree overlay (.dtbo) file ///