diff --git a/Cargo.lock b/Cargo.lock index b0af532..d00af3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -377,10 +377,10 @@ dependencies = [ "local-ip-address", "serde", "serde_json", + "snafu 0.8.5", "stackable-operator", "sysinfo", "tracing", - "users", ] [[package]] @@ -2639,6 +2639,7 @@ dependencies = [ "memchr", "ntapi", "rayon", + "serde", "windows", ] @@ -3054,16 +3055,6 @@ dependencies = [ "serde", ] -[[package]] -name = "users" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032" -dependencies = [ - "libc", - "log", -] - [[package]] name = "utf16_iter" version = "1.0.5" diff --git a/Cargo.toml b/Cargo.toml index c71b7c8..4ec3e91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,10 +10,10 @@ hickory-resolver = "0.24.1" local-ip-address = "0.6.3" serde = { version = "1.0.210", features = ["derive"] } serde_json = "1.0.128" +snafu = "0.8.5" stackable-operator = { git = "https://github.com/stackabletech/operator-rs", tag = "stackable-operator-0.83.0" } -sysinfo = "0.32.0" +sysinfo = { version = "0.32.0", features = ["serde"] } tracing = "0.1.40" -users = "0.11.0" [build-dependencies] built = { version = "0.7", features = ["chrono", "git2"] } diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..f231a76 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,51 @@ +use serde::Serialize; +use snafu::Snafu; + +/// Wrapped version of the errors returned by [`sysinfo`], since they are bare [`str`]s. +#[derive(Debug, Snafu)] +#[snafu(display("{msg}"))] +pub struct SysinfoError { + pub msg: &'static str, +} + +/// Wraps errors returned by a component to present them consistently for serialization. +#[derive(Debug, Serialize)] +#[serde(untagged)] +pub enum ComponentResult { + Ok(T), + Err { + #[serde(rename = "$error")] + inner: ComponentError, + }, +} +impl ComponentResult { + #[track_caller] + pub fn report_from_result( + component: &str, + result: Result, + ) -> ComponentResult { + match result { + Ok(x) => ComponentResult::Ok(x), + Err(err) => { + tracing::error!( + error = &err as &dyn std::error::Error, + "error reported by {component}, ignoring...", + ); + err.source(); + ComponentResult::Err { + inner: ComponentError { + message: err.to_string(), + causes: std::iter::successors(err.source(), |err| err.source()) + .map(|err| err.to_string()) + .collect(), + }, + } + } + } + } +} +#[derive(Debug, Serialize)] +pub struct ComponentError { + message: String, + causes: Vec, +} diff --git a/src/main.rs b/src/main.rs index 8bb46e0..cd142e0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +mod error; mod system_information; use clap::{crate_description, crate_version, Parser}; diff --git a/src/system_information/mod.rs b/src/system_information/mod.rs index ef5672a..a37bd1d 100644 --- a/src/system_information/mod.rs +++ b/src/system_information/mod.rs @@ -1,5 +1,7 @@ use serde::Serialize; +use crate::error::ComponentResult; + pub mod disk; pub mod network; pub mod os; @@ -10,7 +12,7 @@ pub mod user; pub struct SystemInformation { pub resources: resources::Resources, pub os: os::OperatingSystem, - pub current_user: user::User, + pub current_user: ComponentResult, pub disks: Vec, pub network: network::SystemNetworkInfo, // TODO: @@ -34,10 +36,19 @@ impl SystemInformation { #[tracing::instrument(name = "SystemInformation::collect")] pub fn collect() -> Self { tracing::info!("Starting data collection"); + + // Please note that we use "new_all" to ensure that all list of + // components, network interfaces, disks and users are already + // filled! + let sys = sysinfo::System::new_all(); + let info = Self { - resources: resources::Resources::collect(), + resources: resources::Resources::collect(&sys), os: os::OperatingSystem::collect(), - current_user: user::User::collect_current(), + current_user: ComponentResult::report_from_result( + "User::collect_current", + user::User::collect_current(&sys), + ), disks: disk::Disk::collect_all(), network: network::SystemNetworkInfo::collect(), }; diff --git a/src/system_information/resources.rs b/src/system_information/resources.rs index a2e6da1..ab7101c 100644 --- a/src/system_information/resources.rs +++ b/src/system_information/resources.rs @@ -21,16 +21,11 @@ pub struct Resources { } impl Resources { - #[tracing::instrument(name = "Resources::collect")] - pub fn collect() -> Self { + #[tracing::instrument(name = "Resources::collect", skip(sys))] + pub fn collect(sys: &System) -> Self { // This style of "declare-then-log-then-merge becomes a bit verbose, // but should help keep each log statement local to where that info is collected. - // Please note that we use "new_all" to ensure that all list of - // components, network interfaces, disks and users are already - // filled! - let sys = System::new_all(); - let cpu_count = sys.cpus().len(); let physical_core_count = sys.physical_core_count(); tracing::info!( diff --git a/src/system_information/user.rs b/src/system_information/user.rs index 5c69ecc..1719f38 100644 --- a/src/system_information/user.rs +++ b/src/system_information/user.rs @@ -1,21 +1,47 @@ use serde::Serialize; +use snafu::{OptionExt, ResultExt, Snafu}; +use sysinfo::{Gid, Pid, Uid}; + +use crate::error::SysinfoError; + +#[derive(Debug, Snafu)] +pub enum Error { + #[snafu(display("failed to get pid of the current process"))] + GetCurrentPid { source: SysinfoError }, + #[snafu(display("current pid {pid} could not be resolved to a proess"))] + ResolveCurrentProcess { pid: Pid }, +} +type Result = std::result::Result; #[derive(Debug, Serialize)] pub struct User { pub name: Option, // The name of the current user - pub uid: u32, // The user ID (UID) - pub gid: u32, // The group ID (GID) + pub uid: Option, // The user ID (UID) + pub gid: Option, // The group ID (GID) } impl User { - pub fn collect_current() -> Self { - let uid = users::get_current_uid(); + #[tracing::instrument(name = "User::collect_current", skip(sys))] + pub fn collect_current(sys: &sysinfo::System) -> Result { + let pid = sysinfo::get_current_pid() + .map_err(|msg| SysinfoError { msg }) + .context(GetCurrentPidSnafu)?; + let current_process = sys + .process(pid) + .context(ResolveCurrentProcessSnafu { pid })?; + let uid = current_process.user_id(); + let os_users = sysinfo::Users::new_with_refreshed_list(); let user = Self { - name: users::get_user_by_uid(uid).map(|user| user.name().to_string_lossy().to_string()), - uid, - gid: users::get_current_gid(), + name: uid.and_then(|uid| Some(os_users.get_user_by_id(uid)?.name().to_string())), + uid: uid.cloned(), + gid: current_process.group_id(), }; - tracing::info!(user.name, user.uid, user.gid, "current user"); - user + tracing::info!( + user.name, + user.uid = user.uid.as_ref().map(|uid| format!("{uid:?}")), + user.gid = user.uid.as_ref().map(|gid| format!("{gid:?}")), + "current user" + ); + Ok(user) } }