diff --git a/Cargo.toml b/Cargo.toml index f16357e..2a9142b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,8 +14,8 @@ strip="symbols" anyhow = "1" clap = { version = "4.5", default-features = false, features = ["derive", "help", "std", "usage"] } inotify = { version = "0.10", default-features = false, features = ["stream"] } -nix = { version = "0.28", default-features = false, features = ["fs"] } -tokio = { version = "1", default-features = false, features = ["fs", "io-util", "macros", "rt-multi-thread", "signal", "sync"] } +nix = { version = "0.28", default-features = false, features = ["fs", "signal"] } +tokio = { version = "1", default-features = false, features = ["fs", "io-std", "io-util", "macros", "process", "rt-multi-thread", "signal", "sync"] } tokio-stream = { version = "0.1", default-features = false } tokio-util = { version = "0.7", default-features = false } tracing = { version = "0.1", default-features = false } diff --git a/com.steampowered.SteamOSManager1.xml b/com.steampowered.SteamOSManager1.xml index 3748618..b500f9c 100644 --- a/com.steampowered.SteamOSManager1.xml +++ b/com.steampowered.SteamOSManager1.xml @@ -104,19 +104,27 @@ UpdateBios: Perform a BIOS update. + + @objectpath: An object path that can be used to pause/resume/cancel/kill the operation - Version available: 7 + Version available: 8 --> - + + + - + + + - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/manager.rs b/src/manager.rs index 2a69fa8..ac34260 100644 --- a/src/manager.rs +++ b/src/manager.rs @@ -17,7 +17,7 @@ use crate::power::{ get_gpu_clocks, get_gpu_performance_level, get_tdp_limit, set_gpu_clocks, set_gpu_performance_level, set_tdp_limit, GPUPerformanceLevel, }; -use crate::process::{run_script, script_output}; +use crate::process::{run_script, script_output, ProcessManager}; use crate::wifi::{ get_wifi_backend, get_wifi_power_management_state, set_wifi_backend, set_wifi_debug_mode, set_wifi_power_management_state, WifiBackend, WifiDebugMode, WifiPowerManagement, @@ -38,6 +38,8 @@ pub struct SteamOSManager { // Whether we should use trace-cmd or not. // True on galileo devices, false otherwise should_trace: bool, + // Used by ProcessManager but need to only have one of these + next_process: u32, } impl SteamOSManager { @@ -47,6 +49,7 @@ impl SteamOSManager { connection, wifi_debug_mode: WifiDebugMode::Off, should_trace: variant().await? == HardwareVariant::Galileo, + next_process: 0, }) } } @@ -142,47 +145,60 @@ impl SteamOSManager { } } - async fn update_bios(&self) -> zbus::fdo::Result<()> { + async fn update_bios(&mut self) -> zbus::fdo::Result { // Update the bios as needed - run_script("/usr/bin/jupiter-biosupdate", &["--auto"]) - .await - .inspect_err(|message| error!("Error updating BIOS: {message}")) - .map_err(to_zbus_fdo_error) - } - - async fn update_dock(&self) -> zbus::fdo::Result<()> { - // Update the dock firmware as needed - run_script( - "/usr/lib/jupiter-dock-updater/jupiter-dock-updater.sh", - &[] as &[String; 0], + ProcessManager::get_command_object_path( + "/usr/bin/jupiter-biosupdate", + &["--auto"], + &mut self.connection, + &mut self.next_process, + "updating BIOS", ) .await - .inspect_err(|message| error!("Error updating dock: {message}")) - .map_err(to_zbus_fdo_error) } - async fn trim_devices(&self) -> zbus::fdo::Result<()> { + async fn update_dock(&mut self) -> zbus::fdo::Result { + // Update the dock firmware as needed + ProcessManager::get_command_object_path( + "/usr/lib/jupiter-dock-updater/jupiter-dock-updater.sh", + &[] as &[String; 0], + &mut self.connection, + &mut self.next_process, + "updating dock", + ) + .await + } + + async fn trim_devices(&mut self) -> zbus::fdo::Result { // Run steamos-trim-devices script - run_script("/usr/lib/hwsupport/trim-devices.sh", &[] as &[String; 0]) - .await - .inspect_err(|message| error!("Error updating trimming devices: {message}")) - .map_err(to_zbus_fdo_error) + ProcessManager::get_command_object_path( + "/usr/lib/hwsupport/trim-devices.sh", + &[] as &[String; 0], + &mut self.connection, + &mut self.next_process, + "trimming devices", + ) + .await } async fn format_device( - &self, + &mut self, device: &str, label: &str, validate: bool, - ) -> zbus::fdo::Result<()> { + ) -> zbus::fdo::Result { let mut args = vec!["--label", label, "--device", device]; if !validate { args.push("--skip-validation"); } - run_script("/usr/lib/hwsupport/format-device.sh", args.as_ref()) - .await - .inspect_err(|message| error!("Error formatting {device}: {message}")) - .map_err(to_zbus_fdo_error) + ProcessManager::get_command_object_path( + "/usr/lib/hwsupport/format-device.sh", + args.as_ref(), + &mut self.connection, + &mut self.next_process, + format!("formatting {device}").as_str(), + ) + .await } #[zbus(property(emits_changed_signal = "false"))] diff --git a/src/process.rs b/src/process.rs index 5a6b88d..c23e3a9 100644 --- a/src/process.rs +++ b/src/process.rs @@ -5,10 +5,108 @@ * SPDX-License-Identifier: MIT */ -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, bail, Result}; +use nix::sys::signal; +use nix::sys::signal::Signal; +use nix::unistd::Pid; use std::ffi::OsStr; -#[cfg(not(test))] -use tokio::process::Command; +use tokio::process::{Child, Command}; +use tracing::error; +use zbus::interface; + +use crate::{to_zbus_fdo_error}; + +const PROCESS_PREFIX: &str = "/com/steampowered/SteamOSManager1/Process"; + +pub struct ProcessManager { + process: Child, +} + +impl ProcessManager { + pub async fn get_command_object_path( + executable: &str, + args: &[impl AsRef], + connection: &mut zbus::Connection, + next_process: &mut u32, + operation_name: &str, + ) -> zbus::fdo::Result { + // Run the given executable and give back an object path + let path = format!("{}{}", PROCESS_PREFIX, next_process); + *next_process += 1; + let pm = ProcessManager::run_long_command(executable, args) + .await + .inspect_err(|message| error!("Error {operation_name}: {message}")) + .map_err(to_zbus_fdo_error)?; + connection.object_server().at(path.as_str(), pm).await?; + zbus::zvariant::OwnedObjectPath::try_from(path).map_err(to_zbus_fdo_error) + } + + fn send_signal(&self, signal: nix::sys::signal::Signal) -> Result<()> { + // if !self.processes.contains_key(&id) { + // println!("no process found with id {id}"); + // return Err(anyhow!("No process found with id {id}")); + // } + + let command = &self.process; + let pid: Result = match command.id() { + Some(id) => match id.try_into() { + Ok(raw_pid) => Ok(raw_pid), + Err(message) => { + bail!("Unable to get pid_t from command {message}"); + } + }, + None => { + bail!("Unable to get pid from command, it likely finished running"); + } + }; + signal::kill(Pid::from_raw(pid.unwrap()), signal)?; + Ok(()) + } + + async fn exit_code_internal(&mut self) -> Result { + let status = self.process.wait().await?; + match status.code() { + Some(code) => Ok(code), + None => bail!("Process exited without giving a code somehow."), + } + } + + pub async fn run_long_command( + executable: &str, + args: &[impl AsRef], + ) -> Result { + // Run the given executable with the given arguments + // Return an id that can be used later to pause/cancel/resume as needed + let child = Command::new(executable).args(args).spawn()?; + Ok(ProcessManager { process: child }) + } +} + +#[interface(name = "com.steampowered.SteamOSManager1.ProcessManager")] +impl ProcessManager { + pub async fn pause(&self) -> zbus::fdo::Result<()> { + // Pause the given process if possible + // Return true on success, false otherwise + self.send_signal(Signal::SIGSTOP).map_err(to_zbus_fdo_error) + } + + pub async fn resume(&self) -> zbus::fdo::Result<()> { + // Resume the given process if possible + self.send_signal(Signal::SIGCONT).map_err(to_zbus_fdo_error) + } + + pub async fn cancel(&self) -> zbus::fdo::Result<()> { + self.send_signal(Signal::SIGTERM).map_err(to_zbus_fdo_error) + } + + pub async fn kill(&self) -> zbus::fdo::Result<()> { + self.send_signal(signal::SIGKILL).map_err(to_zbus_fdo_error) + } + + pub async fn exit_code(&mut self) -> zbus::fdo::Result { + self.exit_code_internal().await.map_err(to_zbus_fdo_error) + } +} #[cfg(not(test))] pub async fn script_exit_code(executable: &str, args: &[impl AsRef]) -> Result {