Replace common functionality with rust implementation

This commit is contained in:
2025-09-03 17:06:13 +02:00
parent d0d162f3e9
commit d31d39473b
38 changed files with 2871 additions and 204 deletions

20
mgr/src/wire/client.rs Normal file
View File

@@ -0,0 +1,20 @@
use std::{
io::{self, Write as _},
os::unix::net::UnixStream,
};
use thiserror::Error;
use super::{super::Action, WireCommand as _};
#[derive(Debug, Error)]
pub enum SendError {
#[error(transparent)]
Io(#[from] io::Error),
}
impl Action {
pub fn send(&self, stream: &mut UnixStream) -> Result<(), SendError> {
Ok(stream.write_all(&self.to_wire())?)
}
}

11
mgr/src/wire/mod.rs Normal file
View File

@@ -0,0 +1,11 @@
pub mod client;
pub mod server;
pub mod socket;
pub(crate) trait WireCommand {
fn parse_wire(input: impl Iterator<Item = u8>) -> Result<Self, server::ParseError>
where
Self: Sized;
fn to_wire(&self) -> Vec<u8>;
}

88
mgr/src/wire/server.rs Normal file
View File

@@ -0,0 +1,88 @@
use std::{
io::{self, Read, Write},
os::unix::net::{SocketAddr, UnixListener, UnixStream},
thread,
};
use thiserror::Error;
use tracing::{Level, event};
use super::{
super::{Action, Exec as _},
WireCommand, socket,
};
#[derive(Debug, Error)]
pub enum Error {
#[error(transparent)]
Io(#[from] io::Error),
#[error(transparent)]
Parse(#[from] ParseError),
#[error(transparent)]
Socket(#[from] socket::Error),
#[error(transparent)]
Exec(#[from] crate::ExecError),
}
#[derive(Debug, Error)]
pub enum ParseError {
#[error("received unexpected eof")]
Eof,
#[error("received unknown byte: {0:#X}")]
Unknown(u8),
#[error("received surplus input: {0:?}")]
Surplus(Vec<u8>),
}
fn handle_client(stream: &mut UnixStream) -> Result<(), Error> {
let input = {
let mut buf = Vec::new();
stream.read_to_end(&mut buf)?;
buf
};
event!(Level::DEBUG, "request data: {input:?}");
let action = Action::parse_wire(input.into_iter())?;
event!(Level::DEBUG, "parsed request: {action:?}");
if let Some(output) = action.execute()? {
stream.write_all(output.as_bytes())?;
}
Ok(())
}
pub fn run() -> Result<(), Error> {
event!(Level::DEBUG, "starting server");
let socket_path = socket::get_socket_path()?;
socket::try_remove_socket(&socket_path)?;
let socket_addr = SocketAddr::from_pathname(socket_path)?;
event!(Level::DEBUG, "socket address {socket_addr:?}");
let listener = UnixListener::bind_addr(&socket_addr)?;
for stream in listener.incoming() {
let mut stream = stream?;
thread::spawn(move || {
event!(Level::DEBUG, "received request");
let result = handle_client(&mut stream);
if let Err(e) = result {
let msg = e.to_string();
event!(Level::ERROR, "action failed: {msg}");
if let Err(e) = stream.write_all(msg.as_bytes()) {
event!(Level::ERROR, "sending \"{msg}\" failed: {e}");
}
}
event!(Level::DEBUG, "closing stream");
drop(stream);
});
}
unreachable!()
}

35
mgr/src/wire/socket.rs Normal file
View File

@@ -0,0 +1,35 @@
use std::{
fs, io,
path::{Path, PathBuf},
};
use thiserror::Error;
use super::super::dirs;
#[derive(Debug, Error)]
pub enum Error {
#[error("could not find a suitable socket path")]
NoSocketPathFound,
#[error(transparent)]
Io(#[from] io::Error),
#[error(transparent)]
Dirs(#[from] dirs::Error),
}
pub fn get_socket_path() -> Result<PathBuf, Error> {
if let Some(mut dir) = dirs::xdg_runtime_dir()? {
dir.push("workstation-mgr.sock");
return Ok(dir);
}
Err(Error::NoSocketPathFound)
}
pub(crate) fn try_remove_socket(path: &Path) -> Result<(), Error> {
match fs::remove_file(path) {
Ok(()) => Ok(()),
Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(()),
Err(e) => Err(e.into()),
}
}