Crate async_rustbus[][src]

Expand description

Async-rustbus is an async-rustbus library built on top of rustbus. It is a multi-threaded client allowing for asynchronous calls to services, and the creation of local services.

Missing Features

  • Eavesdropping using match rules is not currently supported.
  • Monitor mode is not currently supported. There are plans to implement it.
  • There is no support for DBUS_COOKIE_SHA1 authentication. This makes DBus over TCP not as useful with only ANOYNMOUS mode supported when using TCP (EXTERNAL is not available for TCP).

API Stability

As with most crates, breaking changes will only be added after an increment of the most sigificant version number (SemVer). The most likely elements to change in the future is the incoming signal handling and the relation of this crate and rustbus.

Examples

An example client that queues info about the current DBus session server connections:

use std::collections::HashMap;
use futures::future::try_join_all;
use async_rustbus::{RpcConn, MatchRule};
use async_rustbus::rustbus_core::message_builder;
use async_rustbus::rustbus_core::dbus_variant_var;
use message_builder::{MessageBuilder, MarshalledMessage, MessageType};

// Create the DBus connection to the session DBus.
let conn = RpcConn::session_conn(false).await.unwrap();

// Fetch the ID of the DBus
let mut msg = MessageBuilder::new().call("GetId")
    .with_interface("org.freedesktop.DBus")
    .on("/org/freedesktop/DBus")
    .at("org.freedesktop.DBus")
    .build();
let res = conn.send_msg_w_rsp(&msg).await.unwrap().await.unwrap();
assert!(matches!(res.typ, MessageType::Reply));
let id: &str = res.body.parser().get().unwrap();
println!("Info for Dbus {}:", id);

// Get call of the names of all connections.
msg.dynheader.member = Some("ListNames".into());
let res = conn.send_msg_w_rsp(&msg).await.unwrap().await.unwrap();
assert!(matches!(res.typ, MessageType::Reply));
let mut names: Vec<&str> = res.body.parser().get().unwrap();
// Ignore unique names
names.retain(|s| !s.starts_with(":"));

// Get stats for each individual message
let mut dbg_msg = MessageBuilder::new().call("GetConnectionStats")
    .with_interface("org.freedesktop.DBus.Debug.Stats")
    .on("/org/freedesktop/DBus")
    .at("org.freedesktop.DBus")
    .build();
let mut res_futs = Vec::with_capacity(names.len());
for name in names.iter() {
    dbg_msg.body.reset();
    dbg_msg.body.push_param(name).unwrap();
    let res_fut = conn.send_msg_w_rsp(&dbg_msg).await.unwrap();
    res_futs.push(res_fut);
}
let stats = try_join_all(res_futs).await.unwrap();

// Parse responses and print out some info
dbus_variant_var!(StatVariant, U32 => u32; Str => &'buf str);
for (name, stat_msg) in names.into_iter().zip(stats) {
    if !matches!(stat_msg.typ, MessageType::Reply) {
        continue;
    }
    let mut stat_map: HashMap<&str, StatVariant> = stat_msg.body.parser().get().unwrap();
    let unique = match stat_map["UniqueName"] {
        StatVariant::Str(s) => s,
        _ => continue,
    };
    let peak_out = match stat_map["PeakOutgoingBytes"] {
        StatVariant::U32(s) => s,
        _ => continue,
    };
    let peak_in = match stat_map["PeakIncomingBytes"] {
        StatVariant::U32(s) => s,
        _ => continue,
    };
    println!("\t{} ({}):", name, unique);
    println!("\t\t PeakIncomingBytes: {}, PeakOutgoingBytes: {}\n", peak_in, peak_out);
}

A simple example server that gives out the time in millis since Epoch and a reference time:

use async_rustbus::{RpcConn, MatchRule, CallAction};
use async_rustbus::rustbus_core;
use rustbus_core::message_builder::{MessageBuilder, MessageType};
use std::time::{Instant, SystemTime, UNIX_EPOCH};
let conn = RpcConn::session_conn(false).await.unwrap();
conn.insert_call_path("/example/TimeServer", CallAction::Exact).await.unwrap();
conn.insert_call_path("/", CallAction::Intro).await.unwrap();
conn.request_name("example.TimeServer").await.unwrap();
let start = Instant::now();
loop {
    let call = match conn.get_call("/example/TimeServer").await {
        Ok(c) => c,
        Err(e) => {
            eprintln!("Error occurred waiting for calls: {:?}", e);
              break;
        }
       };
    assert!(matches!(call.typ, MessageType::Call));
    let res = match (call.dynheader.interface.as_deref().unwrap(), call.dynheader.member.as_deref().unwrap()) {
        ("example.TimeServer", "GetUnixTime") => {
            let mut res = call.dynheader.make_response();
            let cur_time = UNIX_EPOCH.elapsed().unwrap().as_millis() as u64;
            res.body.push_param(cur_time).unwrap();
            res
        }
        ("example.TimeServer", "GetRefTime") => {
            let mut res = call.dynheader.make_response();
            let elapsed = start.elapsed().as_millis() as u64;
            res.body.push_param(elapsed).unwrap();
            res
        }
        ("org.freedesktop.DBus.Introspectable", "Introspect") => {
            todo!("We need to put a introspect impl so that other connection can discover this object.");
        }
        _ => {
            call.dynheader.make_error_response("UnknownInterface", None)
        }
    };
    conn.send_msg_wo_rsp(&res).await.unwrap();
}

Modules

Low level non-blocking implementation of the DBus connection and some helper functions.

Data structures and methods related to DBus wire-format.

Structs

Represents a match for incoming signals.

RpcConn is used to create and interact with a DBus connection. It can be used to easily connect to either session (user) or system DBus daemons. RpcConn is thread-safe and can be used from within an Arc if desired.

Enums

For use with RpcConn::insert_call_path, this enum determines what should be done when receiving incoming method calls.

A address for connecting to a DBus dubs. These can be file systems paths to a Unix socket, a TCP address, or an abstract Unix socket.

Constants

A match that accepts every signal. Every field is None. This is also the Default MatchRule.

Functions

Get and parse address of the session DBus from the environment.

Get the path of the system bus if it exists.