#![allow(dead_code)]
use std::os::raw::{c_char, c_int, c_uint, c_void};
use libloading::{Library, Symbol};
const LIB_NAME: &str = "libdbus-1.so.3";
pub enum DBusConnection {}
pub enum DBusMessage {}
pub type DBusBool = u32;
pub const TRUE: DBusBool = 1;
pub const FALSE: DBusBool = 0;
#[repr(C)]
pub struct DBusError {
pub name: *const c_char,
pub message: *const c_char,
dummy_bits: c_uint,
padding1: *mut c_void,
}
impl DBusError {
pub fn zeroed() -> DBusError {
DBusError {
name: std::ptr::null(),
message: std::ptr::null(),
dummy_bits: 0,
padding1: std::ptr::null_mut(),
}
}
pub fn is_set(&self) -> bool {
!self.message.is_null()
}
pub fn message(&self) -> String {
if self.message.is_null() {
String::new()
} else {
unsafe { std::ffi::CStr::from_ptr(self.message) }
.to_string_lossy()
.into_owned()
}
}
}
#[repr(C)]
pub struct DBusMessageIter {
dummy1: *mut c_void,
dummy2: *mut c_void,
dummy3: u32,
dummy4: c_int,
dummy5: c_int,
dummy6: c_int,
dummy7: c_int,
dummy8: c_int,
dummy9: c_int,
dummy10: c_int,
dummy11: c_int,
pad1: c_int,
pad2: c_int,
pad3: *mut c_void,
}
impl DBusMessageIter {
pub fn uninit() -> DBusMessageIter {
unsafe { std::mem::zeroed() }
}
}
pub type DBusObjectPathMessageFunction =
unsafe extern "C" fn(*mut DBusConnection, *mut DBusMessage, *mut c_void) -> c_int;
pub type DBusObjectPathUnregisterFunction = unsafe extern "C" fn(*mut DBusConnection, *mut c_void);
pub type DBusFreeFunction = unsafe extern "C" fn(*mut c_void);
#[repr(C)]
pub struct DBusObjectPathVTable {
pub unregister_function: Option<DBusObjectPathUnregisterFunction>,
pub message_function: Option<DBusObjectPathMessageFunction>,
pub pad1: Option<unsafe extern "C" fn(*mut c_void)>,
pub pad2: Option<unsafe extern "C" fn(*mut c_void)>,
pub pad3: Option<unsafe extern "C" fn(*mut c_void)>,
pub pad4: Option<unsafe extern "C" fn(*mut c_void)>,
}
pub const DBUS_BUS_SESSION: c_int = 0;
pub const DBUS_BUS_SYSTEM: c_int = 1;
pub const DBUS_BUS_STARTER: c_int = 2;
pub const DBUS_MESSAGE_TYPE_INVALID: c_int = 0;
pub const DBUS_MESSAGE_TYPE_METHOD_CALL: c_int = 1;
pub const DBUS_MESSAGE_TYPE_METHOD_RETURN: c_int = 2;
pub const DBUS_MESSAGE_TYPE_ERROR: c_int = 3;
pub const DBUS_MESSAGE_TYPE_SIGNAL: c_int = 4;
pub const DBUS_TYPE_INVALID: c_int = 0;
pub const DBUS_TYPE_BYTE: c_int = b'y' as c_int;
pub const DBUS_TYPE_BOOLEAN: c_int = b'b' as c_int;
pub const DBUS_TYPE_INT16: c_int = b'n' as c_int;
pub const DBUS_TYPE_UINT16: c_int = b'q' as c_int;
pub const DBUS_TYPE_INT32: c_int = b'i' as c_int;
pub const DBUS_TYPE_UINT32: c_int = b'u' as c_int;
pub const DBUS_TYPE_INT64: c_int = b'x' as c_int;
pub const DBUS_TYPE_UINT64: c_int = b't' as c_int;
pub const DBUS_TYPE_DOUBLE: c_int = b'd' as c_int;
pub const DBUS_TYPE_STRING: c_int = b's' as c_int;
pub const DBUS_TYPE_OBJECT_PATH: c_int = b'o' as c_int;
pub const DBUS_TYPE_SIGNATURE: c_int = b'g' as c_int;
pub const DBUS_TYPE_ARRAY: c_int = b'a' as c_int;
pub const DBUS_TYPE_VARIANT: c_int = b'v' as c_int;
pub const DBUS_TYPE_STRUCT: c_int = b'r' as c_int;
pub const DBUS_TYPE_DICT_ENTRY: c_int = b'e' as c_int;
pub const DBUS_HANDLER_RESULT_HANDLED: c_int = 0;
pub const DBUS_HANDLER_RESULT_NOT_YET_HANDLED: c_int = 1;
pub const DBUS_HANDLER_RESULT_NEED_MEMORY: c_int = 2;
pub const DBUS_NAME_FLAG_ALLOW_REPLACEMENT: c_uint = 1;
pub const DBUS_NAME_FLAG_REPLACE_EXISTING: c_uint = 2;
pub const DBUS_NAME_FLAG_DO_NOT_QUEUE: c_uint = 4;
pub const DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER: c_int = 1;
pub const DBUS_REQUEST_NAME_REPLY_IN_QUEUE: c_int = 2;
pub const DBUS_REQUEST_NAME_REPLY_EXISTS: c_int = 3;
pub const DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER: c_int = 4;
pub const DBUS_TIMEOUT_INFINITE: c_int = 0x7fff_ffff;
pub const DBUS_TIMEOUT_USE_DEFAULT: c_int = -1;
macro_rules! dbus_bindings {
( $( fn $name:ident ( $($arg:ty),* $(,)? ) $( -> $ret:ty )? ; )+ ) => {
#[allow(non_snake_case)]
pub struct DBus {
$( pub $name: unsafe extern "C" fn( $($arg),* ) $( -> $ret )?, )+
_lib: Library,
}
impl DBus {
pub fn load() -> crate::error::Result<DBus> {
let lib = unsafe { Library::new(LIB_NAME) }
.map_err(|e| crate::error::Error::LibraryLoad(e.to_string()))?;
Ok(DBus {
$( $name: unsafe {
load_sym(&lib, concat!(stringify!($name), "\0").as_bytes())?
}, )+
_lib: lib,
})
}
}
};
}
unsafe fn load_sym<T: Copy>(lib: &Library, symbol: &[u8]) -> crate::error::Result<T> {
let sym: Symbol<T> = unsafe { lib.get(symbol) }.map_err(|e| {
let pretty = String::from_utf8_lossy(symbol.strip_suffix(b"\0").unwrap_or(symbol));
crate::error::Error::LibraryLoad(format!("missing symbol {pretty}: {e}"))
})?;
Ok(*sym)
}
dbus_bindings! {
fn dbus_error_init(*mut DBusError);
fn dbus_error_free(*mut DBusError);
fn dbus_bus_get_private(c_int, *mut DBusError) -> *mut DBusConnection;
fn dbus_connection_set_exit_on_disconnect(*mut DBusConnection, DBusBool);
fn dbus_connection_close(*mut DBusConnection);
fn dbus_connection_unref(*mut DBusConnection);
fn dbus_connection_flush(*mut DBusConnection);
fn dbus_connection_read_write_dispatch(*mut DBusConnection, c_int) -> DBusBool;
fn dbus_connection_send(*mut DBusConnection, *mut DBusMessage, *mut u32) -> DBusBool;
fn dbus_connection_send_with_reply_and_block(
*mut DBusConnection, *mut DBusMessage, c_int, *mut DBusError,
) -> *mut DBusMessage;
fn dbus_connection_try_register_object_path(
*mut DBusConnection, *const c_char, *const DBusObjectPathVTable, *mut c_void, *mut DBusError,
) -> DBusBool;
fn dbus_connection_unregister_object_path(*mut DBusConnection, *const c_char) -> DBusBool;
fn dbus_connection_add_filter(
*mut DBusConnection, DBusObjectPathMessageFunction, *mut c_void, Option<DBusFreeFunction>,
) -> DBusBool;
fn dbus_connection_remove_filter(
*mut DBusConnection, DBusObjectPathMessageFunction, *mut c_void,
);
fn dbus_bus_get_unique_name(*mut DBusConnection) -> *const c_char;
fn dbus_bus_request_name(*mut DBusConnection, *const c_char, c_uint, *mut DBusError) -> c_int;
fn dbus_bus_add_match(*mut DBusConnection, *const c_char, *mut DBusError);
fn dbus_message_new_method_call(
*const c_char, *const c_char, *const c_char, *const c_char,
) -> *mut DBusMessage;
fn dbus_message_new_signal(*const c_char, *const c_char, *const c_char) -> *mut DBusMessage;
fn dbus_message_new_method_return(*mut DBusMessage) -> *mut DBusMessage;
fn dbus_message_new_error(*mut DBusMessage, *const c_char, *const c_char) -> *mut DBusMessage;
fn dbus_message_unref(*mut DBusMessage);
fn dbus_message_get_type(*mut DBusMessage) -> c_int;
fn dbus_message_get_interface(*mut DBusMessage) -> *const c_char;
fn dbus_message_get_member(*mut DBusMessage) -> *const c_char;
fn dbus_message_get_path(*mut DBusMessage) -> *const c_char;
fn dbus_message_get_sender(*mut DBusMessage) -> *const c_char;
fn dbus_message_is_method_call(*mut DBusMessage, *const c_char, *const c_char) -> DBusBool;
fn dbus_message_iter_init_append(*mut DBusMessage, *mut DBusMessageIter);
fn dbus_message_iter_append_basic(*mut DBusMessageIter, c_int, *const c_void) -> DBusBool;
fn dbus_message_iter_open_container(
*mut DBusMessageIter, c_int, *const c_char, *mut DBusMessageIter,
) -> DBusBool;
fn dbus_message_iter_close_container(*mut DBusMessageIter, *mut DBusMessageIter) -> DBusBool;
fn dbus_message_iter_append_fixed_array(
*mut DBusMessageIter, c_int, *const c_void, c_int,
) -> DBusBool;
fn dbus_message_iter_init(*mut DBusMessage, *mut DBusMessageIter) -> DBusBool;
fn dbus_message_iter_next(*mut DBusMessageIter) -> DBusBool;
fn dbus_message_iter_get_arg_type(*mut DBusMessageIter) -> c_int;
fn dbus_message_iter_get_basic(*mut DBusMessageIter, *mut c_void);
fn dbus_message_iter_recurse(*mut DBusMessageIter, *mut DBusMessageIter);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_symbols_resolve_when_library_present() {
if unsafe { Library::new(LIB_NAME) }.is_err() {
eprintln!("{LIB_NAME} not present; skipping symbol-resolution test");
return;
}
DBus::load().expect("every dbus_* symbol must resolve when libdbus is present");
}
#[test]
fn error_helpers() {
let err = DBusError::zeroed();
assert!(!err.is_set());
assert_eq!(err.message(), "");
}
}