add sbot process stats function, struct and error variants
This commit is contained in:
parent
66555f19bf
commit
816d6c8a73
|
@ -1,7 +1,7 @@
|
|||
//! Custom error type for `peach-stats`.
|
||||
|
||||
use probes::ProbeError;
|
||||
use std::{error, fmt, io::Error as IoError};
|
||||
use std::{error, fmt, io::Error as IoError, str::Utf8Error};
|
||||
|
||||
/// Custom error type encapsulating all possible errors when retrieving system
|
||||
/// statistics.
|
||||
|
@ -17,6 +17,10 @@ pub enum StatsError {
|
|||
MemStat(ProbeError),
|
||||
/// Failed to retrieve system uptime.
|
||||
Uptime(IoError),
|
||||
/// Systemctl command returned an error.
|
||||
Systemctl(IoError),
|
||||
/// Failed to interpret sequence of `u8` as a string.
|
||||
Utf8String(Utf8Error),
|
||||
}
|
||||
|
||||
impl error::Error for StatsError {}
|
||||
|
@ -39,6 +43,12 @@ impl fmt::Display for StatsError {
|
|||
StatsError::Uptime(ref source) => {
|
||||
write!(f, "Failed to retrieve system uptime: {}", source)
|
||||
}
|
||||
StatsError::Systemctl(ref source) => {
|
||||
write!(f, "Systemctl command returned an error: {}", source)
|
||||
}
|
||||
StatsError::Utf8String(ref source) => {
|
||||
write!(f, "Failed to convert stdout to string: {}", source)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
//! ```
|
||||
|
||||
pub mod error;
|
||||
pub mod sbot;
|
||||
pub mod stats;
|
||||
|
||||
pub use crate::error::StatsError;
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
//! Systemd go-sbot process statistics retrieval functions and associated data types.
|
||||
|
||||
use std::{process::Command, str};
|
||||
|
||||
use crate::StatsError;
|
||||
|
||||
/// go-sbot process statistics.
|
||||
#[derive(Debug)]
|
||||
pub struct SbotStat {
|
||||
/// Current process state.
|
||||
state: String,
|
||||
/// Current process memory usage in bytes.
|
||||
memory: Option<u32>,
|
||||
/// Uptime for the process (if state is `active`).
|
||||
uptime: Option<String>,
|
||||
/// Downtime for the process (if state is `inactive`).
|
||||
downtime: Option<String>,
|
||||
}
|
||||
|
||||
impl SbotStat {
|
||||
/// Default builder for `SbotStat`.
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
state: String::new(),
|
||||
memory: None,
|
||||
uptime: None,
|
||||
downtime: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve statistics for the go-sbot systemd process by querying `systemctl`.
|
||||
pub fn sbot_stats() -> Result<SbotStat, StatsError> {
|
||||
let mut status = SbotStat::default();
|
||||
|
||||
let info_output = Command::new("/usr/bin/systemctl")
|
||||
.arg("--user")
|
||||
.arg("show")
|
||||
.arg("go-sbot.service")
|
||||
.arg("--no-page")
|
||||
.output()
|
||||
.map_err(StatsError::Systemctl)?;
|
||||
|
||||
let service_info = std::str::from_utf8(&info_output.stdout).map_err(StatsError::Utf8String)?;
|
||||
|
||||
for line in service_info.lines() {
|
||||
if line.starts_with("ActiveState=") {
|
||||
if let Some(state) = line.strip_prefix("ActiveState=") {
|
||||
status.state = state.to_string()
|
||||
}
|
||||
} else if line.starts_with("MemoryCurrent=") {
|
||||
if let Some(memory) = line.strip_prefix("MemoryCurrent=") {
|
||||
status.memory = memory.parse().ok()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let status_output = Command::new("/usr/bin/systemctl")
|
||||
.arg("--user")
|
||||
.arg("status")
|
||||
.arg("go-sbot.service")
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
let service_status = str::from_utf8(&status_output.stdout).map_err(StatsError::Utf8String)?;
|
||||
|
||||
// example of the output line we're looking for:
|
||||
// `Active: active (running) since Mon 2022-01-24 16:22:51 SAST; 4min 14s ago`
|
||||
|
||||
for line in service_status.lines() {
|
||||
if line.contains("Active:") {
|
||||
let before_time = line.find(';');
|
||||
let after_time = line.find(" ago");
|
||||
if let (Some(start), Some(end)) = (before_time, after_time) {
|
||||
// extract the uptime / downtime from the `Active: ...` line
|
||||
// using the index of ';' + 2 and the index of " ago"
|
||||
let time = Some(&line[start + 2..end]);
|
||||
// if service is active then the `time` reading is uptime
|
||||
if status.state == "active" {
|
||||
status.uptime = time.map(|t| t.to_string())
|
||||
// if service is inactive then the `time` reading is downtime
|
||||
} else if status.state == "inactive" {
|
||||
status.downtime = time.map(|t| t.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(status)
|
||||
}
|
Loading…
Reference in New Issue