Close the only exploitable auth bypass: validate VerifyStatus signal sender against fprintd's unique bus name. Fix fingerprint D-Bus lifecycle so devices are properly released on verify-match and async restarts check the running flag between awaits. Security: num_msg guard in PAM callback, symlink rejection for background_path, peek icon disabled, TOML parse errors logged, panic hook before logging. Performance: blur and avatar textures cached across monitors, release profile with LTO/strip.
49 lines
1.8 KiB
Rust
49 lines
1.8 KiB
Rust
// ABOUTME: Power actions — reboot and shutdown via systemctl.
|
|
// ABOUTME: Wrappers around system commands for the lockscreen UI.
|
|
|
|
use std::fmt;
|
|
use std::process::Command;
|
|
|
|
#[derive(Debug)]
|
|
pub enum PowerError {
|
|
CommandFailed { action: &'static str, message: String },
|
|
}
|
|
|
|
impl fmt::Display for PowerError {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
PowerError::CommandFailed { action, message } => write!(f, "{action} failed: {message}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for PowerError {}
|
|
|
|
fn run_command(action: &'static str, program: &str, args: &[&str]) -> Result<(), PowerError> {
|
|
let output = Command::new(program)
|
|
.args(args)
|
|
.stderr(std::process::Stdio::piped())
|
|
.output()
|
|
.map_err(|e| PowerError::CommandFailed { action, message: e.to_string() })?;
|
|
if !output.status.success() {
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
return Err(PowerError::CommandFailed {
|
|
action, message: format!("exit code {}: {}", output.status, stderr.trim()),
|
|
});
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn reboot() -> Result<(), PowerError> { run_command("reboot", "/usr/bin/systemctl", &["--no-ask-password", "reboot"]) }
|
|
pub fn shutdown() -> Result<(), PowerError> { run_command("shutdown", "/usr/bin/systemctl", &["--no-ask-password", "poweroff"]) }
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test] fn power_error_display() { assert_eq!(PowerError::CommandFailed { action: "reboot", message: "fail".into() }.to_string(), "reboot failed: fail"); }
|
|
#[test] fn missing_binary() { assert!(run_command("test", "nonexistent-xyz", &[]).is_err()); }
|
|
#[test] fn nonzero_exit() { assert!(run_command("test", "false", &[]).is_err()); }
|
|
#[test] fn success() { assert!(run_command("test", "true", &[]).is_ok()); }
|
|
}
|