diff --git a/Cargo.lock b/Cargo.lock index c27b3b6c..6e90ca2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -696,6 +696,7 @@ dependencies = [ "jay-cmm", "jay-config", "jay-criteria", + "jay-dbus-core", "jay-edid", "jay-formats", "jay-geometry", @@ -762,6 +763,18 @@ dependencies = [ "regex", ] +[[package]] +name = "jay-dbus-core" +version = "0.1.0" +dependencies = [ + "bstr", + "jay-bufio", + "jay-io-uring", + "jay-utils", + "thiserror", + "uapi", +] + [[package]] name = "jay-edid" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index b4424131..8237e3c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ members = [ "async-engine", "io-uring", "bufio", + "dbus-core", "toml-config", "algorithms", "toml-spec", @@ -61,6 +62,7 @@ jay-tracy = { version = "0.1.0", path = "tracy" } jay-async-engine = { version = "0.1.0", path = "async-engine" } jay-io-uring = { version = "0.1.0", path = "io-uring" } jay-bufio = { version = "0.1.0", path = "bufio" } +jay-dbus-core = { version = "0.1.0", path = "dbus-core" } uapi = "0.2.13" thiserror = "2.0.11" diff --git a/dbus-core/Cargo.toml b/dbus-core/Cargo.toml new file mode 100644 index 00000000..e87c8500 --- /dev/null +++ b/dbus-core/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "jay-dbus-core" +version = "0.1.0" +edition = "2024" +license = "GPL-3.0-only" + +[dependencies] +jay-bufio = { version = "0.1.0", path = "../bufio" } +jay-io-uring = { version = "0.1.0", path = "../io-uring" } +jay-utils = { version = "0.1.0", path = "../utils" } + +bstr = { version = "1.9.0", default-features = false, features = ["std"] } +thiserror = "2.0.11" +uapi = "0.2.13" diff --git a/src/dbus/dynamic_type.rs b/dbus-core/src/dynamic_type.rs similarity index 95% rename from src/dbus/dynamic_type.rs rename to dbus-core/src/dynamic_type.rs index 765da57f..14a020b5 100644 --- a/src/dbus/dynamic_type.rs +++ b/dbus-core/src/dynamic_type.rs @@ -3,10 +3,8 @@ use { TY_ARRAY, TY_BOOLEAN, TY_BYTE, TY_DOUBLE, TY_INT16, TY_INT32, TY_INT64, TY_OBJECT_PATH, TY_SIGNATURE, TY_STRING, TY_UINT16, TY_UINT32, TY_UINT64, TY_UNIX_FD, TY_VARIANT, }, - crate::{ - dbus::{DbusError, DynamicType, Parser, types::Variant}, - utils::buf::DynamicBuf, - }, + crate::{types::Variant, DbusError, DynamicType, Parser}, + jay_utils::buf::DynamicBuf, std::ops::Deref, }; @@ -156,11 +154,8 @@ impl DynamicType { } let mut vals = vec![]; { - let mut parser = Parser { - buf: &parser.buf[..parser.pos + len], - pos: parser.pos, - fds: parser.fds, - }; + let mut parser = + Parser::new_at(&parser.buf[..parser.pos + len], parser.pos, parser.fds); while !parser.eof() { vals.push(el.parse(&mut parser)?); } diff --git a/src/dbus/formatter.rs b/dbus-core/src/formatter.rs similarity index 97% rename from src/dbus/formatter.rs rename to dbus-core/src/formatter.rs index 798332e2..edf39bba 100644 --- a/src/dbus/formatter.rs +++ b/dbus-core/src/formatter.rs @@ -1,8 +1,6 @@ use { - crate::{ - dbus::{DbusType, Formatter, types::Variant}, - utils::buf::DynamicBuf, - }, + crate::{types::Variant, DbusType, Formatter}, + jay_utils::buf::DynamicBuf, std::rc::Rc, uapi::{OwnedFd, Packed}, }; diff --git a/dbus-core/src/lib.rs b/dbus-core/src/lib.rs new file mode 100644 index 00000000..c75d05a1 --- /dev/null +++ b/dbus-core/src/lib.rs @@ -0,0 +1,232 @@ +pub use {property::{Get, GetReply}, types::*}; +use { + jay_bufio::BufIoError, + jay_io_uring::IoUringError, + jay_utils::{buf::DynamicBuf, oserror::OsError}, + std::{borrow::Cow, fmt::Display, rc::Rc}, + thiserror::Error, + uapi::OwnedFd, +}; + +mod dynamic_type; +mod formatter; +mod parser; +pub mod property; +pub mod types; + +#[derive(Debug)] +pub struct CallError { + pub name: String, + pub msg: Option, +} + +impl Display for CallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(msg) = &self.msg { + write!(f, "{}: {}", self.name, msg) + } else { + write!(f, "{}", self.name) + } + } +} + +#[derive(Debug, Error)] +pub enum DbusError { + #[error("Encountered an unknown type in a signature")] + UnknownType, + #[error("Function call reply does not contain a reply serial")] + NoReplySerial, + #[error("Signal message contains no interface or member or path")] + MissingSignalHeaders, + #[error("Method call message contains no interface or member or path")] + MissingMethodCallHeaders, + #[error("Error has no error name")] + NoErrorName, + #[error("The socket was killed")] + Killed, + #[error("{0}")] + CallError(CallError), + #[error("FD index is out of bounds")] + OobFds, + #[error("Variant has an invalid type")] + InvalidVariantType, + #[error("Could not create a socket")] + Socket(#[source] OsError), + #[error("Could not connect")] + Connect(#[source] IoUringError), + #[error("Could not write to the dbus socket")] + WriteError(#[source] IoUringError), + #[error("Could not read from the dbus socket")] + ReadError(#[source] IoUringError), + #[error("timeout")] + IoUringError(#[source] Box), + #[error("Server did not send auth challenge")] + NoChallenge, + #[error("Server did not accept our authentication")] + Auth, + #[error("Array length is not a multiple of the element size")] + PodArrayLength, + #[error("Peer did not send enough fds")] + TooFewFds, + #[error("Variant signature is not a single type")] + TrailingVariantSignature, + #[error("Dict signature does not contain a terminating '}}'")] + UnterminatedDict, + #[error("Struct signature does not contain a terminating '}}'")] + UnterminatedStruct, + #[error("Dict signature contains trailing types")] + DictTrailing, + #[error("String does not contain valid UTF-8")] + InvalidUtf8, + #[error("Unexpected end of message")] + UnexpectedEof, + #[error("Boolean value was not 0 or 1")] + InvalidBoolValue, + #[error("Signature is empty")] + EmptySignature, + #[error("The session bus address is not set")] + SessionBusAddressNotSet, + #[error("Server does not support FD passing")] + UnixFd, + #[error("Server message has a different endianess than ourselves")] + InvalidEndianess, + #[error("Server speaks an unexpected protocol version")] + InvalidProtocol, + #[error("Signature contains an invalid type")] + InvalidSignatureType, + #[error("The signal already has a handler")] + AlreadyHandled, + #[error(transparent)] + BufIoError(#[from] BufIoError), + #[error(transparent)] + DbusError(Rc), +} + +impl From for DbusError { + fn from(e: IoUringError) -> Self { + DbusError::IoUringError(Box::new(e)) + } +} + +const TY_BYTE: u8 = b'y'; +const TY_BOOLEAN: u8 = b'b'; +const TY_INT16: u8 = b'n'; +const TY_UINT16: u8 = b'q'; +const TY_INT32: u8 = b'i'; +const TY_UINT32: u8 = b'u'; +const TY_INT64: u8 = b'x'; +const TY_UINT64: u8 = b't'; +const TY_DOUBLE: u8 = b'd'; +const TY_STRING: u8 = b's'; +const TY_OBJECT_PATH: u8 = b'o'; +const TY_SIGNATURE: u8 = b'g'; +const TY_ARRAY: u8 = b'a'; +const TY_VARIANT: u8 = b'v'; +const TY_UNIX_FD: u8 = b'h'; + +#[derive(Clone, Debug)] +pub enum DynamicType { + U8, + Bool, + I16, + U16, + I32, + U32, + I64, + U64, + F64, + String, + ObjectPath, + Signature, + Variant, + Fd, + Array(Box), + DictEntry(Box, Box), + Struct(Vec), +} + +pub struct Parser<'a> { + pub(crate) buf: &'a [u8], + pub(crate) pos: usize, + pub(crate) fds: &'a [Rc], +} + +pub struct Formatter<'a> { + fds: &'a mut Vec>, + buf: &'a mut DynamicBuf, +} + +pub unsafe trait Message<'a>: Sized + 'a { + const SIGNATURE: &'static str; + const INTERFACE: &'static str; + const MEMBER: &'static str; + type Generic<'b>: Message<'b>; + + fn marshal(&self, w: &mut Formatter); + fn unmarshal(p: &mut Parser<'a>) -> Result; + fn num_fds(&self) -> u32; +} + +pub struct ErrorMessage<'a> { + pub msg: Cow<'a, str>, +} + +unsafe impl<'a> Message<'a> for ErrorMessage<'a> { + const SIGNATURE: &'static str = "s"; + const INTERFACE: &'static str = ""; + const MEMBER: &'static str = ""; + type Generic<'b> = ErrorMessage<'b>; + + fn marshal(&self, w: &mut Formatter) { + self.msg.marshal(w) + } + + fn unmarshal(p: &mut Parser<'a>) -> Result { + Ok(Self { + msg: p.unmarshal()?, + }) + } + + fn num_fds(&self) -> u32 { + 0 + } +} + +pub trait Property { + const INTERFACE: &'static str; + const PROPERTY: &'static str; + type Type: DbusType<'static>; +} + +pub trait Signal<'a>: Message<'a> {} + +pub trait MethodCall<'a>: Message<'a> { + type Reply: Message<'static>; +} + +pub unsafe trait DbusType<'a>: Clone + 'a { + const ALIGNMENT: usize; + const IS_POD: bool; + type Generic<'b>: DbusType<'b> + 'b; + + fn consume_signature(s: &mut &[u8]) -> Result<(), DbusError>; + #[allow(clippy::allow_attributes, dead_code)] + fn write_signature(w: &mut Vec); + fn marshal(&self, fmt: &mut Formatter); + fn unmarshal(parser: &mut Parser<'a>) -> Result; + + fn num_fds(&self) -> u32 { + 0 + } +} + +pub mod prelude { + pub use { + super::{ + DbusError, DbusType, Formatter, Message, MethodCall, Parser, Property, Signal, + types::{Bool, DictEntry, ObjectPath, Variant}, + }, + std::{borrow::Cow, rc::Rc}, + uapi::OwnedFd, + }; +} diff --git a/src/dbus/parser.rs b/dbus-core/src/parser.rs similarity index 92% rename from src/dbus/parser.rs rename to dbus-core/src/parser.rs index 95b2525c..1821e283 100644 --- a/src/dbus/parser.rs +++ b/dbus-core/src/parser.rs @@ -1,7 +1,7 @@ use { - crate::dbus::{ + crate::{ + types::{Bool, ObjectPath, Signature, Variant, FALSE, TRUE}, DbusError, DbusType, DynamicType, Parser, - types::{Bool, FALSE, ObjectPath, Signature, TRUE, Variant}, }, bstr::ByteSlice, std::{borrow::Cow, rc::Rc}, @@ -10,7 +10,11 @@ use { impl<'a> Parser<'a> { pub fn new(buf: &'a [u8], fds: &'a [Rc]) -> Self { - Self { buf, pos: 0, fds } + Self::new_at(buf, 0, fds) + } + + pub fn new_at(buf: &'a [u8], pos: usize, fds: &'a [Rc]) -> Self { + Self { buf, pos, fds } } pub fn eof(&self) -> bool { @@ -107,11 +111,7 @@ impl<'a> Parser<'a> { self.pos += len; Ok(Cow::Borrowed(slice)) } else { - let mut parser = Parser { - buf: &self.buf[..self.pos + len], - pos: self.pos, - fds: self.fds, - }; + let mut parser = Parser::new_at(&self.buf[..self.pos + len], self.pos, self.fds); self.pos += len; let mut res = vec![]; while !parser.eof() { diff --git a/src/dbus/property.rs b/dbus-core/src/property.rs similarity index 95% rename from src/dbus/property.rs rename to dbus-core/src/property.rs index 651e1767..3fd454fa 100644 --- a/src/dbus/property.rs +++ b/dbus-core/src/property.rs @@ -1,5 +1,5 @@ use { - crate::dbus::{DbusError, DbusType, Formatter, Message, MethodCall, Parser}, + crate::{DbusError, DbusType, Formatter, Message, MethodCall, Parser}, std::{borrow::Cow, marker::PhantomData}, }; diff --git a/src/dbus/types.rs b/dbus-core/src/types.rs similarity index 97% rename from src/dbus/types.rs rename to dbus-core/src/types.rs index d83292f2..5e00897c 100644 --- a/src/dbus/types.rs +++ b/dbus-core/src/types.rs @@ -1,12 +1,10 @@ use { crate::{ - dbus::{ - DbusError, DbusType, DynamicType, Formatter, Parser, TY_ARRAY, TY_BOOLEAN, TY_BYTE, - TY_DOUBLE, TY_INT16, TY_INT32, TY_INT64, TY_OBJECT_PATH, TY_SIGNATURE, TY_STRING, - TY_UINT16, TY_UINT32, TY_UINT64, TY_UNIX_FD, TY_VARIANT, - }, - utils::buf::DynamicBuf, + DbusError, DbusType, DynamicType, Formatter, Parser, TY_ARRAY, TY_BOOLEAN, TY_BYTE, + TY_DOUBLE, TY_INT16, TY_INT32, TY_INT64, TY_OBJECT_PATH, TY_SIGNATURE, TY_STRING, + TY_UINT16, TY_UINT32, TY_UINT64, TY_UNIX_FD, TY_VARIANT, }, + jay_utils::buf::DynamicBuf, std::{borrow::Cow, ops::Deref, rc::Rc}, uapi::{OwnedFd, Packed, Pod}, }; diff --git a/src/dbus.rs b/src/dbus.rs index 24f2c758..b24d8002 100644 --- a/src/dbus.rs +++ b/src/dbus.rs @@ -1,16 +1,13 @@ -pub use types::*; +pub use jay_dbus_core::*; use { crate::{ async_engine::{AsyncEngine, SpawnedFuture}, - dbus::{property::GetReply, types::{ObjectPath, Signature, Variant}}, - io_uring::{IoUring, IoUringError}, + io_uring::IoUring, utils::{ - buf::DynamicBuf, - bufio::{BufIo, BufIoError}, + bufio::BufIo, clonecell::CloneCell, copyhashmap::CopyHashMap, numcell::NumCell, - oserror::OsError, run_toplevel::RunToplevel, stack::Stack, vecstorage::VecStorage, @@ -21,7 +18,7 @@ use { std::{ borrow::Cow, cell::{Cell, RefCell}, - fmt::{Debug, Display}, + fmt::Debug, future::Future, marker::PhantomData, mem, @@ -29,109 +26,14 @@ use { rc::Rc, task::{Context, Poll, Waker}, }, - thiserror::Error, uapi::OwnedFd, }; mod auth; -mod dynamic_type; -mod formatter; mod holder; mod incoming; mod outgoing; -mod parser; -mod property; mod socket; -mod types; - -#[derive(Debug)] -pub struct CallError { - pub name: String, - pub msg: Option, -} - -impl Display for CallError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if let Some(msg) = &self.msg { - write!(f, "{}: {}", self.name, msg) - } else { - write!(f, "{}", self.name) - } - } -} - -#[derive(Debug, Error)] -pub enum DbusError { - #[error("Encountered an unknown type in a signature")] - UnknownType, - #[error("Function call reply does not contain a reply serial")] - NoReplySerial, - #[error("Signal message contains no interface or member or path")] - MissingSignalHeaders, - #[error("Method call message contains no interface or member or path")] - MissingMethodCallHeaders, - #[error("Error has no error name")] - NoErrorName, - #[error("The socket was killed")] - Killed, - #[error("{0}")] - CallError(CallError), - #[error("FD index is out of bounds")] - OobFds, - #[error("Variant has an invalid type")] - InvalidVariantType, - #[error("Could not create a socket")] - Socket(#[source] OsError), - #[error("Could not connect")] - Connect(#[source] IoUringError), - #[error("Could not write to the dbus socket")] - WriteError(#[source] IoUringError), - #[error("Could not read from the dbus socket")] - ReadError(#[source] IoUringError), - #[error("timeout")] - IoUringError(#[source] Box), - #[error("Server did not send auth challenge")] - NoChallenge, - #[error("Server did not accept our authentication")] - Auth, - #[error("Array length is not a multiple of the element size")] - PodArrayLength, - #[error("Peer did not send enough fds")] - TooFewFds, - #[error("Variant signature is not a single type")] - TrailingVariantSignature, - #[error("Dict signature does not contain a terminating '}}'")] - UnterminatedDict, - #[error("Struct signature does not contain a terminating '}}'")] - UnterminatedStruct, - #[error("Dict signature contains trailing types")] - DictTrailing, - #[error("String does not contain valid UTF-8")] - InvalidUtf8, - #[error("Unexpected end of message")] - UnexpectedEof, - #[error("Boolean value was not 0 or 1")] - InvalidBoolValue, - #[error("Signature is empty")] - EmptySignature, - #[error("The session bus address is not set")] - SessionBusAddressNotSet, - #[error("Server does not support FD passing")] - UnixFd, - #[error("Server message has a different endianess than ourselves")] - InvalidEndianess, - #[error("Server speaks an unexpected protocol version")] - InvalidProtocol, - #[error("Signature contains an invalid type")] - InvalidSignatureType, - #[error("The signal already has a handler")] - AlreadyHandled, - #[error(transparent)] - BufIoError(#[from] BufIoError), - #[error(transparent)] - DbusError(Rc), -} -efrom!(DbusError, IoUringError); const DBUS_SESSION_BUS_ADDRESS: &str = "DBUS_SESSION_BUS_ADDRESS"; @@ -274,22 +176,6 @@ pub struct DbusSocket { signal_handlers: RefCell>, } -const TY_BYTE: u8 = b'y'; -const TY_BOOLEAN: u8 = b'b'; -const TY_INT16: u8 = b'n'; -const TY_UINT16: u8 = b'q'; -const TY_INT32: u8 = b'i'; -const TY_UINT32: u8 = b'u'; -const TY_INT64: u8 = b'x'; -const TY_UINT64: u8 = b't'; -const TY_DOUBLE: u8 = b'd'; -const TY_STRING: u8 = b's'; -const TY_OBJECT_PATH: u8 = b'o'; -const TY_SIGNATURE: u8 = b'g'; -const TY_ARRAY: u8 = b'a'; -const TY_VARIANT: u8 = b'v'; -const TY_UNIX_FD: u8 = b'h'; - const HDR_PATH: u8 = 1; const HDR_INTERFACE: u8 = 2; const HDR_MEMBER: u8 = 3; @@ -357,102 +243,6 @@ impl Drop for DbusHolder { } } -#[derive(Clone, Debug)] -pub enum DynamicType { - U8, - Bool, - I16, - U16, - I32, - U32, - I64, - U64, - F64, - String, - ObjectPath, - Signature, - Variant, - Fd, - Array(Box), - DictEntry(Box, Box), - Struct(Vec), -} - -pub struct Parser<'a> { - buf: &'a [u8], - pos: usize, - fds: &'a [Rc], -} - -pub struct Formatter<'a> { - fds: &'a mut Vec>, - buf: &'a mut DynamicBuf, -} - -pub unsafe trait Message<'a>: Sized + 'a { - const SIGNATURE: &'static str; - const INTERFACE: &'static str; - const MEMBER: &'static str; - type Generic<'b>: Message<'b>; - - fn marshal(&self, w: &mut Formatter); - fn unmarshal(p: &mut Parser<'a>) -> Result; - fn num_fds(&self) -> u32; -} - -pub struct ErrorMessage<'a> { - pub msg: Cow<'a, str>, -} - -unsafe impl<'a> Message<'a> for ErrorMessage<'a> { - const SIGNATURE: &'static str = "s"; - const INTERFACE: &'static str = ""; - const MEMBER: &'static str = ""; - type Generic<'b> = ErrorMessage<'b>; - - fn marshal(&self, w: &mut Formatter) { - self.msg.marshal(w) - } - - fn unmarshal(p: &mut Parser<'a>) -> Result { - Ok(Self { - msg: p.unmarshal()?, - }) - } - - fn num_fds(&self) -> u32 { - 0 - } -} - -pub trait Property { - const INTERFACE: &'static str; - const PROPERTY: &'static str; - type Type: DbusType<'static>; -} - -pub trait Signal<'a>: Message<'a> {} - -pub trait MethodCall<'a>: Message<'a> { - type Reply: Message<'static>; -} - -pub unsafe trait DbusType<'a>: Clone + 'a { - const ALIGNMENT: usize; - const IS_POD: bool; - type Generic<'b>: DbusType<'b> + 'b; - - fn consume_signature(s: &mut &[u8]) -> Result<(), DbusError>; - #[allow(clippy::allow_attributes, dead_code)] - fn write_signature(w: &mut Vec); - fn marshal(&self, fmt: &mut Formatter); - fn unmarshal(parser: &mut Parser<'a>) -> Result; - - fn num_fds(&self) -> u32 { - 0 - } -} - pub struct Reply> { socket: Rc, buf: Vec, @@ -609,14 +399,3 @@ struct InterfaceSignalHandlers { unconditional: Option>, conditional: AHashMap>, } - -pub mod prelude { - pub use { - super::{ - DbusError, DbusType, Formatter, Message, MethodCall, Parser, Property, Signal, - types::{Bool, DictEntry, ObjectPath, Variant}, - }, - std::{borrow::Cow, rc::Rc}, - uapi::OwnedFd, - }; -} diff --git a/src/dbus/incoming.rs b/src/dbus/incoming.rs index a7b10358..db0ab623 100644 --- a/src/dbus/incoming.rs +++ b/src/dbus/incoming.rs @@ -83,11 +83,8 @@ impl Incoming { return Err(DbusError::TooFewFds); } let fds: Vec<_> = self.incoming.fds.drain(..unix_fds).collect(); - let mut parser = Parser { - buf: msg_buf, - pos: FIXED_HEADER_SIZE + dyn_header_len as usize, - fds: &fds, - }; + let mut parser = + Parser::new_at(msg_buf, FIXED_HEADER_SIZE + dyn_header_len as usize, &fds); match msg_ty { MSG_METHOD_CALL => { let sender = match &headers.sender {