diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index bcb4634e7..3142ee79a 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -6,7 +6,6 @@ on: - zbus-* paths: - book/**/* - - book-1.0/**/* - .github/workflows/deploy.yml jobs: @@ -29,10 +28,8 @@ jobs: - name: Build Books run: | mdbook build book - mdbook build book-1.0 mkdir -p public/1.0 cp -r ./book/book/* ./public - cp -r ./book-1.0/book/* ./public/1.0/ - name: Deploy 🚀 uses: JamesIves/github-pages-deploy-action@4.1.7 with: diff --git a/book-1.0/.gitignore b/book-1.0/.gitignore deleted file mode 100644 index 7585238ef..000000000 --- a/book-1.0/.gitignore +++ /dev/null @@ -1 +0,0 @@ -book diff --git a/book-1.0/book.toml b/book-1.0/book.toml deleted file mode 100644 index 0d0577e01..000000000 --- a/book-1.0/book.toml +++ /dev/null @@ -1,12 +0,0 @@ -[book] -authors = ["Marc-André Lureau"] -language = "en" -multilingual = false -src = "src" -title = "zbus: D-Bus for Rust made easy" - -[output.html] -default-theme = "Rust" -curly-quotes = true -git-repository-url = "https://github.com/dbus2/zbus" -git-repository-icon = "fa-gitlab" diff --git a/book-1.0/src/SUMMARY.md b/book-1.0/src/SUMMARY.md deleted file mode 100644 index a35f62b94..000000000 --- a/book-1.0/src/SUMMARY.md +++ /dev/null @@ -1,11 +0,0 @@ -# Summary - -[Introduction](introduction.md) -- [Some D-Bus concepts](concepts.md) -- [Establishing connections](connection.md) -- [Writing a client proxy](client.md) -- [Writing a server interface](server.md) - ------------ - -[Contributors](contributors.md) diff --git a/book-1.0/src/client.md b/book-1.0/src/client.md deleted file mode 100644 index f643b8a65..000000000 --- a/book-1.0/src/client.md +++ /dev/null @@ -1,568 +0,0 @@ -> **Note** -> -> This version of the book is based on older zbus 1.0 API. The 2.0 version of this book is available -> [here](https://dbus2.github.io/zbus/). - -# Writing a client proxy - -In this chapter, we are going to see how to make low-level D-Bus method calls. Then we are going to -dive in, and derive from a trait to make a convenient Rust binding. Finally, we will learn about -*xmlgen*, a tool to help us generate a boilerplate trait from the XML of an introspected service. - -To make this learning "hands-on", we are going to call and bind the cross-desktop notification -service (please refer to this -[reference](https://specifications.freedesktop.org/notification-spec/notification-spec-latest.html) -document for further details on this API). - -Let's start by playing with the service from the shell, and notify the desktop with [`busctl`][^busctl]: - -```bash -busctl --user call \ - org.freedesktop.Notifications \ - /org/freedesktop/Notifications \ - org.freedesktop.Notifications \ - Notify \ - susssasa\{sv\}i \ - "my-app" 0 "dialog-information" "A summary" "Some body" 0 0 5000 -``` - -**Note**: `busctl` has very good auto-completion support in bash or zsh. - -Running this command should pop-up a notification dialog on your desktop. If it does not, your -desktop does not support the notification service, and this example will be less interactive. -Nonetheless you can use a similar approach for other services. - -This command shows us several aspects of the D-Bus communication: - - - `--user`: Connect to and use the user/session bus. - - - `call`: Send a method call message. (D-Bus also supports signals, error messages, and method - replies) - - - **destination**: The name of the service (`org.freedesktop.Notifications`). - - - **object path**: Object/interface path (`/org/freedesktop/Notifications`). - - - **interface**: The interface name (methods are organized in interfaces, here - `org.freedesktop.Notifications`, same name as the service). - - - **method**: The name of the method to call, `Notify`. - - - **signature**: That `susssasa{sv}i` means the method takes 8 arguments of various types. 's', for - example, is for a string. 'as' is for array of strings. - - - The method arguments. - -See [`busctl`] man page for more details. - -## Low-level call from a `zbus::Connection` - -zbus `Connection` has a `call_method()` method, which you can use directly: - -```rust,no_run -use std::collections::HashMap; -use std::error::Error; - -use zbus::Connection; -use zvariant::Value; - -fn main() -> Result<(), Box> { - let connection = Connection::new_session()?; - - let m = connection.call_method( - Some("org.freedesktop.Notifications"), - "/org/freedesktop/Notifications", - Some("org.freedesktop.Notifications"), - "Notify", - &("my-app", 0u32, "dialog-information", "A summary", "Some body", - vec![""; 0], HashMap::<&str, &Value>::new(), 5000), - )?; - let reply: u32 = m.body().unwrap(); - dbg!(reply); - Ok(()) -} -``` - -Although this is already quite flexible, and handles various details for you (such as the message -signature), it is also somewhat inconvenient and error-prone: you can easily miss arguments, or give -arguments with the wrong type or other kind of errors (what would happen if you typed `0`, instead -of `0u32`?). - -Instead, we want to wrap this `Notify` D-Bus method in a Rust function. Let's see how next. - -## Trait-derived proxy call - -A trait declaration `T` with a `dbus_proxy` attribute will have a derived `TProxy` implemented -thanks to procedural macros. The trait methods will have respective `impl` methods wrapping the -D-Bus calls: - -```rust,no_run -use std::collections::HashMap; -use std::error::Error; - -use zbus::dbus_proxy; -use zvariant::Value; - -#[dbus_proxy] -trait Notifications { - /// Call the org.freedesktop.Notifications.Notify D-Bus method - fn notify(&self, - app_name: &str, - replaces_id: u32, - app_icon: &str, - summary: &str, - body: &str, - actions: &[&str], - hints: HashMap<&str, &Value>, - expire_timeout: i32) -> zbus::Result; -} - -fn main() -> Result<(), Box> { - let connection = zbus::Connection::new_session()?; - - let proxy = NotificationsProxy::new(&connection)?; - let reply = proxy.notify("my-app", 0, "dialog-information", "A summary", "Some body", - &[], HashMap::new(), 5000)?; - dbg!(reply); - - Ok(()) -} -``` - -A `TProxy` has a few associated methods, such as `new(connection)`, using the default associated -service name and object path, and `new_for(connection, service_name, object_path)` if you need to -specify something different. - -This should help to avoid the kind of mistakes we saw earlier. It's also a bit easier to use, thanks -to Rust type inference. This makes it also possible to have higher-level types, they fit more -naturally with the rest of the code. You can further document the D-Bus API or provide additional -helpers. - -### Properties - -Interfaces can have associated properties, which can be read or set with the -`org.freedesktop.DBus.Properties` interface. Here again, the `#[dbus_proxy]` attribute comes to the -rescue to help you. You can annotate a trait method to be a getter: - -```rust -# use zbus::{dbus_proxy, Result}; -# -#[dbus_proxy] -trait MyInterface { - #[dbus_proxy(property)] - fn state(&self) -> Result; -} -``` - -The `state()` method will translate to a `"State"` property `Get` call. - -To set the property, prefix the name of the property with `set_`. - -For a more real world example, let's try and read two properties from systemd's main service: - -```rust,no_run -# use std::error::Error; -# use zbus::dbus_proxy; -# -#[dbus_proxy( - interface = "org.freedesktop.systemd1.Manager", - default_service = "org.freedesktop.systemd1", - default_path = "/org/freedesktop/systemd1" -)] -trait SystemdManager { - #[dbus_proxy(property)] - fn architecture(&self) -> zbus::Result; - #[dbus_proxy(property)] - fn environment(&self) -> zbus::Result>; -} - -fn main() -> Result<(), Box> { - let connection = zbus::Connection::new_session()?; - - let proxy = SystemdManagerProxy::new(&connection)?; - println!("Host architecture: {}", proxy.architecture()?); - println!("Environment:"); - for env in proxy.environment()? { - println!(" {}", env); - } - - Ok(()) -} -``` - -You should get an output similar to this: - -```none -Host architecture: x86-64 -Environment variables: - HOME=/home/zeenix - LANG=en_US.UTF-8 - LC_ADDRESS=de_DE.UTF-8 - LC_IDENTIFICATION=de_DE.UTF-8 - LC_MEASUREMENT=de_DE.UTF-8 - LC_MONETARY=de_DE.UTF-8 - LC_NAME=de_DE.UTF-8 - LC_NUMERIC=de_DE.UTF-8 - LC_PAPER=de_DE.UTF-8 - LC_TELEPHONE=de_DE.UTF-8 - LC_TIME=de_DE.UTF-8 - ... -``` - -### Signals - -Signals are like methods, except they don't expect a reply. They are typically emitted by services -to notify interested peers of any changes to the state of the service. zbus provides you with an API -to register signal handler functions, and to receive and call them. - -Let's look at this API in action, with an example where we get our location from -[Geoclue](https://gitlab.freedesktop.org/geoclue/geoclue/-/blob/master/README.md): - -```rust,no_run -use zbus::{Connection, dbus_proxy, Result}; -use zvariant::{ObjectPath, OwnedObjectPath}; - -#[dbus_proxy( - default_service = "org.freedesktop.GeoClue2", - interface = "org.freedesktop.GeoClue2.Manager", - default_path = "/org/freedesktop/GeoClue2/Manager" -)] -trait Manager { - fn get_client(&self) -> Result; -} - -#[dbus_proxy( - default_service = "org.freedesktop.GeoClue2", - interface = "org.freedesktop.GeoClue2.Client" -)] -trait Client { - fn start(&self) -> Result<()>; - fn stop(&self) -> Result<()>; - - #[dbus_proxy(property)] - fn set_desktop_id(&mut self, id: &str) -> Result<()>; - - #[dbus_proxy(signal)] - fn location_updated(&self, old: ObjectPath<'_>, new: ObjectPath<'_>) -> Result<()>; -} - -#[dbus_proxy( - default_service = "org.freedesktop.GeoClue2", - interface = "org.freedesktop.GeoClue2.Location" -)] -trait Location { - #[dbus_proxy(property)] - fn latitude(&self) -> Result; - #[dbus_proxy(property)] - fn longitude(&self) -> Result; -} -let conn = Connection::new_system().unwrap(); -let manager = ManagerProxy::new(&conn).unwrap(); -let client_path = manager.get_client().unwrap(); -let mut client = ClientProxy::new_for_path(&conn, &client_path).unwrap(); -// Gotta do this, sorry! -client.set_desktop_id("org.freedesktop.zbus").unwrap(); - -client - .connect_location_updated(move |_old, new| { - let location = LocationProxy::new_for_path(&conn, &new); - println!( - "Latitude: {}\nLongitude: {}", - location.latitude()?, - location.longitude()?, - ); - - Ok(()) - }) - .unwrap(); - -client.start().unwrap(); - -// Wait till there is a signal that was handled. -while client.next_signal().unwrap().is_some() {} -``` - -While the Geoclue's D-Bus API is a bit involved, we still ended-up with a not-so-complicated (~60 -LOC) code for getting our location. As you may've notice, we use a blocking call to wait for a -signal on one proxy. This works fine but in the real world, you would typically have many proxies -and you'd want to wait for signals from them all at once. Not to worry, zbus provides a way to wait -on [multiple proxies at once as well](https://docs.rs/zbus/1.5.0/zbus/struct.SignalReceiver.html). - -Let's make use of `SignalReceiver` and `zbus::fdo` API to make sure the client is actually started -by watching for `Active` property (that we must set to be able to get location from Geoclue) -actually getting set: - -```rust,no_run -# use zbus::{Connection, dbus_proxy, Result}; -# use zvariant::{ObjectPath, OwnedObjectPath}; -# -# #[dbus_proxy( -# default_service = "org.freedesktop.GeoClue2", -# interface = "org.freedesktop.GeoClue2.Manager", -# default_path = "/org/freedesktop/GeoClue2/Manager" -# )] -# trait Manager { -# fn get_client(&self) -> Result; -# } -# -# #[dbus_proxy(interface = "org.freedesktop.GeoClue2.Client")] -# trait Client { -# fn start(&self) -> Result<()>; -# -# #[dbus_proxy(property)] -# fn set_desktop_id(&mut self, id: &str) -> Result<()>; -# -# #[dbus_proxy(signal)] -# fn location_updated(&self, old: ObjectPath, new: ObjectPath) -> Result<()>; -# } -# -# #[dbus_proxy( -# default_service = "org.freedesktop.GeoClue2", -# interface = "org.freedesktop.GeoClue2.Location" -# )] -# trait Location { -# #[dbus_proxy(property)] -# fn latitude(&self) -> Result; -# #[dbus_proxy(property)] -# fn longitude(&self) -> Result; -# } -# -# let conn = Connection::new_system().unwrap(); -# let manager = ManagerProxy::new(&conn).unwrap(); -# let client_path = manager.get_client().unwrap(); -# let mut client = -# ClientProxy::new_for(&conn, "org.freedesktop.GeoClue2", &client_path); -# // Gotta do this, sorry! -# client.set_desktop_id("org.freedesktop.zbus").unwrap(); -# -// Everything else remains the same before this point. - -let conn_clone = conn.clone(); -client.connect_location_updated(move |_old, new| { - let location = LocationProxy::new_for( - &conn_clone, - "org.freedesktop.GeoClue2", - &new, - )?; - println!( - "Latitude: {}\nLongitude: {}", - location.latitude()?, - location.longitude()?, - ); - - Ok(()) -}).unwrap(); - -let props = zbus::fdo::PropertiesProxy::new_for( - &conn, - "org.freedesktop.GeoClue2", - &client_path, -).unwrap(); -props.connect_properties_changed(|iface, changed, _| { - for (name, value) in changed.iter() { - println!("{}.{} changed to `{:?}`", iface, name, value); - } - - Ok(()) -}).unwrap(); - -let mut receiver = zbus::SignalReceiver::new(conn); -receiver.receive_for(&client); -receiver.receive_for(&props); - -client.start().unwrap(); - -// 3 signals will be emitted, that we handle -let mut num_handled = 0; -while num_handled < 3 { - if receiver.next_signal().unwrap().is_none() { - num_handled += 1; - } -} -``` - -## Generating the trait from an XML interface - -The `zbus_xmlgen` crate provides a [developer-friendly tool], that can generate Rust traits from a -given D-Bus introspection XML for you. - -**Note:** This tool should not be considered a drop-in Rust-specific replacement for similar tools -available for low-level languages, such as [`gdbus-codegen`]. Unlike those tools, this is only meant -as a starting point to generate the code, once. In many cases, you will want to tweak the generated -code. - -The tool can be used to generate rust code directly from a D-Bus service running on our system: - -```bash -zbus-xmlgen --session \ - org.freedesktop.Notifications \ - /org/freedesktop/Notifications -``` - -Alternatively you can also get the XML interface from a different source and use it to generate the -interface code. Some packages may also provide the XML directly as an installed file, allowing it to -be queried using [`pkg-config`], for example. - -We can fetch the XML interface of the notification service, using the `--xml-interface` option of -the `busctl`[^busctl] command. This option was introduced to `busctl` in systemd v243. - -```bash -busctl --user --xml-interface introspect \ - org.freedesktop.Notifications \ - /org/freedesktop/Notifications -``` - -You should get a similar output: - -```xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -``` - -Save the output to a `notify.xml` file. Then call: - -```bash -zbus-xmlgen notify.xml -``` - -This will give back effortlessly the corresponding Rust traits boilerplate -code: - -```rust -# use zbus::dbus_proxy; -# -#[dbus_proxy(interface = "org.freedesktop.Notifications")] -trait Notifications { - /// CloseNotification method - fn close_notification(&self, arg_0: u32) -> zbus::Result<()>; - - /// GetCapabilities method - fn get_capabilities(&self) -> zbus::Result>; - - /// GetServerInformation method - fn get_server_information(&self) -> zbus::Result<(String, String, String, String)>; - - /// Notify method - fn notify( - &self, - arg_0: &str, - arg_1: u32, - arg_2: &str, - arg_3: &str, - arg_4: &str, - arg_5: &[&str], - arg_6: std::collections::HashMap<&str, zvariant::Value>, - arg_7: i32, - ) -> zbus::Result; - - /// ActionInvoked signal - #[dbus_proxy(signal)] - fn action_invoked(&self, arg_0: u32, arg_1: &str) -> zbus::Result<()>; - - /// NotificationClosed signal - #[dbus_proxy(signal)] - fn notification_closed(&self, arg_0: u32, arg_1: u32) -> zbus::Result<()>; -} -``` - -It should be usable as such. But you may as well improve a bit the naming of the arguments, use -better types (using `BitFlags`, structs or other custom types), add extra documentation, and other -functions to make the binding more pleasing to use from Rust. - -For example, the generated `GetServerInformation` method can be improved to a nicer version: - -```rust -# use serde::{Serialize, Deserialize}; -# use zvariant::derive::Type; -# use zbus::dbus_proxy; -# -#[derive(Debug, Type, Serialize, Deserialize)] -pub struct ServerInformation { - /// The product name of the server. - pub name: String, - - /// The vendor name. For example "KDE," "GNOME," "freedesktop.org" or "Microsoft". - pub vendor: String, - - /// The server's version number. - pub version: String, - - /// The specification version the server is compliant with. - pub spec_version: String, -} - -trait Notifications { - /// Get server information. - /// - /// This message returns the information on the server. - fn get_server_information(&self) -> zbus::Result; -} -``` - -You can learn more from the zbus-ify [binding of -PolicyKit](https://github.com/dbus2/zbus/tree/main/zbus_polkit#readme), for example, which was -implemented starting from the *xmlgen* output. - -There you have it, a Rust-friendly binding for your D-Bus service! - -[`busctl`]: https://www.freedesktop.org/software/systemd/man/busctl.html -[developer-friendly tool]: https://crates.io/crates/zbus_xmlgen -[`gdbus-codegen`]: https://developer.gnome.org/gio/stable/gdbus-codegen.html -[`pkg-config`]: https://www.freedesktop.org/wiki/Software/pkg-config/ - -[^busctl]: `busctl` is part of [`systemd`](https://www.freedesktop.org/wiki/Software/systemd/). diff --git a/book-1.0/src/concepts.md b/book-1.0/src/concepts.md deleted file mode 100644 index 2038f98b2..000000000 --- a/book-1.0/src/concepts.md +++ /dev/null @@ -1,77 +0,0 @@ -> **Note** -> -> This version of the book is based on older zbus 1.0 API. The 2.0 version of this book is available -> [here](https://dbus2.github.io/zbus/). - -# Some D-Bus concepts to help newcomers - -## Bus - -A D-Bus "bus" is a kind of server that handles several connections in a bus-topology fashion. As -such, it relays messages between connected endpoints, and allows to discover endpoints or sending -broadcast messages (signals). - -Typically, a Linux system has a system bus, and a session bus. The latter is per-user. It is also -possible to have private buses or no bus at all (i-e direct peer-to-peer communication instead). - -## Bus name / service name - -An endpoint can have various names, which allows to address messages to it on the bus. All endpoints -are assigned a unique name by the bus at start. Since this name is not static, most services use -something called a *well-known bus name* and typically it's this name, that you'll be concerned -with. - -An example would be the [FreeDesktop Notifications Service] that uses -`org.freedesktop.Notifications` as its well-known bus name. - -For further details on bus names, please refer to the [Bus names chapter] of the D-Bus specification. - -## Objects and Object paths - -An object is akin to the concept of an object or an instance in many programming languages. All -services expose at least one object on the bus and all clients interact with the service through -these objects. These objects can be ephemeral or they could live as long as the service itself. - -Every object is identified by a string, which is referred to as its path. An example of an object -path is `/org/freedesktop/Notifications`, which identities the only object exposed by the -[FreeDesktop Notifications Service]. - -For further details on object paths, please refer to the [Basic types chapter] of the D-Bus -specification. - -## Interfaces - -An interface defines the API exposed by object on the bus. They are akin to the concept of -interfaces in many programming languages and traits in Rust. Each object can (and typically do) -provide multiple interfaces at the same time. A D-Bus interface can have methods, properties and -signals. - -While each interface of a service is identified by a [unique name], its API is described by an XML -description. It is mostly a machine-level detail. Most services can be queried for this description -through a D-Bus standard [introspection interface]. - -zbus provides convenient macro that implements the introspection interface for services, and helper -to generate client-side Rust API, given an XML description. We'll see both of these in action in the -following chapters. - -## Good practices & API design - -It is recommended to organise the service name, object paths and interface name by using -fully-qualified domain names, in order to avoid potential conflicts. - -Please read the [D-Bus API Design Guidelines] carefully for other similar considerations. - -## Asynchronous API - -At the moment, we only provide [low-level asynchronous API] but high-level asynchronous API will -hopefully be added soon. It's very high on our priority. - -Onwards to implementation details & examples! - -[FreeDesktop Notifications Service]: https://specifications.freedesktop.org/notification-spec/notification-spec-latest.html -[D-Bus API Design Guidelines]: https://dbus.freedesktop.org/doc/dbus-api-design.html -[Bus names chapter]: https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names-bus -[Basic types chapter]: https://dbus.freedesktop.org/doc/dbus-specification.html#basic-types -[unique name]: https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names-interface -[introspection interface]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-introspectable -[low-level asynchronous API]: https://docs.rs/zbus/latest/zbus/connection/struct.Connection.html diff --git a/book-1.0/src/connection.md b/book-1.0/src/connection.md deleted file mode 100644 index e8d5528a8..000000000 --- a/book-1.0/src/connection.md +++ /dev/null @@ -1,47 +0,0 @@ -> **Note** -> -> This version of the book is based on older zbus 1.0 API. The 2.0 version of this book is available -> [here](https://dbus2.github.io/zbus/). - -# Establishing a connection - -The first thing you will have to do is to connect to a D-Bus bus or to a D-Bus peer. This is the -entry point of the zbus API. - -## Connection to the bus - -To connect to the session bus (the *per-user* bus), simply call `Connection::new_session()`. It -returns an instance of the connection (if all went well). - -Similarly, to connect to the system bus (to communicate with services such as [NetworkManager], -[BlueZ] or [PID1]), use `Connection::new_system()`. - -**Note:** it is common for a D-Bus library to provide a "shared" connection to a bus for a process: -all `new_session()` share the same underlying connection for example. At the time of this writing, -zbus doesn't do that. - -## Using a custom bus address - -You may also specify a custom bus with `Connection::new_for_address()` which takes a D-Bus address -[as specified in the -specification](https://dbus.freedesktop.org/doc/dbus-specification.html#addresses). - -## Peer to peer connection - -Peer-to-peer connections are bus-less[^bus-less], and the initial handshake protocol is a bit -different. There is the notion of client & server endpoints, but that distinction doesn't matter -once the connection is established (both ends are equal, and can send any messages). - -To create a bus-less peer-to-peer connection on Unix, you can make a `socketpair()` (or have a -listening socket server, accepting multiple connections), and hand over the socket FDs to -`Connection::new_unix_server` and `Connection::new_unix_client` for each side. After success, you -can call the `Connection` methods to send and receive messages on both ends. - -See the `unix_p2p` test in the [zbus source code] for a simple example. - -[NetworkManager]: https://developer.gnome.org/NetworkManager/stable/spec.html -[BlueZ]: https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc -[PID1]: https://www.freedesktop.org/wiki/Software/systemd/dbus/ -[zbus source code]: https://github.com/dbus2/zbus/blob/main/zbus/src/connection.rs - -[^bus-less] Unless you implemented them, none of the bus methods will exist. diff --git a/book-1.0/src/contributors.md b/book-1.0/src/contributors.md deleted file mode 100644 index 535ec568d..000000000 --- a/book-1.0/src/contributors.md +++ /dev/null @@ -1,13 +0,0 @@ -> **Note** -> -> This version of the book is based on older zbus 1.0 API. The 2.0 version of this book is available -> [here](https://dbus2.github.io/zbus/). - -# Contributors - -Here is a list of the contributors who have helped improving zbus. Big shout-out to them! - -- Zeeshan Ali Khan ([@zeenix](https://github.com/zeenix)) -- Marc-André Lureau ([@elmarco](https://github.com/elmarco)) - -If you feel you're missing from this list, feel free to add yourself in a PR. diff --git a/book-1.0/src/introduction.md b/book-1.0/src/introduction.md deleted file mode 100644 index 717cdb021..000000000 --- a/book-1.0/src/introduction.md +++ /dev/null @@ -1,65 +0,0 @@ -

- -

- -> **Note** -> -> This version of the book is based on older zbus 1.0 API. The 2.0 version of this book is available -> [here](https://dbus2.github.io/zbus/). - -# Introduction - -**[zbus]** is a **[Rust]** crate for **[D-Bus]**. If you are not familiar with D-Bus, you should -read [what is D-Bus?] first[^outdated]. In short, zbus allows you to communicate from one program -to another, using the D-Bus protocol. In other words, it's an *inter-process* communication (IPC) -solution. It is a very popular IPC solution on Linux and many Linux services (e.g systemd, -NetworkManager) and desktop environments (e.g GNOME and KDE), rely on D-Bus for their IPC needs. -There are many tools and implementations available, making it easy to interact with D-Bus programs -from different languages and environments. - -zbus is a 100% Rust-native implementation of the D-Bus protocol. It provides both an API to send -and receive messages over a connection, as well as API to interact with peers through high-level -concepts like method calls, signals and properties[^high-level-api]. Thanks to the power of Rust -macros, zbus is able to make interacting with D-Bus very easy. - -zbus project provides two crates: - -## zvariant - -D-Bus defines a marshalling format for its messages. The [zvariant] crate provides a [serde]-based -[API] to serialize/deserialize Rust data types to/from this format. Outside of D-Bus context, a -modified form of this format, [GVariant](https://developer.gnome.org/glib/stable/glib-GVariant.html) -is very commonly used for efficient storage of arbitrary data and is also supported by this crate. - -## zbus - -The [zbus crate] provides the main API you will use to interact with D-Bus from Rust. It takes care -of the establishment of a connection, the creation, sending and receiving of different kind of D-Bus -messages (method calls, signals etc) for you. - -zbus crate is currently Linux-specific[^otheros]. - -[zbus]: https://github.com/dbus2/zbus -[Rust]: https://www.rust-lang.org/ -[D-Bus]: https://dbus.freedesktop.org/ -[what is D-Bus?]: https://www.freedesktop.org/wiki/Software/dbus/#index1h1 -[serde]: https://serde.rs/ -[zvariant]: https://crates.io/crates/zvariant -[zbus crate]: https://crates.io/crates/zbus -[API]: https://docs.rs/zvariant/ - -[^outdated]: D-Bus is ~15y old, unfortunately many documents out there are - sometime aging or misleading. - -[^high-level-api]: These concepts are explained in the -[following chapter](concepts.html#interfaces). - -[^otheros]: Support for other OS exist, but it is not supported to the same - extent. D-Bus clients in javascript (running from any browser) do exist - though. And zbus may also be working from the browser sometime in the future - too, thanks to Rust 🦀 and WebAssembly 🕸. - -

- -

diff --git a/book-1.0/src/server.md b/book-1.0/src/server.md deleted file mode 100644 index 2b4be225d..000000000 --- a/book-1.0/src/server.md +++ /dev/null @@ -1,164 +0,0 @@ -> **Note** -> -> This version of the book is based on older zbus 1.0 API. The 2.0 version of this book is available -> [here](https://dbus2.github.io/zbus/). - -# Writing a server interface - -Let see how to provide a server method "SayHello", to greet a client. - -## Taking a service name - -As we know from the chapter on [D-Bus concepts], each connection on the bus is given a unique name -(such as ":1.27"). This could be all you need, depending on your use case, and the design of your -D-Bus API. However, typically services use a service name (aka *well-known name*) so peers (clients, -in this context) can easily discover them. - -In this example, that is exactly what we're going to do and request the bus for the service name of -our choice. To achieve that, we will we call the [`RequestName`] method on the bus, using -`zbus::fdo` module: - -```rust,no_run -use std::error::Error; - -use zbus::Connection; -use zbus::fdo; - -fn main() -> std::result::Result<(), Box> { - let connection = Connection::new_session()?; - - fdo::DBusProxy::new(&connection)?.request_name( - "org.zbus.MyGreeter", - fdo::RequestNameFlags::ReplaceExisting.into(), - )?; - - loop {} -} -``` - -We can check our service is running and is associated with the service name: - -```bash -$ busctl --user list | grep zbus -org.zbus.MyGreeter 412452 server elmarco :1.396 user@1000.service - - -``` - -### âš  Hang on - -This example is not handling incoming messages yet. Any attempt to call the server will time out -(including the shell completion!). - -## Handling low-level messages - -At the low-level, you can handle method calls by checking the incoming messages manually. - -Let's write a `SayHello` method, that takes a string as argument, and reply with a "hello" greeting -by replacing the loop above with this code: - -```rust,no_run -# fn main() -> Result<(), Box> { -# let connection = zbus::Connection::new_session()?; -# zbus::fdo::DBusProxy::new(&connection)?.request_name( -# "org.zbus.MyGreeter", -# zbus::fdo::RequestNameFlags::ReplaceExisting.into(), -# )?; -# -loop { - let msg = connection.receive_message()?; - let msg_header = msg.header()?; - dbg!(&msg); - - match msg_header.message_type()? { - zbus::message::Type::MethodCall => { - // real code would check msg_header path(), interface() and member() - // handle invalid calls, introspection, errors etc - let arg: &str = msg.body()?; - connection.reply(&msg, &(format!("Hello {}!", arg)))?; - } - _ => continue, - } -} -# } -``` - -And check if it works as expected: - -```bash -$ busctl --user call org.zbus.MyGreeter /org/zbus/MyGreeter org.zbus.MyGreeter1 SayHello s "zbus" -s "Hello zbus!" -``` - -This is the crust of low-level message handling. It should give you all the flexibility you ever -need, but it is also easy to get it wrong. Fortunately, zbus has a simpler solution to offer. - -## Using the `ObjectServer` - -One can write an `impl` with a set of methods and let the `dbus_interface` procedural macro write -the D-Bus details for us. It will dispatch all the incoming method calls to their respective -handlers, and implicilty handle introspection requests. It also has support for properties and -signal emission. - -Let see how to use it: - -```rust,no_run -# use std::error::Error; -# use zbus::{dbus_interface, fdo}; -# -struct Greeter; - -#[dbus_interface(name = "org.zbus.MyGreeter1")] -impl Greeter { - fn say_hello(&self, name: &str) -> String { - format!("Hello {}!", name) - } -} - -fn main() -> Result<(), Box> { - let connection = zbus::Connection::new_session()?; -# fdo::DBusProxy::new(&connection)?.request_name( -# "org.zbus.MyGreeter", -# fdo::RequestNameFlags::ReplaceExisting.into(), -# )?; - - let object_server = zbus::ObjectServer::new(&connection); - object_server.at(&"/org/zbus/MyGreeter".try_into()?, Greeter)?; - loop { - if let Err(err) = object_server.try_handle_next() { - eprintln!("{}", err); - } - } - -} -``` - -(check it works with the same `busctl` command as last time) - -This time, we can also introspect the server: - -```bash -$ busctl --user introspect org.zbus.MyGreeter /org/zbus/MyGreeter -NAME TYPE SIGNATURE RESULT/VALUE FLAGS -org.freedesktop.DBus.Introspectable interface - - - -.Introspect method - s - -org.freedesktop.DBus.Peer interface - - - -.GetMachineId method - s - -.Ping method - - - -org.freedesktop.DBus.Properties interface - - - -.Get method ss v - -.GetAll method s a{sv} - -.Set method ssv - - -.PropertiesChanged signal sa{sv}as - - -org.zbus.MyGreeter1 interface - - - -.SayHello method s s - -``` - -Easy-peasy! - -> **Note:** As you must have noticed, your code needed to run a loop to continuously read incoming -messages (register the associated FD for input in poll/select to avoid blocking). This is because -at the time of the this writing (*pre-1.0*), zbus neither provides an event loop API, nor any -integration with other event loop implementations. We are evaluating different options to make this -easier, especially with *async* support. - -[D-Bus concepts]: concepts.html#bus-name--service-name -[`RequestName`]: https://dbus.freedesktop.org/doc/dbus-specification.html#bus-messages-request-name