28个版本 (17个重大更改)
0.19.3 | 2023年8月29日 |
---|---|
0.19.2 | 2023年1月25日 |
0.19.1 | 2022年12月4日 |
0.19.0 | 2022年11月25日 |
0.3.2 | 2020年3月2日 |
#52 在 Unix API 中
1,730 每月下载量
用于 14 个crate(6个直接使用)
395KB
10K SLoC
Rustbus
Rustbus实现了针对本地Unix套接字的dbus规范。它不是一个总线实现,而是一个库,允许客户端通过dbus守护进程进行通信。
这是仅通过阅读https://dbus.freedesktop.org/doc/dbus-specification.html中的规范创建的。在实现规范时,我做出了一些错误的假设,这主要是我个人的错误。似乎文档足够编写一个可工作的实现,而不必查看其他代码。
这提供了什么?
此库提供了通过dbus总线发送和接收消息的手段。这意味着:信号、调用和(错误)回复。它还提供了一些标准消息以方便使用。还有一个MessageBuilder,可以帮助您方便地构建自己的消息。
技术上,Dbus可以在任何传输上工作,但目前仅支持Unix流套接字。对其他传输的支持应该相当简单,但它们需要实现一些其他认证机制。
传输文件描述符是可行的。当前的消息限制为每条消息10个,但由于dbus-daemon的限制更为严格,这应该没问题。
本项目状态
有一些针对正确性的测试,并且dbus-daemon似乎通常接受此库发送的所有消息。
与libdbus的互操作性尚未彻底测试,但dbus-monitor工具正确显示了'examples/sig.rs'示例中发送的信号,该示例使用了几乎所有可能出现的不同类型。
反序列化已进行模糊测试,并且至今未在任何输入上引发恐慌。如果您想帮助模糊测试,只需使用以下命令:cargo +nightly fuzz run fuzz_unmarshal
API仍在开发中,预计会有破坏性更改。
在哪里是什么?
rustbus
是包含总线连接和(反)序列化代码的核心crate。如果您想编写应用程序,只需要这个。rustbus_derive
包含用于派生(Un-)Marshal特质的procmacros。这些宏由rustbus重新导出,因此您无需担心这一点。rustbus_derive_test
仅用于验证派生是否正确执行。procmacro crates显然不能包含自己的测试。example_keywallet
存在- 作为更复杂的示例,展示了rustbus的使用
- 是测试新想法的场所,以验证它对实际开发的影响
快速入门
use rustbus::{connection::{Timeout, ll_conn::force_finish_on_error}, standard_messages, MessageBuilder, MessageType, RpcConn};
fn main() {
// Connect to the session bus. This also takes care of the mandatory hello messages
let mut rpc_con = RpcConn::session_conn(Timeout::Infinite).unwrap();
// Create a new call to a method on a remote object
let mut call = MessageBuilder::new()
.call("evaluateScript")
.with_interface("org.kde.PlasmaShell")
.on("/PlasmaShell")
.at("org.kde.plasmashell")
.build();
// Send the message and remeber the id, so we can retrieve the answer
let id = rpc_con
.send_message(&mut call)
.expect("Wanna send message :(")
.write_all()
.map_err(force_finish_on_error)
.expect("Wanna send message :(");
// Retrieve the answer to this message
let message = rpc_con
.wait_response(id, Timeout::Infinite)
.expect("Get failed");
}
连接类型
- 低级别连接是构建更抽象包装的基础。您可能不希望在特殊情况下之外使用它。
- RpcConn是为客户端在总线上的服务上调用方法而设计的(如快速入门所示)
- DispatchConn是为需要将调用分派给许多处理器的服务而设计的。
由于不同的用例有不同的约束,您可能需要围绕低级别连接编写自己的包装器。如果您复制现有的并修改以满足您的需求,这应该不会太困难。如果您有一个对其他人有帮助的问题,我当然会考虑将其添加到这个库中。
低级别连接示例
use rustbus::{connection::Timeout, get_session_bus_path, DuplexConn, MessageBuilder};
fn main() -> Result<(), rustbus::connection::Error> {
// To get a connection going you need to connect to a bus. You will likely use either the session or the system bus.
let session_path = get_session_bus_path()?;
let mut con: DuplexConn = DuplexConn::connect_to_bus(session_path, true)?;
// Dont forget to send the **mandatory** hello message. send_hello wraps the call and parses the response for convenience.
let _unique_name: String = con.send_hello(Timeout::Infinite)?;
// Next you will probably want to create a new message to send out to the world
let mut sig = MessageBuilder::new()
.signal("io.killing.spark", "TestSignal", r#"/io/killing/spark"#)
.build();
// To put parameters into that message you use the sig.body.push_param functions. These accept anything that can be marshalled into a dbus parameter
// You can derive or manually implement that trait for your own types if you need that.
sig.body.push_param("My cool new Signal!").unwrap();
// Now send you signal to all that want to hear it!
con.send.send_message(&sig)?.write_all().unwrap();
// To receive messages sent to you you can call the various functions on the RecvConn. The simplest is this:
let message = con.recv.get_next_message(Timeout::Infinite)?;
// Now you can inspect the message.dynheader for all the metadata on the message
println!("The messages dynamic header: {:?}", message.dynheader);
// After inspecting that dynheader you should know which content the message should contain
let cool_string = message.body.parser().get::<&str>().unwrap();
println!("Received a cool string: {}", cool_string);
Ok(())
}
参数和序列化和反序列化
这个库最初是试图理解dbus是如何工作的。因此,我将类型尽可能地与枚举进行建模,这仍然在params模块中。这保留了下来,以防在需要的地方可能是必要的,但它们通常不应该被使用。
相反,您应该使用Marshal和Unmarshal traits,这些traits为大多数常见类型实现了。想法是将rust类型尽可能地映射到dbus类型。对于像String和u64这样的简单类型,处理起来很简单。对于元组结构体,有上限大小的实现。在那之后,您需要从该库复制实现并相应地扩展它。如果Rust添加了变通泛型,这可能在将来得到解决。
对于结构体,有一个派生proc-macro,可以为您派生必要的特实现。如果您需要,请查看rustbus_derive。
对于枚举,也有一个proc-macro可以为您派生必要的特实现。有两个遗留宏:dbus_variant_sig!
和dbus_variant_var!
。它们实际上做的是同样的事情,但遗留宏向我们的枚举添加了一个CatchAll
,以帮助处理意外的类型,其中proc-macros会由于错误而取消反序列化。
特质的文档提供了更多关于如何实现它们的详细信息,如果需要的话。
在examples/user_defined_types.rs
中有一个所有这些的示例。对于结构体的派生,还有examples/deriving.rs
中的示例。
文件描述符
Dbus可以为您发送文件描述符。Rustbus支持这一点。在wire模块中有一个特殊的包装器类型。此类型尝试以合理的方式处理发送和接收文件描述符的陷阱。如果您在API或希望扩展API方面遇到任何问题,请提出问题。
字节序
Dbus 支持大端和小端字节序,rustbus 也同样支持。在创建 MessageBuilder 时,您可以指定消息应该如何打包。消息可以在任何字节序中接收,并且会透明地解包成您 CPU 使用的字节序。请注意,从/到本地字节序的解包将更快。默认的字节序是编译目标的本地字节序。
依赖项
~3MB
~70K SLoC