Files
Forge/rust/src/core/privilege.rs
T

105 lines
3.7 KiB
Rust

use std::process::Stdio;
use tokio::io::{AsyncBufReadExt, BufReader};
use tokio::process::Command;
use crate::core::types::AppError;
pub struct PrivilegedCommandRunner;
impl PrivilegedCommandRunner {
/// Проверка, запущен ли процесс уже с root-правами
pub fn is_root() -> bool {
// Проверка через EUID (в Linux root = 0)
unsafe { libc_geteuid() == 0 }
}
/// Асинхронный запуск команды с повышением привилегий через pkexec (Polkit).
/// Вывод процесса построчно передается в callback `on_output_line`.
/// UI-поток Flutter не блокируется, так как выполнение происходит в Tokio runtime.
pub async fn run_privileged<F>(
program: &str,
args: &[&str],
mut on_output_line: F,
) -> Result<(), AppError>
where
F: FnMut(String) + Send + 'static,
{
let mut cmd = if Self::is_root() {
let mut c = Command::new(program);
c.args(args);
c
} else {
// Запуск через PolKit (pkexec) вызывает нативный GUI-диалог авторизации Linux
let mut c = Command::new("pkexec");
c.arg(program).args(args);
c
};
cmd.stdout(Stdio::piped());
cmd.stderr(Stdio::piped());
let mut child = cmd.spawn().map_err(|e| {
AppError::ExecutionFailed(format!("Failed to spawn privileged command {}: {}", program, e))
})?;
// Читаем stdout асинхронно построчно
let stdout = child.stdout.take();
let stderr = child.stderr.take();
let stdout_handle = tokio::spawn(async move {
let mut lines_vec = Vec::new();
if let Some(stdout) = stdout {
let mut reader = BufReader::new(stdout).lines();
while let Ok(Some(line)) = reader.next_line().await {
lines_vec.push(line);
}
}
lines_vec
});
let stderr_handle = tokio::spawn(async move {
let mut err_vec = Vec::new();
if let Some(stderr) = stderr {
let mut reader = BufReader::new(stderr).lines();
while let Ok(Some(line)) = reader.next_line().await {
err_vec.push(line);
}
}
err_vec
});
// Ожидаем завершения процесса
let status = child.wait().await.map_err(|e| {
AppError::ExecutionFailed(format!("Failed waiting for child process: {}", e))
})?;
if let Ok(lines) = stdout_handle.await {
for line in lines {
on_output_line(line);
}
}
if let Ok(err_lines) = stderr_handle.await {
for line in err_lines {
on_output_line(format!("[STDERR] {}", line));
}
}
match status.code() {
Some(0) => Ok(()),
Some(126) => Err(AppError::PrivilegeDenied("Polkit authorization was cancelled or dismissed by user".into())),
Some(127) => Err(AppError::PrivilegeDenied("Polkit authorization failed or pkexec not found".into())),
Some(code) => Err(AppError::ExecutionFailed(format!("Process exited with error code: {}", code))),
None => Err(AppError::ExecutionFailed("Process terminated by signal".into())),
}
}
}
// Fallback функция получения euid без лишней зависимости от nix
unsafe fn libc_geteuid() -> u32 {
extern "C" {
fn geteuid() -> u32;
}
geteuid()
}