Files
lapctl/src/utils/system.rs
T
sankalp 0219f1c8ce refactor(src/utils): improve error handling and messaging in system utilitiesNew Features
- None

Enhancements- Added detailed error reporting with exit codes for privilege escalation, initramfs rebuild, service management, and session termination
- Implemented graceful handling of permission‑setting failures with proper logging
- Enhanced error messages for systemctl and loginctl commands, providing clearer diagnostics

Bug Fixes- Fixed incomplete error propagation in status matching, now reports actual exit codes or underlying errors
2026-05-25 14:50:00 +05:45

218 lines
7.7 KiB
Rust

use log::{debug, error, info, warn};
use std::fs;
use std::os::unix::fs::PermissionsExt;
use std::path::Path;
use std::process::Command;
pub fn assert_root() {
let output = Command::new("id").arg("-u").output();
if let Ok(output) = output {
let is_root = String::from_utf8_lossy(&output.stdout).trim() == "0";
if !is_root {
// Try to re-exec with pkexec
if let Ok(exe) = std::env::current_exe() {
let args: Vec<String> = std::env::args().skip(1).collect();
info!("Not running as root. Attempting to elevate privileges via pkexec...");
let status = Command::new("pkexec").arg(exe).args(&args).status();
match status {
Ok(s) if s.success() => std::process::exit(0),
Ok(s) => {
error!("Privilege escalation failed with exit code: {}", s);
std::process::exit(1);
}
Err(e) => {
error!("Privilege escalation failed: {}", e);
std::process::exit(1);
}
}
} else {
error!(
"This operation requires root privileges and could not determine executable path."
);
std::process::exit(1);
}
}
}
}
pub fn create_file(path: &str, content: &str, executable: bool) {
let path_obj = Path::new(path);
if let Some(parent) = path_obj.parent()
&& !parent.exists()
&& let Err(e) = fs::create_dir_all(parent)
{
error!("Failed to create directories for '{}': {}", path, e);
return;
}
match fs::write(path_obj, content) {
Ok(_) => {
info!("Created file {}", path);
debug!("{}", content);
if executable && let Ok(mut perms) = fs::metadata(path_obj).map(|m| m.permissions()) {
perms.set_mode(perms.mode() | 0o111);
if let Err(e) = fs::set_permissions(path_obj, perms) {
error!("Failed to set executable permission on '{}': {}", path, e);
} else {
info!("Added execution privilege to file {}", path);
}
}
}
Err(e) => {
error!("Failed to create file '{}': {}", path, e);
}
}
}
pub fn get_display_manager() -> Option<String> {
let output = Command::new("systemctl")
.args(["show", "-p", "FragmentPath", "display-manager.service"])
.output()
.ok()?;
let stdout = String::from_utf8_lossy(&output.stdout);
if let Some(line) = stdout.lines().next()
&& let Some(path_str) = line.strip_prefix("FragmentPath=")
&& !path_str.is_empty()
&& path_str != "/dev/null"
{
let dm = Path::new(path_str)
.file_name()?
.to_string_lossy()
.to_string();
info!("Found {} Display Manager", dm);
return Some(dm);
}
warn!("Display Manager detection is not available");
None
}
pub fn is_service_active(name: &str) -> bool {
let output = Command::new("systemctl").args(["is-active", name]).output();
if let Ok(output) = output {
return String::from_utf8_lossy(&output.stdout).trim() == "active";
}
false
}
pub fn get_active_graphical_sessions() -> Vec<String> {
let mut sessions = Vec::new();
let output = Command::new("loginctl")
.args(["list-sessions", "--no-legend"])
.output();
if let Ok(output) = output {
let stdout = String::from_utf8_lossy(&output.stdout);
for line in stdout.lines() {
let parts: Vec<&str> = line.split_whitespace().collect();
if let Some(id) = parts.first() {
let show_output = Command::new("loginctl")
.args(["show-session", id, "-p", "Type", "-p", "State"])
.output();
if let Ok(show_output) = show_output {
let show_stdout = String::from_utf8_lossy(&show_output.stdout);
let mut is_graphical = false;
let mut is_active = false;
for show_line in show_stdout.lines() {
if let Some(t) = show_line.strip_prefix("Type=") {
if t == "x11" || t == "wayland" {
is_graphical = true;
}
} else if let Some(s) = show_line.strip_prefix("State=")
&& (s == "active" || s == "online")
{
is_active = true;
}
}
if is_graphical && is_active {
sessions.push(id.to_string());
}
}
}
}
}
sessions
}
pub fn rebuild_initramfs() {
let mut command: Vec<&str> = Vec::new();
if Path::new("/ostree").exists() || Path::new("/sysroot/ostree").exists() {
println!("Rebuilding the initramfs with rpm-ostree...");
command = vec!["rpm-ostree", "initramfs", "--enable", "--arg=--force"];
} else if Path::new("/etc/debian_version").exists() {
command = vec!["update-initramfs", "-u", "-k", "all"];
} else if Path::new("/etc/redhat-release").exists() || Path::new("/usr/bin/zypper").exists() {
command = vec!["dracut", "--force", "--regenerate-all"];
} else if Path::new("/usr/lib/endeavouros-release").exists()
&& Path::new("/usr/bin/dracut").exists()
{
command = vec!["dracut-rebuild"];
} else if Path::new("/etc/altlinux-release").exists() {
command = vec!["make-initrd"];
} else if Path::new("/etc/arch-release").exists() {
command = vec!["mkinitcpio", "-P"];
}
if !command.is_empty() {
println!("Rebuilding the initramfs...");
let is_debug = log::log_enabled!(log::Level::Debug);
let mut cmd = Command::new(command[0]);
cmd.args(&command[1..]);
if !is_debug {
cmd.stdout(std::process::Stdio::null());
cmd.stderr(std::process::Stdio::null());
}
match cmd.status() {
Ok(status) if status.success() => {
println!("Successfully rebuilt the initramfs!");
}
Ok(status) => {
error!("initramfs rebuild failed with exit code: {}", status);
}
Err(e) => {
error!("Failed to run initramfs rebuild command: {}", e);
}
}
} else {
log::warn!(
"Unknown distribution: initramfs was not rebuilt. You may need to rebuild it manually before rebooting."
);
}
}
pub fn manage_service(name: &str, action: &str) -> Result<(), String> {
info!("Performing {} on service {}", action, name);
let mut cmd = Command::new("systemctl");
cmd.args([action, name]);
match cmd.status() {
Ok(s) if s.success() => Ok(()),
Ok(s) => Err(format!(
"systemctl {} {} failed with exit code: {}",
action, name, s
)),
Err(e) => Err(format!(
"Failed to run systemctl {} {}: {}",
action, name, e
)),
}
}
pub fn terminate_session(id: &str) -> Result<(), String> {
info!("Terminating session {}", id);
let mut cmd = Command::new("loginctl");
cmd.args(["terminate-session", id]);
match cmd.status() {
Ok(s) if s.success() => Ok(()),
Ok(s) => Err(format!(
"loginctl terminate-session {} failed with exit code: {}",
id, s
)),
Err(e) => Err(format!("Failed to run loginctl: {}", e)),
}
}