aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLia Lenckowski <lialenck@protonmail.com>2024-09-02 01:40:22 +0200
committerLia Lenckowski <lialenck@protonmail.com>2024-09-02 01:40:22 +0200
commitdbe911b3575f0370eb1e058487f83780c30a1566 (patch)
treebf0ba15452b9bbb816ae49042b38a1c82d153fb8 /src
parent86c2d75a8f9bd600da03a9a3637eb490eb6fa424 (diff)
downloadstatuspage-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.rs38
-rw-r--r--src/dbus.rs92
-rw-r--r--src/main.rs1
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};