Replace common functionality with rust implementation
This commit is contained in:
20
mgr/src/wire/client.rs
Normal file
20
mgr/src/wire/client.rs
Normal 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
11
mgr/src/wire/mod.rs
Normal 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
88
mgr/src/wire/server.rs
Normal 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
35
mgr/src/wire/socket.rs
Normal 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()),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user