diff --git a/Cargo.lock b/Cargo.lock index cba86dc9..29aea613 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -429,6 +429,7 @@ dependencies = [ "once_cell", "pango", "pangocairo", + "pin-project", "rand", "renderdoc", "repc", @@ -620,6 +621,26 @@ dependencies = [ "system-deps", ] +[[package]] +name = "pin-project" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.8" diff --git a/Cargo.toml b/Cargo.toml index 073dd772..ffab7a52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ pango = { version = "0.15.2", features = ["v1_44"] } i4config = { path = "i4config" } default-config = { path = "default-config" } x11rb = { version = "0.9.0", features = ["composite", "cursor"] } +pin-project = "1.0.10" [build-dependencies] repc = "0.1.1" diff --git a/build/tokens.rs b/build/tokens.rs index 94639e29..946e4440 100644 --- a/build/tokens.rs +++ b/build/tokens.rs @@ -158,7 +158,7 @@ impl<'a> Tokenizer<'a> { b'{' => self.tokenize_tree(TreeDelim::Brace)?, c @ (b')' | b'}') => { if self.delim.map(|d| d.closing()) != Some(c) { - bail!("Unexpected '{}' in line {}", c, self.line); + bail!("Unexpected {:?} in line {}", c as char, self.line); } return Ok(false); } diff --git a/build/wire_dbus.rs b/build/wire_dbus.rs index 9670f985..74501c0a 100644 --- a/build/wire_dbus.rs +++ b/build/wire_dbus.rs @@ -5,7 +5,6 @@ use bstr::{BStr, BString, ByteSlice}; use std::collections::hash_map::Entry; use std::collections::HashMap; use std::io::Write; -use std::mem; use std::os::unix::ffi::OsStrExt; #[derive(Debug)] @@ -42,18 +41,28 @@ struct Function { out_fields: Vec, } +#[derive(Debug)] +struct Property { + name: BString, + ty: Type, +} + struct Parser<'a> { pos: usize, tokens: &'a [Token<'a>], } impl<'a> Parser<'a> { - fn parse(&mut self) -> Result> { - let mut res = vec![]; + fn parse(&mut self) -> Result { + let mut res = Component { + functions: vec![], + properties: vec![], + }; while !self.eof() { let (line, ty) = self.expect_ident()?; match ty.as_bytes() { - b"fn" => res.push(self.parse_fn()?.val), + b"fn" => res.functions.push(self.parse_fn()?.val), + b"prop" => res.properties.push(self.parse_prop()?.val), _ => bail!("In line {}: Unexpected entry {:?}", line, ty), } } @@ -71,6 +80,22 @@ impl<'a> Parser<'a> { Ok(()) } + fn parse_prop(&mut self) -> Result> { + let (line, name) = self.expect_ident()?; + let res: Result<_> = (|| { + self.expect_symbol(Symbol::Equals)?; + let ty = self.parse_type()?; + Ok(Lined { + line, + val: Property { + name: name.to_owned(), + ty, + }, + }) + })(); + res.with_context(|| format!("While parsing property starting at line {}", line)) + } + fn parse_fn(&mut self) -> Result> { let (line, name) = self.expect_ident()?; let res: Result<_> = (|| { @@ -101,7 +126,7 @@ impl<'a> Parser<'a> { }, }) })(); - res.with_context(|| format!("While parsing message starting at line {}", line)) + res.with_context(|| format!("While parsing function starting at line {}", line)) } fn parse_field(&mut self) -> Result { @@ -252,7 +277,7 @@ impl<'a> Parser<'a> { } } -fn parse_functions(s: &[u8]) -> Result> { +fn parse_component(s: &[u8]) -> Result { let tokens = tokenize(s)?; let mut parser = Parser { pos: 0, @@ -343,6 +368,10 @@ fn write_signature(f: &mut W, ty: &Type) -> Result<()> { } fn write_type(f: &mut W, ty: &Type) -> Result<()> { + write_type2(f, "'a", ty) +} + +fn write_type2(f: &mut W, lt: &str, ty: &Type) -> Result<()> { let ty = match ty { Type::U8 => "u8", Type::Bool => "Bool", @@ -353,22 +382,34 @@ fn write_type(f: &mut W, ty: &Type) -> Result<()> { Type::I64 => "AlignedI64", Type::U64 => "AlignedU64", Type::F64 => "AlignedF64", - Type::String => "Cow<'a, str>", - Type::ObjectPath => "ObjectPath<'a>", - Type::Signature => "Signature<'a>", - Type::Variant => "Variant<'a>", + Type::String => { + write!(f, "Cow<{}, str>", lt)?; + return Ok(()); + } + Type::ObjectPath => { + write!(f, "ObjectPath<{}>", lt)?; + return Ok(()); + } + Type::Signature => { + write!(f, "Signature<{}>", lt)?; + return Ok(()); + } + Type::Variant => { + write!(f, "Variant<{}>", lt)?; + return Ok(()); + } Type::Fd => "Rc", Type::Array(e) => { - write!(f, "Cow<'a, [")?; - write_type(f, &e)?; - write!(f, ">")?; + write!(f, "Cow<{}, [", lt)?; + write_type2(f, lt, &e)?; + write!(f, "]>")?; return Ok(()); } Type::DictEntry(k, v) => { write!(f, "DictEntry<")?; - write_type(f, &k)?; + write_type2(f, lt, &k)?; write!(f, ", ")?; - write_type(f, &v)?; + write_type2(f, lt, &v)?; write!(f, ">")?; return Ok(()); } @@ -378,7 +419,7 @@ fn write_type(f: &mut W, ty: &Type) -> Result<()> { if idx > 0 { write!(f, ", ")?; } - write_type(f, &fs)?; + write_type2(f, lt, &fs)?; } write!(f, ")")?; return Ok(()); @@ -400,7 +441,9 @@ fn write_message( ) -> Result<()> { let needs_lt = fields.iter().any(|f| needs_lifetime(&f.ty)); let lt = if needs_lt { "<'a>" } else { "" }; + let ltb = if needs_lt { "<'b>" } else { "" }; writeln!(f)?; + writeln!(f, "{}#[derive(Debug)]", indent)?; if fields.is_empty() { writeln!(f, "{}pub struct {}{};", indent, name, lt)?; } else { @@ -413,7 +456,11 @@ fn write_message( writeln!(f, "{}}}", indent)?; } writeln!(f)?; - writeln!(f, "{}impl<'a> Message<'a> for {}{} {{", indent, name, lt)?; + writeln!( + f, + "{}unsafe impl<'a> Message<'a> for {}{} {{", + indent, name, lt + )?; write!(f, "{} const SIGNATURE: &'static str = \"", indent)?; for field in fields { write_signature(f, &field.ty)?; @@ -429,6 +476,7 @@ fn write_message( "{} const MEMBER: &'static str = \"{}\";", indent, fun.name )?; + writeln!(f, "{} type Generic<'b> = {}{};", indent, name, ltb,)?; writeln!(f)?; writeln!(f, "{} fn marshal(&self, fmt: &mut Formatter) {{", indent)?; if fields.is_empty() { @@ -471,41 +519,86 @@ fn write_message( writeln!(f, "{} }}", indent)?; writeln!(f, "{}}}", indent)?; if let Some(rn) = reply_name { - let reply_lt = if reply_has_lt { "<'b>" } else { "" }; + let reply_lt = if reply_has_lt { "<'static>" } else { "" }; writeln!(f)?; writeln!(f, "{}impl<'a> MethodCall<'a> for {}{} {{", indent, name, lt)?; - writeln!(f, "{} type Reply<'b> = {}{};", indent, rn, reply_lt)?; + writeln!(f, "{} type Reply = {}{};", indent, rn, reply_lt)?; writeln!(f, "{}}}", indent)?; } Ok(()) } -fn write_interface(f: &mut W, element: &Element, indent: &str) -> Result<()> { - for fun in &element.functions { - let in_name = format!("{}Call", fun.name); - let out_name = format!("{}Reply", fun.name); - let reply_has_lt = fun.out_fields.iter().any(|f| needs_lifetime(&f.ty)); - write_message( - f, - element, - fun, - &in_name, - indent, - &fun.in_fields, - Some(&out_name), - reply_has_lt, - )?; - write_message( - f, - element, - fun, - &out_name, - indent, - &fun.out_fields, - None, - false, - )?; +fn write_component( + f: &mut W, + element: &Element, + component: &Component, + indent: &str, +) -> Result<()> { + for fun in &component.functions { + write_function(f, element, fun, indent)?; } + for prop in &component.properties { + write_property(f, element, prop, indent)?; + } + Ok(()) +} + +fn write_property( + f: &mut W, + el: &Element, + property: &Property, + indent: &str, +) -> Result<()> { + writeln!(f)?; + writeln!(f, "{}pub struct {};", indent, property.name)?; + writeln!(f)?; + writeln!(f, "{}impl Property for {} {{", indent, property.name)?; + writeln!( + f, + "{} const INTERFACE: &'static str = \"{}\";", + indent, el.interface + )?; + writeln!( + f, + "{} const PROPERTY: &'static str = \"{}\";", + indent, property.name, + )?; + write!(f, "{} type Type = ", indent)?; + write_type2(f, "'static", &property.ty)?; + writeln!(f, ";")?; + writeln!(f, "{}}}", indent)?; + Ok(()) +} + +fn write_function( + f: &mut W, + element: &Element, + fun: &Function, + indent: &str, +) -> Result<()> { + let in_name = format!("{}", fun.name); + let out_name = format!("{}Reply", fun.name); + let reply_has_lt = fun.out_fields.iter().any(|f| needs_lifetime(&f.ty)); + write_message( + f, + element, + fun, + &in_name, + indent, + &fun.in_fields, + Some(&out_name), + reply_has_lt, + )?; + write_message( + f, + element, + fun, + &out_name, + indent, + &fun.out_fields, + None, + false, + )?; Ok(()) } @@ -524,18 +617,27 @@ fn write_element(f: &mut W, element: Element, indent: &str) -> Result< writeln!(f, "{} use crate::dbus::prelude::*;", indent)?; { let indent = format!("{} ", indent); - write_interface(f, &element, &indent)?; + for component in &element.components { + write_component(f, &element, &component, &indent)?; + } write_module(f, element, &indent)?; } writeln!(f, "{}}}", indent)?; Ok(()) } +#[derive(Debug)] +struct Component { + functions: Vec, + properties: Vec, +} + +#[derive(Debug)] struct Element { name: BString, interface: BString, children: HashMap, - functions: Vec, + components: Vec, } fn collect_interfaces() -> Result { @@ -543,7 +645,7 @@ fn collect_interfaces() -> Result { name: Default::default(), interface: Default::default(), children: Default::default(), - functions: vec![], + components: vec![], }; let mut files = vec![]; for file in std::fs::read_dir("wire-dbus")? { @@ -553,7 +655,7 @@ fn collect_interfaces() -> Result { let file_name = file.file_name(); let file_name = file_name.as_bytes().as_bstr(); println!("cargo:rerun-if-changed=wire-dbus/{}", file_name); - let mut interface = file_name + let interface = file_name .rsplitn_str(2, ".") .skip(1) .next() @@ -562,37 +664,27 @@ fn collect_interfaces() -> Result { .to_owned(); let mut components: Vec<_> = file_name.split_str(".").collect(); components.pop(); - let functions = (|| { + let component = (|| { let contents = std::fs::read(file.path())?; - parse_functions(&contents) + parse_component(&contents) })(); - let mut functions = - functions.with_context(|| format!("While parsing file {}", file.path().display()))?; + let component = + component.with_context(|| format!("While parsing file {}", file.path().display()))?; let mut target = &mut root; - for (i, comp) in components.iter().enumerate() { - let comp = comp.as_bstr(); - if i + 1 < components.len() { - target = match target.children.entry(comp.to_owned()) { - Entry::Occupied(o) => o.into_mut(), - Entry::Vacant(v) => v.insert(Element { - name: comp.to_owned(), - interface: Default::default(), - children: HashMap::new(), - functions: Vec::new(), - }), - }; - } else { - target.children.insert( - comp.to_owned(), - Element { - name: to_snake(comp), - interface: mem::take(&mut interface), - children: Default::default(), - functions: mem::take(&mut functions), - }, - ); - } + for comp in components.iter() { + let comp = to_snake(comp.as_bstr()); + target = match target.children.entry(comp.to_owned()) { + Entry::Occupied(o) => o.into_mut(), + Entry::Vacant(v) => v.insert(Element { + name: comp.to_owned(), + interface: Default::default(), + children: HashMap::new(), + components: vec![], + }), + }; } + target.interface = interface; + target.components.push(component); } Ok(root) } diff --git a/src/dbus.rs b/src/dbus.rs index d3cf9dac..6a5ed5a4 100644 --- a/src/dbus.rs +++ b/src/dbus.rs @@ -1,14 +1,20 @@ use crate::async_engine::{AsyncFd, SpawnedFuture}; +use crate::dbus::property::GetReply; use crate::dbus::types::{ObjectPath, Signature, Variant}; use crate::utils::copyhashmap::CopyHashMap; use crate::utils::stack::Stack; use crate::utils::vecstorage::VecStorage; -use crate::{AsyncEngine, AsyncError, AsyncQueue, CloneCell, NumCell}; +use crate::{AsyncEngine, AsyncError, AsyncQueue, CloneCell, NumCell, RunToplevel}; use std::borrow::Cow; use std::cell::{Cell, RefCell}; +use std::fmt::{Debug, Display}; +use std::future::Future; +use std::mem; +use std::pin::Pin; use std::rc::Rc; -use std::task::Waker; +use std::task::{Context, Poll, Waker}; use thiserror::Error; +pub use types::*; use uapi::OwnedFd; mod auth; @@ -18,17 +24,40 @@ 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("timeout")] - Timeout, #[error("Encountered an unknown type in a signature")] UnknownType, #[error("BUS closed the connection")] Closed, + #[error("Function call reply does not contain a reply serial")] + NoReplySerial, + #[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")] @@ -71,10 +100,8 @@ pub enum DbusError { InvalidEndianess, #[error("Server speaks an unexpected protocol version")] InvalidProtocol, - #[error("Could not read from the socket")] - ReadFailed(#[source] std::io::Error), - #[error(transparent)] - Rc(Rc), + #[error("Signature contains an invalid type")] + InvalidSignatureType, } efrom!(DbusError, AsyncError); @@ -84,32 +111,46 @@ pub struct Dbus { } impl Dbus { - pub fn new(eng: &Rc) -> Self { + pub fn new(eng: &Rc, run_toplevel: &Rc) -> Self { Self { eng: eng.clone(), - system: Default::default(), + system: Rc::new(DbusHolder::new(run_toplevel)), } } pub fn system(&self) -> Result, DbusError> { self.system - .get(&self.eng, "/var/run/dbus/system_bus_socket") + .get(&self.eng, "/var/run/dbus/system_bus_socket", "System bus") } } +unsafe trait ReplyHandler { + fn signature(&self) -> &str; + fn handle_error(self: Box, socket: &Rc, error: DbusError); + fn handle( + self: Box, + socket: &Rc, + headers: &Headers, + parser: &mut Parser, + buf: Vec, + ) -> Result<(), DbusError>; +} + pub struct DbusSocket { + bus_name: &'static str, fd: AsyncFd, eng: Rc, next_serial: NumCell, bufs: Stack>, + unique_name: CloneCell>, outgoing: AsyncQueue, - waiters: CopyHashMap, - replies: CopyHashMap, + reply_handlers: CopyHashMap>, incoming: Cell>>, outgoing_: Cell>>, auth: Cell>>, dead: Cell, headers: RefCell)>>, + run_toplevel: Rc, } const TY_BYTE: u8 = b'y'; @@ -138,6 +179,15 @@ const HDR_SENDER: u8 = 7; const HDR_SIGNATURE: u8 = 8; const HDR_UNIX_FDS: u8 = 9; +const MSG_METHOD_CALL: u8 = 1; +const MSG_METHOD_RETURN: u8 = 2; +const MSG_ERROR: u8 = 3; +const MSG_SIGNAL: u8 = 4; + +const NO_REPLY_EXPECTED: u8 = 0x1; +const NO_AUTO_START: u8 = 0x2; +const ALLOW_INTERACTIVE_AUTHORIZATION: u8 = 0x4; + #[derive(Default, Debug)] struct Headers<'a> { path: Option>, @@ -153,6 +203,16 @@ struct Headers<'a> { struct DbusHolder { socket: CloneCell>>, + run_toplevel: Rc, +} + +impl DbusHolder { + pub fn new(run_toplevel: &Rc) -> Self { + Self { + socket: Default::default(), + run_toplevel: run_toplevel.clone(), + } + } } impl Drop for DbusHolder { @@ -165,23 +225,11 @@ impl Drop for DbusHolder { } } -impl Default for DbusHolder { - fn default() -> Self { - Self { - socket: Default::default(), - } - } -} - struct DbusMessage { fds: Vec>, buf: Vec, } -struct Reply { - signature: String, -} - #[derive(Clone, Debug)] pub enum DynamicType { U8, @@ -214,24 +262,33 @@ pub struct Formatter<'a> { buf: &'a mut Vec, } -pub trait Message<'a>: Sized { +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 trait MethodCall<'a>: Message<'a> { - type Reply<'b>: Message<'b>; +pub trait Property { + const INTERFACE: &'static str; + const PROPERTY: &'static str; + type Type: DbusType<'static>; } -pub unsafe trait DbusType<'a>: Clone { +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>; fn write_signature(w: &mut Vec); fn marshal(&self, fmt: &mut Formatter); fn unmarshal(parser: &mut Parser<'a>) -> Result; @@ -241,10 +298,105 @@ pub unsafe trait DbusType<'a>: Clone { } } +pub struct Reply> { + socket: Rc, + buf: Vec, + t: T::Generic<'static>, +} + +pub struct PropertyValue { + reply: Reply>, +} + +impl Debug for PropertyValue +where + for<'a> >::Generic<'a>: Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.get().fmt(f) + } +} + +impl PropertyValue { + pub fn get<'a>(&'a self) -> &'a >::Generic<'a> { + &self.reply.get().value + } +} + +impl> Debug for Reply +where + for<'a> T::Generic<'a>: Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.get().fmt(f) + } +} + +impl> Reply { + pub fn get<'a>(&'a self) -> &'a T::Generic<'a> { + unsafe { mem::transmute(&self.t) } + } +} + +impl> Drop for Reply { + fn drop(&mut self) { + self.socket.bufs.push(mem::take(&mut self.buf)); + } +} + +struct AsyncReplySlot> { + data: Cell, DbusError>>>, + waker: Cell>, +} + +pub struct AsyncReply> { + socket: Rc, + serial: u32, + slot: Rc>, +} + +#[pin_project::pin_project] +pub struct AsyncProperty { + #[pin] + reply: AsyncReply>, +} + +impl> Drop for AsyncReply { + fn drop(&mut self) { + self.socket.reply_handlers.remove(&self.serial); + } +} + +impl> Future for AsyncReply { + type Output = Result, DbusError>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if let Some(d) = self.slot.data.take() { + Poll::Ready(d) + } else { + self.slot.waker.set(Some(cx.waker().clone())); + Poll::Pending + } + } +} + +impl Future for AsyncProperty { + type Output = Result, DbusError>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + AsyncProperty::project(self) + .reply + .poll(cx) + .map(|r| r.map(|v| PropertyValue { reply: v })) + } +} + pub mod prelude { pub use super::{ types::{Bool, DictEntry, ObjectPath, Signature, Variant}, - DbusError, DbusType, Formatter, Message, MethodCall, Parser, + DbusError, DbusType, Formatter, Message, MethodCall, Parser, Property, }; pub use std::borrow::Cow; + pub use std::rc::Rc; + pub use uapi::OwnedFd; } diff --git a/src/dbus/auth.rs b/src/dbus/auth.rs index db3ad4ff..872c3984 100644 --- a/src/dbus/auth.rs +++ b/src/dbus/auth.rs @@ -30,12 +30,15 @@ struct Auth { impl Auth { async fn run(&mut self) { if let Err(e) = self.handle_auth().await { - log::error!("Could not authenticate to dbus socket: {}", ErrorFmt(e)); - self.socket.dead.set(true); - self.socket.auth.take(); + log::error!( + "{}: Could not authenticate to dbus socket: {}", + self.socket.bus_name, + ErrorFmt(e) + ); + self.socket.kill(); return; } - log::info!("Authenticated"); + log::info!("{}: Authenticated", self.socket.bus_name); self.socket.incoming.set(Some( self.socket.eng.spawn(handle_incoming(self.socket.clone())), )); diff --git a/src/dbus/dynamic_type.rs b/src/dbus/dynamic_type.rs index 21aaedf3..5d270d6e 100644 --- a/src/dbus/dynamic_type.rs +++ b/src/dbus/dynamic_type.rs @@ -145,7 +145,7 @@ impl DynamicType { DynamicType::Fd => Variant::Fd(parser.read_fd()?), DynamicType::Array(el) => { let len: u32 = parser.read_pod()?; - parser.align_to(el.alignment()); + parser.align_to(el.alignment())?; let len = len as usize; if parser.buf.len() - parser.pos < len { return Err(DbusError::UnexpectedEof); @@ -165,12 +165,12 @@ impl DynamicType { Variant::Array(el.deref().clone(), vals) } DynamicType::DictEntry(k, v) => { - parser.align_to(8); + parser.align_to(8)?; Variant::DictEntry(Box::new(k.parse(parser)?), Box::new(v.parse(parser)?)) } DynamicType::Struct(fields) => { let mut vals = vec![]; - parser.align_to(8); + parser.align_to(8)?; for field in fields { vals.push(field.parse(parser)?); } diff --git a/src/dbus/holder.rs b/src/dbus/holder.rs index d1bf8cc5..0747a75d 100644 --- a/src/dbus/holder.rs +++ b/src/dbus/holder.rs @@ -1,6 +1,6 @@ use crate::dbus::auth::handle_auth; use crate::dbus::{DbusError, DbusHolder, DbusSocket}; -use crate::{AsyncEngine, NumCell}; +use crate::{org, AsyncEngine, ErrorFmt, NumCell, RunToplevel}; use std::cell::Cell; use std::rc::Rc; use uapi::c; @@ -10,6 +10,7 @@ impl DbusHolder { self: &Rc, eng: &Rc, addr: &str, + name: &'static str, ) -> Result, DbusError> { if let Some(c) = self.socket.get() { if c.dead.get() { @@ -18,13 +19,18 @@ impl DbusHolder { return Ok(c); } } - let socket = connect(eng, addr)?; + let socket = connect(eng, addr, name, &self.run_toplevel)?; self.socket.set(Some(socket.clone())); Ok(socket) } } -fn connect(eng: &Rc, addr: &str) -> Result, DbusError> { +fn connect( + eng: &Rc, + addr: &str, + name: &'static str, + run_toplevel: &Rc, +) -> Result, DbusError> { let socket = match uapi::socket( c::AF_UNIX, c::SOCK_STREAM | c::SOCK_NONBLOCK | c::SOCK_CLOEXEC, @@ -41,19 +47,37 @@ fn connect(eng: &Rc, addr: &str) -> Result, DbusErro return Err(DbusError::Connect(e.into())); } let socket = Rc::new(DbusSocket { + bus_name: name, fd: eng.fd(&Rc::new(socket))?, eng: eng.clone(), next_serial: NumCell::new(1), bufs: Default::default(), + unique_name: Default::default(), outgoing: Default::default(), - waiters: Default::default(), - replies: Default::default(), + reply_handlers: Default::default(), incoming: Default::default(), outgoing_: Default::default(), auth: Default::default(), dead: Cell::new(false), headers: Default::default(), + run_toplevel: run_toplevel.clone(), }); + let skt = socket.clone(); + socket.call( + "org.freedesktop.DBus", + "/org/freedesktop/dbus", + org::freedesktop::dbus::Hello, + move |res| match res { + Ok(name) => { + log::info!("{}: Acquired unique name {}", skt.bus_name, name.name); + let _ = skt.unique_name.set(Rc::new(name.name.to_string())); + } + Err(e) => { + log::error!("{}: Hello call failed: {}", skt.bus_name, ErrorFmt(e)); + skt.kill(); + } + }, + ); let future = eng.spawn(handle_auth(socket.clone())); socket.auth.set(Some(future)); Ok(socket) diff --git a/src/dbus/incoming.rs b/src/dbus/incoming.rs index c8029967..e49c337b 100644 --- a/src/dbus/incoming.rs +++ b/src/dbus/incoming.rs @@ -2,17 +2,21 @@ use super::{ HDR_DESTINATION, HDR_ERROR_NAME, HDR_INTERFACE, HDR_MEMBER, HDR_PATH, HDR_REPLY_SERIAL, HDR_SENDER, HDR_SIGNATURE, HDR_UNIX_FDS, }; -use crate::dbus::{DbusError, DbusSocket, DynamicType, Headers, Parser}; +use crate::dbus::{ + CallError, DbusError, DbusSocket, Headers, Parser, MSG_ERROR, MSG_METHOD_RETURN, +}; +use crate::utils::ptr_ext::{MutPtrExt, PtrExt}; use crate::ErrorFmt; +use std::cell::UnsafeCell; use std::collections::VecDeque; use std::mem::MaybeUninit; +use std::ops::Deref; use std::rc::Rc; use uapi::{c, Errno, MaybeUninitSliceExt, MsghdrMut, OwnedFd}; pub async fn handle_incoming(socket: Rc) { let mut incoming = Incoming { socket, - msg_buf: vec![], buf: Box::new([MaybeUninit::uninit(); 4096]), buf_start: 0, buf_end: 0, @@ -25,7 +29,6 @@ pub async fn handle_incoming(socket: Rc) { pub struct Incoming { socket: Rc, - msg_buf: Vec, buf: Box<[MaybeUninit; 4096]>, buf_start: usize, buf_end: usize, @@ -36,36 +39,46 @@ pub struct Incoming { impl Incoming { async fn run(&mut self) { loop { + if self.socket.dead.get() { + return; + } if let Err(e) = self.handle_msg().await { - log::error!("Could not process an incoming message: {}", ErrorFmt(e)); - self.socket.incoming.take(); - self.socket.outgoing_.take(); + log::error!( + "{}: Could not process an incoming message: {}", + self.socket.bus_name, + ErrorFmt(e) + ); + self.socket.kill(); return; } } } async fn handle_msg(&mut self) -> Result<(), DbusError> { - self.msg_buf.clear(); + let msg_buf_data = UnsafeCell::new(self.socket.bufs.pop().unwrap_or_default()); + let msg_buf = unsafe { msg_buf_data.get().deref_mut() }; + msg_buf.clear(); const FIXED_HEADER_SIZE: usize = 16; - self.fill_msg_buf(FIXED_HEADER_SIZE).await?; - let endianess = self.msg_buf[0]; + self.fill_msg_buf(FIXED_HEADER_SIZE, msg_buf).await?; + let endianess = msg_buf[0]; if (endianess == b'l') != cfg!(target_endian = "little") { return Err(DbusError::InvalidEndianess); } - let msg_ty = self.msg_buf[1]; - let flags = self.msg_buf[2]; - let protocol = self.msg_buf[3]; + let msg_ty = msg_buf[1]; + let _flags = msg_buf[2]; + let protocol = msg_buf[3]; if protocol != 1 { - return Err(DbusError::InvalidEndianess); + return Err(DbusError::InvalidProtocol); } let mut fields2 = [0u32; 3]; - uapi::pod_write(&self.msg_buf[4..], &mut fields2[..]).unwrap(); - let [body_len, serial, headers_len] = fields2; + uapi::pod_write(&msg_buf[4..], &mut fields2[..]).unwrap(); + let [body_len, _serial, headers_len] = fields2; let dyn_header_len = headers_len + (headers_len.wrapping_neg() & 7); let remaining = dyn_header_len + body_len; - self.fill_msg_buf(remaining as usize).await?; - let headers = &self.msg_buf[FIXED_HEADER_SIZE..FIXED_HEADER_SIZE + headers_len as usize]; + self.fill_msg_buf(remaining as usize, msg_buf).await?; + drop(msg_buf); + let msg_buf = unsafe { msg_buf_data.get().deref().deref() }; + let headers = &msg_buf[FIXED_HEADER_SIZE..FIXED_HEADER_SIZE + headers_len as usize]; let headers = self.parse_headers(headers)?; let unix_fds = headers.unix_fds.unwrap_or(0) as usize; if self.fds.len() < unix_fds { @@ -73,19 +86,60 @@ impl Incoming { } let fds: Vec<_> = self.fds.drain(..unix_fds).collect(); let mut parser = Parser { - buf: &self.msg_buf, + buf: &msg_buf, pos: FIXED_HEADER_SIZE + dyn_header_len as usize, fds: &fds, }; - log::info!("headers = {:?}", headers); - if let Some(sig) = headers.signature { - let mut sig = sig.0.as_bytes(); - while sig.len() > 0 { - let (dt, rem) = DynamicType::from_signature(sig)?; - sig = rem; - let val = dt.parse(&mut parser)?; - log::info!("{:?}", val); + match msg_ty { + MSG_METHOD_RETURN | MSG_ERROR => { + let serial = match headers.reply_serial { + Some(s) => s, + _ => return Err(DbusError::NoReplySerial), + }; + if let Some(reply) = self.socket.reply_handlers.remove(&serial) { + if msg_ty == MSG_ERROR { + let ename = match headers.error_name { + Some(n) => n.into_owned(), + _ => return Err(DbusError::NoErrorName), + }; + let mut emsg = None; + if let Some(sig) = headers.signature { + if sig.0.starts_with("s") { + emsg = Some(parser.read_string()?.into_owned()); + } + } + let error = CallError { + name: ename, + msg: emsg, + }; + reply.handle_error(&self.socket, DbusError::CallError(error)); + } else { + let sig = headers.signature.as_deref().unwrap_or(""); + if sig != reply.signature() { + log::error!( + "{}: Message reply has an invalid signature: expected: {}, actual: {}", + self.socket.bus_name, + sig, + reply.signature() + ); + } else { + let buf = unsafe { std::mem::take(msg_buf_data.get().deref_mut()) }; + if let Err(e) = reply.handle(&self.socket, &headers, &mut parser, buf) { + log::error!( + "{}: Could not handle reply: {}", + self.socket.bus_name, + ErrorFmt(e) + ); + } + } + } + } } + _ => {} + } + let msg_buf = msg_buf_data.into_inner(); + if msg_buf.capacity() > 0 { + self.socket.bufs.push(msg_buf); } Ok(()) } @@ -113,7 +167,7 @@ impl Incoming { Ok(headers) } - async fn fill_msg_buf(&mut self, mut n: usize) -> Result<(), DbusError> { + async fn fill_msg_buf(&mut self, mut n: usize, buf: &mut Vec) -> Result<(), DbusError> { while n > 0 { if self.buf_start == self.buf_end { while let Err(e) = self.recvmsg() { @@ -129,17 +183,9 @@ impl Incoming { let read = n.min(self.buf_end - self.buf_start); let buf_start = self.buf_start % self.buf.len(); unsafe { - if buf_start + read <= self.buf.len() { - self.msg_buf.extend_from_slice( - self.buf[buf_start..buf_start + read].slice_assume_init_ref(), - ); - } else { - self.msg_buf - .extend_from_slice(self.buf[buf_start..].slice_assume_init_ref()); - self.msg_buf.extend_from_slice( - self.buf[..read - (self.buf.len() - buf_start)].slice_assume_init_ref(), - ); - } + buf.extend_from_slice( + self.buf[buf_start..buf_start + read].slice_assume_init_ref(), + ); } n -= read; self.buf_start += read; diff --git a/src/dbus/outgoing.rs b/src/dbus/outgoing.rs index 25e18f78..0e431a7c 100644 --- a/src/dbus/outgoing.rs +++ b/src/dbus/outgoing.rs @@ -1,8 +1,10 @@ use crate::dbus::{DbusMessage, DbusSocket}; -use crate::utils::vec_ext::VecExt; +use crate::utils::vec_ext::{UninitVecExt, VecExt}; use crate::utils::vecstorage::VecStorage; +use crate::ErrorFmt; use std::collections::VecDeque; use std::mem; +use std::mem::MaybeUninit; use std::ptr::NonNull; use std::rc::Rc; use uapi::{c, Errno, Msghdr}; @@ -27,7 +29,7 @@ struct Outgoing { socket: Rc, msgs: VecDeque, - cmsg: Vec, + cmsg: Vec>, fds: Vec, iovecs: VecStorage>, } @@ -37,7 +39,15 @@ impl Outgoing { loop { self.socket.outgoing.non_empty().await; while let Err(e) = self.try_flush() { - if e != Errno(c::EAGAIN) {} + if e != Errno(c::EAGAIN) { + log::error!( + "{}: Could not send a message to the bus: {}", + self.socket.bus_name, + ErrorFmt(e) + ); + self.socket.kill(); + return; + } let _ = self.socket.fd.writable().await; } } @@ -68,16 +78,14 @@ impl Outgoing { self.fds.extend(fds.iter().map(|f| f.raw())); let cmsg_space = uapi::cmsg_space(fds.len() * mem::size_of::()); self.cmsg.reserve(cmsg_space); - let (_, mut spare) = self.cmsg.split_at_spare_mut_ext(); + let (_, mut spare) = self.cmsg.split_at_spare_mut_bytes_ext(); let hdr = c::cmsghdr { cmsg_len: 0, cmsg_level: c::SOL_SOCKET, cmsg_type: c::SCM_RIGHTS, }; let len = uapi::cmsg_write(&mut spare, hdr, &self.fds).unwrap(); - unsafe { - self.cmsg.set_len(len); - } + self.cmsg.set_len_safe(len); } let msg = Msghdr { iov: &iovecs[..], diff --git a/src/dbus/parser.rs b/src/dbus/parser.rs index 1be279a6..df45c24e 100644 --- a/src/dbus/parser.rs +++ b/src/dbus/parser.rs @@ -38,7 +38,7 @@ impl<'a> Parser<'a> { } pub fn read_pod<'b, T: DbusType<'b> + Pod>(&mut self) -> Result { - self.align_to(T::ALIGNMENT); + self.align_to(T::ALIGNMENT)?; match uapi::pod_read_init(&self.buf[self.pos..]) { Ok(v) => { self.pos += mem::size_of::(); @@ -74,7 +74,7 @@ impl<'a> Parser<'a> { } fn read_string_(&mut self, len: usize) -> Result<&'a str, DbusError> { - if self.buf.len() - self.pos < len + 1 { + if len == usize::MAX || self.buf.len() - self.pos < len + 1 { return Err(DbusError::UnexpectedEof); } let s = &self.buf[self.pos..self.pos + len]; @@ -128,4 +128,14 @@ impl<'a> Parser<'a> { } parser.parse(self) } + + pub fn read_variant_as>(&mut self) -> Result { + let sig = self.read_signature()?; + let mut sig = sig.0.as_bytes(); + T::consume_signature(&mut sig)?; + if sig.len() > 0 { + return Err(DbusError::TrailingVariantSignature); + } + T::unmarshal(self) + } } diff --git a/src/dbus/property.rs b/src/dbus/property.rs new file mode 100644 index 00000000..c454d28e --- /dev/null +++ b/src/dbus/property.rs @@ -0,0 +1,66 @@ +use crate::dbus::{DbusError, DbusType, Formatter, Message, MethodCall, Parser}; +use std::borrow::Cow; +use std::marker::PhantomData; + +#[derive(Debug)] +pub struct Get<'a, T: DbusType<'static>> { + pub interface_name: Cow<'a, str>, + pub property_name: Cow<'a, str>, + pub _phantom: PhantomData, +} + +unsafe impl<'a, T: DbusType<'static>> Message<'a> for Get<'a, T> { + const SIGNATURE: &'static str = "ss"; + const INTERFACE: &'static str = "org.freedesktop.DBus.Properties"; + const MEMBER: &'static str = "Get"; + type Generic<'b> = Get<'b, T>; + + fn marshal(&self, fmt: &mut Formatter) { + fmt.marshal(&self.interface_name); + fmt.marshal(&self.property_name); + } + + fn unmarshal(parser: &mut Parser<'a>) -> Result { + Ok(Self { + interface_name: parser.unmarshal()?, + property_name: parser.unmarshal()?, + _phantom: Default::default(), + }) + } + + fn num_fds(&self) -> u32 { + 0 + } +} + +impl<'a, T: DbusType<'static>> MethodCall<'a> for Get<'a, T> { + type Reply = GetReply<'static, T>; +} + +#[derive(Debug)] +pub struct GetReply<'a, T: DbusType<'a>> { + pub value: T, + pub _phantom: PhantomData<&'a ()>, +} + +unsafe impl<'a, T: DbusType<'a>> Message<'a> for GetReply<'a, T> { + const SIGNATURE: &'static str = "v"; + const INTERFACE: &'static str = "org.freedesktop.DBus.Properties"; + const MEMBER: &'static str = "Get"; + type Generic<'b> = GetReply<'b, T::Generic<'b>>; + + fn marshal(&self, _fmt: &mut Formatter) { + unimplemented!(); + } + + fn unmarshal(parser: &mut Parser<'a>) -> Result { + Ok(Self { + value: parser.read_variant_as()?, + _phantom: Default::default(), + }) + } + + fn num_fds(&self) -> u32 { + self.value.num_fds() + } +} diff --git a/src/dbus/socket.rs b/src/dbus/socket.rs index 1bca6798..2a4a11dc 100644 --- a/src/dbus/socket.rs +++ b/src/dbus/socket.rs @@ -1,39 +1,150 @@ +use crate::dbus::property::Get; use crate::dbus::types::{ObjectPath, Signature, Variant}; use crate::dbus::{ - DbusMessage, DbusSocket, DbusType, Formatter, Message, MethodCall, HDR_DESTINATION, - HDR_INTERFACE, HDR_MEMBER, HDR_PATH, HDR_SIGNATURE, HDR_UNIX_FDS, + AsyncProperty, AsyncReply, AsyncReplySlot, DbusError, DbusMessage, DbusSocket, DbusType, + Formatter, Headers, Message, MethodCall, Parser, Property, Reply, ReplyHandler, + HDR_DESTINATION, HDR_INTERFACE, HDR_MEMBER, HDR_PATH, HDR_SIGNATURE, HDR_UNIX_FDS, + MSG_METHOD_CALL, NO_REPLY_EXPECTED, }; - -const MESSAGE_CALL: u8 = 1; -const MESSAGE_RETURN: u8 = 2; -const ERROR: u8 = 3; -const SIGNAL: u8 = 4; +use std::cell::Cell; +use std::marker::PhantomData; +use std::mem; +use std::ops::DerefMut; +use std::rc::Rc; +use uapi::c; impl DbusSocket { - pub fn new() -> Self { - todo!(); + pub(super) fn kill(self: &Rc) { + self.dead.set(true); + self.auth.take(); + self.incoming.take(); + self.outgoing_.take(); + let _ = uapi::shutdown(self.fd.raw(), c::SHUT_RDWR); + let replies = mem::take(self.reply_handlers.lock().deref_mut()); + for (_, handler) in replies { + handler.handle_error(self, DbusError::Killed); + } } pub fn call_noreply<'a, T: MethodCall<'a>>(&self, destination: &str, path: &str, msg: T) { - let (msg, _) = self.format_call(path, Some(destination), &msg); + if !self.dead.get() { + self.send_call(path, destination, NO_REPLY_EXPECTED, &msg); + } + } + + fn serial(&self) -> u32 { + self.next_serial.fetch_add(1) + } + + pub fn call<'a, T, F>(&self, destination: &str, path: &str, msg: T, f: F) + where + T: MethodCall<'a>, + F: for<'b> FnOnce(Result<&>::Generic<'b>, DbusError>) + + 'static, + { + if self.dead.get() { + self.run_toplevel + .schedule(move || f(Err(DbusError::Killed))); + return; + } + let serial = self.send_call(path, destination, 0, &msg); + self.reply_handlers + .set(serial, Box::new(SyncReplyHandler(f, PhantomData))); + } + + pub fn call_async<'a, T>( + self: &Rc, + destination: &str, + path: &str, + msg: T, + ) -> AsyncReply + where + T: MethodCall<'a>, + { + if self.dead.get() { + return AsyncReply { + socket: self.clone(), + serial: self.serial(), + slot: Rc::new(AsyncReplySlot { + data: Cell::new(Some(Err(DbusError::Killed))), + waker: Cell::new(None), + }), + }; + } + let serial = self.send_call(path, destination, 0, &msg); + let slot = Rc::new(AsyncReplySlot { + data: Cell::new(None), + waker: Cell::new(None), + }); + self.reply_handlers + .set(serial, Box::new(AsyncReplyHandler(slot.clone()))); + AsyncReply { + socket: self.clone(), + serial, + slot, + } + } + + pub fn get(&self, destination: &str, path: &str, f: F) + where + T: Property, + F: for<'b> FnOnce(Result<&>::Generic<'b>, DbusError>) + + 'static, + { + let msg: Get = Get { + interface_name: T::INTERFACE.into(), + property_name: T::PROPERTY.into(), + _phantom: PhantomData, + }; + self.call(destination, path, msg, move |res| { + f(res.map(|v| &v.value)); + }); + } + + pub fn get_async( + self: &Rc, + destination: &str, + path: &str, + ) -> AsyncProperty { + let msg: Get = Get { + interface_name: T::INTERFACE.into(), + property_name: T::PROPERTY.into(), + _phantom: PhantomData, + }; + AsyncProperty { + reply: self.call_async(destination, path, msg), + } + } + + fn send_call<'a, T: Message<'a>>( + &self, + path: &str, + destination: &str, + flags: u8, + msg: &T, + ) -> u32 { + let (msg, serial) = self.format_call(path, destination, flags, msg); self.outgoing.push(msg); + serial } fn format_call<'a, T: Message<'a>>( &self, path: &str, - destination: Option<&str>, + destination: &str, + flags: u8, msg: &T, ) -> (DbusMessage, u32) { let num_fds = msg.num_fds(); let mut fds = Vec::with_capacity(num_fds as _); - let serial = self.next_serial.fetch_add(1); + let serial = self.serial(); let mut buf = self.bufs.pop().unwrap_or_default(); buf.clear(); let mut fmt = Formatter::new(&mut fds, &mut buf); self.format_header( &mut fmt, - MESSAGE_CALL, + MSG_METHOD_CALL, + flags, serial, path, T::INTERFACE, @@ -53,11 +164,12 @@ impl DbusSocket { &self, fmt: &mut Formatter, ty: u8, + flags: u8, serial: u32, path: &str, interface: &str, member: &str, - destination: Option<&str>, + destination: &str, signature: &str, fds: u32, ) { @@ -66,7 +178,7 @@ impl DbusSocket { #[cfg(not(target_endian = "little"))] b'b'.marshal(fmt); ty.marshal(fmt); - 0u8.marshal(fmt); + flags.marshal(fmt); 1u8.marshal(fmt); 0u32.marshal(fmt); serial.marshal(fmt); @@ -75,9 +187,7 @@ impl DbusSocket { headers.push((HDR_PATH, Variant::ObjectPath(ObjectPath(path.into())))); headers.push((HDR_INTERFACE, Variant::String(interface.into()))); headers.push((HDR_MEMBER, Variant::String(member.into()))); - if let Some(dst) = destination { - headers.push((HDR_DESTINATION, Variant::String(dst.into()))); - } + headers.push((HDR_DESTINATION, Variant::String(destination.into()))); if signature.len() > 0 { headers.push(( HDR_SIGNATURE, @@ -91,3 +201,72 @@ impl DbusSocket { fmt.pad_to(8); } } + +struct SyncReplyHandler(F, PhantomData); + +unsafe impl ReplyHandler for SyncReplyHandler +where + T: Message<'static>, + F: for<'b> FnOnce(Result<&T::Generic<'b>, DbusError>), +{ + fn signature(&self) -> &str { + T::SIGNATURE + } + + fn handle_error(self: Box, _socket: &Rc, error: DbusError) { + (self.0)(Err(error)) + } + + fn handle<'a>( + self: Box, + socket: &Rc, + _headers: &Headers, + parser: &mut Parser<'a>, + buf: Vec, + ) -> Result<(), DbusError> { + let msg = as Message>::unmarshal(parser)?; + (self.0)(Ok(&msg)); + socket.bufs.push(buf); + Ok(()) + } +} + +struct AsyncReplyHandler>(Rc>); + +unsafe impl ReplyHandler for AsyncReplyHandler +where + T: Message<'static>, +{ + fn signature(&self) -> &str { + T::SIGNATURE + } + + fn handle_error(self: Box, _socket: &Rc, error: DbusError) { + self.0.data.set(Some(Err(error))); + if let Some(waker) = self.0.waker.take() { + waker.wake(); + } + } + + fn handle<'a>( + self: Box, + socket: &Rc, + _headers: &Headers, + parser: &mut Parser<'a>, + buf: Vec, + ) -> Result<(), DbusError> { + let msg = as Message<'static>>::unmarshal(unsafe { + mem::transmute::<&mut Parser<'a>, &mut Parser<'static>>(parser) + })?; + let reply = Reply { + socket: socket.clone(), + buf, + t: msg, + }; + self.0.data.set(Some(Ok(reply))); + if let Some(waker) = self.0.waker.take() { + waker.wake(); + } + Ok(()) + } +} diff --git a/src/dbus/types.rs b/src/dbus/types.rs index fa166b40..3d2536bd 100644 --- a/src/dbus/types.rs +++ b/src/dbus/types.rs @@ -9,13 +9,37 @@ use std::ops::Deref; use std::rc::Rc; use uapi::{OwnedFd, Packed, Pod}; +macro_rules! consume_signature_body { + ($s:expr, $ty:expr) => {{ + if $s.is_empty() { + return Err(DbusError::EmptySignature); + } + if $s[0] != $ty { + return Err(DbusError::InvalidSignatureType); + } + *$s = &(*$s)[1..]; + }}; +} + +macro_rules! signature { + ($ty:expr) => { + fn consume_signature(s: &mut &[u8]) -> Result<(), DbusError> { + consume_signature_body!(s, $ty); + Ok(()) + } + + fn write_signature(w: &mut Vec) { + w.push(TY_BYTE); + } + }; +} + unsafe impl<'a> DbusType<'a> for u8 { const ALIGNMENT: usize = 1; const IS_POD: bool = true; + type Generic<'b> = Self; - fn write_signature(w: &mut Vec) { - w.push(TY_BYTE); - } + signature!(TY_BYTE); fn marshal(&self, fmt: &mut Formatter) { fmt.write_packed(self); @@ -38,10 +62,9 @@ unsafe impl Packed for Bool {} unsafe impl<'a> DbusType<'a> for Bool { const ALIGNMENT: usize = 4; const IS_POD: bool = true; + type Generic<'b> = Self; - fn write_signature(w: &mut Vec) { - w.push(TY_BOOLEAN); - } + signature!(TY_BOOLEAN); fn marshal(&self, fmt: &mut Formatter) { fmt.write_packed(self); @@ -55,10 +78,9 @@ unsafe impl<'a> DbusType<'a> for Bool { unsafe impl<'a> DbusType<'a> for i16 { const ALIGNMENT: usize = 2; const IS_POD: bool = true; + type Generic<'b> = Self; - fn write_signature(w: &mut Vec) { - w.push(TY_INT16); - } + signature!(TY_INT16); fn marshal(&self, fmt: &mut Formatter) { fmt.write_packed(self); @@ -72,10 +94,9 @@ unsafe impl<'a> DbusType<'a> for i16 { unsafe impl<'a> DbusType<'a> for u16 { const ALIGNMENT: usize = 2; const IS_POD: bool = true; + type Generic<'b> = Self; - fn write_signature(w: &mut Vec) { - w.push(TY_UINT16) - } + signature!(TY_UINT16); fn marshal(&self, fmt: &mut Formatter) { fmt.write_packed(self); @@ -89,10 +110,9 @@ unsafe impl<'a> DbusType<'a> for u16 { unsafe impl<'a> DbusType<'a> for i32 { const ALIGNMENT: usize = 4; const IS_POD: bool = true; + type Generic<'b> = Self; - fn write_signature(w: &mut Vec) { - w.push(TY_INT32) - } + signature!(TY_INT32); fn marshal(&self, fmt: &mut Formatter) { fmt.write_packed(self); @@ -106,10 +126,9 @@ unsafe impl<'a> DbusType<'a> for i32 { unsafe impl<'a> DbusType<'a> for u32 { const ALIGNMENT: usize = 4; const IS_POD: bool = true; + type Generic<'b> = Self; - fn write_signature(w: &mut Vec) { - w.push(TY_UINT32) - } + signature!(TY_UINT32); fn marshal(&self, fmt: &mut Formatter) { fmt.write_packed(self); @@ -123,10 +142,9 @@ unsafe impl<'a> DbusType<'a> for u32 { unsafe impl<'a> DbusType<'a> for AlignedI64 { const ALIGNMENT: usize = 8; const IS_POD: bool = true; + type Generic<'b> = Self; - fn write_signature(w: &mut Vec) { - w.push(TY_INT64) - } + signature!(TY_INT64); fn marshal(&self, fmt: &mut Formatter) { fmt.write_packed(self); @@ -140,10 +158,9 @@ unsafe impl<'a> DbusType<'a> for AlignedI64 { unsafe impl<'a> DbusType<'a> for AlignedU64 { const ALIGNMENT: usize = 8; const IS_POD: bool = true; + type Generic<'b> = Self; - fn write_signature(w: &mut Vec) { - w.push(TY_UINT64) - } + signature!(TY_UINT64); fn marshal(&self, fmt: &mut Formatter) { fmt.write_packed(self); @@ -157,10 +174,9 @@ unsafe impl<'a> DbusType<'a> for AlignedU64 { unsafe impl<'a> DbusType<'a> for AlignedF64 { const ALIGNMENT: usize = 8; const IS_POD: bool = true; + type Generic<'b> = Self; - fn write_signature(w: &mut Vec) { - w.push(TY_DOUBLE) - } + signature!(TY_DOUBLE); fn marshal(&self, fmt: &mut Formatter) { fmt.write_packed(self); @@ -174,10 +190,9 @@ unsafe impl<'a> DbusType<'a> for AlignedF64 { unsafe impl<'a> DbusType<'a> for Cow<'a, str> { const ALIGNMENT: usize = 4; const IS_POD: bool = false; + type Generic<'b> = Cow<'b, str>; - fn write_signature(w: &mut Vec) { - w.push(TY_STRING) - } + signature!(TY_STRING); fn marshal(&self, fmt: &mut Formatter) { fmt.write_str(self); @@ -191,13 +206,20 @@ unsafe impl<'a> DbusType<'a> for Cow<'a, str> { #[derive(Clone, Debug, Eq, PartialEq)] pub struct Signature<'a>(pub Cow<'a, str>); +impl<'a> Deref for Signature<'a> { + type Target = str; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + unsafe impl<'a> DbusType<'a> for Signature<'a> { const ALIGNMENT: usize = 1; const IS_POD: bool = false; + type Generic<'b> = Signature<'b>; - fn write_signature(w: &mut Vec) { - w.push(TY_SIGNATURE) - } + signature!(TY_SIGNATURE); fn marshal(&self, fmt: &mut Formatter) { fmt.write_signature(self.0.as_bytes()); @@ -211,13 +233,20 @@ unsafe impl<'a> DbusType<'a> for Signature<'a> { #[derive(Clone, Debug, Eq, PartialEq)] pub struct ObjectPath<'a>(pub Cow<'a, str>); +impl<'a> Deref for ObjectPath<'a> { + type Target = str; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + unsafe impl<'a> DbusType<'a> for ObjectPath<'a> { const ALIGNMENT: usize = 4; const IS_POD: bool = false; + type Generic<'b> = ObjectPath<'b>; - fn write_signature(w: &mut Vec) { - w.push(TY_OBJECT_PATH) - } + signature!(TY_OBJECT_PATH); fn marshal(&self, fmt: &mut Formatter) { fmt.write_str(&self.0); @@ -228,9 +257,35 @@ unsafe impl<'a> DbusType<'a> for ObjectPath<'a> { } } +unsafe impl<'a> DbusType<'a> for Rc { + const ALIGNMENT: usize = 4; + const IS_POD: bool = false; + type Generic<'b> = Self; + + signature!(TY_UNIX_FD); + + fn marshal(&self, fmt: &mut Formatter) { + fmt.write_fd(self) + } + + fn unmarshal(parser: &mut Parser<'a>) -> Result { + parser.read_fd() + } + + fn num_fds(&self) -> u32 { + 1 + } +} + unsafe impl<'a, T: DbusType<'a>> DbusType<'a> for Cow<'a, [T]> { const ALIGNMENT: usize = 4; const IS_POD: bool = false; + type Generic<'b> = Cow<'b, [T::Generic<'b>]>; + + fn consume_signature(s: &mut &[u8]) -> Result<(), DbusError> { + consume_signature_body!(s, TY_ARRAY); + T::consume_signature(s) + } fn write_signature(w: &mut Vec) { w.push(TY_ARRAY); @@ -263,6 +318,15 @@ pub struct DictEntry { unsafe impl<'a, K: DbusType<'a>, V: DbusType<'a>> DbusType<'a> for DictEntry { const ALIGNMENT: usize = 8; const IS_POD: bool = false; + type Generic<'b> = DictEntry, V::Generic<'b>>; + + fn consume_signature(s: &mut &[u8]) -> Result<(), DbusError> { + consume_signature_body!(s, b'{'); + K::consume_signature(s)?; + V::consume_signature(s)?; + consume_signature_body!(s, b'}'); + Ok(()) + } fn write_signature(w: &mut Vec) { w.push(b'{'); @@ -292,6 +356,16 @@ macro_rules! tuple { unsafe impl<'a, $($p: DbusType<'a>),*> DbusType<'a> for ($($p,)*) { const ALIGNMENT: usize = 8; const IS_POD: bool = false; + type Generic<'b> = ($($p::Generic<'b>,)*); + + fn consume_signature(s: &mut &[u8]) -> Result<(), DbusError> { + consume_signature_body!(s, b'('); + $( + $p::consume_signature(s)?; + )* + consume_signature_body!(s, b')'); + Ok(()) + } fn write_signature(w: &mut Vec) { w.push(b'('); @@ -429,10 +503,9 @@ impl<'a> Variant<'a> { unsafe impl<'a> DbusType<'a> for Variant<'a> { const ALIGNMENT: usize = 1; const IS_POD: bool = false; + type Generic<'b> = Variant<'b>; - fn write_signature(w: &mut Vec) { - w.push(TY_VARIANT); - } + signature!(TY_VARIANT); fn marshal(&self, fmt: &mut Formatter) { fmt.write_variant(self); diff --git a/src/forker/io.rs b/src/forker/io.rs index 4da9fb11..9eba7cb6 100644 --- a/src/forker/io.rs +++ b/src/forker/io.rs @@ -40,7 +40,7 @@ impl IoIn { unsafe { self.scratch.set_len(len); } - let res = bincode::decode_from_slice::(&&self.scratch, bincode_ops()); + let res = bincode::decode_from_slice::(&self.scratch, bincode_ops()); match res { Ok((msg, _)) => Ok(msg), Err(e) => Err(ForkerError::DecodeFailed(e)), diff --git a/src/main.rs b/src/main.rs index c9f2f878..77242aad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,7 +19,7 @@ use crate::backends::dummy::DummyBackend; use crate::backends::xorg::{XorgBackend, XorgBackendError}; use crate::client::Clients; use crate::clientmem::ClientMemError; -use crate::dbus::Dbus; +use crate::dbus::{Dbus, FALSE}; use crate::event_loop::EventLoopError; use crate::forker::ForkerError; use crate::globals::Globals; @@ -40,6 +40,7 @@ use crate::utils::clonecell::CloneCell; use crate::utils::errorfmt::ErrorFmt; use crate::utils::numcell::NumCell; use crate::utils::queue::AsyncQueue; +use crate::utils::run_toplevel::RunToplevel; use crate::wheel::WheelError; use crate::wire_dbus::org; use crate::xkbcommon::XkbContext; @@ -47,7 +48,6 @@ use acceptor::Acceptor; use async_engine::AsyncEngine; use event_loop::EventLoop; use log::LevelFilter; -use std::borrow::Cow; use std::cell::Cell; use std::ops::Deref; use std::rc::Rc; @@ -140,6 +140,7 @@ fn main_() -> Result<(), MainError> { let xkb_keymap = xkb_ctx.keymap_from_str(include_str!("keymap.xkb")).unwrap(); let wheel = Wheel::install(&el)?; let engine = AsyncEngine::install(&el, &wheel)?; + let (_run_toplevel_future, run_toplevel) = RunToplevel::install(&engine); let node_ids = NodeIds::default(); let state = Rc::new(State { xkb_ctx, @@ -175,21 +176,39 @@ fn main_() -> Result<(), MainError> { pending_container_titles: Default::default(), pending_float_layout: Default::default(), pending_float_titles: Default::default(), - dbus: Dbus::new(&engine), + dbus: Dbus::new(&engine, &run_toplevel), + }); + let _future = state.eng.spawn({ + let dbus = state.dbus.system().unwrap(); + async move { + const LOGIND: &str = "org.freedesktop.login1"; + let reply = dbus + .call_async( + LOGIND, + "/org/freedesktop/login1", + org::freedesktop::login1::manager::GetSession { + session_id: std::env::var("XDG_SESSION_ID").unwrap().into(), + }, + ) + .await + .unwrap(); + let reply = dbus + .call_async( + LOGIND, + &reply.get().object_path, + org::freedesktop::login1::session::TakeControl { force: FALSE }, + ) + .await; + log::info!("{:?}", reply); + let reply = dbus + .get_async::( + LOGIND, + "/org/freedesktop/login1", + ) + .await; + log::info!("{:?}", reply); + } }); - state.dbus.system().unwrap().call_noreply( - "org.freedesktop.DBus", - "/org/freedesktop/dbus", - org::freedesktop::dbus::HelloCall, - ); - state.dbus.system().unwrap().call_noreply( - "org.freedesktop.login1", - "/org/freedesktop/login1", - org::freedesktop::login1::manager::GetSessionCall { - // session_id: Cow::Owned(std::env::var("XDG_SESSION_ID").unwrap()), - session_id: Cow::Borrowed("hurr durr"), - }, - ); forker.install(&state); let backend = XorgBackend::new(&state)?; state.backend.set(backend); diff --git a/src/utils/mod.rs b/src/utils/mod.rs index d7bcb866..f01ef01a 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -12,6 +12,7 @@ pub mod linkedlist; pub mod numcell; pub mod ptr_ext; pub mod queue; +pub mod run_toplevel; pub mod smallmap; pub mod stack; pub mod tri; diff --git a/src/utils/run_toplevel.rs b/src/utils/run_toplevel.rs new file mode 100644 index 00000000..dc1b0466 --- /dev/null +++ b/src/utils/run_toplevel.rs @@ -0,0 +1,38 @@ +use crate::async_engine::SpawnedFuture; +use crate::{AsyncEngine, AsyncQueue}; +use std::rc::Rc; + +pub struct RunToplevelFuture { + _future: SpawnedFuture<()>, +} + +pub struct RunToplevel { + queue: AsyncQueue>, +} + +impl RunToplevel { + pub fn install(eng: &Rc) -> (RunToplevelFuture, Rc) { + let slf = Rc::new(RunToplevel { + queue: Default::default(), + }); + let future = eng.spawn({ + let slf = slf.clone(); + async move { + loop { + let f = slf.queue.pop().await; + f(); + } + } + }); + let future = RunToplevelFuture { _future: future }; + (future, slf) + } + + pub fn schedule(&self, f: F) { + self.schedule_dyn(Box::new(f)); + } + + fn schedule_dyn(&self, f: Box) { + self.queue.push(f); + } +} diff --git a/src/utils/vec_ext.rs b/src/utils/vec_ext.rs index 75bcca0c..1291a8e9 100644 --- a/src/utils/vec_ext.rs +++ b/src/utils/vec_ext.rs @@ -4,6 +4,7 @@ use std::slice; pub trait VecExt { fn split_at_spare_mut_ext(&mut self) -> (&mut [T], &mut [MaybeUninit]); + fn split_at_spare_mut_bytes_ext(&mut self) -> (&mut [T], &mut [MaybeUninit]); } impl VecExt for Vec { @@ -20,4 +21,22 @@ impl VecExt for Vec { (initialized, spare) } } + + fn split_at_spare_mut_bytes_ext(&mut self) -> (&mut [T], &mut [MaybeUninit]) { + let (l, r) = self.split_at_spare_mut_ext(); + unsafe { (l, uapi::as_maybe_uninit_bytes_mut2(r)) } + } +} + +pub trait UninitVecExt { + fn set_len_safe(&mut self, n: usize); +} + +impl UninitVecExt for Vec> { + fn set_len_safe(&mut self, n: usize) { + assert!(n <= self.capacity()); + unsafe { + self.set_len(n); + } + } } diff --git a/wire-dbus/org.freedesktop.DBus.Properties.txt b/wire-dbus/org.freedesktop.DBus.Properties.txt new file mode 100644 index 00000000..24261f75 --- /dev/null +++ b/wire-dbus/org.freedesktop.DBus.Properties.txt @@ -0,0 +1,10 @@ +fn Get(interface_name: string, property_name: string) { + value: variant, +} + +fn Set(interface_name: string, property_name: string, value: variant) { +} + +fn GetAll(interface_name: string) { + props: array(dict(string, variant)), +} diff --git a/wire-dbus/org.freedesktop.login1.Manager.txt b/wire-dbus/org.freedesktop.login1.Manager.txt index ab4020fc..52dab994 100644 --- a/wire-dbus/org.freedesktop.login1.Manager.txt +++ b/wire-dbus/org.freedesktop.login1.Manager.txt @@ -3,3 +3,5 @@ fn GetSession( ) { object_path: object_path, } + +prop BootLoaderEntries = array(string) diff --git a/wire-dbus/org.freedesktop.login1.Session.txt b/wire-dbus/org.freedesktop.login1.Session.txt new file mode 100644 index 00000000..d24d94db --- /dev/null +++ b/wire-dbus/org.freedesktop.login1.Session.txt @@ -0,0 +1,10 @@ +fn TakeControl(force: bool) { +} + +fn TakeDevice(major: u32, minor: u32) { + fd: fd, + inactive: bool, +} + +fn PauseDeviceComplete(major: u32, minor: u32) { } +