From db88f2db429b295f0b114a555b5f528c64241c84 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Sun, 27 Feb 2022 01:35:49 +0100 Subject: [PATCH] autocommit 2022-02-27 01:35:49 CET --- build/build.rs | 4 +- build/tokens.rs | 206 +++++++ build/wire_dbus.rs | 608 +++++++++++++++++++ src/dbus.rs | 250 ++++++++ src/dbus/auth.rs | 112 ++++ src/dbus/dynamic_type.rs | 182 ++++++ src/dbus/formatter.rs | 110 ++++ src/dbus/holder.rs | 60 ++ src/dbus/incoming.rs | 173 ++++++ src/dbus/outgoing.rs | 102 ++++ src/dbus/parser.rs | 131 ++++ src/dbus/socket.rs | 93 +++ src/dbus/types.rs | 466 ++++++++++++++ src/ifs/wl_surface/xwindow.rs | 8 +- src/logind.rs | 1 + src/macros.rs | 30 + src/main.rs | 28 +- src/state.rs | 2 + src/utils/aligned.rs | 22 + src/utils/hex.rs | 15 + src/utils/mod.rs | 3 + src/utils/vecstorage.rs | 74 +++ src/wire_dbus.rs | 3 + src/xwayland/xwm.rs | 11 +- wire-dbus/org.freedesktop.DBus.txt | 3 + wire-dbus/org.freedesktop.login1.Manager.txt | 5 + 26 files changed, 2696 insertions(+), 6 deletions(-) create mode 100644 build/tokens.rs create mode 100644 build/wire_dbus.rs create mode 100644 src/dbus.rs create mode 100644 src/dbus/auth.rs create mode 100644 src/dbus/dynamic_type.rs create mode 100644 src/dbus/formatter.rs create mode 100644 src/dbus/holder.rs create mode 100644 src/dbus/incoming.rs create mode 100644 src/dbus/outgoing.rs create mode 100644 src/dbus/parser.rs create mode 100644 src/dbus/socket.rs create mode 100644 src/dbus/types.rs create mode 100644 src/logind.rs create mode 100644 src/utils/aligned.rs create mode 100644 src/utils/hex.rs create mode 100644 src/utils/vecstorage.rs create mode 100644 src/wire_dbus.rs create mode 100644 wire-dbus/org.freedesktop.DBus.txt create mode 100644 wire-dbus/org.freedesktop.login1.Manager.txt diff --git a/build/build.rs b/build/build.rs index a8aea4b2..b5f8bf0a 100644 --- a/build/build.rs +++ b/build/build.rs @@ -4,7 +4,9 @@ use std::path::PathBuf; use std::{env, io}; mod enums; +mod tokens; mod wire; +mod wire_dbus; fn open(s: &str) -> io::Result> { let mut path = PathBuf::from(env::var("OUT_DIR").unwrap()); @@ -20,7 +22,7 @@ fn open(s: &str) -> io::Result> { fn main() -> anyhow::Result<()> { wire::main()?; - + wire_dbus::main()?; enums::main()?; println!("cargo:rerun-if-changed=build/build.rs"); diff --git a/build/tokens.rs b/build/tokens.rs new file mode 100644 index 00000000..94639e29 --- /dev/null +++ b/build/tokens.rs @@ -0,0 +1,206 @@ +use anyhow::{bail, Context, Result}; +use bstr::{BStr, ByteSlice}; + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum TreeDelim { + Paren, + Brace, +} + +impl TreeDelim { + pub fn opening(self) -> u8 { + match self { + TreeDelim::Paren => b'(', + TreeDelim::Brace => b'{', + } + } + + pub fn closing(self) -> u8 { + match self { + TreeDelim::Paren => b')', + TreeDelim::Brace => b'}', + } + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Symbol { + Comma, + Colon, + Equals, +} + +impl Symbol { + pub fn name(self) -> &'static str { + match self { + Symbol::Comma => "','", + Symbol::Colon => "':'", + Symbol::Equals => "'='", + } + } +} + +#[derive(Debug)] +pub struct Token<'a> { + pub line: u32, + pub kind: TokenKind<'a>, +} + +#[derive(Debug)] +pub enum TokenKind<'a> { + Ident(&'a BStr), + Num(u32), + Tree { + delim: TreeDelim, + body: Vec>, + }, + Symbol(Symbol), +} + +impl TokenKind<'_> { + pub fn name(&self) -> &str { + match self { + TokenKind::Ident(_) => "identifier", + TokenKind::Num(_) => "number", + TokenKind::Tree { delim, .. } => match delim { + TreeDelim::Paren => "'('-tree", + TreeDelim::Brace => "'{'-tree", + }, + TokenKind::Symbol(s) => s.name(), + } + } +} + +#[derive(Copy, Clone)] +struct Cursor<'a> { + pos: usize, + s: &'a [u8], +} + +impl Cursor<'_> { + fn eof(&self) -> bool { + self.pos >= self.s.len() + } +} + +pub fn tokenize<'a>(s: &'a [u8]) -> Result>> { + let mut tnz = Tokenizer { + line: 1, + cursor: Cursor { pos: 0, s }, + delim: None, + res: vec![], + }; + tnz.tokenize()?; + Ok(tnz.res) +} + +struct Tokenizer<'a> { + line: u32, + cursor: Cursor<'a>, + delim: Option, + res: Vec>, +} + +impl<'a> Tokenizer<'a> { + fn tokenize_one(&mut self) -> Result { + let c = &mut self.cursor; + while !c.eof() { + let b = c.s[c.pos]; + if matches!(b, b' ' | b'\n' | b'#') { + c.pos += 1; + if b == b'\n' { + self.line += 1; + } else if b == b'#' { + while !c.eof() { + c.pos += 1; + if c.s[c.pos - 1] == b'\n' { + self.line += 1; + break; + } + } + } + } else { + break; + } + } + if c.eof() { + if self.delim.is_some() { + bail!("Unexpected eof"); + } + return Ok(false); + } + let line = self.line; + let b = c.s[c.pos]; + let b_pos = c.pos; + c.pos += 1; + let kind = match b { + b'a'..=b'z' | b'A'..=b'Z' => { + while !c.eof() + && matches!(c.s[c.pos], b'a'..=b'z' | b'A'..=b'Z' | b'_' | b'0'..=b'9') + { + c.pos += 1; + } + TokenKind::Ident(c.s[b_pos..c.pos].as_bstr()) + } + b'0'..=b'9' => { + c.pos -= 1; + let mut num = 0; + while !c.eof() && matches!(c.s[c.pos], b'0'..=b'9') { + num = num * 10 + (c.s[c.pos] - b'0') as u32; + c.pos += 1; + } + TokenKind::Num(num) + } + b',' => TokenKind::Symbol(Symbol::Comma), + b'=' => TokenKind::Symbol(Symbol::Equals), + b':' => TokenKind::Symbol(Symbol::Colon), + b'(' => self.tokenize_tree(TreeDelim::Paren)?, + 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); + } + return Ok(false); + } + _ => bail!("Unexpected byte {:?} in line {}", b as char, self.line), + }; + self.res.push(Token { line, kind }); + Ok(true) + } + + fn tokenize(&mut self) -> Result<()> { + while self.tokenize_one()? { + // nothing + } + Ok(()) + } + + fn tokenize_tree(&mut self, delim: TreeDelim) -> Result> { + let mut tnz = Tokenizer { + line: self.line, + cursor: self.cursor, + delim: Some(delim), + res: vec![], + }; + tnz.tokenize().with_context(|| { + format!( + "While tokenizing {:?} block starting in line {}", + delim.opening() as char, + self.line + ) + })?; + self.cursor.pos = tnz.cursor.pos; + self.line = tnz.line; + Ok(TokenKind::Tree { + delim, + body: tnz.res, + }) + } +} + +#[derive(Debug)] +pub struct Lined { + #[allow(dead_code)] + pub line: u32, + pub val: T, +} diff --git a/build/wire_dbus.rs b/build/wire_dbus.rs new file mode 100644 index 00000000..9670f985 --- /dev/null +++ b/build/wire_dbus.rs @@ -0,0 +1,608 @@ +use crate::open; +use crate::tokens::{tokenize, Lined, Symbol, Token, TokenKind, TreeDelim}; +use anyhow::{bail, Context, Result}; +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)] +enum Type { + U8, + Bool, + I16, + U16, + I32, + U32, + I64, + U64, + F64, + String, + ObjectPath, + Signature, + Variant, + Fd, + Array(Box), + DictEntry(Box, Box), + Struct(Vec), +} + +#[derive(Debug)] +struct Field { + name: BString, + ty: Type, +} + +#[derive(Debug)] +struct Function { + name: BString, + in_fields: Vec, + out_fields: Vec, +} + +struct Parser<'a> { + pos: usize, + tokens: &'a [Token<'a>], +} + +impl<'a> Parser<'a> { + fn parse(&mut self) -> Result> { + let mut res = vec![]; + while !self.eof() { + let (line, ty) = self.expect_ident()?; + match ty.as_bytes() { + b"fn" => res.push(self.parse_fn()?.val), + _ => bail!("In line {}: Unexpected entry {:?}", line, ty), + } + } + Ok(res) + } + + fn eof(&self) -> bool { + self.pos == self.tokens.len() + } + + fn not_eof(&self) -> Result<()> { + if self.eof() { + bail!("Unexpected eof"); + } + Ok(()) + } + + fn parse_fn(&mut self) -> Result> { + let (line, name) = self.expect_ident()?; + let res: Result<_> = (|| { + let (_, body) = self.expect_tree(TreeDelim::Paren)?; + let mut parser = Parser { + pos: 0, + tokens: body, + }; + let mut in_fields = vec![]; + while !parser.eof() { + in_fields.push(parser.parse_field()?); + } + let (_, body) = self.expect_tree(TreeDelim::Brace)?; + let mut parser = Parser { + pos: 0, + tokens: body, + }; + let mut out_fields = vec![]; + while !parser.eof() { + out_fields.push(parser.parse_field()?); + } + Ok(Lined { + line, + val: Function { + name: name.to_owned(), + in_fields, + out_fields, + }, + }) + })(); + res.with_context(|| format!("While parsing message starting at line {}", line)) + } + + fn parse_field(&mut self) -> Result { + let (line, name) = self.expect_ident()?; + let res: Result<_> = (|| { + self.expect_symbol(Symbol::Colon)?; + let ty = self.parse_type()?; + if !self.eof() { + self.expect_symbol(Symbol::Comma)?; + } + Ok(Field { + name: name.to_owned(), + ty, + }) + })(); + res.with_context(|| format!("While parsing field starting at line {}", line)) + } + + fn expect_ident(&mut self) -> Result<(u32, &'a BStr)> { + self.not_eof()?; + let token = &self.tokens[self.pos]; + self.pos += 1; + match &token.kind { + TokenKind::Ident(id) => Ok((token.line, *id)), + k => bail!( + "In line {}: Expected identifier, found {}", + token.line, + k.name() + ), + } + } + + fn expect_symbol(&mut self, symbol: Symbol) -> Result<()> { + self.not_eof()?; + let token = &self.tokens[self.pos]; + self.pos += 1; + match &token.kind { + TokenKind::Symbol(s) if *s == symbol => Ok(()), + k => bail!( + "In line {}: Expected {}, found {}", + token.line, + symbol.name(), + k.name() + ), + } + } + + fn expect_tree_(&mut self) -> Result<(u32, TreeDelim, &'a [Token<'a>])> { + self.not_eof()?; + let token = &self.tokens[self.pos]; + self.pos += 1; + match &token.kind { + TokenKind::Tree { delim, body } => Ok((token.line, *delim, body)), + k => bail!("In line {}: Expected tree, found {}", token.line, k.name()), + } + } + + fn expect_tree(&mut self, exp_delim: TreeDelim) -> Result<(u32, &'a [Token<'a>])> { + let (line, delim, tokens) = self.expect_tree_()?; + if delim == exp_delim { + Ok((line, tokens)) + } else { + bail!( + "In line {}: Expected {:?}-delimited tree, found {:?}-delimited tree", + line, + exp_delim, + delim.opening() + ) + } + } + + fn parse_type(&mut self) -> Result { + self.not_eof()?; + let (_, ty) = self.expect_ident()?; + let ty = match ty.as_bytes() { + b"u8" => Type::U8, + b"bool" => Type::Bool, + b"i16" => Type::I16, + b"u16" => Type::U16, + b"i32" => Type::I32, + b"u32" => Type::U32, + b"i64" => Type::I64, + b"u64" => Type::U64, + b"f64" => Type::F64, + b"string" => Type::String, + b"object_path" => Type::ObjectPath, + b"signature" => Type::Signature, + b"variant" => Type::Variant, + b"fd" => Type::Fd, + b"array" => { + let (line, body) = self.expect_tree(TreeDelim::Paren)?; + let mut parser = Parser { + pos: 0, + tokens: body, + }; + let ty: Result<_> = (|| { + let ty = parser.parse_type()?; + if !parser.eof() { + bail!("Trailing tokens in element type"); + } + Ok(ty) + })(); + let ty = + ty.with_context(|| format!("While parsing array starting in line {}", line))?; + Type::Array(Box::new(ty)) + } + b"dict" => { + let (line, body) = self.expect_tree(TreeDelim::Paren)?; + let mut parser = Parser { + pos: 0, + tokens: body, + }; + let ty: Result<_> = (|| { + let key = parser.parse_type()?; + parser.expect_symbol(Symbol::Comma)?; + let val = parser.parse_type()?; + Ok((key, val)) + })(); + let ty = + ty.with_context(|| format!("While parsing dict starting in line {}", line))?; + Type::DictEntry(Box::new(ty.0), Box::new(ty.1)) + } + b"struct" => { + let (line, body) = self.expect_tree(TreeDelim::Paren)?; + let mut parser = Parser { + pos: 0, + tokens: body, + }; + let mut fields = vec![]; + while !parser.eof() { + let ty: Result<_> = (|| { + let ty = parser.parse_type()?; + if !parser.eof() { + parser.expect_symbol(Symbol::Comma)?; + } + Ok(ty) + })(); + let ty = ty.with_context(|| { + format!("While parsing struct starting in line {}", line) + })?; + fields.push(ty); + } + Type::Struct(fields) + } + _ => bail!("Unknown type {}", ty), + }; + Ok(ty) + } +} + +fn parse_functions(s: &[u8]) -> Result> { + let tokens = tokenize(s)?; + let mut parser = Parser { + pos: 0, + tokens: &tokens, + }; + parser.parse() +} + +fn to_snake(s: &BStr) -> BString { + let mut last_was_lowercase = false; + let mut res = vec![]; + for mut b in s.as_bytes().iter().copied() { + if b.is_ascii_uppercase() { + if last_was_lowercase { + res.push(b'_'); + last_was_lowercase = false; + } + b = b.to_ascii_lowercase(); + } else { + last_was_lowercase = true; + } + res.push(b); + } + res.into() +} + +fn needs_lifetime(ty: &Type) -> bool { + match ty { + Type::U8 => false, + Type::Bool => false, + Type::I16 => false, + Type::U16 => false, + Type::I32 => false, + Type::U32 => false, + Type::I64 => false, + Type::U64 => false, + Type::F64 => false, + Type::String => true, + Type::ObjectPath => true, + Type::Signature => true, + Type::Variant => true, + Type::Fd => false, + Type::Array(_) => true, + Type::DictEntry(k, v) => needs_lifetime(k) || needs_lifetime(v), + Type::Struct(fs) => fs.iter().any(needs_lifetime), + } +} + +fn write_signature(f: &mut W, ty: &Type) -> Result<()> { + let c = match ty { + Type::U8 => 'y', + Type::Bool => 'b', + Type::I16 => 'n', + Type::U16 => 'q', + Type::I32 => 'i', + Type::U32 => 'u', + Type::I64 => 'x', + Type::U64 => 't', + Type::F64 => 'd', + Type::String => 's', + Type::ObjectPath => 'o', + Type::Signature => 'g', + Type::Variant => 'v', + Type::Fd => 'h', + Type::Array(e) => { + write!(f, "a")?; + write_signature(f, e)?; + return Ok(()); + } + Type::DictEntry(k, v) => { + write!(f, "{{")?; + write_signature(f, k)?; + write_signature(f, v)?; + write!(f, "}}")?; + return Ok(()); + } + Type::Struct(fs) => { + write!(f, "(")?; + for fs in fs { + write_signature(f, fs)?; + } + write!(f, ")")?; + return Ok(()); + } + }; + write!(f, "{}", c)?; + Ok(()) +} + +fn write_type(f: &mut W, ty: &Type) -> Result<()> { + let ty = match ty { + Type::U8 => "u8", + Type::Bool => "Bool", + Type::I16 => "i16", + Type::U16 => "u16", + Type::I32 => "i32", + Type::U32 => "u32", + 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::Fd => "Rc", + Type::Array(e) => { + write!(f, "Cow<'a, [")?; + write_type(f, &e)?; + write!(f, ">")?; + return Ok(()); + } + Type::DictEntry(k, v) => { + write!(f, "DictEntry<")?; + write_type(f, &k)?; + write!(f, ", ")?; + write_type(f, &v)?; + write!(f, ">")?; + return Ok(()); + } + Type::Struct(fs) => { + write!(f, "(")?; + for (idx, fs) in fs.iter().enumerate() { + if idx > 0 { + write!(f, ", ")?; + } + write_type(f, &fs)?; + } + write!(f, ")")?; + return Ok(()); + } + }; + write!(f, "{}", ty)?; + Ok(()) +} + +fn write_message( + f: &mut W, + el: &Element, + fun: &Function, + name: &str, + indent: &str, + fields: &[Field], + reply_name: Option<&str>, + reply_has_lt: bool, +) -> Result<()> { + let needs_lt = fields.iter().any(|f| needs_lifetime(&f.ty)); + let lt = if needs_lt { "<'a>" } else { "" }; + writeln!(f)?; + if fields.is_empty() { + writeln!(f, "{}pub struct {}{};", indent, name, lt)?; + } else { + writeln!(f, "{}pub struct {}{} {{", indent, name, lt)?; + for field in fields { + write!(f, "{} pub {}: ", indent, field.name)?; + write_type(f, &field.ty)?; + writeln!(f, ",")?; + } + writeln!(f, "{}}}", indent)?; + } + writeln!(f)?; + writeln!(f, "{}impl<'a> Message<'a> for {}{} {{", indent, name, lt)?; + write!(f, "{} const SIGNATURE: &'static str = \"", indent)?; + for field in fields { + write_signature(f, &field.ty)?; + } + writeln!(f, "\";")?; + writeln!( + f, + "{} const INTERFACE: &'static str = \"{}\";", + indent, el.interface + )?; + writeln!( + f, + "{} const MEMBER: &'static str = \"{}\";", + indent, fun.name + )?; + writeln!(f)?; + writeln!(f, "{} fn marshal(&self, fmt: &mut Formatter) {{", indent)?; + if fields.is_empty() { + writeln!(f, "{} let _ = fmt;", indent)?; + } + for field in fields { + writeln!(f, "{} fmt.marshal(&self.{});", indent, field.name)?; + } + writeln!(f, "{} }}", indent)?; + writeln!(f)?; + writeln!( + f, + "{} fn unmarshal(parser: &mut Parser<'a>) -> Result {{", + indent + )?; + if fields.is_empty() { + writeln!(f, "{} let _ = parser;", indent)?; + } + writeln!(f, "{} Ok(Self {{", indent)?; + for field in fields { + writeln!( + f, + "{} {}: parser.unmarshal()?,", + indent, field.name + )?; + } + writeln!(f, "{} }})", indent)?; + writeln!(f, "{} }}", indent)?; + writeln!(f)?; + writeln!(f, "{} fn num_fds(&self) -> u32 {{", indent)?; + if fields.is_empty() { + writeln!(f, "{} 0", indent)?; + } else { + writeln!(f, "{} let mut res = 0;", indent)?; + for field in fields { + writeln!(f, "{} res += self.{}.num_fds();", indent, field.name)?; + } + writeln!(f, "{} res", indent)?; + } + writeln!(f, "{} }}", indent)?; + writeln!(f, "{}}}", indent)?; + if let Some(rn) = reply_name { + let reply_lt = if reply_has_lt { "<'b>" } else { "" }; + writeln!(f)?; + writeln!(f, "{}impl<'a> MethodCall<'a> for {}{} {{", indent, name, lt)?; + writeln!(f, "{} type Reply<'b> = {}{};", 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, + )?; + } + Ok(()) +} + +fn write_module(f: &mut W, element: Element, indent: &str) -> Result<()> { + let mut children: Vec<_> = element.children.into_iter().map(|v| v.1).collect(); + children.sort_by(|c1, c2| c1.name.cmp(&c2.name)); + for child in children { + write_element(f, child, indent)?; + } + Ok(()) +} + +fn write_element(f: &mut W, element: Element, indent: &str) -> Result<()> { + writeln!(f)?; + writeln!(f, "{}pub mod {} {{", indent, element.name)?; + writeln!(f, "{} use crate::dbus::prelude::*;", indent)?; + { + let indent = format!("{} ", indent); + write_interface(f, &element, &indent)?; + write_module(f, element, &indent)?; + } + writeln!(f, "{}}}", indent)?; + Ok(()) +} + +struct Element { + name: BString, + interface: BString, + children: HashMap, + functions: Vec, +} + +fn collect_interfaces() -> Result { + let mut root = Element { + name: Default::default(), + interface: Default::default(), + children: Default::default(), + functions: vec![], + }; + let mut files = vec![]; + for file in std::fs::read_dir("wire-dbus")? { + files.push(file?); + } + for file in files { + 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 + .rsplitn_str(2, ".") + .skip(1) + .next() + .unwrap() + .as_bstr() + .to_owned(); + let mut components: Vec<_> = file_name.split_str(".").collect(); + components.pop(); + let functions = (|| { + let contents = std::fs::read(file.path())?; + parse_functions(&contents) + })(); + let mut functions = + functions.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), + }, + ); + } + } + } + Ok(root) +} + +pub fn main() -> Result<()> { + println!("cargo:rerun-if-changed=wire-dbus"); + + let mut f = open("wire_dbus.rs")?; + for (_, child) in collect_interfaces()?.children { + write_element(&mut f, child, "")?; + } + Ok(()) +} diff --git a/src/dbus.rs b/src/dbus.rs new file mode 100644 index 00000000..d3cf9dac --- /dev/null +++ b/src/dbus.rs @@ -0,0 +1,250 @@ +use crate::async_engine::{AsyncFd, SpawnedFuture}; +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 std::borrow::Cow; +use std::cell::{Cell, RefCell}; +use std::rc::Rc; +use std::task::Waker; +use thiserror::Error; +use uapi::OwnedFd; + +mod auth; +mod dynamic_type; +mod formatter; +mod holder; +mod incoming; +mod outgoing; +mod parser; +mod socket; +mod types; + +#[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("FD index is out of bounds")] + OobFds, + #[error("Variant has an invalid type")] + InvalidVariantType, + #[error("Could not create a socket")] + Socket(#[source] std::io::Error), + #[error("Could not connect")] + Connect(#[source] std::io::Error), + #[error("Could not write to the dbus socket")] + WriteError(#[source] std::io::Error), + #[error("Could not read from the dbus socket")] + ReadError(#[source] std::io::Error), + #[error("timeout")] + AsyncError(#[source] Box), + #[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("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("Could not read from the socket")] + ReadFailed(#[source] std::io::Error), + #[error(transparent)] + Rc(Rc), +} +efrom!(DbusError, AsyncError); + +pub struct Dbus { + eng: Rc, + system: Rc, +} + +impl Dbus { + pub fn new(eng: &Rc) -> Self { + Self { + eng: eng.clone(), + system: Default::default(), + } + } + + pub fn system(&self) -> Result, DbusError> { + self.system + .get(&self.eng, "/var/run/dbus/system_bus_socket") + } +} + +pub struct DbusSocket { + fd: AsyncFd, + eng: Rc, + next_serial: NumCell, + bufs: Stack>, + outgoing: AsyncQueue, + waiters: CopyHashMap, + replies: CopyHashMap, + incoming: Cell>>, + outgoing_: Cell>>, + auth: Cell>>, + dead: Cell, + headers: 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; +const HDR_ERROR_NAME: u8 = 4; +const HDR_REPLY_SERIAL: u8 = 5; +const HDR_DESTINATION: u8 = 6; +const HDR_SENDER: u8 = 7; +const HDR_SIGNATURE: u8 = 8; +const HDR_UNIX_FDS: u8 = 9; + +#[derive(Default, Debug)] +struct Headers<'a> { + path: Option>, + interface: Option>, + member: Option>, + error_name: Option>, + reply_serial: Option, + destination: Option>, + sender: Option>, + signature: Option>, + unix_fds: Option, +} + +struct DbusHolder { + socket: CloneCell>>, +} + +impl Drop for DbusHolder { + fn drop(&mut self) { + if let Some(socket) = self.socket.take() { + socket.auth.take(); + socket.outgoing_.take(); + socket.incoming.take(); + } + } +} + +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, + 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 Vec, +} + +pub trait Message<'a>: Sized { + const SIGNATURE: &'static str; + const INTERFACE: &'static str; + const MEMBER: &'static str; + + 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 unsafe trait DbusType<'a>: Clone { + const ALIGNMENT: usize; + const IS_POD: bool; + + 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::{ + types::{Bool, DictEntry, ObjectPath, Signature, Variant}, + DbusError, DbusType, Formatter, Message, MethodCall, Parser, + }; + pub use std::borrow::Cow; +} diff --git a/src/dbus/auth.rs b/src/dbus/auth.rs new file mode 100644 index 00000000..db3ad4ff --- /dev/null +++ b/src/dbus/auth.rs @@ -0,0 +1,112 @@ +use crate::dbus::incoming::handle_incoming; +use crate::dbus::outgoing::handle_outgoing; +use crate::dbus::{DbusError, DbusSocket}; +use crate::utils::hex; +use crate::ErrorFmt; +use std::io::Write; +use std::rc::Rc; +use uapi::{c, Errno}; + +pub(super) async fn handle_auth(socket: Rc) { + let mut auth = Auth { + socket: socket.clone(), + buf: Box::new([0; BUF_SIZE]), + buf_start: 0, + buf_stop: 0, + }; + auth.run().await; +} + +const BUF_SIZE: usize = 128; + +struct Auth { + socket: Rc, + + buf: Box<[u8; BUF_SIZE]>, + buf_start: usize, + buf_stop: usize, +} + +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(); + return; + } + log::info!("Authenticated"); + self.socket.incoming.set(Some( + self.socket.eng.spawn(handle_incoming(self.socket.clone())), + )); + self.socket.outgoing_.set(Some( + self.socket.eng.spawn(handle_outgoing(self.socket.clone())), + )); + self.socket.auth.take(); + } + + async fn handle_auth(&mut self) -> Result<(), DbusError> { + let uid = hex::to_hex(&uapi::getuid().to_string()); + let mut out_buf = Vec::new(); + let _ = write!(out_buf, "\0AUTH EXTERNAL {}\r\n", uid); + self.write_buf(&mut out_buf).await?; + let line = self.readline().await?; + let (cmd, _) = line_to_cmd(&line); + if cmd != "OK" { + return Err(DbusError::Auth); + } + let _ = write!(out_buf, "NEGOTIATE_UNIX_FD\r\n"); + self.write_buf(&mut out_buf).await?; + let line = self.readline().await?; + let (cmd, _) = line_to_cmd(&line); + if cmd != "AGREE_UNIX_FD" { + return Err(DbusError::UnixFd); + } + let _ = write!(out_buf, "BEGIN\r\n"); + self.write_buf(&mut out_buf).await?; + Ok(()) + } + + async fn readline(&mut self) -> Result { + let mut s = String::new(); + loop { + for i in self.buf_start..self.buf_stop { + let c = self.buf[i % BUF_SIZE] as char; + s.push(c); + if c == '\n' { + self.buf_start = i + 1; + return Ok(s); + } + } + self.buf_start = 0; + self.buf_stop = 0; + match uapi::read(self.socket.fd.raw(), &mut self.buf[..]) { + Ok(n) => self.buf_stop = n.len(), + Err(Errno(c::EAGAIN)) => { + let _ = self.socket.fd.readable().await; + } + Err(e) => return Err(DbusError::ReadError(e.into())), + } + } + } + + async fn write_buf(&mut self, buf: &mut Vec) -> Result<(), DbusError> { + let mut start = 0; + while start < buf.len() { + match uapi::write(self.socket.fd.raw(), &buf[start..]) { + Ok(n) => start += n, + Err(Errno(c::EAGAIN)) => { + let _ = self.socket.fd.writable().await; + } + Err(e) => return Err(DbusError::WriteError(e.into())), + } + } + buf.clear(); + Ok(()) + } +} + +fn line_to_cmd(line: &str) -> (&str, &str) { + let line = line.trim(); + line.split_once(' ').unwrap_or((line, "")) +} diff --git a/src/dbus/dynamic_type.rs b/src/dbus/dynamic_type.rs new file mode 100644 index 00000000..21aaedf3 --- /dev/null +++ b/src/dbus/dynamic_type.rs @@ -0,0 +1,182 @@ +use super::{ + 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, +}; +use crate::dbus::types::Variant; +use crate::dbus::{DbusError, DynamicType, Parser}; +use std::ops::Deref; + +impl DynamicType { + pub fn from_signature<'a>(mut s: &'a [u8]) -> Result<(DynamicType, &'a [u8]), DbusError> { + if s.is_empty() { + return Err(DbusError::EmptySignature); + } + let first = s[0]; + s = &s[1..]; + let dp = match first { + TY_BYTE => DynamicType::U8, + TY_BOOLEAN => DynamicType::Bool, + TY_INT16 => DynamicType::I16, + TY_UINT16 => DynamicType::U16, + TY_INT32 => DynamicType::I32, + TY_UINT32 => DynamicType::U32, + TY_INT64 => DynamicType::I64, + TY_UINT64 => DynamicType::U64, + TY_DOUBLE => DynamicType::F64, + TY_STRING => DynamicType::String, + TY_OBJECT_PATH => DynamicType::ObjectPath, + TY_SIGNATURE => DynamicType::Signature, + TY_VARIANT => DynamicType::Variant, + TY_UNIX_FD => DynamicType::Fd, + TY_ARRAY => { + let (elty, rem) = Self::from_signature(s)?; + s = rem; + DynamicType::Array(Box::new(elty)) + } + b'{' => { + let (keyty, rem) = Self::from_signature(s)?; + let (valty, rem) = Self::from_signature(rem)?; + if rem.is_empty() { + return Err(DbusError::UnterminatedDict); + } + if rem[0] != b'}' { + return Err(DbusError::DictTrailing); + } + s = &rem[1..]; + DynamicType::DictEntry(Box::new(keyty), Box::new(valty)) + } + b'(' => { + let mut fields = vec![]; + loop { + if s.is_empty() { + return Err(DbusError::UnterminatedStruct); + } + if s[0] == b')' { + s = &s[1..]; + break DynamicType::Struct(fields); + } + let (fieldty, rem) = Self::from_signature(s)?; + s = rem; + fields.push(fieldty); + } + } + _ => return Err(DbusError::UnknownType), + }; + Ok((dp, s)) + } + + pub fn alignment(&self) -> usize { + match self { + DynamicType::U8 => 1, + DynamicType::Bool => 4, + DynamicType::I16 => 2, + DynamicType::U16 => 2, + DynamicType::I32 => 4, + DynamicType::U32 => 4, + DynamicType::I64 => 8, + DynamicType::U64 => 8, + DynamicType::F64 => 8, + DynamicType::String => 4, + DynamicType::ObjectPath => 4, + DynamicType::Signature => 1, + DynamicType::Variant => 1, + DynamicType::Array(_) => 4, + DynamicType::DictEntry(_, _) => 8, + DynamicType::Struct(_) => 8, + DynamicType::Fd => 4, + } + } + + pub fn write_signature(&self, w: &mut Vec) { + let c = match self { + DynamicType::U8 => TY_BYTE, + DynamicType::Bool => TY_BOOLEAN, + DynamicType::I16 => TY_INT16, + DynamicType::U16 => TY_UINT16, + DynamicType::I32 => TY_INT32, + DynamicType::U32 => TY_UINT32, + DynamicType::I64 => TY_INT64, + DynamicType::U64 => TY_UINT64, + DynamicType::F64 => TY_DOUBLE, + DynamicType::String => TY_STRING, + DynamicType::ObjectPath => TY_OBJECT_PATH, + DynamicType::Signature => TY_SIGNATURE, + DynamicType::Variant => TY_VARIANT, + DynamicType::Fd => TY_UNIX_FD, + DynamicType::Array(el) => { + w.push(TY_ARRAY); + el.write_signature(w); + return; + } + DynamicType::DictEntry(k, v) => { + w.push(b'{'); + k.write_signature(w); + v.write_signature(w); + w.push(b'}'); + return; + } + DynamicType::Struct(f) => { + w.push(b'('); + for f in f { + f.write_signature(w); + } + w.push(b')'); + return; + } + }; + w.push(c); + } + + pub fn parse<'a>(&self, parser: &mut Parser<'a>) -> Result, DbusError> { + let var = match self { + DynamicType::U8 => Variant::U8(parser.read_pod()?), + DynamicType::Bool => Variant::Bool(parser.read_bool()?), + DynamicType::I16 => Variant::I16(parser.read_pod()?), + DynamicType::U16 => Variant::U16(parser.read_pod()?), + DynamicType::I32 => Variant::I32(parser.read_pod()?), + DynamicType::U32 => Variant::U32(parser.read_pod()?), + DynamicType::I64 => Variant::I64(parser.read_pod()?), + DynamicType::U64 => Variant::U64(parser.read_pod()?), + DynamicType::F64 => Variant::F64(parser.read_pod()?), + DynamicType::String => Variant::String(parser.read_string()?), + DynamicType::ObjectPath => Variant::ObjectPath(parser.read_object_path()?), + DynamicType::Signature => Variant::Signature(parser.read_signature()?), + DynamicType::Variant => Variant::Variant(Box::new(parser.read_variant()?)), + DynamicType::Fd => Variant::Fd(parser.read_fd()?), + DynamicType::Array(el) => { + let len: u32 = parser.read_pod()?; + parser.align_to(el.alignment()); + let len = len as usize; + if parser.buf.len() - parser.pos < len { + return Err(DbusError::UnexpectedEof); + } + let mut vals = vec![]; + { + let mut parser = Parser { + buf: &parser.buf[..parser.pos + len], + pos: parser.pos, + fds: parser.fds, + }; + while !parser.eof() { + vals.push(el.parse(&mut parser)?); + } + } + parser.pos += len; + Variant::Array(el.deref().clone(), vals) + } + DynamicType::DictEntry(k, v) => { + 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); + for field in fields { + vals.push(field.parse(parser)?); + } + Variant::Struct(vals) + } + }; + Ok(var) + } +} diff --git a/src/dbus/formatter.rs b/src/dbus/formatter.rs new file mode 100644 index 00000000..2e192d8e --- /dev/null +++ b/src/dbus/formatter.rs @@ -0,0 +1,110 @@ +use crate::dbus::types::Variant; +use crate::dbus::{DbusType, Formatter}; +use std::rc::Rc; +use uapi::{OwnedFd, Packed}; + +impl<'a> Formatter<'a> { + pub fn new(fds: &'a mut Vec>, buf: &'a mut Vec) -> Self { + Self { fds, buf } + } + + pub fn marshal<'b, T: DbusType<'b>>(&mut self, t: &T) { + t.marshal(self) + } + + pub fn pad_to(&mut self, alignment: usize) { + static BUF: [u8; 8] = [0; 8]; + let len = self.buf.len().wrapping_neg() & (alignment - 1); + self.buf.extend_from_slice(&BUF[..len]); + } + + pub fn len(&self) -> usize { + self.buf.len() + } + + pub fn write_packed<'b, T: DbusType<'b> + Packed>(&mut self, t: &T) { + self.pad_to(T::ALIGNMENT); + self.buf.extend_from_slice(uapi::as_bytes(t)); + } + + pub fn write_str(&mut self, s: &str) { + self.write_packed(&(s.len() as u32)); + self.buf.extend_from_slice(s.as_bytes()); + self.buf.push(0); + } + + pub fn write_signature(&mut self, s: &[u8]) { + self.write_packed(&(s.len() as u8)); + self.buf.extend_from_slice(s); + self.buf.push(0); + } + + pub fn write_array<'b, T: DbusType<'b>>(&mut self, a: &[T]) { + self.pad_to(4); + let len_pos = self.buf.len(); + self.write_packed(&0u32); + self.pad_to(T::ALIGNMENT); + let start = self.buf.len(); + for v in a { + v.marshal(self); + } + let len = (self.buf.len() - start) as u32; + self.buf[len_pos..len_pos + 4].copy_from_slice(uapi::as_bytes(&len)); + } + + pub fn write_fd(&mut self, fd: &Rc) { + self.write_packed(&(self.fds.len() as u32)); + self.fds.push(fd.clone()); + } + + pub fn write_variant(&mut self, variant: &Variant) { + let pos = self.buf.len(); + self.buf.push(0); + variant.write_signature(&mut self.buf); + self.buf.push(0); + self.buf[pos] = (self.buf.len() - pos - 2) as u8; + self.write_variant_body(variant); + } + + pub fn write_variant_body(&mut self, variant: &Variant) { + match variant { + Variant::U8(v) => v.marshal(self), + Variant::Bool(v) => v.marshal(self), + Variant::I16(v) => v.marshal(self), + Variant::U16(v) => v.marshal(self), + Variant::I32(v) => v.marshal(self), + Variant::U32(v) => v.marshal(self), + Variant::I64(v) => v.marshal(self), + Variant::U64(v) => v.marshal(self), + Variant::F64(v) => v.marshal(self), + Variant::String(v) => v.marshal(self), + Variant::ObjectPath(v) => v.marshal(self), + Variant::Signature(v) => v.marshal(self), + Variant::Variant(v) => v.marshal(self), + Variant::Fd(f) => f.marshal(self), + Variant::Array(el, v) => { + self.pad_to(4); + let len_pos = self.buf.len(); + self.write_packed(&0u32); + self.pad_to(el.alignment()); + let start = self.buf.len(); + for v in v { + self.write_variant_body(v); + } + let len = (self.buf.len() - start) as u32; + self.buf[len_pos..len_pos + 4].copy_from_slice(uapi::as_bytes(&len)); + } + Variant::DictEntry(k, v) => { + self.pad_to(8); + self.write_variant_body(k); + self.write_variant_body(v); + } + Variant::Struct(f) => { + self.pad_to(8); + for v in f { + self.write_variant_body(v); + } + } + } + } +} diff --git a/src/dbus/holder.rs b/src/dbus/holder.rs new file mode 100644 index 00000000..d1bf8cc5 --- /dev/null +++ b/src/dbus/holder.rs @@ -0,0 +1,60 @@ +use crate::dbus::auth::handle_auth; +use crate::dbus::{DbusError, DbusHolder, DbusSocket}; +use crate::{AsyncEngine, NumCell}; +use std::cell::Cell; +use std::rc::Rc; +use uapi::c; + +impl DbusHolder { + pub(super) fn get( + self: &Rc, + eng: &Rc, + addr: &str, + ) -> Result, DbusError> { + if let Some(c) = self.socket.get() { + if c.dead.get() { + self.socket.take(); + } else { + return Ok(c); + } + } + let socket = connect(eng, addr)?; + self.socket.set(Some(socket.clone())); + Ok(socket) + } +} + +fn connect(eng: &Rc, addr: &str) -> Result, DbusError> { + let socket = match uapi::socket( + c::AF_UNIX, + c::SOCK_STREAM | c::SOCK_NONBLOCK | c::SOCK_CLOEXEC, + 0, + ) { + Ok(s) => s, + Err(e) => return Err(DbusError::Socket(e.into())), + }; + let mut sadr: c::sockaddr_un = uapi::pod_zeroed(); + sadr.sun_family = c::AF_UNIX as _; + let sun_path = uapi::as_bytes_mut(&mut sadr.sun_path[..]); + sun_path[..addr.len()].copy_from_slice(addr.as_bytes()); + if let Err(e) = uapi::connect(socket.raw(), &sadr) { + return Err(DbusError::Connect(e.into())); + } + let socket = Rc::new(DbusSocket { + fd: eng.fd(&Rc::new(socket))?, + eng: eng.clone(), + next_serial: NumCell::new(1), + bufs: Default::default(), + outgoing: Default::default(), + waiters: Default::default(), + replies: Default::default(), + incoming: Default::default(), + outgoing_: Default::default(), + auth: Default::default(), + dead: Cell::new(false), + headers: Default::default(), + }); + 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 new file mode 100644 index 00000000..c8029967 --- /dev/null +++ b/src/dbus/incoming.rs @@ -0,0 +1,173 @@ +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::ErrorFmt; +use std::collections::VecDeque; +use std::mem::MaybeUninit; +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, + fds: Default::default(), + cmsg: Box::new([MaybeUninit::uninit(); 256]), + }; + incoming.run().await; +} + +pub struct Incoming { + socket: Rc, + + msg_buf: Vec, + buf: Box<[MaybeUninit; 4096]>, + buf_start: usize, + buf_end: usize, + fds: VecDeque>, + cmsg: Box<[MaybeUninit; 256]>, +} + +impl Incoming { + async fn run(&mut self) { + loop { + 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(); + return; + } + } + } + + async fn handle_msg(&mut self) -> Result<(), DbusError> { + self.msg_buf.clear(); + const FIXED_HEADER_SIZE: usize = 16; + self.fill_msg_buf(FIXED_HEADER_SIZE).await?; + let endianess = self.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]; + if protocol != 1 { + return Err(DbusError::InvalidEndianess); + } + let mut fields2 = [0u32; 3]; + uapi::pod_write(&self.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]; + let headers = self.parse_headers(headers)?; + let unix_fds = headers.unix_fds.unwrap_or(0) as usize; + if self.fds.len() < unix_fds { + return Err(DbusError::TooFewFds); + } + let fds: Vec<_> = self.fds.drain(..unix_fds).collect(); + let mut parser = Parser { + buf: &self.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); + } + } + Ok(()) + } + + fn parse_headers<'a>(&self, buf: &'a [u8]) -> Result, DbusError> { + let mut parser = Parser::new(buf, &[]); + let mut headers = Headers::default(); + while !parser.eof() { + parser.align_to(8)?; + let ty: u8 = parser.read_pod()?; + let val = parser.read_variant()?; + match ty { + HDR_PATH => headers.path = Some(val.into_object_path()?), + HDR_INTERFACE => headers.interface = Some(val.into_string()?), + HDR_MEMBER => headers.member = Some(val.into_string()?), + HDR_ERROR_NAME => headers.error_name = Some(val.into_string()?), + HDR_REPLY_SERIAL => headers.reply_serial = Some(val.into_u32()?), + HDR_DESTINATION => headers.destination = Some(val.into_string()?), + HDR_SENDER => headers.sender = Some(val.into_string()?), + HDR_SIGNATURE => headers.signature = Some(val.into_signature()?), + HDR_UNIX_FDS => headers.unix_fds = Some(val.into_u32()?), + _ => {} + } + } + Ok(headers) + } + + async fn fill_msg_buf(&mut self, mut n: usize) -> Result<(), DbusError> { + while n > 0 { + if self.buf_start == self.buf_end { + while let Err(e) = self.recvmsg() { + if e.0 != c::EAGAIN { + return Err(DbusError::ReadError(e.into())); + } + let _ = self.socket.fd.readable().await; + } + if self.buf_start == self.buf_end { + return Err(DbusError::Closed); + } + } + 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(), + ); + } + } + n -= read; + self.buf_start += read; + } + Ok(()) + } + + fn recvmsg(&mut self) -> Result<(), Errno> { + self.buf_start = 0; + self.buf_end = 0; + let mut iov = [&mut self.buf[..]]; + let mut hdr = MsghdrMut { + iov: &mut iov[..], + control: Some(&mut self.cmsg[..]), + name: uapi::sockaddr_none_mut(), + flags: 0, + }; + let (ivec, _, mut cmsg) = + uapi::recvmsg(self.socket.fd.raw(), &mut hdr, c::MSG_CMSG_CLOEXEC)?; + self.buf_end += ivec.len(); + while cmsg.len() > 0 { + let (_, hdr, body) = uapi::cmsg_read(&mut cmsg)?; + if hdr.cmsg_level == c::SOL_SOCKET && hdr.cmsg_type == c::SCM_RIGHTS { + for fd in uapi::pod_iter(body)? { + self.fds.push_back(Rc::new(OwnedFd::new(fd))); + } + } + } + Ok(()) + } +} diff --git a/src/dbus/outgoing.rs b/src/dbus/outgoing.rs new file mode 100644 index 00000000..25e18f78 --- /dev/null +++ b/src/dbus/outgoing.rs @@ -0,0 +1,102 @@ +use crate::dbus::{DbusMessage, DbusSocket}; +use crate::utils::vec_ext::VecExt; +use crate::utils::vecstorage::VecStorage; +use std::collections::VecDeque; +use std::mem; +use std::ptr::NonNull; +use std::rc::Rc; +use uapi::{c, Errno, Msghdr}; + +pub async fn handle_outgoing(socket: Rc) { + let mut outgoing = Outgoing { + socket, + msgs: Default::default(), + cmsg: vec![], + fds: vec![], + iovecs: Default::default(), + }; + outgoing.run().await +} + +struct DbusMessageOffset { + msg: DbusMessage, + offset: usize, +} + +struct Outgoing { + socket: Rc, + + msgs: VecDeque, + cmsg: Vec, + fds: Vec, + iovecs: VecStorage>, +} + +impl Outgoing { + async fn run(&mut self) { + loop { + self.socket.outgoing.non_empty().await; + while let Err(e) = self.try_flush() { + if e != Errno(c::EAGAIN) {} + let _ = self.socket.fd.writable().await; + } + } + } + + fn try_flush(&mut self) -> Result<(), Errno> { + loop { + while let Some(msg) = self.socket.outgoing.try_pop() { + self.msgs.push_back(DbusMessageOffset { msg, offset: 0 }); + } + if self.msgs.is_empty() { + return Ok(()); + } + let mut iovecs = self.iovecs.take_as(); + let mut fds = &[][..]; + for msg in &mut self.msgs { + if msg.msg.fds.len() > 0 { + if fds.len() > 0 || iovecs.len() > 0 { + break; + } + fds = &msg.msg.fds; + } + iovecs.push(&msg.msg.buf[msg.offset..]); + } + self.cmsg.clear(); + if fds.len() > 0 { + self.fds.clear(); + 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 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); + } + } + let msg = Msghdr { + iov: &iovecs[..], + control: Some(&self.cmsg[..]), + name: uapi::sockaddr_none_ref(), + }; + let mut n = uapi::sendmsg(self.socket.fd.raw(), &msg, c::MSG_DONTWAIT)?; + drop(iovecs); + self.msgs[0].msg.fds.clear(); + while n > 0 { + let len = self.msgs[0].msg.buf.len() - self.msgs[0].offset; + if n < len { + self.msgs[0].offset += n; + break; + } + n -= len; + let msg = self.msgs.pop_front().unwrap(); + self.socket.bufs.push(msg.msg.buf); + } + } + } +} diff --git a/src/dbus/parser.rs b/src/dbus/parser.rs new file mode 100644 index 00000000..1be279a6 --- /dev/null +++ b/src/dbus/parser.rs @@ -0,0 +1,131 @@ +use crate::dbus::types::{Bool, ObjectPath, Signature, Variant, FALSE, TRUE}; +use crate::dbus::{DbusError, DbusType, DynamicType, Parser}; +use bstr::ByteSlice; +use std::borrow::Cow; +use std::mem; +use std::rc::Rc; +use uapi::{OwnedFd, Pod}; + +impl<'a> Parser<'a> { + pub fn new(buf: &'a [u8], fds: &'a [Rc]) -> Self { + Self { buf, pos: 0, fds } + } + + pub fn eof(&self) -> bool { + self.pos == self.buf.len() + } + + pub fn unmarshal>(&mut self) -> Result { + T::unmarshal(self) + } + + pub fn align_to(&mut self, n: usize) -> Result<(), DbusError> { + let new = self.pos + (self.pos.wrapping_neg() & (n - 1)); + if new > self.buf.len() { + return Err(DbusError::UnexpectedEof); + } + self.pos = new; + Ok(()) + } + + pub fn read_fd(&mut self) -> Result, DbusError> { + let idx: u32 = self.read_pod()?; + let idx = idx as usize; + if idx >= self.fds.len() { + return Err(DbusError::OobFds); + } + Ok(self.fds[idx].clone()) + } + + pub fn read_pod<'b, T: DbusType<'b> + Pod>(&mut self) -> Result { + self.align_to(T::ALIGNMENT); + match uapi::pod_read_init(&self.buf[self.pos..]) { + Ok(v) => { + self.pos += mem::size_of::(); + Ok(v) + } + _ => Err(DbusError::UnexpectedEof), + } + } + + pub fn read_bool(&mut self) -> Result { + let v: u32 = self.read_pod()?; + match v { + 0 => Ok(FALSE), + 1 => Ok(TRUE), + _ => Err(DbusError::InvalidBoolValue), + } + } + + pub fn read_object_path(&mut self) -> Result, DbusError> { + self.read_string().map(ObjectPath) + } + + pub fn read_string(&mut self) -> Result, DbusError> { + let len: u32 = self.read_pod()?; + let s = self.read_string_(len as usize)?; + Ok(Cow::Borrowed(s)) + } + + pub fn read_signature(&mut self) -> Result, DbusError> { + let len: u8 = self.read_pod()?; + let s = self.read_string_(len as usize)?; + Ok(Signature(Cow::Borrowed(s))) + } + + fn read_string_(&mut self, len: usize) -> Result<&'a str, DbusError> { + if self.buf.len() - self.pos < len + 1 { + return Err(DbusError::UnexpectedEof); + } + let s = &self.buf[self.pos..self.pos + len]; + self.pos += len + 1; + match s.to_str() { + Ok(s) => Ok(s), + _ => Err(DbusError::InvalidUtf8), + } + } + + pub fn read_array>(&mut self) -> Result, DbusError> { + let len: u32 = self.read_pod()?; + let len = len as usize; + self.align_to(T::ALIGNMENT)?; + if self.buf.len() - self.pos < len { + return Err(DbusError::UnexpectedEof); + } + if T::IS_POD { + if len % mem::size_of::() != 0 { + return Err(DbusError::PodArrayLength); + } + let slice = unsafe { + std::slice::from_raw_parts( + self.buf[self.pos..].as_ptr() as *const T, + len / mem::size_of::(), + ) + }; + self.pos += len; + Ok(Cow::Borrowed(slice)) + } else { + let mut parser = Parser { + buf: &self.buf[..self.pos + len], + pos: self.pos, + fds: self.fds, + }; + self.pos += len; + let mut res = vec![]; + while !parser.eof() { + res.push(T::unmarshal(&mut parser)?); + } + Ok(Cow::Owned(res)) + } + } + + pub fn read_variant(&mut self) -> Result, DbusError> { + let sig = self.read_signature()?; + let sig = sig.0.as_bytes(); + let (parser, rem) = DynamicType::from_signature(sig)?; + if rem.len() > 0 { + return Err(DbusError::TrailingVariantSignature); + } + parser.parse(self) + } +} diff --git a/src/dbus/socket.rs b/src/dbus/socket.rs new file mode 100644 index 00000000..1bca6798 --- /dev/null +++ b/src/dbus/socket.rs @@ -0,0 +1,93 @@ +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, +}; + +const MESSAGE_CALL: u8 = 1; +const MESSAGE_RETURN: u8 = 2; +const ERROR: u8 = 3; +const SIGNAL: u8 = 4; + +impl DbusSocket { + pub fn new() -> Self { + todo!(); + } + + pub fn call_noreply<'a, T: MethodCall<'a>>(&self, destination: &str, path: &str, msg: T) { + let (msg, _) = self.format_call(path, Some(destination), &msg); + self.outgoing.push(msg); + } + + fn format_call<'a, T: Message<'a>>( + &self, + path: &str, + destination: Option<&str>, + 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 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, + serial, + path, + T::INTERFACE, + T::MEMBER, + destination, + T::SIGNATURE, + num_fds, + ); + let body_start = fmt.len(); + msg.marshal(&mut fmt); + let body_len = (buf.len() - body_start) as u32; + buf[4..8].copy_from_slice(uapi::as_bytes(&body_len)); + (DbusMessage { fds, buf }, serial) + } + + fn format_header( + &self, + fmt: &mut Formatter, + ty: u8, + serial: u32, + path: &str, + interface: &str, + member: &str, + destination: Option<&str>, + signature: &str, + fds: u32, + ) { + #[cfg(target_endian = "little")] + b'l'.marshal(fmt); + #[cfg(not(target_endian = "little"))] + b'b'.marshal(fmt); + ty.marshal(fmt); + 0u8.marshal(fmt); + 1u8.marshal(fmt); + 0u32.marshal(fmt); + serial.marshal(fmt); + let mut headers = self.headers.borrow_mut(); + let mut headers = headers.take_as::<(u8, Variant)>(); + 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()))); + } + if signature.len() > 0 { + headers.push(( + HDR_SIGNATURE, + Variant::Signature(Signature(signature.into())), + )); + } + if fds > 0 { + headers.push((HDR_UNIX_FDS, Variant::U32(fds))); + } + fmt.write_array(&headers); + fmt.pad_to(8); + } +} diff --git a/src/dbus/types.rs b/src/dbus/types.rs new file mode 100644 index 00000000..fa166b40 --- /dev/null +++ b/src/dbus/types.rs @@ -0,0 +1,466 @@ +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, +}; +use crate::utils::aligned::{AlignedF64, AlignedI64, AlignedU64}; +use std::borrow::Cow; +use std::ops::Deref; +use std::rc::Rc; +use uapi::{OwnedFd, Packed, Pod}; + +unsafe impl<'a> DbusType<'a> for u8 { + const ALIGNMENT: usize = 1; + const IS_POD: bool = true; + + fn write_signature(w: &mut Vec) { + w.push(TY_BYTE); + } + + fn marshal(&self, fmt: &mut Formatter) { + fmt.write_packed(self); + } + + fn unmarshal(parser: &mut Parser<'a>) -> Result { + parser.read_pod() + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[repr(transparent)] +pub struct Bool(u32); +pub const FALSE: Bool = Bool(0); +pub const TRUE: Bool = Bool(1); + +unsafe impl Pod for Bool {} +unsafe impl Packed for Bool {} + +unsafe impl<'a> DbusType<'a> for Bool { + const ALIGNMENT: usize = 4; + const IS_POD: bool = true; + + fn write_signature(w: &mut Vec) { + w.push(TY_BOOLEAN); + } + + fn marshal(&self, fmt: &mut Formatter) { + fmt.write_packed(self); + } + + fn unmarshal(parser: &mut Parser<'a>) -> Result { + parser.read_bool() + } +} + +unsafe impl<'a> DbusType<'a> for i16 { + const ALIGNMENT: usize = 2; + const IS_POD: bool = true; + + fn write_signature(w: &mut Vec) { + w.push(TY_INT16); + } + + fn marshal(&self, fmt: &mut Formatter) { + fmt.write_packed(self); + } + + fn unmarshal(parser: &mut Parser<'a>) -> Result { + parser.read_pod() + } +} + +unsafe impl<'a> DbusType<'a> for u16 { + const ALIGNMENT: usize = 2; + const IS_POD: bool = true; + + fn write_signature(w: &mut Vec) { + w.push(TY_UINT16) + } + + fn marshal(&self, fmt: &mut Formatter) { + fmt.write_packed(self); + } + + fn unmarshal(parser: &mut Parser<'a>) -> Result { + parser.read_pod() + } +} + +unsafe impl<'a> DbusType<'a> for i32 { + const ALIGNMENT: usize = 4; + const IS_POD: bool = true; + + fn write_signature(w: &mut Vec) { + w.push(TY_INT32) + } + + fn marshal(&self, fmt: &mut Formatter) { + fmt.write_packed(self); + } + + fn unmarshal(parser: &mut Parser<'a>) -> Result { + parser.read_pod() + } +} + +unsafe impl<'a> DbusType<'a> for u32 { + const ALIGNMENT: usize = 4; + const IS_POD: bool = true; + + fn write_signature(w: &mut Vec) { + w.push(TY_UINT32) + } + + fn marshal(&self, fmt: &mut Formatter) { + fmt.write_packed(self); + } + + fn unmarshal(parser: &mut Parser<'a>) -> Result { + parser.read_pod() + } +} + +unsafe impl<'a> DbusType<'a> for AlignedI64 { + const ALIGNMENT: usize = 8; + const IS_POD: bool = true; + + fn write_signature(w: &mut Vec) { + w.push(TY_INT64) + } + + fn marshal(&self, fmt: &mut Formatter) { + fmt.write_packed(self); + } + + fn unmarshal(parser: &mut Parser<'a>) -> Result { + parser.read_pod() + } +} + +unsafe impl<'a> DbusType<'a> for AlignedU64 { + const ALIGNMENT: usize = 8; + const IS_POD: bool = true; + + fn write_signature(w: &mut Vec) { + w.push(TY_UINT64) + } + + fn marshal(&self, fmt: &mut Formatter) { + fmt.write_packed(self); + } + + fn unmarshal(parser: &mut Parser<'a>) -> Result { + parser.read_pod() + } +} + +unsafe impl<'a> DbusType<'a> for AlignedF64 { + const ALIGNMENT: usize = 8; + const IS_POD: bool = true; + + fn write_signature(w: &mut Vec) { + w.push(TY_DOUBLE) + } + + fn marshal(&self, fmt: &mut Formatter) { + fmt.write_packed(self); + } + + fn unmarshal(parser: &mut Parser<'a>) -> Result { + parser.read_pod() + } +} + +unsafe impl<'a> DbusType<'a> for Cow<'a, str> { + const ALIGNMENT: usize = 4; + const IS_POD: bool = false; + + fn write_signature(w: &mut Vec) { + w.push(TY_STRING) + } + + fn marshal(&self, fmt: &mut Formatter) { + fmt.write_str(self); + } + + fn unmarshal(parser: &mut Parser<'a>) -> Result { + parser.read_string() + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Signature<'a>(pub Cow<'a, str>); + +unsafe impl<'a> DbusType<'a> for Signature<'a> { + const ALIGNMENT: usize = 1; + const IS_POD: bool = false; + + fn write_signature(w: &mut Vec) { + w.push(TY_SIGNATURE) + } + + fn marshal(&self, fmt: &mut Formatter) { + fmt.write_signature(self.0.as_bytes()); + } + + fn unmarshal(parser: &mut Parser<'a>) -> Result { + parser.read_signature() + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ObjectPath<'a>(pub Cow<'a, str>); + +unsafe impl<'a> DbusType<'a> for ObjectPath<'a> { + const ALIGNMENT: usize = 4; + const IS_POD: bool = false; + + fn write_signature(w: &mut Vec) { + w.push(TY_OBJECT_PATH) + } + + fn marshal(&self, fmt: &mut Formatter) { + fmt.write_str(&self.0); + } + + fn unmarshal(parser: &mut Parser<'a>) -> Result { + parser.read_object_path() + } +} + +unsafe impl<'a, T: DbusType<'a>> DbusType<'a> for Cow<'a, [T]> { + const ALIGNMENT: usize = 4; + const IS_POD: bool = false; + + fn write_signature(w: &mut Vec) { + w.push(TY_ARRAY); + T::write_signature(w); + } + + fn marshal(&self, fmt: &mut Formatter) { + fmt.write_array(self); + } + + fn unmarshal(parser: &mut Parser<'a>) -> Result { + parser.read_array() + } + + fn num_fds(&self) -> u32 { + let mut res = 0; + for t in self.deref() { + res += t.num_fds(); + } + res + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DictEntry { + pub key: K, + pub value: V, +} + +unsafe impl<'a, K: DbusType<'a>, V: DbusType<'a>> DbusType<'a> for DictEntry { + const ALIGNMENT: usize = 8; + const IS_POD: bool = false; + + fn write_signature(w: &mut Vec) { + w.push(b'{'); + K::write_signature(w); + V::write_signature(w); + w.push(b'}'); + } + + fn marshal(&self, fmt: &mut Formatter) { + fmt.pad_to(8); + self.key.marshal(fmt); + self.value.marshal(fmt); + } + + fn unmarshal(parser: &mut Parser<'a>) -> Result { + parser.align_to(8)?; + Ok(Self { + key: K::unmarshal(parser)?, + value: V::unmarshal(parser)?, + }) + } +} + +macro_rules! tuple { + ($($p:ident),*) => { + #[allow(non_snake_case)] + unsafe impl<'a, $($p: DbusType<'a>),*> DbusType<'a> for ($($p,)*) { + const ALIGNMENT: usize = 8; + const IS_POD: bool = false; + + fn write_signature(w: &mut Vec) { + w.push(b'('); + $( + $p::write_signature(w); + )* + w.push(b')'); + } + + fn marshal(&self, fmt: &mut Formatter) { + let ($($p,)*) = self; + fmt.pad_to(8); + $( + $p.marshal(fmt); + )* + } + + fn unmarshal(parser: &mut Parser<'a>) -> Result { + parser.align_to(8)?; + Ok(($($p::unmarshal(parser)?,)*)) + } + + fn num_fds(&self) -> u32 { + let mut res = 0; + let ($($p,)*) = self; + $( + res += $p.num_fds(); + )* + res + } + } + } +} + +tuple!(A); +tuple!(A, B); +tuple!(A, B, C); +tuple!(A, B, C, D); +tuple!(A, B, C, D, E); +tuple!(A, B, C, D, E, F); +tuple!(A, B, C, D, E, F, G); +tuple!(A, B, C, D, E, F, G, H); + +#[derive(Clone, Debug)] +pub enum Variant<'a> { + U8(u8), + Bool(Bool), + I16(i16), + U16(u16), + I32(i32), + U32(u32), + I64(AlignedI64), + U64(AlignedU64), + F64(AlignedF64), + String(Cow<'a, str>), + ObjectPath(ObjectPath<'a>), + Signature(Signature<'a>), + Variant(Box>), + Fd(Rc), + Array(DynamicType, Vec>), + DictEntry(Box>, Box>), + Struct(Vec>), +} + +impl<'a> Variant<'a> { + pub fn into_string(self) -> Result, DbusError> { + match self { + Variant::String(s) => Ok(s), + _ => Err(DbusError::InvalidVariantType), + } + } + + pub fn into_object_path(self) -> Result, DbusError> { + match self { + Variant::ObjectPath(s) => Ok(s), + _ => Err(DbusError::InvalidVariantType), + } + } + + pub fn into_signature(self) -> Result, DbusError> { + match self { + Variant::Signature(s) => Ok(s), + _ => Err(DbusError::InvalidVariantType), + } + } + + pub fn into_u32(self) -> Result { + match self { + Variant::U32(s) => Ok(s), + _ => Err(DbusError::InvalidVariantType), + } + } + + pub fn write_signature(&self, w: &mut Vec) { + let c = match self { + Variant::U8(..) => TY_BYTE, + Variant::Bool(..) => TY_BOOLEAN, + Variant::I16(..) => TY_INT16, + Variant::U16(..) => TY_UINT16, + Variant::I32(..) => TY_INT32, + Variant::U32(..) => TY_UINT32, + Variant::I64(..) => TY_INT64, + Variant::U64(..) => TY_UINT64, + Variant::F64(..) => TY_DOUBLE, + Variant::String(..) => TY_STRING, + Variant::ObjectPath(..) => TY_OBJECT_PATH, + Variant::Signature(..) => TY_SIGNATURE, + Variant::Variant(..) => TY_VARIANT, + Variant::Fd(..) => TY_UNIX_FD, + Variant::Array(el, _) => { + w.push(TY_ARRAY); + el.write_signature(w); + return; + } + Variant::DictEntry(k, v) => { + w.push(b'{'); + k.write_signature(w); + v.write_signature(w); + w.push(b'}'); + return; + } + Variant::Struct(f) => { + w.push(b'('); + for f in f { + f.write_signature(w); + } + w.push(b')'); + return; + } + }; + w.push(c); + } +} + +unsafe impl<'a> DbusType<'a> for Variant<'a> { + const ALIGNMENT: usize = 1; + const IS_POD: bool = false; + + fn write_signature(w: &mut Vec) { + w.push(TY_VARIANT); + } + + fn marshal(&self, fmt: &mut Formatter) { + fmt.write_variant(self); + } + + fn unmarshal(parser: &mut Parser<'a>) -> Result { + parser.read_variant() + } + + fn num_fds(&self) -> u32 { + match self { + Variant::U8(_) => 0, + Variant::Bool(_) => 0, + Variant::I16(_) => 0, + Variant::U16(_) => 0, + Variant::I32(_) => 0, + Variant::U32(_) => 0, + Variant::I64(_) => 0, + Variant::U64(_) => 0, + Variant::F64(_) => 0, + Variant::String(_) => 0, + Variant::ObjectPath(_) => 0, + Variant::Signature(_) => 0, + Variant::Variant(v) => v.num_fds(), + Variant::Array(_, a) => a.iter().map(|e| e.num_fds()).sum(), + Variant::DictEntry(k, v) => k.num_fds() + v.num_fds(), + Variant::Struct(f) => f.iter().map(|f| f.num_fds()).sum(), + Variant::Fd(_) => 1, + } + } +} diff --git a/src/ifs/wl_surface/xwindow.rs b/src/ifs/wl_surface/xwindow.rs index d640698c..04b5a97d 100644 --- a/src/ifs/wl_surface/xwindow.rs +++ b/src/ifs/wl_surface/xwindow.rs @@ -45,7 +45,13 @@ pub struct Xwindow { impl XwindowData { pub fn new(state: &Rc, event: &CreateNotifyEvent, client: &Rc) -> Self { - let extents = Rect::new_sized(event.x as _, event.y as _, event.width as _, event.height as _).unwrap(); + let extents = Rect::new_sized( + event.x as _, + event.y as _, + event.width as _, + event.height as _, + ) + .unwrap(); log::info!("extents = {:?}", extents); Self { state: state.clone(), diff --git a/src/logind.rs b/src/logind.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src/logind.rs @@ -0,0 +1 @@ + diff --git a/src/macros.rs b/src/macros.rs index 7d6f6899..8dd34011 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -314,3 +314,33 @@ macro_rules! dedicated_add_global { } }; } + +macro_rules! assert_size_eq { + ($t:ty, $u:ty) => {{ + struct AssertEqSize(std::marker::PhantomData, std::marker::PhantomData); + impl AssertEqSize { + const VAL: usize = { + if std::mem::size_of::() != std::mem::size_of::() { + panic!("Types have different size"); + } + 1 + }; + } + let _ = AssertEqSize::<$t, $u>::VAL; + }}; +} + +macro_rules! assert_align_eq { + ($t:ty, $u:ty) => {{ + struct AssertEqAlign(std::marker::PhantomData, std::marker::PhantomData); + impl AssertEqAlign { + const VAL: usize = { + if std::mem::align_of::() != std::mem::align_of::() { + panic!("Types have different alignment"); + } + 1 + }; + } + let _ = AssertEqAlign::<$t, $u>::VAL; + }}; +} diff --git a/src/main.rs b/src/main.rs index 33686dcc..c9f2f878 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,10 @@ -#![feature(c_variadic, thread_local, label_break_value, try_blocks)] +#![feature( + c_variadic, + thread_local, + label_break_value, + try_blocks, + generic_associated_types +)] #![allow( clippy::len_zero, clippy::needless_lifetimes, @@ -13,6 +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::event_loop::EventLoopError; use crate::forker::ForkerError; use crate::globals::Globals; @@ -34,11 +41,13 @@ use crate::utils::errorfmt::ErrorFmt; use crate::utils::numcell::NumCell; use crate::utils::queue::AsyncQueue; use crate::wheel::WheelError; +use crate::wire_dbus::org; use crate::xkbcommon::XkbContext; 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; @@ -58,6 +67,7 @@ mod client; mod clientmem; mod config; mod cursor; +mod dbus; mod drm; mod event_loop; mod fixed; @@ -65,6 +75,7 @@ mod forker; mod format; mod globals; mod ifs; +mod logind; mod object; mod pixman; mod rect; @@ -80,6 +91,7 @@ mod tree; mod utils; mod wheel; mod wire; +mod wire_dbus; mod xkbcommon; mod xwayland; @@ -163,7 +175,21 @@ fn main_() -> Result<(), MainError> { pending_container_titles: Default::default(), pending_float_layout: Default::default(), pending_float_titles: Default::default(), + dbus: Dbus::new(&engine), }); + 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/state.rs b/src/state.rs index 909be635..cac0eb77 100644 --- a/src/state.rs +++ b/src/state.rs @@ -6,6 +6,7 @@ use crate::backend::{ use crate::client::{Client, Clients}; use crate::config::ConfigProxy; use crate::cursor::ServerCursors; +use crate::dbus::Dbus; use crate::event_loop::EventLoop; use crate::forker::ForkerProxy; use crate::globals::{Globals, GlobalsError, WaylandGlobal}; @@ -63,6 +64,7 @@ pub struct State { pub pending_container_titles: AsyncQueue>, pub pending_float_layout: AsyncQueue>, pub pending_float_titles: AsyncQueue>, + pub dbus: Dbus, } pub struct MouseData { diff --git a/src/utils/aligned.rs b/src/utils/aligned.rs new file mode 100644 index 00000000..4638fbd4 --- /dev/null +++ b/src/utils/aligned.rs @@ -0,0 +1,22 @@ +use uapi::{Packed, Pod}; + +#[repr(C, align(8))] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct AlignedI64(pub i64); + +unsafe impl Pod for AlignedI64 {} +unsafe impl Packed for AlignedI64 {} + +#[repr(C, align(8))] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct AlignedU64(pub u64); + +unsafe impl Pod for AlignedU64 {} +unsafe impl Packed for AlignedU64 {} + +#[repr(C, align(8))] +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct AlignedF64(pub f64); + +unsafe impl Pod for AlignedF64 {} +unsafe impl Packed for AlignedF64 {} diff --git a/src/utils/hex.rs b/src/utils/hex.rs new file mode 100644 index 00000000..8b2ade8f --- /dev/null +++ b/src/utils/hex.rs @@ -0,0 +1,15 @@ +pub fn to_hex(b: &str) -> String { + let mut s = String::with_capacity(b.len() * 2); + for &b in b.as_bytes() { + s.push(nibble_to_hex(b >> 4) as char); + s.push(nibble_to_hex(b & 7) as char); + } + s +} + +fn nibble_to_hex(n: u8) -> u8 { + match n { + n @ 0..=9 => b'0' + n, + n => b'a' + n, + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 7e934fc4..d7bcb866 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,3 +1,4 @@ +pub mod aligned; pub mod array; pub mod asyncevent; pub mod bitflags; @@ -6,6 +7,7 @@ pub mod clonecell; pub mod copyhashmap; pub mod debug_fn; pub mod errorfmt; +pub mod hex; pub mod linkedlist; pub mod numcell; pub mod ptr_ext; @@ -14,3 +16,4 @@ pub mod smallmap; pub mod stack; pub mod tri; pub mod vec_ext; +pub mod vecstorage; diff --git a/src/utils/vecstorage.rs b/src/utils/vecstorage.rs new file mode 100644 index 00000000..6e00b88d --- /dev/null +++ b/src/utils/vecstorage.rs @@ -0,0 +1,74 @@ +use std::mem::ManuallyDrop; +use std::ops::{Deref, DerefMut}; + +pub struct VecStorage { + ptr: *mut T, + cap: usize, +} + +impl Default for VecStorage { + fn default() -> Self { + let mut v = ManuallyDrop::new(vec![]); + Self { + ptr: v.as_mut_ptr(), + cap: v.capacity(), + } + } +} + +impl VecStorage { + #[allow(dead_code)] + pub fn take<'a>(&'a mut self) -> RealizedVec<'a, T, T> { + self.take_as() + } + + pub fn take_as<'a, U>(&'a mut self) -> RealizedVec<'a, T, U> { + assert_size_eq!(T, U); + assert_align_eq!(T, U); + unsafe { + RealizedVec { + vec: ManuallyDrop::new(self.to_vector()), + storage: self, + } + } + } + + unsafe fn to_vector(&mut self) -> Vec { + Vec::from_raw_parts(self.ptr as _, 0, self.cap) + } +} + +impl Drop for VecStorage { + fn drop(&mut self) { + unsafe { + drop(self.to_vector::()); + } + } +} + +pub struct RealizedVec<'a, T, U> { + vec: ManuallyDrop>, + storage: &'a mut VecStorage, +} + +impl<'a, T, U> Drop for RealizedVec<'a, T, U> { + fn drop(&mut self) { + self.vec.clear(); + self.storage.ptr = self.vec.as_mut_ptr() as _; + self.storage.cap = self.vec.capacity(); + } +} + +impl<'a, T, U> Deref for RealizedVec<'a, T, U> { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + self.vec.deref() + } +} + +impl<'a, T, U> DerefMut for RealizedVec<'a, T, U> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.vec.deref_mut() + } +} diff --git a/src/wire_dbus.rs b/src/wire_dbus.rs new file mode 100644 index 00000000..1001bc55 --- /dev/null +++ b/src/wire_dbus.rs @@ -0,0 +1,3 @@ +#![allow(unused_imports)] + +include!(concat!(env!("OUT_DIR"), "/wire_dbus.rs")); diff --git a/src/xwayland/xwm.rs b/src/xwayland/xwm.rs index 39690dcb..0937664d 100644 --- a/src/xwayland/xwm.rs +++ b/src/xwayland/xwm.rs @@ -2,6 +2,7 @@ use crate::async_engine::AsyncFd; use crate::client::Client; use crate::ifs::wl_surface::xwindow::{Xwindow, XwindowData}; use crate::ifs::wl_surface::WlSurface; +use crate::rect::Rect; use crate::wire::WlSurfaceId; use crate::xwayland::{XWaylandError, XWaylandEvent}; use crate::{AsyncQueue, ErrorFmt, State}; @@ -17,11 +18,14 @@ use x11rb::connection::Connection; use x11rb::cursor::Handle; use x11rb::errors::ConnectionError; use x11rb::protocol::composite::{ConnectionExt as _, Redirect}; -use x11rb::protocol::xproto::{ChangeWindowAttributesAux, ClientMessageEvent, ConfigureNotifyEvent, ConfigureRequestEvent, ConfigureWindowAux, ConnectionExt as _, CreateNotifyEvent, CreateWindowAux, DestroyNotifyEvent, EventMask, MapRequestEvent, Window, WindowClass}; +use x11rb::protocol::xproto::{ + ChangeWindowAttributesAux, ClientMessageEvent, ConfigureNotifyEvent, ConfigureRequestEvent, + ConfigureWindowAux, ConnectionExt as _, CreateNotifyEvent, CreateWindowAux, DestroyNotifyEvent, + EventMask, MapRequestEvent, Window, WindowClass, +}; use x11rb::protocol::Event; use x11rb::resource_manager::Database; use x11rb::rust_connection::{DefaultStream, RustConnection}; -use crate::rect::Rect; atom_manager! { pub Atoms: AtomsCookie { @@ -372,7 +376,8 @@ impl Wm { event.y as _, event.width as _, event.height as _, - ).unwrap(); + ) + .unwrap(); let changed = data.extents.replace(extents) != extents; if changed { self.state.tree_changed(); diff --git a/wire-dbus/org.freedesktop.DBus.txt b/wire-dbus/org.freedesktop.DBus.txt new file mode 100644 index 00000000..35f32840 --- /dev/null +++ b/wire-dbus/org.freedesktop.DBus.txt @@ -0,0 +1,3 @@ +fn Hello() { + name: string, +} diff --git a/wire-dbus/org.freedesktop.login1.Manager.txt b/wire-dbus/org.freedesktop.login1.Manager.txt new file mode 100644 index 00000000..ab4020fc --- /dev/null +++ b/wire-dbus/org.freedesktop.login1.Manager.txt @@ -0,0 +1,5 @@ +fn GetSession( + session_id: string, +) { + object_path: object_path, +}