diff --git a/Cargo.lock b/Cargo.lock index 1fbedd0..c91cbaa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "anstyle" version = "1.0.7" @@ -308,6 +317,17 @@ dependencies = [ "typenum", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "digest" version = "0.10.7" @@ -318,6 +338,12 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "either" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" + [[package]] name = "endi" version = "1.1.0" @@ -543,6 +569,15 @@ dependencies = [ "libc", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -761,6 +796,35 @@ dependencies = [ "getrandom", ] +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -867,11 +931,12 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "steamos-manager" -version = "24.4.1" +version = "24.5.0" dependencies = [ "anyhow", "clap", "inotify", + "itertools", "libc", "nix", "tempfile", @@ -1258,14 +1323,15 @@ dependencies = [ [[package]] name = "zbus" -version = "4.2.0" +version = "4.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aea58d1af0aaa8abf87f3d9ade9b8f46bf13727e5f9fb24bc31ee9d94a9b4ad" +checksum = "c9ff46f2a25abd690ed072054733e0bc3157e3d4c45f41bd183dce09c2ff8ab9" dependencies = [ "async-broadcast", "async-process", "async-recursion", "async-trait", + "derivative", "enumflags2", "event-listener 5.3.0", "futures-core", @@ -1291,13 +1357,14 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "4.2.0" +version = "4.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf2b496ec1e2d3c4a7878e351607f7a2bec1e1029b353683dfc28a22999e369" +checksum = "4e0e3852c93dcdb49c9462afe67a2a468f7bd464150d866e861eaf06208633e0" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", + "regex", "syn 1.0.109", "zvariant_utils", ] @@ -1328,9 +1395,9 @@ dependencies = [ [[package]] name = "zvariant" -version = "4.0.3" +version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e9282c6945d9e27742ba7ad7191325546636295de7b83f6735af73159b32ac7" +checksum = "2c1b3ca6db667bfada0f1ebfc94b2b1759ba25472ee5373d4551bb892616389a" dependencies = [ "endi", "enumflags2", @@ -1341,9 +1408,9 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "4.0.3" +version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0142549e559746ff09d194dd43d256a554299d286cc56460a082b8ae24652aa1" +checksum = "b7a4b236063316163b69039f77ce3117accb41a09567fd24c168e43491e521bc" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -1354,9 +1421,9 @@ dependencies = [ [[package]] name = "zvariant_utils" -version = "1.1.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75fa7291bdd68cd13c4f97cc9d78cbf16d96305856dfc7ac942aeff4c2de7d5a" +checksum = "00bedb16a193cc12451873fee2a1bc6550225acece0e36f333e68326c73c8172" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 11b8855..922dd12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "steamos-manager" -version = "24.4.1" +version = "24.5.0" edition = "2021" [dev-dependencies] @@ -15,10 +15,12 @@ anyhow = "1" clap = { version = "4.5", default-features = false, features = ["derive", "help", "std", "usage"] } inotify = { version = "0.10", default-features = false, features = ["stream"] } libc = "0.2" +itertools = "0.12" 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 } tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] } -zbus = { version = "4", default-features = false, features = ["tokio"] } +# pinning to 4.1.2 until a fix for https://github.com/dbus2/zbus/issues/764 gets released +zbus = { version = "4.1", default-features = false, features = ["tokio"] } diff --git a/src/bin/steamos-manager.rs b/src/bin/steamos-manager.rs new file mode 100644 index 0000000..b6d9303 --- /dev/null +++ b/src/bin/steamos-manager.rs @@ -0,0 +1,28 @@ +/* + * Copyright © 2023 Collabora Ltd. + * Copyright © 2024 Valve Software + * + * SPDX-License-Identifier: MIT + */ + +use anyhow::Result; +use clap::Parser; + +use steamos_manager::{RootDaemon, UserDaemon}; + +#[derive(Parser)] +struct Args { + /// Run the root manager daemon + #[arg(short, long)] + root: bool, +} + +#[tokio::main] +pub async fn main() -> Result<()> { + let args = Args::parse(); + if args.root { + RootDaemon().await + } else { + UserDaemon().await + } +} diff --git a/src/bin/steamosctl.rs b/src/bin/steamosctl.rs new file mode 100644 index 0000000..043f7ff --- /dev/null +++ b/src/bin/steamosctl.rs @@ -0,0 +1,237 @@ +/* + * Copyright © 2023 Collabora Ltd. + * Copyright © 2024 Valve Software + * + * SPDX-License-Identifier: MIT + */ + +use anyhow::Result; +use clap::{Parser, Subcommand}; +use itertools::Itertools; +use std::ops::Deref; +use std::str::FromStr; +use steamos_manager::{ManagerProxy, WifiBackend}; +use zbus::fdo::PropertiesProxy; +use zbus::names::InterfaceName; +use zbus::{zvariant, Connection}; + +#[derive(Parser)] +#[command(version, about, long_about = None)] +struct Args { + /// Optionally get all properties + #[arg(short, long)] + all_properties: bool, + #[command(subcommand)] + command: Option, +} + +#[derive(Subcommand)] +enum Commands { + GetAlsCalibrationGain {}, + GetHardwareCurrentlySupported {}, + + SetFanControlState { + // Set the fan control state. + // 0 - BIOS, 1 - OS + #[arg(short, long)] + value: u32, + }, + + GetFanControlState {}, + + SetGPUPerformanceLevel { + // Set the gpu performance level + // 0 = Auto, 1 = Low, 2 = High, 3 = Manual, 4 = Profile Peak + #[arg(short, long)] + value: u32, + }, + + GetGPUPerformanceLevel {}, + + SetManualGPUClock { + // Set the GPU clock value manually + // Controls the GPU clock frequency in MHz when GPUPerformanceLevel is set to Manual + #[arg(short, long)] + value: u32, + }, + + GetManualGPUClock {}, + GetManualGPUClockMax {}, + GetManualGPUClockMin {}, + + SetTDPLimit { + // Set the TDP limit + #[arg(short, long)] + value: u32, + }, + + GetTDPLimit {}, + GetTDPLimitMax {}, + GetTDPLimitMin {}, + + GetVersion {}, + + SetWifiBackend { + // Set the wifi backend to given string if possible + // Supported values are iwd|wpa_supplicant + #[arg(short, long)] + backend: String, + }, + + GetWifiBackend {}, + + SetWifiDebugMode { + // Set wifi debug mode to given value + // 1 for on, 0 for off currently + #[arg(short, long)] + mode: u32, + }, + + GetWifiDebugMode {}, + + SetWifiPowerManagementState { + // Set the wifi power management state + // 0 - disabled, 1 - enabled + #[arg(short, long)] + value: u32, + }, + + GetWifiPowerManagementState {}, + + UpdateBios {}, + UpdateDock {}, + TrimDevices {}, + FactoryReset {}, +} + +#[tokio::main] +async fn main() -> Result<()> { + // This is a command-line utility that calls api using dbus + + // First set up which command line arguments we support + let args = Args::parse(); + + // Then get a connection to the service + let conn = Connection::session().await?; + let proxy = ManagerProxy::builder(&conn).build().await?; + + if args.all_properties { + let properties_proxy = PropertiesProxy::new( + &conn, + "com.steampowered.SteamOSManager1", + "/com/steampowered/SteamOSManager1", + ) + .await?; + let name = InterfaceName::try_from("com.steampowered.SteamOSManager1.Manager")?; + let properties = properties_proxy + .get_all(zvariant::Optional::from(Some(name))) + .await?; + for key in properties.keys().sorted() { + let value = &properties[key]; + let val = value.deref(); + println!("{key}: {val}"); + } + } + + // Then process arguments + match &args.command { + Some(Commands::GetAlsCalibrationGain {}) => { + let gain = proxy.als_calibration_gain().await?; + println!("ALS calibration gain: {gain}"); + } + Some(Commands::GetHardwareCurrentlySupported {}) => { + let supported = proxy.hardware_currently_supported().await?; + println!("Hardware currently supported: {supported}"); + } + Some(Commands::GetVersion {}) => { + let version = proxy.version().await?; + println!("Version: {version}"); + } + Some(Commands::SetFanControlState { value }) => { + proxy.set_fan_control_state(*value).await?; + } + Some(Commands::SetGPUPerformanceLevel { value }) => { + proxy.set_gpu_performance_level(*value).await?; + } + Some(Commands::GetGPUPerformanceLevel {}) => { + let level = proxy.gpu_performance_level().await?; + println!("GPU performance level: {level}"); + } + Some(Commands::SetManualGPUClock { value }) => { + proxy.set_manual_gpu_clock(*value).await?; + } + Some(Commands::GetManualGPUClock {}) => { + let clock = proxy.manual_gpu_clock().await?; + println!("Manual GPU Clock: {clock}"); + } + Some(Commands::GetManualGPUClockMax {}) => { + let value = proxy.manual_gpu_clock_max().await?; + println!("Manual GPU Clock Max: {value}"); + } + Some(Commands::GetManualGPUClockMin {}) => { + let value = proxy.manual_gpu_clock_min().await?; + println!("Manual GPU Clock Min: {value}"); + } + Some(Commands::SetTDPLimit { value }) => { + proxy.set_tdp_limit(*value).await?; + } + Some(Commands::GetTDPLimit {}) => { + let limit = proxy.tdp_limit().await?; + println!("TDP limit: {limit}"); + } + Some(Commands::GetFanControlState {}) => { + let state = proxy.fan_control_state().await?; + println!("Fan control state: {state}"); + } + Some(Commands::GetTDPLimitMax {}) => { + let value = proxy.tdp_limit_max().await?; + println!("TDP limit max: {value}"); + } + Some(Commands::GetTDPLimitMin {}) => { + let value = proxy.tdp_limit_min().await?; + println!("TDP limit min: {value}"); + } + Some(Commands::SetWifiBackend { backend }) => match WifiBackend::from_str(backend) { + Ok(b) => { + proxy.set_wifi_backend(b as u32).await?; + } + Err(_) => { + println!("Unknown wifi backend {backend}"); + } + }, + Some(Commands::GetWifiBackend {}) => { + let backend = proxy.wifi_backend().await?; + let backend_string = WifiBackend::try_from(backend).unwrap().to_string(); + println!("Wifi backend: {backend_string}"); + } + Some(Commands::SetWifiDebugMode { mode }) => { + proxy.set_wifi_debug_mode(*mode, 20000).await?; + } + Some(Commands::GetWifiDebugMode {}) => { + let mode = proxy.wifi_debug_mode_state().await?; + println!("Wifi debug mode: {mode}"); + } + Some(Commands::SetWifiPowerManagementState { value }) => { + proxy.set_wifi_power_management_state(*value).await?; + } + Some(Commands::GetWifiPowerManagementState {}) => { + let state = proxy.wifi_power_management_state().await?; + println!("Wifi power management state: {state}"); + } + Some(Commands::UpdateBios {}) => { + let _ = proxy.update_bios().await?; + } + Some(Commands::UpdateDock {}) => { + let _ = proxy.update_dock().await?; + } + Some(Commands::FactoryReset {}) => { + let _ = proxy.prepare_factory_reset().await?; + } + Some(Commands::TrimDevices {}) => { + let _ = proxy.trim_devices().await?; + } + None => {} + } + + Ok(()) +} diff --git a/src/main.rs b/src/lib.rs similarity index 94% rename from src/main.rs rename to src/lib.rs index b7e444c..670bcc5 100644 --- a/src/main.rs +++ b/src/lib.rs @@ -6,7 +6,6 @@ */ use anyhow::{anyhow, Result}; -use clap::Parser; use std::future::Future; use std::path::{Path, PathBuf}; use tokio::fs::File; @@ -22,6 +21,7 @@ mod hardware; mod manager; mod power; mod process; +mod proxy; mod root; mod sls; mod systemd; @@ -32,9 +32,14 @@ mod wifi; #[cfg(test)] mod testing; +pub use proxy::ManagerProxy; +pub use root::daemon as RootDaemon; +pub use user::daemon as UserDaemon; +pub use wifi::{WifiBackend, WifiDebugMode, WifiPowerManagement}; + const API_VERSION: u32 = 8; -trait Service +pub trait Service where Self: Sized + Send, { @@ -67,13 +72,6 @@ where } } -#[derive(Parser)] -struct Args { - /// Run the root manager daemon - #[arg(short, long)] - root: bool, -} - #[cfg(not(test))] pub fn path>(path: S) -> PathBuf { PathBuf::from(path.as_ref()) @@ -156,16 +154,6 @@ pub fn zbus_to_zbus_fdo(error: zbus::Error) -> zbus::fdo::Error { } } -#[tokio::main] -pub async fn main() -> Result<()> { - let args = Args::parse(); - if args.root { - root::daemon().await - } else { - user::daemon().await - } -} - #[cfg(test)] mod test { use crate::testing; diff --git a/src/proxy.rs b/src/proxy.rs new file mode 100644 index 0000000..02285f3 --- /dev/null +++ b/src/proxy.rs @@ -0,0 +1,140 @@ +//! # D-Bus interface proxy for: `com.steampowered.SteamOSManager1.Manager` +//! and `com.steampowered.SteamOSManager1.Subprocess` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `com.steampowered.SteamOSManager1.xml`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; + +#[proxy( + default_service = "com.steampowered.SteamOSManager1", + default_path = "/com/steampowered/SteamOSManager1", + interface = "com.steampowered.SteamOSManager1.Manager" +)] +trait Manager { + /// FormatDevice method + fn format_device( + &self, + device: &str, + label: &str, + validate: bool, + ) -> zbus::Result; + + /// GetAlsIntegrationTimeFileDescriptor method + fn get_als_integration_time_file_descriptor(&self) -> zbus::Result; + + /// PrepareFactoryReset method + fn prepare_factory_reset(&self) -> zbus::Result; + + /// SetWifiDebugMode method + fn set_wifi_debug_mode(&self, mode: u32, buffer_size: u32) -> zbus::Result<()>; + + /// TrimDevices method + fn trim_devices(&self) -> zbus::Result; + + /// UpdateBIOS method + fn update_bios(&self) -> zbus::Result; + + /// UpdateDock method + fn update_dock(&self) -> zbus::Result; + + /// AlsCalibrationGain property + #[zbus(property)] + fn als_calibration_gain(&self) -> zbus::Result; + + /// FanControlState property + #[zbus(property)] + fn fan_control_state(&self) -> zbus::Result; + #[zbus(property)] + fn set_fan_control_state(&self, value: u32) -> zbus::Result<()>; + + /// GpuPerformanceLevel property + #[zbus(property)] + fn gpu_performance_level(&self) -> zbus::Result; + #[zbus(property)] + fn set_gpu_performance_level(&self, value: u32) -> zbus::Result<()>; + + /// HardwareCurrentlySupported property + #[zbus(property)] + fn hardware_currently_supported(&self) -> zbus::Result; + + /// ManualGpuClock property + #[zbus(property)] + fn manual_gpu_clock(&self) -> zbus::Result; + #[zbus(property)] + fn set_manual_gpu_clock(&self, value: u32) -> zbus::Result<()>; + + /// ManualGpuClockMax property + #[zbus(property)] + fn manual_gpu_clock_max(&self) -> zbus::Result; + + /// ManualGpuClockMin property + #[zbus(property)] + fn manual_gpu_clock_min(&self) -> zbus::Result; + + /// TdpLimit property + #[zbus(property)] + fn tdp_limit(&self) -> zbus::Result; + #[zbus(property)] + fn set_tdp_limit(&self, value: u32) -> zbus::Result<()>; + + /// TdpLimitMax property + #[zbus(property)] + fn tdp_limit_max(&self) -> zbus::Result; + + /// TdpLimitMin property + #[zbus(property)] + fn tdp_limit_min(&self) -> zbus::Result; + + /// Version property + #[zbus(property)] + fn version(&self) -> zbus::Result; + + /// WifiBackend property + #[zbus(property)] + fn wifi_backend(&self) -> zbus::Result; + #[zbus(property)] + fn set_wifi_backend(&self, value: u32) -> zbus::Result<()>; + + /// WifiDebugModeState property + #[zbus(property)] + fn wifi_debug_mode_state(&self) -> zbus::Result; + + /// WifiPowerManagementState property + #[zbus(property)] + fn wifi_power_management_state(&self) -> zbus::Result; + #[zbus(property)] + fn set_wifi_power_management_state(&self, value: u32) -> zbus::Result<()>; +} + +#[proxy( + interface = "com.steampowered.SteamOSManager1.SubProcess", + assume_defaults = true +)] +trait SubProcess { + /// Cancel method + fn cancel(&self) -> zbus::Result<()>; + + /// ExitCode method + fn exit_code(&self) -> zbus::Result; + + /// WaitForExitCode method + fn wait_for_exit_code(&self) -> zbus::Result; + + /// Kill method + fn kill(&self) -> zbus::Result<()>; + + /// Pause method + fn pause(&self) -> zbus::Result<()>; + + /// Resume method + fn resume(&self) -> zbus::Result<()>; +}