Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typesafe IPC #331

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
[workspace]
# cargo complains that this defaults to one in virtual package manifests (for some reason)
resolver = "2"
members = ["client", "daemon", "common"]
default-members = ["client", "daemon"]

Expand Down
36 changes: 16 additions & 20 deletions client/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use clap::Parser;
use std::time::Duration;
use std::{path::Path, time::Duration};

use common::{
cache,
ipc::{self, connect_to_socket, get_socket_path, read_socket, Answer, RequestSend},
};
use clap::Parser;
use common::cache;
use common::ipc::{self, Answer, Client, IpcSocket, RequestSend};
use common::mmap::Mmap;

mod imgproc;
use imgproc::*;
Expand All @@ -19,10 +18,10 @@ fn main() -> Result<(), String> {
return cache::clean().map_err(|e| format!("failed to clean the cache: {e}"));
}

let socket = IpcSocket::connect().map_err(|err| err.to_string())?;
loop {
let socket = connect_to_socket(&get_socket_path(), 5, 100)?;
RequestSend::Ping.send(&socket)?;
let bytes = read_socket(&socket)?;
let bytes = socket.recv().map_err(|err| err.to_string())?;
let answer = Answer::receive(bytes);
if let Answer::Ping(configured) = answer {
if configured {
Expand All @@ -42,29 +41,27 @@ fn process_swww_args(args: &Swww) -> Result<(), String> {
Some(request) => request,
None => return Ok(()),
};
let socket = connect_to_socket(&get_socket_path(), 5, 100)?;
let socket = IpcSocket::connect().map_err(|err| err.to_string())?;
request.send(&socket)?;
let bytes = read_socket(&socket)?;
let bytes = socket.recv().map_err(|err| err.to_string())?;
drop(socket);
match Answer::receive(bytes) {
Answer::Err(msg) => return Err(msg.to_string()),
Answer::Info(info) => info.iter().for_each(|i| println!("{}", i)),
Answer::Ok => {
if let Swww::Kill = args {
#[cfg(debug_assertions)]
let tries = 20;
#[cfg(not(debug_assertions))]
let tries = 10;
let socket_path = get_socket_path();
let path = IpcSocket::<Client>::path();
let path = Path::new(path);
for _ in 0..tries {
if !socket_path.exists() {
if !path.exists() {
return Ok(());
}
std::thread::sleep(Duration::from_millis(100));
}
return Err(format!(
"Could not confirm socket deletion at: {socket_path:?}"
));
return Err(format!("Could not confirm socket deletion at: {path:?}"));
}
}
Answer::Ping(_) => {
Expand Down Expand Up @@ -113,7 +110,7 @@ fn make_img_request(
dims: &[(u32, u32)],
pixel_format: ipc::PixelFormat,
outputs: &[Vec<String>],
) -> Result<ipc::Mmap, String> {
) -> Result<Mmap, String> {
let transition = make_transition(img);
let mut img_req_builder = ipc::ImageRequestBuilder::new(transition);

Expand Down Expand Up @@ -214,9 +211,9 @@ fn get_format_dims_and_outputs(
let mut dims: Vec<(u32, u32)> = Vec::new();
let mut imgs: Vec<ipc::BgImg> = Vec::new();

let socket = connect_to_socket(&get_socket_path(), 5, 100)?;
let socket = IpcSocket::connect().map_err(|err| err.to_string())?;
RequestSend::Query.send(&socket)?;
let bytes = read_socket(&socket)?;
let bytes = socket.recv().map_err(|err| err.to_string())?;
drop(socket);
let answer = Answer::receive(bytes);
match answer {
Expand Down Expand Up @@ -249,7 +246,6 @@ fn get_format_dims_and_outputs(
Ok((format, dims, outputs))
}
}
Answer::Err(e) => Err(format!("daemon error when sending query: {e}")),
_ => unreachable!(),
}
}
Expand Down
4 changes: 3 additions & 1 deletion common/src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ use std::{
path::{Path, PathBuf},
};

use crate::ipc::{Animation, Mmap, PixelFormat};
use crate::ipc::Animation;
use crate::ipc::PixelFormat;
use crate::mmap::Mmap;

pub(crate) fn store(output_name: &str, img_path: &str) -> io::Result<()> {
let mut filepath = cache_dir()?;
Expand Down
6 changes: 5 additions & 1 deletion common/src/compression/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ use comp::pack_bytes;
use decomp::{unpack_bytes_3channels, unpack_bytes_4channels};
use std::ffi::{c_char, c_int};

use crate::ipc::{ImageRequestBuilder, Mmap, MmappedBytes, PixelFormat};
use crate::ipc::ImageRequestBuilder;
use crate::ipc::PixelFormat;
use crate::mmap::Mmap;
use crate::mmap::MmappedBytes;

mod comp;
mod cpu;
mod decomp;
Expand Down
87 changes: 87 additions & 0 deletions common/src/ipc/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use std::error::Error;
use std::fmt;

use rustix::io::Errno;

/// Failiures if IPC with added context
#[derive(Debug)]
pub struct IpcError {
err: Errno,
kind: IpcErrorKind,
}

impl IpcError {
pub(crate) fn new(kind: IpcErrorKind, err: Errno) -> Self {
Self { err, kind }
}
}

#[derive(Debug)]
pub enum IpcErrorKind {
/// Failed to create file descriptor
Socket,
/// Failed to connect to socket
Connect,
/// Binding on socket failed
Bind,
/// Listening on socket failed
Listen,
/// Socket file wasn't found
NoSocketFile,
/// Socket timeout couldn't be set
SetTimeout,
/// IPC contained invalid identification code
BadCode,
/// IPC payload was broken
MalformedMsg,
/// Reading socket failed
Read,
}

impl IpcErrorKind {
fn description(&self) -> &'static str {
match self {
Self::Socket => "failed to create socket file descriptor",
Self::Connect => "failed to connect to socket",
Self::Bind => "failed to bind to socket",
Self::Listen => "failed to listen on socket",
Self::NoSocketFile => "Socket file not found. Are you sure swww-daemon is running?",
Self::SetTimeout => "failed to set read timeout for socket",
Self::BadCode => "invalid message code",
Self::MalformedMsg => "malformed ancillary message",
Self::Read => "failed to receive message",
}
}
}

impl fmt::Display for IpcError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.kind.description())
}
}

impl Error for IpcError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(&self.err)
}
}

/// Simplify generating [`IpcError`]s from [`Errno`]
pub(crate) trait ErrnoExt {
type Output;
fn context(self, kind: IpcErrorKind) -> Self::Output;
}

impl ErrnoExt for Errno {
type Output = IpcError;
fn context(self, kind: IpcErrorKind) -> Self::Output {
IpcError::new(kind, self)
}
}

impl<T> ErrnoExt for Result<T, Errno> {
type Output = Result<T, IpcError>;
fn context(self, kind: IpcErrorKind) -> Self::Output {
self.map_err(|error| error.context(kind))
}
}