diff --git a/README.md b/README.md index 550cfec..d026b3b 100644 --- a/README.md +++ b/README.md @@ -40,8 +40,6 @@ sudo systemctl enable --now lactd ``` You can now use the application. -Note: By default, only members of the group `wheel` can change settings. This can be changed by editing `/etc/lact.json` and changing the `group` value. - # CLI There is also a cli available. diff --git a/daemon/Cargo.toml b/daemon/Cargo.toml index aae5efe..ab8dd63 100644 --- a/daemon/Cargo.toml +++ b/daemon/Cargo.toml @@ -16,4 +16,5 @@ env_logger = "0.8" rand = "0.8" signal-hook = "0.3" pciid-parser = { git = "https://github.com/ilyazzz/pci-id-parser.git" } -reqwest = { version = "0.11", features = ["blocking", "json"] } \ No newline at end of file +reqwest = { version = "0.11", features = ["blocking", "json"] } +nix = "0.20" diff --git a/daemon/src/daemon_connection.rs b/daemon/src/daemon_connection.rs index 9aef5ce..0d96cda 100644 --- a/daemon/src/daemon_connection.rs +++ b/daemon/src/daemon_connection.rs @@ -1,53 +1,84 @@ +use crate::config::Config; +use crate::gpu_controller::{FanControlInfo, GpuStats}; use crate::gpu_controller::{GpuInfo, PowerProfile}; +use crate::Daemon; use crate::DaemonError; -use crate::{ - config::Config, - gpu_controller::{FanControlInfo, GpuStats}, -}; use crate::{Action, DaemonResponse, SOCK_PATH}; use std::collections::{BTreeMap, HashMap}; -use std::io::{Read, Write}; -use std::os::unix::net::UnixStream; #[derive(Clone, Copy)] pub struct DaemonConnection {} +pub const BUFFER_SIZE: usize = 4096; + impl DaemonConnection { pub fn new() -> Result { - match UnixStream::connect(SOCK_PATH) { - Ok(mut stream) => { - stream - .write(&bincode::serialize(&Action::CheckAlive).unwrap()) - .unwrap(); + let addr = nix::sys::socket::SockAddr::Unix( + nix::sys::socket::UnixAddr::new_abstract(SOCK_PATH.as_bytes()).unwrap(), + ); + let socket = nix::sys::socket::socket( + nix::sys::socket::AddressFamily::Unix, + nix::sys::socket::SockType::Stream, + nix::sys::socket::SockFlag::empty(), + None, + ) + .expect("Creating socket failed"); + nix::sys::socket::connect(socket, &addr).expect("Socket connect failed"); - stream - .shutdown(std::net::Shutdown::Write) - .expect("Could not shut down"); + nix::unistd::write(socket, &bincode::serialize(&Action::CheckAlive).unwrap()) + .expect("Writing check alive to socket failed"); - let mut buffer = Vec::::new(); - stream.read_to_end(&mut buffer).unwrap(); + nix::sys::socket::shutdown(socket, nix::sys::socket::Shutdown::Write) + .expect("Could not shut down"); - let result: Result = - bincode::deserialize(&buffer).unwrap(); - match result { - Ok(_) => Ok(DaemonConnection {}), - Err(_) => Err(DaemonError::ConnectionFailed), + let mut buffer = Vec::::new(); + buffer.resize(BUFFER_SIZE, 0); + loop { + match nix::unistd::read(socket, &mut buffer) { + Ok(0) => { + break; + } + Ok(n) => { + assert!(n < buffer.len()); + if n < buffer.len() { + buffer.resize(n, 0); + } + break; + } + Err(e) => { + panic!("Error reading from socket: {}", e); } } + } + let result: Result = + bincode::deserialize(&buffer).expect("failed to deserialize message"); + + match result { + Ok(_) => Ok(DaemonConnection {}), Err(_) => Err(DaemonError::ConnectionFailed), } } fn send_action(&self, action: Action) -> Result { - let mut s = UnixStream::connect(SOCK_PATH).expect("Connection to daemon dropped"); + let addr = nix::sys::socket::SockAddr::Unix( + nix::sys::socket::UnixAddr::new_abstract(SOCK_PATH.as_bytes()).unwrap(), + ); + let socket = nix::sys::socket::socket( + nix::sys::socket::AddressFamily::Unix, + nix::sys::socket::SockType::Stream, + nix::sys::socket::SockFlag::empty(), + None, + ) + .expect("Socket failed"); + nix::sys::socket::connect(socket, &addr).expect("connect failed"); - s.write(&bincode::serialize(&action).unwrap()) - .expect("Failed to write"); - s.shutdown(std::net::Shutdown::Write) - .expect("Could nto shut down"); + let b = bincode::serialize(&action).unwrap(); + nix::unistd::write(socket, &b).expect("Writing action to socket failed"); - let mut buffer = Vec::::new(); - s.read_to_end(&mut buffer).unwrap(); + nix::sys::socket::shutdown(socket, nix::sys::socket::Shutdown::Write) + .expect("Could not shut down"); + + let buffer = Daemon::read_buffer(socket); bincode::deserialize(&buffer).expect("failed to deserialize message") } @@ -156,9 +187,19 @@ impl DaemonConnection { } pub fn shutdown(&self) { - let mut s = UnixStream::connect(SOCK_PATH).unwrap(); - s.write_all(&bincode::serialize(&Action::Shutdown).unwrap()) - .unwrap(); + let addr = nix::sys::socket::SockAddr::Unix( + nix::sys::socket::UnixAddr::new_abstract(SOCK_PATH.as_bytes()).unwrap(), + ); + let socket = nix::sys::socket::socket( + nix::sys::socket::AddressFamily::Unix, + nix::sys::socket::SockType::Stream, + nix::sys::socket::SockFlag::empty(), + None, + ) + .expect("Socket failed"); + nix::sys::socket::connect(socket, &addr).expect("connect failed"); + nix::unistd::write(socket, &mut &bincode::serialize(&Action::Shutdown).unwrap()) + .expect("Writing shutdown to socket failed"); } pub fn get_config(&self) -> Result { diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs index d84f0f6..e2f3e3e 100644 --- a/daemon/src/lib.rs +++ b/daemon/src/lib.rs @@ -8,24 +8,22 @@ use gpu_controller::PowerProfile; use pciid_parser::PciDatabase; use rand::prelude::*; use serde::{Deserialize, Serialize}; -use std::os::unix::net::{UnixListener, UnixStream}; -use std::process::Command; +use std::path::PathBuf; use std::{ collections::{BTreeMap, HashMap}, fs, }; -use std::{ - io::{Read, Write}, - path::PathBuf, -}; use crate::gpu_controller::GpuController; -pub const SOCK_PATH: &str = "/tmp/amdgpu-configurator.sock"; +// Abstract socket allows anyone to connect without worrying about permissions +// https://unix.stackexchange.com/questions/579612/unix-domain-sockets-for-non-root-user +pub const SOCK_PATH: &str = "amdgpu-configurator.sock"; +pub const BUFFER_SIZE: usize = 4096; pub struct Daemon { gpu_controllers: HashMap, - listener: UnixListener, + listener: std::os::unix::io::RawFd, config: Config, } @@ -53,9 +51,18 @@ pub enum Action { impl Daemon { pub fn new(unprivileged: bool) -> Daemon { - if fs::metadata(SOCK_PATH).is_ok() { - fs::remove_file(SOCK_PATH).expect("Failed to take control over socket"); - } + let addr = nix::sys::socket::SockAddr::Unix( + nix::sys::socket::UnixAddr::new_abstract(SOCK_PATH.as_bytes()).unwrap(), + ); + let listener = nix::sys::socket::socket( + nix::sys::socket::AddressFamily::Unix, + nix::sys::socket::SockType::Stream, + nix::sys::socket::SockFlag::empty(), + None, + ) + .expect("Socket failed"); + nix::sys::socket::bind(listener, &addr).expect("Bind failed"); + nix::sys::socket::listen(listener, 128).expect("Listen failed"); let config_path = PathBuf::from("/etc/lact.json"); let mut config = if unprivileged { @@ -75,26 +82,6 @@ impl Daemon { } }; - let listener = UnixListener::bind(SOCK_PATH).unwrap(); - - Command::new("chmod") - .arg("664") - .arg(SOCK_PATH) - .output() - .expect("Failed to chmod"); - - Command::new("chown") - .arg("nobody:wheel") - .arg(SOCK_PATH) - .output() - .expect("Failed to chown"); - - Command::new("chown") - .arg(&format!("nobody:{}", config.group)) - .arg(SOCK_PATH) - .output() - .expect("Failed to chown"); - log::info!("Using config {:?}", config); let gpu_controllers = Self::load_gpu_controllers(&mut config); @@ -190,27 +177,45 @@ impl Daemon { } pub fn listen(mut self) { - let listener = self.listener.try_clone().expect("couldn't try_clone"); - for stream in listener.incoming() { - match stream { - Ok(stream) => { - //let mut controller = self.gpu_controller.clone(); - //thread::spawn(move || Daemon::handle_connection(&mut controller, stream)); - //Daemon::handle_connection(&mut self.gpu_controllers, stream); - Daemon::handle_connection(&mut self, stream); - } - Err(err) => { - log::error!("Error: {}", err); - break; - } + loop { + let stream = nix::sys::socket::accept(self.listener).expect("Accept failed"); + if stream < 0 { + log::error!("Error from accept"); + break; + } else { + Daemon::handle_connection(&mut self, stream); } } } - fn handle_connection(&mut self, mut stream: UnixStream) { + pub fn read_buffer(stream: i32) -> Vec { log::trace!("Reading buffer"); let mut buffer = Vec::::new(); - stream.read_to_end(&mut buffer).unwrap(); + buffer.resize(BUFFER_SIZE, 0); + loop { + match nix::unistd::read(stream, &mut buffer) { + Ok(0) => { + break; + } + Ok(n) => { + assert!(n < buffer.len()); + if n < buffer.len() { + buffer.resize(n, 0); + } + break; + } + Err(e) => { + panic!("Error reading from socket: {}", e); + } + } + } + + buffer + } + + fn handle_connection(&mut self, stream: i32) { + let buffer = Self::read_buffer(stream); + //log::trace!("finished reading, buffer size {}", buffer.len()); log::trace!("Attempting to deserialize {:?}", &buffer); //log::trace!("{:?}", action); @@ -418,7 +423,6 @@ impl Daemon { controller.stop_fan_control(); } } - fs::remove_file(SOCK_PATH).expect("Failed to remove socket"); } std::process::exit(0); } @@ -432,10 +436,10 @@ impl Daemon { Action::GetConfig => Ok(DaemonResponse::Config(self.config.clone())), }; - log::trace!("Responding"); - stream - .write_all(&bincode::serialize(&response).unwrap()) - .expect("Failed writing response"); + let buffer = bincode::serialize(&response).unwrap(); + + log::trace!("Responding, buffer length {}", buffer.len()); + nix::unistd::write(stream, &buffer).expect("Writing response to socket failed"); //stream // .shutdown(std::net::Shutdown::Write) // .expect("Could not shut down");