eraden
7616ddd8fc
GUI in iced Basic plot manipulation Working solution Fix documentation Fix build scripts Slice application Change views Very basic GUI Reload config GUI Helper for root tasks - maybe sock files for all services will be better Add save button Xorg gui and save config to target file Documentation and clippy fixes Avoid compiling gui on CI Readme files Add missing dependencies Add missing pgp key Refactor workflow Refactor workflow Add drag and drop
203 lines
6.2 KiB
Rust
203 lines
6.2 KiB
Rust
//! Special daemon with root privileges. Since GUI should not have (and sometimes can't have) root
|
|
//! privileges and service processes are designed to be as small as possible this is proxy.
|
|
//!
|
|
//! It is responsible for:
|
|
//! * Loading all amdfand processes. In order to do this process needs to be killed with signal 0 to check if it still is alive
|
|
//! * Reload amdfand process with signal SIGHUP
|
|
//! * Save changed config file
|
|
//!
|
|
//! It is using `/tmp/amdgui-helper.sock` file and `ron` serialization for communication.
|
|
//! After each operation connection is terminated so each command needs new connection.
|
|
#![allow(clippy::non_octal_unix_permissions)]
|
|
|
|
use amdgpu::helper_cmd::{Command, Pid, Response};
|
|
use amdgpu::IoFailure;
|
|
use std::ffi::OsStr;
|
|
use std::fs::Permissions;
|
|
use std::io::{Read, Write};
|
|
use std::net::Shutdown;
|
|
use std::os::unix::fs::PermissionsExt;
|
|
use std::os::unix::net::{UnixListener, UnixStream};
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
pub enum Error {
|
|
#[error("{0}")]
|
|
Io(#[from] amdgpu::IoFailure),
|
|
#[error("{0}")]
|
|
Lock(#[from] amdgpu::AmdGpuError),
|
|
}
|
|
|
|
pub type Result<T> = std::result::Result<T, Error>;
|
|
|
|
fn main() -> Result<()> {
|
|
if std::env::var("RUST_LOG").is_err() {
|
|
std::env::set_var("RUST_LOG", "DEBUG");
|
|
}
|
|
pretty_env_logger::init();
|
|
|
|
let mut lock = amdgpu::lock_file::PidLock::new("amdgui", String::from("helper"))?;
|
|
lock.acquire()?;
|
|
|
|
let sock_path = amdgpu::helper_cmd::sock_file();
|
|
let listener = {
|
|
let _ = std::fs::remove_file(&sock_path);
|
|
|
|
UnixListener::bind(&sock_path).map_err(|io| IoFailure {
|
|
io,
|
|
path: sock_path.clone(),
|
|
})?
|
|
};
|
|
if let Err(e) = std::fs::set_permissions(&sock_path, Permissions::from_mode(0x777)) {
|
|
log::error!("Failed to change gui helper socket file mode. {:?}", e);
|
|
}
|
|
|
|
while let Ok((stream, _addr)) = listener.accept() {
|
|
handle_connection(stream);
|
|
}
|
|
|
|
lock.release()?;
|
|
Ok(())
|
|
}
|
|
|
|
pub struct Service(UnixStream);
|
|
|
|
impl Service {
|
|
/// Serialize and send command
|
|
pub fn write_response(&mut self, res: amdgpu::helper_cmd::Response) {
|
|
match ron::to_string(&res) {
|
|
Ok(buffer) => match self.0.write_all(buffer.as_bytes()) {
|
|
Ok(_) => {
|
|
log::info!("Response successfully written")
|
|
}
|
|
Err(e) => log::warn!("Failed to write response. {:?}", e),
|
|
},
|
|
Err(e) => {
|
|
log::warn!("Failed to serialize response {:?}. {:?}", res, e)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Read from `.sock` file new line separated commands
|
|
pub fn read_command(&mut self) -> Option<String> {
|
|
let mut command = String::with_capacity(100);
|
|
log::info!("Reading stream...");
|
|
read_line(&mut self.0, &mut command);
|
|
if command.is_empty() {
|
|
return None;
|
|
}
|
|
Some(command)
|
|
}
|
|
|
|
/// Close connection with no operation response
|
|
pub fn kill(mut self) {
|
|
self.write_response(Response::NoOp);
|
|
self.close();
|
|
}
|
|
|
|
pub fn close(self) {
|
|
let _ = self.0.shutdown(Shutdown::Both);
|
|
}
|
|
}
|
|
|
|
fn handle_connection(stream: UnixStream) {
|
|
let mut service = Service(stream);
|
|
|
|
let command = match service.read_command() {
|
|
Some(s) => s,
|
|
_ => return service.kill(),
|
|
};
|
|
|
|
log::info!("Incoming {:?}", command);
|
|
let cmd = match ron::from_str::<amdgpu::helper_cmd::Command>(command.trim()) {
|
|
Ok(cmd) => cmd,
|
|
Err(e) => {
|
|
log::warn!("Invalid message {:?}. {:?}", command, e);
|
|
return service.kill();
|
|
}
|
|
};
|
|
handle_command(service, cmd);
|
|
}
|
|
|
|
fn handle_command(mut service: Service, cmd: Command) {
|
|
match cmd {
|
|
Command::ReloadConfig { pid } => {
|
|
log::info!("Reloading config file for pid {:?}", pid);
|
|
handle_reload_config(service, pid);
|
|
}
|
|
Command::FanServices => handle_fan_services(service),
|
|
Command::SaveFanConfig { path, content } => {
|
|
handle_save_fan_config(&mut service, path, content)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn handle_save_fan_config(service: &mut Service, path: String, content: String) {
|
|
match std::fs::write(path, content) {
|
|
Err(e) => service.write_response(Response::ConfigFileSaveFailed(format!("{:?}", e))),
|
|
Ok(..) => service.write_response(Response::ConfigFileSaved),
|
|
}
|
|
}
|
|
|
|
fn handle_fan_services(mut service: Service) {
|
|
log::info!("Loading fan services");
|
|
let services = read_fan_services();
|
|
log::info!("Loaded fan services pid {:?}", services);
|
|
service.write_response(Response::Services(services));
|
|
}
|
|
|
|
fn read_line(stream: &mut UnixStream, command: &mut String) {
|
|
let mut buffer = [0];
|
|
while stream.read_exact(&mut buffer).is_ok() {
|
|
if buffer[0] == b'\n' {
|
|
break;
|
|
}
|
|
match std::str::from_utf8(&buffer) {
|
|
Ok(s) => {
|
|
command.push_str(s);
|
|
}
|
|
Err(e) => {
|
|
log::error!("Failed to read from client. {:?}", e);
|
|
let _ = stream.shutdown(Shutdown::Both);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn handle_reload_config(service: Service, pid: Pid) {
|
|
unsafe {
|
|
nix::libc::kill(pid.0, nix::sys::signal::Signal::SIGHUP as i32);
|
|
}
|
|
service.kill();
|
|
}
|
|
|
|
fn read_fan_services() -> Vec<Pid> {
|
|
if let Ok(entry) = std::fs::read_dir("/var/lib/amdfand") {
|
|
entry
|
|
.filter(|e| {
|
|
e.as_ref()
|
|
.map(|e| {
|
|
log::info!("Extension is {:?}", e.path().extension());
|
|
e.path().extension().and_then(OsStr::to_str) == Some("pid")
|
|
})
|
|
.ok()
|
|
.unwrap_or_default()
|
|
})
|
|
.filter_map(|e| {
|
|
log::info!("Found entry {:?}", e);
|
|
match e {
|
|
Ok(entry) => std::fs::read_to_string(entry.path())
|
|
.ok()
|
|
.and_then(|s| s.parse::<i32>().ok())
|
|
.filter(|pid| unsafe { nix::libc::kill(*pid, 0) } == 0),
|
|
_ => None,
|
|
}
|
|
})
|
|
.map(Pid)
|
|
.collect()
|
|
} else {
|
|
log::warn!("Directory /var/lib/amdfand not found");
|
|
vec![]
|
|
}
|
|
}
|