diff options
author | Lia Lenckowski <lialenck@protonmail.com> | 2024-09-02 01:40:22 +0200 |
---|---|---|
committer | Lia Lenckowski <lialenck@protonmail.com> | 2024-09-02 01:40:22 +0200 |
commit | dbe911b3575f0370eb1e058487f83780c30a1566 (patch) | |
tree | bf0ba15452b9bbb816ae49042b38a1c82d153fb8 /src | |
parent | 86c2d75a8f9bd600da03a9a3637eb490eb6fa424 (diff) | |
download | statuspage-dbe911b3575f0370eb1e058487f83780c30a1566.tar statuspage-dbe911b3575f0370eb1e058487f83780c30a1566.tar.bz2 statuspage-dbe911b3575f0370eb1e058487f83780c30a1566.tar.zst |
add systemd-user-global, perform systemd-global und systemd-user-global via dbus
Diffstat (limited to 'src')
-rw-r--r-- | src/check.rs | 38 | ||||
-rw-r--r-- | src/dbus.rs | 92 | ||||
-rw-r--r-- | src/main.rs | 1 |
3 files changed, 105 insertions, 26 deletions
diff --git a/src/check.rs b/src/check.rs index a922055..42812c9 100644 --- a/src/check.rs +++ b/src/check.rs @@ -10,11 +10,18 @@ use tokio::{ time::{sleep, timeout}, }; +use crate::dbus::*; + #[derive(Debug, Deserialize)] #[serde(rename_all = "kebab-case")] pub enum Check { Systemd(String), SystemdGlobal, +// SystemdUser { +// user: String, +// name: String, +// }, + SystemdUserGlobal(String), Pacman(String), Http { title: Option<String>, @@ -104,32 +111,10 @@ impl Check { } } Check::SystemdGlobal => { - let output = Command::new("systemctl") - .arg("show") - .arg("--no-pager") - .output() - .await?; - let output = String::from_utf8(output.stdout).context("systemctl output")?; - - let mut nfailed = 0; - for line in output.split("\n") { - if let Some((key, value)) = line.split_once("=") { - match key { - "NFailedUnits" => { - nfailed = value.parse().context("systemctl nfailed output")? - } - _ => (), - } - } - } - if nfailed > 0 { - Err(anyhow!( - "{nfailed} unit{} failed", - if nfailed > 1 { "s" } else { "" } - )) - } else { - Ok("running".to_string()) - } + check_systemd_all(None).await + } + Check::SystemdUserGlobal(username) => { + check_systemd_all(Some(username)).await } Check::Shell { command, output, .. @@ -183,6 +168,7 @@ impl Check { Check::Shell { title, .. } => title.to_owned(), Check::Pacman(_) => "Installed".to_string(), Check::SystemdGlobal => "System Services".to_string(), + Check::SystemdUserGlobal(username) => format!("User services for {username}"), } } } diff --git a/src/dbus.rs b/src/dbus.rs new file mode 100644 index 0000000..8fd2302 --- /dev/null +++ b/src/dbus.rs @@ -0,0 +1,92 @@ +use anyhow::{anyhow, Result}; +use serde::{Deserialize, Serialize}; +use zbus::{ + proxy, + zvariant::{OwnedObjectPath, Type}, + Connection, connection::Builder, address::{Address, transport::{Unix, Transport, UnixSocket}}, +}; +use std::{sync::Arc, collections::{BTreeMap, btree_map::Entry::*}, path::PathBuf}; +use tokio::sync::Mutex; + +static CONNECTIONS: Mutex<(BTreeMap<String, Arc<Connection>>, Option<Arc<Connection>>)> = Mutex::const_new((BTreeMap::new(), None)); + +#[derive(Debug, Type, Deserialize, Serialize)] +struct ServiceStatus { + name: String, + description: String, + loaded: String, + active: String, + substate: String, + following_state: String, + unit_path: OwnedObjectPath, + job_id: u32, + job_type: String, + job_path: OwnedObjectPath, +} + +#[proxy( + interface = "org.freedesktop.systemd1.Manager", + default_service = "org.freedesktop.systemd1", + default_path = "/org/freedesktop/systemd1" +)] +trait Manager { + async fn list_units(&self) -> Result<Vec<ServiceStatus>>; + async fn get_unit(&self, unit_name: String) -> Result<OwnedObjectPath>; +} + +async fn ensure_system_conn() -> Result<Arc<Connection>> { + let mut connections = CONNECTIONS.lock().await; + + match &connections.1 { + Some(system_bus) => Ok(system_bus.clone()), + None => { + let system_bus = Arc::new(Connection::system().await?); + connections.1 = Some(system_bus.clone()); + Ok(system_bus) + }, + } +} + +async fn ensure_user_conn(user: &str) -> Result<Arc<Connection>> { + let mut connections = CONNECTIONS.lock().await; + + match connections.0.entry(user.to_owned()) { + Occupied(oe) => Ok(oe.get().clone()), + Vacant(ve) => { + let mut path = PathBuf::new(); + path.push("/run/user"); + path.push(format!("{}", users::get_user_by_name(user).ok_or(anyhow!("Couldn't find user"))?.uid())); + path.push("bus"); + + let trans = Transport::Unix(Unix::new(UnixSocket::File(path))); + + let conn = Builder::address(Address::new(trans))?.build().await?; + Ok(ve.insert(Arc::new(conn)).clone()) + }, + } +} + +pub(crate) async fn check_systemd_all(user: Option<&str>) -> Result<String> { + let conn = match user { + None => ensure_system_conn().await, + Some(username) => ensure_user_conn(username).await, + }?; + + let manager = ManagerProxy::new(&conn).await?; + + let (good, bad) = manager.list_units().await?.into_iter().fold((0, vec![]), |(old_good, mut old_bad), unit| { + if matches!(unit.substate.as_str(), "active" | "inactive" | "plugged" | "mounted" | "dead" | "listening" | "running" | "exited" | "waiting" | "abandoned" | "elapsed") { + (old_good + 1, old_bad) + } + else { + old_bad.push(format!("{}: {}", unit.name, unit.substate)); + (old_good, old_bad) + } + }); + + if bad.is_empty() { + Ok(format!("{good} good services")) + } else { + Err(anyhow!("Bad services: {}", bad.join(", "))) + } +} diff --git a/src/main.rs b/src/main.rs index bfa26a1..69034b3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ pub mod check; pub mod log; pub mod mail; pub mod web; +pub mod dbus; use ::log::error; use anyhow::{anyhow, Result}; |