├── dbus-crossroads ├── src_old │ ├── mod.rs │ ├── lib.rs │ ├── path.rs │ └── context.rs ├── Cargo.toml ├── LICENSE-MIT ├── src │ ├── lib.rs │ └── context.rs ├── README.md └── examples │ └── server_cr.rs ├── dbus-codegen-tests ├── src │ ├── lib.rs │ └── user_type.rs ├── tests │ ├── policykit │ │ └── mod.rs │ ├── policykit_cr │ │ └── mod.rs │ ├── policykit_asref │ │ └── mod.rs │ ├── policykit_client │ │ └── mod.rs │ ├── policykit_blocking │ │ └── mod.rs │ ├── policykit_nonblock │ │ └── mod.rs │ ├── policykit_asref_mtsync │ │ └── mod.rs │ ├── user_types_dbus │ │ ├── mod.rs │ │ ├── user_types_cr.rs │ │ ├── user_types_blocking.rs │ │ ├── user_types_client.rs │ │ └── user_types_nonblock.rs │ ├── test_parse_properties.rs │ ├── test_cr.rs │ ├── test2.rs │ ├── test1.rs │ ├── test_asref.rs │ ├── test_asref_mtsync.rs │ └── test_user_types.rs └── Cargo.toml ├── .gitignore ├── .gitmodules ├── dbus-codegen ├── src │ ├── lib.rs │ └── main.rs ├── LICENSE-MIT ├── Cargo.toml ├── data │ ├── standard_interfaces.xml │ └── org.freedesktop.DBus.xml ├── examples │ └── laundry_cr.rs └── README.md ├── dbus-native ├── README.md ├── Cargo.toml ├── src │ └── lib.rs └── tests │ └── upandrunning.rs ├── Cargo.toml ├── dbus-native-channel ├── src │ ├── lib.rs │ ├── machineid.rs │ ├── sys.rs │ ├── address.rs │ └── authentication.rs └── Cargo.toml ├── dbus ├── examples │ ├── properties_msgitem.rs │ ├── client.rs │ ├── unity_focused_window.rs │ ├── rtkit.rs │ ├── match_signal.rs │ ├── properties.rs │ └── monitor.rs ├── LICENSE-MIT ├── Cargo.toml ├── src │ ├── filters.rs │ ├── lib.rs │ ├── message │ │ ├── signalargs.rs │ │ └── matchrule.rs │ ├── prop.rs │ ├── blocking │ │ └── generated_org_freedesktop_standard_interfaces.rs │ ├── nonblock │ │ └── generated_org_freedesktop_standard_interfaces.rs │ ├── error.rs │ ├── channel.rs │ └── arg │ │ └── variantstruct_impl.rs └── changes-in-0.7.md ├── rustfmt.toml ├── dbus-strings ├── Cargo.toml └── LICENSE-MIT ├── dbus-tokio ├── src │ └── lib.rs ├── LICENSE-MIT ├── Cargo.toml ├── README.md └── examples │ ├── tokio_client.rs │ ├── tokio_server_cr.rs │ └── tokio_adv_server_cr.rs ├── .travis.yml ├── dbus-tree ├── Cargo.toml ├── LICENSE-MIT ├── src │ ├── lib.rs │ ├── utils.rs │ └── factory.rs └── examples │ ├── server.rs │ └── adv_server.rs ├── libdbus-sys ├── README.md ├── LICENSE-MIT ├── build.rs ├── Cargo.toml └── cross_compile.md ├── LICENSE-MIT └── .github └── workflows └── dbus-rs-github-ci.yml /dbus-crossroads/src_old/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /dbus-codegen-tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod user_type; 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/target 2 | **/Cargo.lock 3 | rls.toml 4 | -------------------------------------------------------------------------------- /dbus-codegen-tests/tests/policykit/mod.rs: -------------------------------------------------------------------------------- 1 | include!(concat!(env!("OUT_DIR"), "/policykit.rs")); 2 | -------------------------------------------------------------------------------- /dbus-codegen-tests/tests/policykit_cr/mod.rs: -------------------------------------------------------------------------------- 1 | include!(concat!(env!("OUT_DIR"), "/policykit_cr.rs")); 2 | -------------------------------------------------------------------------------- /dbus-codegen-tests/tests/policykit_asref/mod.rs: -------------------------------------------------------------------------------- 1 | include!(concat!(env!("OUT_DIR"), "/policykit_asref.rs")); 2 | -------------------------------------------------------------------------------- /dbus-codegen-tests/tests/policykit_client/mod.rs: -------------------------------------------------------------------------------- 1 | include!(concat!(env!("OUT_DIR"), "/policykit_client.rs")); 2 | -------------------------------------------------------------------------------- /dbus-codegen-tests/tests/policykit_blocking/mod.rs: -------------------------------------------------------------------------------- 1 | include!(concat!(env!("OUT_DIR"), "/policykit_blocking.rs")); 2 | -------------------------------------------------------------------------------- /dbus-codegen-tests/tests/policykit_nonblock/mod.rs: -------------------------------------------------------------------------------- 1 | include!(concat!(env!("OUT_DIR"), "/policykit_nonblock.rs")); 2 | -------------------------------------------------------------------------------- /dbus-codegen-tests/tests/policykit_asref_mtsync/mod.rs: -------------------------------------------------------------------------------- 1 | include!(concat!(env!("OUT_DIR"), "/policykit_asref_mtsync.rs")); 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libdbus-sys/vendor/dbus"] 2 | path = libdbus-sys/vendor/dbus 3 | url = https://gitlab.freedesktop.org/dbus/dbus.git 4 | -------------------------------------------------------------------------------- /dbus-codegen/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate xml; 2 | 3 | mod generate; 4 | 5 | pub use crate::generate::{generate, GenOpts, ServerAccess, ConnectionType}; 6 | -------------------------------------------------------------------------------- /dbus-codegen-tests/tests/user_types_dbus/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod user_types_blocking; 2 | pub mod user_types_client; 3 | pub mod user_types_cr; 4 | pub mod user_types_nonblock; 5 | -------------------------------------------------------------------------------- /dbus-codegen-tests/tests/user_types_dbus/user_types_cr.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![deny(trivial_casts)] 3 | include!(concat!(env!("OUT_DIR"), "/user_types_cr.rs")); 4 | -------------------------------------------------------------------------------- /dbus-native/README.md: -------------------------------------------------------------------------------- 1 | Experimental crate that replaces some functions in the libdbus C library with similar functions written in Rust. 2 | 3 | There is not much here yet. 4 | -------------------------------------------------------------------------------- /dbus-codegen-tests/tests/user_types_dbus/user_types_blocking.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![deny(trivial_casts)] 3 | include!(concat!(env!("OUT_DIR"), "/user_types_blocking.rs")); 4 | -------------------------------------------------------------------------------- /dbus-codegen-tests/tests/user_types_dbus/user_types_client.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![deny(trivial_casts)] 3 | include!(concat!(env!("OUT_DIR"), "/user_types_client.rs")); 4 | -------------------------------------------------------------------------------- /dbus-codegen-tests/tests/user_types_dbus/user_types_nonblock.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![deny(trivial_casts)] 3 | include!(concat!(env!("OUT_DIR"), "/user_types_nonblock.rs")); 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["libdbus-sys", "dbus", "dbus-tokio", "dbus-codegen", "dbus-codegen-tests", 3 | "dbus-crossroads", "dbus-native", "dbus-strings", "dbus-tree"] 4 | 5 | exclude = ["dbus-futures", "dbus-native-channel"] 6 | -------------------------------------------------------------------------------- /dbus-native-channel/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(unsafe_code)] 2 | 3 | /// This is a low-level crate meant for use by the dbus crate. 4 | 5 | pub mod machineid; 6 | 7 | pub mod address; 8 | 9 | pub mod authentication; 10 | 11 | #[allow(unsafe_code)] 12 | mod sys; 13 | -------------------------------------------------------------------------------- /dbus-native-channel/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dbus-native-channel" 3 | version = "0.1.0" 4 | authors = ["David Henningsson "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | libc = "0.2.80" 11 | -------------------------------------------------------------------------------- /dbus-native/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dbus-native" 3 | version = "0.1.0" 4 | authors = ["David Henningsson "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | libc = "0.2.66" 11 | dbus-strings = { path = "../dbus-strings" } 12 | dbus-native-channel = { path = "../dbus-native-channel" } 13 | -------------------------------------------------------------------------------- /dbus/examples/properties_msgitem.rs: -------------------------------------------------------------------------------- 1 | use dbus::ffidisp::Connection; 2 | use dbus::arg::messageitem::Props; 3 | 4 | fn main() { 5 | let c = Connection::new_system().unwrap(); 6 | let p = Props::new(&c, "org.freedesktop.PolicyKit1", "/org/freedesktop/PolicyKit1/Authority", 7 | "org.freedesktop.PolicyKit1.Authority", 10000); 8 | println!("BackendVersion: {:?}", p.get("BackendVersion").unwrap()) 9 | } 10 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 140 2 | fn_single_line = true 3 | where_single_line = true 4 | unstable_features = true 5 | fn_args_layout = "Compressed" 6 | use_small_heuristics = "Max" 7 | 8 | "Please don't run rustfmt on this repository" = true 9 | # Basically rustfmt has a tendency to split things over way too many lines 10 | # despite the above attempts to configure it. E g, 11 | # https://github.com/rust-lang/rustfmt/issues/3745 12 | 13 | -------------------------------------------------------------------------------- /dbus-strings/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dbus-strings" 3 | version = "0.1.0" 4 | authors = ["David Henningsson "] 5 | edition = "2018" 6 | 7 | description = "Rust native implementation of different D-Bus string types" 8 | repository = "https://github.com/diwic/dbus-rs" 9 | documentation = "https://docs.rs/dbus-strings" 10 | keywords = ["D-Bus", "DBus", "IPC"] 11 | license = "Apache-2.0/MIT" 12 | 13 | [dependencies] 14 | -------------------------------------------------------------------------------- /dbus-native/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(unsafe_code)] 2 | 3 | 4 | /// This is a low-level crate meant for use by the dbus crate. 5 | /// 6 | /// No stability guarantees for this crate. 7 | 8 | pub use dbus_native_channel::{machineid, address, authentication}; 9 | 10 | pub mod message; 11 | 12 | pub mod types; 13 | 14 | pub mod marshalled; 15 | 16 | pub mod strings { 17 | //! Re-export of the dbus_strings crate 18 | pub use dbus_strings::*; 19 | } 20 | -------------------------------------------------------------------------------- /dbus-tokio/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(missing_docs)] 2 | //! Tokio integration for dbus 3 | //! 4 | //! This crate contains code for interfacing `dbus` with `tokio`, enabling async/non-blocking operation. 5 | //! 6 | //! This crate contains only the connection components, most of the async code is in the `dbus::nonblock` 7 | //! module, where you can find additional information. 8 | //! 9 | //! There are some examples in the examples directory to help you get started. 10 | 11 | pub mod connection; 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | dist: xenial 3 | 4 | cache: 5 | cargo: true 6 | timeout: 1000 7 | 8 | before_install: 9 | - sudo apt-get install --no-install-recommends -y libdbus-1-dev dbus at-spi2-core 10 | 11 | before_script: 12 | - export DBUS_SESSION_BUS_ADDRESS=`dbus-daemon --session --print-address --fork` 13 | 14 | script: 15 | - cargo update 16 | - cargo check --all 17 | - cargo test --all -- --nocapture --color always 18 | - cd dbus-codegen && cargo test --all --no-default-features -- --nocapture --color always 19 | -------------------------------------------------------------------------------- /dbus-tree/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dbus-tree" 3 | version = "0.9.2" 4 | authors = ["David Henningsson "] 5 | edition = "2018" 6 | 7 | description = "Framework for writing D-Bus method handlers (legacy)" 8 | repository = "https://github.com/diwic/dbus-rs" 9 | documentation = "https://docs.rs/dbus-tree" 10 | keywords = ["D-Bus", "DBus", "IPC"] 11 | license = "Apache-2.0/MIT" 12 | categories = ["os::unix-apis", "api-bindings"] 13 | readme = "../README.md" 14 | 15 | [dependencies] 16 | dbus = { path = "../dbus", version = "0.9" } 17 | 18 | [badges] 19 | maintenance = { status = "passively-maintained" } 20 | -------------------------------------------------------------------------------- /dbus-crossroads/src_old/lib.rs: -------------------------------------------------------------------------------- 1 | //! Will eventually superseed the "tree" module. It's unstable and experimental for now. 2 | #![allow(unused_imports, dead_code, missing_docs, unused_variables)] 3 | 4 | mod info; 5 | mod handlers; 6 | mod crossroads; 7 | mod stdimpl; 8 | mod path; 9 | mod context; 10 | 11 | pub use dbus::tree::MethodErr as MethodErr; 12 | 13 | // pub use self::info::{IfaceInfo, MethodInfo, PropInfo}; 14 | 15 | pub use self::crossroads::{Crossroads}; 16 | 17 | pub use self::path::{Path, PathData}; 18 | 19 | pub use self::handlers::{Handlers, Par, Local}; 20 | 21 | pub use self::context::{MsgCtx, RefCtx, AsyncMsgCtx}; 22 | -------------------------------------------------------------------------------- /dbus-native-channel/src/machineid.rs: -------------------------------------------------------------------------------- 1 | fn is_hex_char(b: u8) -> bool { 2 | match b { 3 | b'0'..=b'9' | b'a'..=b'f' | b'A'..=b'F' => true, 4 | _ => false, 5 | } 6 | } 7 | 8 | pub fn read_machine_id() -> Result> { 9 | let mut v = std::fs::read("/etc/machine-id")?; 10 | while v.last() == Some(&b'\n') { v.pop(); }; 11 | if v.len() != 32 || v.iter().any(|x| !is_hex_char(*x)) { Err("Malformed machine-id file")? }; 12 | let v = String::from_utf8(v)?; 13 | Ok(v) 14 | } 15 | 16 | #[test] 17 | fn machineid() { 18 | let m = read_machine_id().unwrap(); 19 | println!("My machine id is: {}", m); 20 | } 21 | -------------------------------------------------------------------------------- /dbus-crossroads/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dbus-crossroads" 3 | version = "0.5.3" 4 | authors = ["David Henningsson "] 5 | edition = "2018" 6 | description = "Framework for writing D-Bus method handlers" 7 | repository = "https://github.com/diwic/dbus-rs/" 8 | documentation = "https://docs.rs/dbus-crossroads" 9 | keywords = ["D-Bus", "DBus", "IPC"] 10 | license = "Apache-2.0/MIT" 11 | categories = ["os::unix-apis", "api-bindings"] 12 | readme = "README.md" 13 | 14 | [dependencies] 15 | dbus = { path = "../dbus", version = "0.9.3" } 16 | 17 | [dev-dependencies] 18 | tokio = { version = "1.14.0", features = ["rt", "test-util", "macros", "sync"] } 19 | dbus-tokio = { path = "../dbus-tokio" } 20 | 21 | [badges] 22 | maintenance = { status = "actively-developed" } 23 | -------------------------------------------------------------------------------- /dbus-codegen-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Dummy crate to genenerate code from latest codegen binary and run tests against that code. 2 | 3 | [package] 4 | name = "codegen-tests" 5 | version = "0.1.0" 6 | authors = ["Zeeshan Ali ", 7 | "David Henningsson "] 8 | license = "Apache-2.0/MIT" 9 | build = "build.rs" 10 | edition = "2018" 11 | 12 | [lib] 13 | path = "src/lib.rs" 14 | 15 | [dependencies] 16 | dbus = { path = "../dbus", version = "0.9", features=["futures"] } 17 | dbus-tree = { path = "../dbus-tree", version = "0.9" } 18 | dbus-crossroads = { path = "../dbus-crossroads" } 19 | dbus-tokio = { path = "../dbus-tokio" } 20 | tokio = {version = "1.0", features=["time", "net", "rt", "rt-multi-thread"]} 21 | 22 | [build-dependencies] 23 | dbus-codegen = { path = "../dbus-codegen" } 24 | -------------------------------------------------------------------------------- /libdbus-sys/README.md: -------------------------------------------------------------------------------- 1 | Raw FFI bindings to libdbus 2 | --------------------------- 3 | 4 | Libdbus is licensed under GPL-2.0+/AFL (Academic free license), whereas the bindings are licensed under MIT/Apache-2.0. 5 | 6 | By default, libdbus is dynamically linked, meaning that `libdbus-1.so` must be installed on the target system (which it is, by default, in all common Linux distributions). 7 | 8 | As an option, libdbus can be built from source and included in the final executable. For this, enable the `vendored` feature. The crates.io package contains source code from libdbus; but it is only included in the build if the `vendored` feature is enabled. 9 | 10 | The `vendored` feature is the current recommended way to cross compile dbus-rs, although some other methods are mentioned [here](https://github.com/diwic/dbus-rs/blob/master/libdbus-sys/cross_compile.md). 11 | 12 | -------------------------------------------------------------------------------- /dbus/examples/client.rs: -------------------------------------------------------------------------------- 1 | use dbus::blocking::Connection; 2 | use std::time::Duration; 3 | 4 | fn main() -> Result<(), Box> { 5 | // First open up a connection to the session bus. 6 | let conn = Connection::new_session()?; 7 | 8 | // Second, create a wrapper struct around the connection that makes it easy 9 | // to send method calls to a specific destination and path. 10 | let proxy = conn.with_proxy("org.freedesktop.DBus", "/", Duration::from_millis(5000)); 11 | 12 | // Now make the method call. The ListNames method call takes zero input parameters and 13 | // one output parameter which is an array of strings. 14 | // Therefore the input is a zero tuple "()", and the output is a single tuple "(names,)". 15 | let (names,): (Vec,) = proxy.method_call("org.freedesktop.DBus", "ListNames", ())?; 16 | 17 | // Let's print all the names to stdout. 18 | for name in names { println!("{}", name); } 19 | 20 | Ok(()) 21 | } 22 | 23 | -------------------------------------------------------------------------------- /dbus/examples/unity_focused_window.rs: -------------------------------------------------------------------------------- 1 | extern crate dbus; 2 | 3 | // Tracks currently focused window under the Unity desktop by listening to the 4 | // FocusedWindowChanged signal. The signal contains "window_id", "app_id" and "stage", 5 | // we print only "app_id". 6 | 7 | use dbus::{ffidisp::Connection, Message, MessageType}; 8 | 9 | fn focus_msg(msg: &Message) -> Option<&str> { 10 | if msg.msg_type() != MessageType::Signal { return None }; 11 | if &*msg.interface().unwrap() != "com.canonical.Unity.WindowStack" { return None }; 12 | if &*msg.member().unwrap() != "FocusedWindowChanged" { return None }; 13 | let (_, app) = msg.get2::(); 14 | app 15 | } 16 | 17 | fn main() { 18 | let c = Connection::new_session().unwrap(); 19 | c.add_match("interface='com.canonical.Unity.WindowStack',member='FocusedWindowChanged'").unwrap(); 20 | 21 | loop { 22 | if let Some(msg) = c.incoming(1000).next() { 23 | if let Some(app) = focus_msg(&msg) { 24 | println!("{} has now focus.", app); 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2018 David Henningsson and other contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /dbus/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2018 David Henningsson and other contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /dbus-codegen/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2018 David Henningsson and other contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /dbus-strings/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2018 David Henningsson and other contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /dbus-tokio/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2018 David Henningsson and other contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /dbus-tree/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2018 David Henningsson and other contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /libdbus-sys/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2018 David Henningsson and other contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /dbus-crossroads/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2018 David Henningsson and other contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /dbus-codegen-tests/tests/test_parse_properties.rs: -------------------------------------------------------------------------------- 1 | #[allow(dead_code)] 2 | #[deny(trivial_casts)] 3 | mod policykit_client; 4 | 5 | use dbus::arg::{RefArg, Variant}; 6 | use policykit_client::OrgFreedesktopPolicyKit1AuthorityProperties; 7 | use std::collections::HashMap; 8 | 9 | #[test] 10 | fn test_parse_properties() { 11 | let mut properties: HashMap>> = HashMap::new(); 12 | properties.insert("BackendFeatures".to_string(), Variant(Box::new(42u32))); 13 | properties.insert( 14 | "BackendName".to_string(), 15 | Variant(Box::new("name".to_string())), 16 | ); 17 | let mut interfaces = HashMap::new(); 18 | interfaces.insert( 19 | "org.freedesktop.PolicyKit1.Authority".to_string(), 20 | properties, 21 | ); 22 | 23 | let authority_properties = 24 | OrgFreedesktopPolicyKit1AuthorityProperties::from_interfaces(&interfaces).unwrap(); 25 | assert_eq!(authority_properties.backend_features(), Some(42)); 26 | assert_eq!( 27 | authority_properties.backend_name().cloned(), 28 | Some("name".to_string()) 29 | ); 30 | assert_eq!(authority_properties.backend_version(), None); 31 | } 32 | -------------------------------------------------------------------------------- /dbus-codegen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dbus-codegen" 3 | version = "0.12.0" 4 | authors = ["David Henningsson "] 5 | description = "Binary crate to generate Rust code from XML introspection data" 6 | license = "Apache-2.0/MIT" 7 | categories = ["os::unix-apis", "api-bindings"] 8 | repository = "https://github.com/diwic/dbus-rs" 9 | keywords = ["D-Bus", "DBus"] 10 | readme = "README.md" 11 | edition = "2018" 12 | 13 | [lib] 14 | path = "src/lib.rs" 15 | 16 | [[bin]] 17 | name = "dbus-codegen-rust" 18 | path = "src/main.rs" 19 | required-features = ["clap"] 20 | 21 | [features] 22 | default = ["dbus", "clap"] 23 | 24 | [dependencies] 25 | xml-rs = "0.8.3" 26 | dbus = { path = "../dbus", version = "0.9", optional = true } 27 | dbus-tree = { path = "../dbus-tree", version = "0.9", optional = true } 28 | dbus-crossroads = { path = "../dbus-crossroads", version = "0.5", optional = true } 29 | clap = { version = "4.5", optional = true } 30 | 31 | [badges] 32 | maintenance = { status = "actively-developed" } 33 | 34 | [[example]] 35 | name = "adv_server_codegen" 36 | required-features = ["dbus", "dbus-tree"] 37 | 38 | [[example]] 39 | name = "laundry_cr" 40 | required-features = ["dbus", "dbus-crossroads"] 41 | -------------------------------------------------------------------------------- /dbus-crossroads/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! dbus-crossroads is a framework for quickly implementing interfaces on object paths, i e 2 | //! server side. 3 | //! 4 | //! To get started, you can jump into the commented examples, 5 | //! one for [sync](https://github.com/diwic/dbus-rs/blob/master/dbus-crossroads/examples/server_cr.rs) 6 | //! one for [async (dbus-tokio)](https://github.com/diwic/dbus-rs/blob/master/dbus-tokio/examples/tokio_server_cr.rs), 7 | //! and one [slightly more advanced](https://github.com/diwic/dbus-rs/blob/master/dbus-tokio/examples/tokio_adv_server_cr.rs), 8 | //! or familiarize yourself using this API reference. 9 | 10 | mod context; 11 | mod crossroads; 12 | mod ifacedesc; 13 | mod stdimpl; 14 | 15 | pub use dbus::MethodErr as MethodErr; 16 | 17 | pub use context::Context; 18 | pub use stdimpl::PropContext; 19 | pub use crossroads::{Crossroads, IfaceToken}; 20 | 21 | pub use ifacedesc::{MethodDesc, SignalDesc, IfaceBuilder, PropBuilder}; 22 | 23 | #[cfg(test)] 24 | mod test; 25 | 26 | mod utils { 27 | use std::fmt; 28 | pub (crate) struct Dbg(pub T); 29 | 30 | impl fmt::Debug for Dbg { 31 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "...") } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /libdbus-sys/build.rs: -------------------------------------------------------------------------------- 1 | extern crate pkg_config; 2 | use std::error::Error; 3 | 4 | #[cfg(feature = "vendored")] 5 | mod build_vendored; 6 | 7 | fn main() -> Result<(), Box> { 8 | // Invalidate the built crate whenever these files change 9 | println!("cargo:rerun-if-changed=build.rs"); 10 | println!("cargo:rerun-if-changed=build_vendored.rs"); 11 | 12 | // See https://github.com/joshtriplett/metadeps/issues/9 for why we don't use 13 | // metadeps here, but instead keep this manually in sync with Cargo.toml. 14 | #[cfg(not(feature = "vendored"))] 15 | if let Err(e) = pkg_config::Config::new().atleast_version("1.6").probe("dbus-1") { 16 | eprintln!("pkg_config failed: {}", e); 17 | eprintln!( 18 | "One possible solution is to check whether packages\n\ 19 | 'libdbus-1-dev' and 'pkg-config' are installed:\n\ 20 | On Ubuntu:\n\ 21 | sudo apt install libdbus-1-dev pkg-config\n\ 22 | On Fedora:\n\ 23 | sudo dnf install dbus-devel pkgconf-pkg-config\n" 24 | ); 25 | panic!(); 26 | } 27 | 28 | #[cfg(feature = "vendored")] 29 | build_vendored::build_libdbus()?; 30 | Ok(()) 31 | } 32 | -------------------------------------------------------------------------------- /libdbus-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "libdbus-sys" 4 | version = "0.2.6" 5 | authors = ["David Henningsson "] 6 | edition = "2018" 7 | 8 | description = "FFI bindings to libdbus." 9 | repository = "https://github.com/diwic/dbus-rs" 10 | documentation = "https://docs.rs/libdbus-sys" 11 | keywords = ["D-Bus", "DBus", "libdbus"] 12 | license = "Apache-2.0/MIT" 13 | categories = ["os::unix-apis", "external-ffi-bindings"] 14 | build = "build.rs" 15 | links = "dbus" 16 | readme = "README.md" 17 | exclude = ["/vendor/dbus/tools/", "/vendor/dbus/test/", "/vendor/dbus/maint/"] 18 | 19 | [features] 20 | # By default use pkg-config to locate the local libdbus installation 21 | default = ["pkg-config"] 22 | # Provide vendoring as an option, which will compile libdbus from source for the target 23 | vendored = ["cc"] 24 | 25 | [build-dependencies] 26 | pkg-config = {version = "0.3", optional = true} 27 | cc = {version = "1.0.78", optional = true } 28 | 29 | [package.metadata.pkg-config] 30 | dbus-1 = "1.6" 31 | 32 | [badges] 33 | is-it-maintained-open-issues = { repository = "diwic/dbus-rs" } 34 | is-it-maintained-issue-resolution = { repository = "diwic/dbus-rs" } 35 | travis-ci = { repository = "diwic/dbus-rs" } 36 | -------------------------------------------------------------------------------- /dbus-codegen-tests/tests/test_cr.rs: -------------------------------------------------------------------------------- 1 | use dbus_crossroads::Crossroads; 2 | 3 | #[allow(dead_code)] 4 | #[deny(trivial_casts)] 5 | mod policykit_cr; 6 | 7 | impl policykit_cr::DBusProperties for () { 8 | fn get(&mut self, interfacename: String, propertyname: String) -> Result>, dbus::MethodErr> { 9 | assert_eq!(interfacename, "Interface.Name"); 10 | assert_eq!(propertyname, "Property.Name"); 11 | Ok(::dbus::arg::Variant(Box::new(5u8))) 12 | } 13 | 14 | fn get_all(&mut self, _interfacename: String) -> 15 | Result<::std::collections::HashMap>>, dbus::MethodErr> { unimplemented!() } 16 | 17 | fn set(&mut self, _interfacename: String, _propertyname: String, value: dbus::arg::Variant>) -> Result<(), dbus::MethodErr> { 18 | assert_eq!(dbus::arg::RefArg::as_str(&value), Some("Hello")); 19 | Err(("A.B.C", "Error.Message").into()) 20 | } 21 | } 22 | 23 | 24 | #[test] 25 | fn test_cr() { 26 | let mut cr = Crossroads::new(); 27 | let token = policykit_cr::register_dbus_properties::<()>(&mut cr); 28 | cr.insert("/", &[token], ()); 29 | // TODO 30 | } 31 | -------------------------------------------------------------------------------- /dbus-tokio/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["David Henningsson "] 3 | name = "dbus-tokio" 4 | version = "0.7.6" 5 | 6 | description = "Makes it possible to use Tokio with D-Bus, which is a bus commonly used on Linux for inter-process communication." 7 | repository = "https://github.com/diwic/dbus-rs" 8 | documentation = "https://docs.rs/dbus-tokio" 9 | keywords = ["D-Bus", "DBus"] 10 | license = "Apache-2.0/MIT" 11 | categories = ["os::unix-apis", "api-bindings", "asynchronous"] 12 | edition = "2018" 13 | readme = "README.md" 14 | 15 | [dependencies] 16 | dbus = { path = "../dbus", version = "0.9", features=["futures"] } 17 | libc = "0.2.69" 18 | tokio = {version = "1.0", features=["time", "net"]} 19 | dbus-crossroads = { path = "../dbus-crossroads", optional = true, version = "0.5" } 20 | 21 | [dev-dependencies] 22 | futures = "0.3.1" 23 | tokio = {version = "1.0", features=["time", "net", "macros", "rt-multi-thread"]} 24 | dbus-tree = {path = "../dbus-tree", version="0.9"} 25 | 26 | [badges] 27 | maintenance = { status = "actively-developed" } 28 | 29 | [[example]] 30 | name = "tokio_server_cr" 31 | required-features = ["dbus-crossroads"] 32 | 33 | [[example]] 34 | name = "tokio_adv_server_cr" 35 | required-features = ["dbus-crossroads"] 36 | -------------------------------------------------------------------------------- /dbus-native-channel/src/sys.rs: -------------------------------------------------------------------------------- 1 | use std::os::unix::net::UnixStream; 2 | 3 | pub fn getuid() -> u32 { 4 | let x = unsafe { libc::getuid() }; 5 | x as u32 6 | } 7 | 8 | pub fn connect_blocking(addr: &libc::sockaddr_un) -> Result> { 9 | // We have to do this manually because rust std does not support abstract sockets. 10 | // https://github.com/rust-lang/rust/issues/42048 11 | 12 | assert_eq!(addr.sun_family, libc::AF_UNIX as libc::sa_family_t); 13 | 14 | let fd = unsafe { libc::socket(libc::AF_UNIX, libc::SOCK_STREAM, 0) }; 15 | if fd < 0 { Err("Unable to create unix socket")? } 16 | 17 | let mut sock_len = std::mem::size_of::() as libc::socklen_t; 18 | let mut x = addr.sun_path.len()-1; 19 | while addr.sun_path[x] == 0 { 20 | x -= 1; 21 | sock_len -= 1; 22 | } 23 | 24 | let addr_ptr = addr as *const _ as *const libc::sockaddr; 25 | let r = unsafe { libc::connect(fd, addr_ptr, sock_len) }; 26 | if r != 0 { 27 | let errno = unsafe { (*libc::__errno_location()) as i32 }; 28 | Err(format!("Unable to connect to unix socket, errno = {}", errno))? 29 | } 30 | 31 | use std::os::unix::io::FromRawFd; 32 | let u = unsafe { UnixStream::from_raw_fd(fd) }; 33 | Ok(u) 34 | } 35 | -------------------------------------------------------------------------------- /dbus-tree/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Contains functionality for dispatching methods on a D-Bus "server". 2 | //! 3 | //! # Example 4 | //! ```rust,no_run 5 | //! use dbus_tree::Factory; 6 | //! use dbus::ffidisp::Connection; 7 | //! let f = Factory::new_fn::<()>(); 8 | //! /* Add a method returning "Thanks!" on interface "com.example.dbus.rs" 9 | //! on object path "/example". */ 10 | //! let t = f.tree(()).add(f.object_path("/example", ()).introspectable() 11 | //! .add(f.interface("com.example.dbus.rs", ()) 12 | //! .add_m(f.method("CallMe", (), |m| { 13 | //! Ok(vec!(m.msg.method_return().append1("Thanks!"))) } 14 | //! ).out_arg("s")) 15 | //! )); 16 | //! 17 | //! let c = Connection::new_session().unwrap(); 18 | //! t.set_registered(&c, true).unwrap(); 19 | //! c.add_handler(t); 20 | //! /* Run forever */ 21 | //! loop { c.incoming(1000).next(); } 22 | //! ``` 23 | //! 24 | //! See `examples/server.rs` and `examples/adv_server.rs` for more thorough examples. 25 | 26 | mod utils; 27 | mod methodtype; 28 | mod leaves; 29 | mod objectpath; 30 | mod factory; 31 | 32 | pub use dbus::MethodErr; 33 | 34 | pub use self::utils::{Argument, Iter}; 35 | pub use self::methodtype::{MethodInfo, PropInfo, MethodResult, MethodType, DataType, MTFn, MTFnMut, MTSync}; 36 | pub use self::leaves::{Method, Signal, Property, Access, EmitsChangedSignal}; 37 | pub use self::objectpath::{Interface, ObjectPath, Tree, TreeServer}; 38 | pub use self::factory::Factory; 39 | -------------------------------------------------------------------------------- /dbus/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "dbus" 4 | version = "0.9.9" 5 | authors = ["David Henningsson "] 6 | 7 | description = "Bindings to D-Bus, which is a bus commonly used on Linux for inter-process communication." 8 | repository = "https://github.com/diwic/dbus-rs" 9 | documentation = "https://docs.rs/dbus" 10 | keywords = ["D-Bus", "DBus", "IPC"] 11 | license = "Apache-2.0/MIT" 12 | categories = ["os::unix-apis", "api-bindings"] 13 | readme = "../README.md" 14 | edition = "2018" 15 | 16 | [dependencies] 17 | libc = "0.2.66" 18 | libdbus-sys = { path = "../libdbus-sys", version = "0.2.6" } 19 | futures-util = { version = "0.3", optional = true, default-features = false } 20 | futures-channel = { version = "0.3", optional = true } 21 | futures-executor = { version = "0.3", optional = true } 22 | # dbus-native-channel = { path = "../dbus-native-channel", version = "0.1", optional = true } 23 | 24 | [target.'cfg(windows)'.dependencies] 25 | windows-sys = { version = "0.61.0", features = ["Win32_Networking_WinSock"] } 26 | 27 | [dev-dependencies] 28 | tempfile = "3" 29 | 30 | [features] 31 | no-string-validation = [] 32 | stdfd = [] 33 | vendored = ["libdbus-sys/vendored"] 34 | futures = ["futures-util", "futures-channel"] 35 | # Not ready yet 36 | # native-channel = ["futures-executor", "futures-util/io", "dbus-native-channel"] 37 | 38 | [badges] 39 | maintenance = { status = "actively-developed" } 40 | 41 | [package.metadata.docs.rs] 42 | features = [ "futures" ] 43 | -------------------------------------------------------------------------------- /dbus-native/tests/upandrunning.rs: -------------------------------------------------------------------------------- 1 | use dbus_native as dbus; 2 | use dbus::{address, message, authentication}; 3 | 4 | #[test] 5 | fn connect_to_session_bus() { 6 | let addr = address::read_session_address().unwrap(); 7 | let stream = address::connect_blocking(&addr).unwrap(); 8 | 9 | let mut reader = std::io::BufReader::new(&stream); 10 | let mut writer = &stream; 11 | assert!(!authentication::Authentication::blocking(&mut reader, &mut writer, false).unwrap()); 12 | writer.flush().unwrap(); 13 | 14 | // Send Hello message 15 | 16 | let m = dbus::message::get_hello_message(); 17 | println!("{:?}", m); 18 | let v = m.marshal(std::num::NonZeroU32::new(1u32).unwrap(), false).unwrap(); 19 | println!("{:?}", v); 20 | 21 | use std::io::{Write}; 22 | writer.write_all(&v).unwrap(); 23 | writer.flush().unwrap(); 24 | 25 | let mut mr = message::MessageReader::new(); 26 | let v = mr.block_until_next_message(&mut reader).unwrap(); 27 | println!("{:?}", v); 28 | let reply = message::Message::demarshal(&v).unwrap().unwrap(); 29 | println!("{:?}", reply); 30 | 31 | let mut body = reply.read_body().iter(); 32 | let r = body.next().unwrap().unwrap(); 33 | let r2 = r.parse().unwrap(); 34 | assert!(body.next().is_none()); 35 | assert_eq!(reply.reply_serial().unwrap().get(), 1u32); 36 | assert!(r2.as_dbus_str().unwrap().starts_with(":1.")); 37 | println!("Our ID is {}", r2.as_dbus_str().unwrap()); 38 | 39 | } 40 | -------------------------------------------------------------------------------- /dbus-tokio/README.md: -------------------------------------------------------------------------------- 1 | Tokio integration for D-Bus 2 | =========================== 3 | 4 | This crate integrates the [dbus](https://docs.rs/dbus) crate with the [tokio](https://tokio.rs) async framework. 5 | 6 | See the [examples](https://github.com/diwic/dbus-rs/tree/master/dbus-tokio/examples) for how to get started. 7 | 8 | For server side functionality, you can use the [dbus-crossroads](https://docs.rs/dbus-crossroads) 9 | crate to have async method and property handlers. 10 | 11 | Invitation 12 | ---------- 13 | 14 | You are hereby invited to participate in the development of dbus-tokio and the other dbus crates: 15 | 16 | * If you have discovered what you believe is a bug, [file an issue](https://github.com/diwic/dbus-rs/issues). 17 | * If you have questions or comments that the documentation cannot answer in an easy way, [start a discussion](https://github.com/diwic/dbus-rs/discussions). 18 | * If you have smaller improvements to code, documentation, examples etc, go ahead and [submit a pull request](https://github.com/diwic/dbus-rs/pulls). 19 | Larger pieces of work are better off discussed first. 20 | 21 | The code is Apache 2.0 / MIT dual licensed. Any code submitted in Pull Requests, discussions or issues are assumed to have this license, 22 | unless explicitly stated otherwise. 23 | 24 | Requirements 25 | ============ 26 | 27 | Same as for the D-Bus crate: [Libdbus](https://dbus.freedesktop.org/releases/dbus/) 1.6 or higher, and latest stable release of [Rust](https://www.rust-lang.org/). 28 | If you run Ubuntu (any maintained version should be okay), this means having the `libdbus-1-dev` and `pkg-config` packages installed while building, 29 | and the `libdbus-1-3` package installed while running. 30 | -------------------------------------------------------------------------------- /dbus-crossroads/README.md: -------------------------------------------------------------------------------- 1 | [![crates.io](https://img.shields.io/crates/v/dbus-crossroads.svg)](https://crates.io/crates/dbus-crossroads) 2 | [![API documentation](https://docs.rs/dbus-crossroads/badge.svg)](https://docs.rs/dbus-crossroads) 3 | [![license](https://img.shields.io/crates/l/dbus-crossroads.svg)](https://crates.io/crates/dbus-crossroads) 4 | 5 | dbus-crossroads is a library that helps you implement interfaces on object paths, i e 6 | "server side" method handler code. 7 | 8 | It is not as mature as `dbus::tree` yet, but the API should be fairly stable and easier 9 | to use than `dbus::tree`. Go ahead and use it, and report any issues you find! 10 | 11 | To get started, you can jump into the commented examples, 12 | one for [sync](https://github.com/diwic/dbus-rs/blob/master/dbus-crossroads/examples/server_cr.rs) 13 | one for [async (dbus-tokio)](https://github.com/diwic/dbus-rs/blob/master/dbus-tokio/examples/tokio_server_cr.rs), 14 | and one [slightly more advanced](https://github.com/diwic/dbus-rs/blob/master/dbus-tokio/examples/tokio_adv_server_cr.rs), 15 | or familiarize yourself using [the API reference](https://docs.rs/dbus-crossroads). 16 | 17 | Design decisions (compared to `dbus::tree`): 18 | 19 | * First class support for both sync and async methods 20 | * Detect method arguments automatically through generics 21 | * ...but less generics in structs you use, which means less cluttered API 22 | * The tree/crossroads instance is `Send` (but not `Sync`). 23 | * You can now modify the instance from within a method handler 24 | * It is objects (paths) that can contain custom data, and they can contain different data for different objects 25 | * Interface descriptions are kept in a registry, which means less reference counting 26 | -------------------------------------------------------------------------------- /dbus/src/filters.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | use std::vec::Vec; 3 | use crate::message::MatchRule; 4 | use crate::Message; 5 | use crate::channel::Token; 6 | 7 | pub struct Filters { 8 | list: BTreeMap, F)>, 9 | nextid: Token, 10 | } 11 | 12 | 13 | impl Default for Filters { 14 | fn default() -> Self { Filters { 15 | list: BTreeMap::new(), 16 | nextid: Token(1), 17 | }} 18 | } 19 | 20 | impl Filters { 21 | pub fn add(&mut self, m: MatchRule<'static>, f: F) -> Token { 22 | let id = self.nextid; 23 | self.nextid.0 += 1; 24 | self.list.insert(id, (m, f)); 25 | id 26 | } 27 | 28 | pub fn insert(&mut self, (t, m, f): (Token, MatchRule<'static>, F)) { 29 | self.list.insert(t, (m, f)); 30 | } 31 | 32 | pub fn remove(&mut self, id: Token) -> Option<(MatchRule<'static>, F)> { 33 | self.list.remove(&id) 34 | } 35 | 36 | /// Removes and returns the first filter which matches the given message. 37 | pub fn remove_first_matching(&mut self, msg: &Message) -> Option<(Token, MatchRule<'static>, F)> { 38 | if let Some(k) = self.list.iter_mut().find_map(|(k, v)| if v.0.matches(&msg) { Some(*k) } else { None }) { 39 | let v = self.list.remove(&k).unwrap(); 40 | Some((k, v.0, v.1)) 41 | } else { 42 | None 43 | } 44 | } 45 | 46 | /// Removes and returns all filters which match the given message. 47 | pub fn remove_all_matching(&mut self, msg: &Message) -> Vec<(Token, MatchRule<'static>, F)> { 48 | let matching: Vec<_> = self.list.iter().filter_map(|(k, v)| if v.0.matches(&msg) { Some(*k) } else { None }).collect(); 49 | matching 50 | .into_iter() 51 | .map(|k| { 52 | let v = self.list.remove(&k).unwrap(); 53 | (k, v.0, v.1) 54 | }) 55 | .collect() 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /dbus-codegen-tests/src/user_type.rs: -------------------------------------------------------------------------------- 1 | use dbus::arg::{Append, IterAppend, Iter, Arg, ArgType, Get, RefArg}; 2 | use dbus::strings::Signature; 3 | use std::any; 4 | 5 | #[derive(Debug, Clone, PartialEq, Eq)] 6 | pub struct MyType 7 | { 8 | f: (String, String), 9 | } 10 | 11 | impl MyType { 12 | pub fn new(s1: String, s2: String) -> Self { 13 | MyType{f:(s1, s2)} 14 | } 15 | } 16 | 17 | impl Append for MyType { 18 | fn append_by_ref(&self, i: &mut IterAppend) { 19 | self.f.append_by_ref(i); 20 | } 21 | } 22 | 23 | impl Arg for MyType { 24 | const ARG_TYPE: ArgType = ArgType::Struct; 25 | fn signature() -> Signature<'static> { 26 | Signature::new("(ss)".to_string()).unwrap() 27 | } 28 | } 29 | 30 | impl<'a> Get<'a> for MyType { 31 | fn get(i: &mut Iter<'a>) -> Option { 32 | let f = <(String, String)>::get(i)?; 33 | Some(MyType{f}) 34 | } 35 | } 36 | 37 | impl RefArg for MyType { 38 | fn arg_type(&self) -> ArgType { 39 | ::ARG_TYPE 40 | } 41 | fn signature(&self) -> Signature<'static> { 42 | ::signature() 43 | } 44 | fn append(&self, i: &mut IterAppend) { 45 | ::append_by_ref(self, i) 46 | } 47 | fn as_any(&self) -> &dyn any::Any { 48 | self 49 | } 50 | fn as_any_mut(&mut self) -> &mut dyn any::Any { 51 | self 52 | } 53 | fn as_i64(&self) -> Option { 54 | None 55 | } 56 | fn as_u64(&self) -> Option { 57 | None 58 | } 59 | fn as_f64(&self) -> Option { 60 | None 61 | } 62 | fn as_str(&self) -> Option<&str> { 63 | None 64 | } 65 | fn box_clone(&self) -> Box { 66 | Box::new(self.clone()) 67 | } 68 | fn array_clone(v: &[Self]) -> Option> 69 | where 70 | Self: Sized, 71 | { 72 | Some(Box::new(v.to_vec())) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /dbus/examples/rtkit.rs: -------------------------------------------------------------------------------- 1 | /* This example asks the rtkit service to make our thread realtime priority. 2 | Rtkit puts a few limitations on us to let us become realtime, such as setting 3 | RLIMIT_RTTIME correctly, hence the syscalls. */ 4 | 5 | use std::cmp; 6 | use std::time::Duration; 7 | 8 | fn make_realtime(prio: u32) -> Result> { 9 | let c = dbus::blocking::Connection::new_system()?; 10 | 11 | let proxy = c.with_proxy("org.freedesktop.RealtimeKit1", "/org/freedesktop/RealtimeKit1", 12 | Duration::from_millis(10000)); 13 | use dbus::blocking::stdintf::org_freedesktop_dbus::Properties; 14 | 15 | // Make sure we don't fail by wanting too much 16 | let max_prio: i32 = proxy.get("org.freedesktop.RealtimeKit1", "MaxRealtimePriority")?; 17 | let prio = cmp::min(prio, max_prio as u32); 18 | 19 | // Enforce RLIMIT_RTPRIO, also a must before asking rtkit for rtprio 20 | let max_rttime: i64 = proxy.get("org.freedesktop.RealtimeKit1", "RTTimeUSecMax")?; 21 | let new_limit = libc::rlimit64 { rlim_cur: max_rttime as u64, rlim_max: max_rttime as u64 }; 22 | let mut old_limit = new_limit; 23 | if unsafe { libc::getrlimit64(libc::RLIMIT_RTTIME, &mut old_limit) } < 0 { 24 | return Err(Box::from("getrlimit failed")); 25 | } 26 | if unsafe { libc::setrlimit64(libc::RLIMIT_RTTIME, &new_limit) } < 0 { 27 | return Err(Box::from("setrlimit failed")); 28 | } 29 | 30 | // Finally, let's ask rtkit to make us realtime 31 | let thread_id = unsafe { libc::syscall(libc::SYS_gettid) }; 32 | let r: Result<(), _> = proxy.method_call("org.freedesktop.RealtimeKit1", "MakeThreadRealtime", (thread_id as u64, prio)); 33 | 34 | if r.is_err() { 35 | unsafe { libc::setrlimit64(libc::RLIMIT_RTTIME, &old_limit) }; 36 | } 37 | 38 | r?; 39 | Ok(prio) 40 | } 41 | 42 | 43 | fn main() { 44 | match make_realtime(5) { 45 | Ok(n) => println!("Got rtprio, level {}", n), 46 | Err(e) => println!("No rtprio: {}", e), 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /dbus-codegen/data/standard_interfaces.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /dbus/examples/match_signal.rs: -------------------------------------------------------------------------------- 1 | /// Connects to the "server" example. Have the server example running when you're running this example. 2 | 3 | use dbus::arg; 4 | 5 | // First, let's autogenerate some code using the dbus-codegen-rust tool. 6 | // With the server example running, the below was (part of) the output when running: 7 | // `dbus-codegen-rust -d com.example.dbustest -p /hello -m None` 8 | 9 | #[derive(Debug)] 10 | pub struct ComExampleDbustestHelloHappened { 11 | pub sender: String, 12 | } 13 | 14 | impl arg::AppendAll for ComExampleDbustestHelloHappened { 15 | fn append(&self, i: &mut arg::IterAppend) { 16 | arg::RefArg::append(&self.sender, i); 17 | } 18 | } 19 | 20 | impl arg::ReadAll for ComExampleDbustestHelloHappened { 21 | fn read(i: &mut arg::Iter) -> Result { 22 | Ok(ComExampleDbustestHelloHappened { 23 | sender: i.read()?, 24 | }) 25 | } 26 | } 27 | 28 | impl dbus::message::SignalArgs for ComExampleDbustestHelloHappened { 29 | const NAME: &'static str = "HelloHappened"; 30 | const INTERFACE: &'static str = "com.example.dbustest"; 31 | } 32 | 33 | // Autogenerated code ends here. 34 | 35 | use dbus::blocking::Connection; 36 | use dbus::Message; 37 | use std::error::Error; 38 | use std::time::Duration; 39 | 40 | 41 | fn main() -> Result<(), Box> { 42 | // Let's start by starting up a connection to the session bus. 43 | let c = Connection::new_session()?; 44 | 45 | { 46 | let proxy = c.with_proxy("com.example.dbustest", "/hello", Duration::from_millis(5000)); 47 | 48 | // Let's start listening to signals. 49 | let _id = proxy.match_signal(|h: ComExampleDbustestHelloHappened, _: &Connection, _: &Message| { 50 | println!("Hello happened from sender: {}", h.sender); 51 | true 52 | }); 53 | 54 | // Say hello to the server example, so we get a signal. 55 | let (s,): (String,) = proxy.method_call("com.example.dbustest", "Hello", ("match_signal example",))?; 56 | println!("{}", s); 57 | } 58 | 59 | // Listen to incoming signals forever. 60 | loop { c.process(Duration::from_millis(1000))?; } 61 | } 62 | -------------------------------------------------------------------------------- /dbus-codegen/examples/laundry_cr.rs: -------------------------------------------------------------------------------- 1 | mod generated { 2 | // This is the output of dbus-codegen-rust. 3 | // Usually, code generated is put in its own file. It is inlined here for convenience. 4 | 5 | #[allow(unused_imports)] 6 | use dbus::arg; 7 | use dbus_crossroads as crossroads; 8 | 9 | pub trait OrgExampleTest { 10 | fn foo(&mut self, bar: i32) -> Result; 11 | } 12 | 13 | #[derive(Debug)] 14 | pub struct OrgExampleTestLaundry { 15 | pub eaten: bool, 16 | } 17 | 18 | impl arg::AppendAll for OrgExampleTestLaundry { 19 | fn append(&self, i: &mut arg::IterAppend) { 20 | arg::RefArg::append(&self.eaten, i); 21 | } 22 | } 23 | 24 | impl arg::ReadAll for OrgExampleTestLaundry { 25 | fn read(i: &mut arg::Iter) -> Result { 26 | Ok(OrgExampleTestLaundry { 27 | eaten: i.read()?, 28 | }) 29 | } 30 | } 31 | 32 | impl dbus::message::SignalArgs for OrgExampleTestLaundry { 33 | const NAME: &'static str = "Laundry"; 34 | const INTERFACE: &'static str = "org.example.test"; 35 | } 36 | 37 | pub fn register_org_example_test(cr: &mut crossroads::Crossroads) -> crossroads::IfaceToken 38 | where T: OrgExampleTest + Send + 'static 39 | { 40 | cr.register("org.example.test", |b| { 41 | b.signal::<(bool,), _>("Laundry", ("eaten",)); 42 | b.method("Foo", ("bar",), ("baz",), |_, t: &mut T, (bar,)| { 43 | t.foo(bar,) 44 | .map(|x| (x,)) 45 | }); 46 | }) 47 | } 48 | } 49 | 50 | impl generated::OrgExampleTest for () { 51 | fn foo(&mut self, bar: i32) -> Result { 52 | Ok(format!("You called me with the argument '{}'", bar)) 53 | } 54 | } 55 | 56 | fn main() -> Result<(), Box>{ 57 | let mut cr = dbus_crossroads::Crossroads::new(); 58 | let token = generated::register_org_example_test(&mut cr); 59 | cr.insert("/", &[token], ()); 60 | 61 | let conn = dbus::blocking::Connection::new_session()?; 62 | conn.request_name("org.example.test", true, true, true)?; 63 | 64 | cr.serve(&conn)?; 65 | Ok(()) 66 | } 67 | -------------------------------------------------------------------------------- /dbus-codegen-tests/tests/test2.rs: -------------------------------------------------------------------------------- 1 | use std::sync::atomic::*; 2 | 3 | #[allow(dead_code)] 4 | #[deny(trivial_casts)] 5 | mod policykit; 6 | 7 | #[allow(dead_code)] 8 | #[deny(trivial_casts)] 9 | mod policykit_client; 10 | 11 | 12 | impl policykit::OrgFreedesktopDBusProperties for () { 13 | fn get(&self, interfacename: &str, propertyname: &str) -> Result<::dbus::arg::Variant>, ::dbus::MethodErr> { 14 | assert_eq!(interfacename, "Interface.Name"); 15 | assert_eq!(propertyname, "Property.Name"); 16 | Ok(::dbus::arg::Variant(Box::new(5u8))) 17 | } 18 | 19 | fn get_all(&self, _interfacename: &str) -> 20 | Result<::std::collections::HashMap>>, ::dbus::MethodErr> { unimplemented!() } 21 | 22 | fn set(&self, _interfacename: &str, _propertyname: &str, value: ::dbus::arg::Variant>) -> Result<(), ::dbus::MethodErr> { 23 | assert_eq!(dbus::arg::RefArg::as_str(&value), Some("Hello")); 24 | Err(("A.B.C", "Error.Message").into()) 25 | } 26 | 27 | } 28 | 29 | #[test] 30 | fn test2() { 31 | let f = dbus_tree::Factory::new_fn::<()>(); 32 | let i1 = policykit::org_freedesktop_dbus_properties_server(&f, (), |minfo| minfo.path.get_data()); 33 | let t = f.tree(()).add(f.object_path("/test", ()).add(i1)); 34 | let c = dbus::ffidisp::Connection::new_session().unwrap(); 35 | t.set_registered(&c, true).unwrap(); 36 | let cname = c.unique_name(); 37 | let quit = std::sync::Arc::new(AtomicBool::new(false)); 38 | let quit2 = quit.clone(); 39 | let _ = std::thread::spawn(move || { 40 | use policykit_client::OrgFreedesktopDBusProperties; 41 | use dbus::arg::RefArg; 42 | 43 | let c2 = dbus::ffidisp::Connection::new_session().unwrap(); 44 | let p = c2.with_path(cname, "/test", 1000); 45 | let v = p.get("Interface.Name", "Property.Name").unwrap(); 46 | assert_eq!(v.as_i64(), Some(5)); 47 | 48 | let vv = p.set("Interface.Name", "Property.Name", dbus::arg::Variant(Box::new("Hello".to_string()))); 49 | assert_eq!(vv.unwrap_err().message(), Some("Error.Message")); 50 | 51 | quit2.store(true, Ordering::SeqCst); 52 | }); 53 | for _ in t.run(&c, c.iter(100)) { if quit.load(Ordering::SeqCst) { break; } } 54 | } 55 | -------------------------------------------------------------------------------- /dbus/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! D-Bus bindings for Rust 2 | //! 3 | //! [D-Bus](http://dbus.freedesktop.org/) is a message bus, and is mainly used in Linux 4 | //! for communication between processes. It is present by default on almost every 5 | //! Linux distribution out there, and runs in two instances - one per session, and one 6 | //! system-wide. 7 | //! 8 | //! In addition to the API documentation, which you're currently reading, you might want to 9 | //! look in the examples directory, which contains many examples and some additional documents. 10 | //! README.md also contains a few quick "getting started" examples (as well as information about 11 | //! the `futures` and `no-string-validation` features). 12 | //! 13 | //! In addition to this crate, there are some companion crates: 14 | //! * dbus-tokio for integrating D-Bus with [Tokio](http://tokio.rs) 15 | //! * dbus-codegen for generating code from D-Bus introspection data 16 | //! * libdbus-sys contains the raw bindings to the C libdbus library. 17 | 18 | #![warn(missing_docs)] 19 | 20 | // We have io-lifetimes and native-channel which are not ready yet 21 | // so for now allow them in the codebase and silence the warning 22 | #![allow(unexpected_cfgs)] 23 | 24 | extern crate libc; 25 | 26 | #[allow(missing_docs)] 27 | extern crate libdbus_sys as ffi; 28 | 29 | pub use crate::message::{Message, MessageType}; 30 | 31 | pub mod message; 32 | 33 | pub mod ffidisp; 34 | 35 | mod error; 36 | pub use error::{Error, MethodErr, Result}; 37 | 38 | pub mod channel; 39 | 40 | mod filters; 41 | 42 | pub mod blocking; 43 | 44 | #[cfg(feature = "futures")] 45 | pub mod nonblock; 46 | 47 | pub mod strings; 48 | pub use crate::strings::{Signature, Path}; 49 | 50 | pub mod arg; 51 | 52 | // pub mod tree; 53 | 54 | static INITDBUS: std::sync::Once = std::sync::Once::new(); 55 | 56 | use std::ffi::{CString, CStr}; 57 | use std::os::raw::c_char; 58 | 59 | fn init_dbus() { 60 | INITDBUS.call_once(|| { 61 | if unsafe { ffi::dbus_threads_init_default() } == 0 { 62 | panic!("Out of memory when trying to initialize D-Bus library!"); 63 | } 64 | }); 65 | } 66 | 67 | fn c_str_to_slice(c: & *const c_char) -> Option<&str> { 68 | if c.is_null() { None } 69 | else { std::str::from_utf8( unsafe { CStr::from_ptr(*c).to_bytes() }).ok() } 70 | } 71 | 72 | fn to_c_str(n: &str) -> CString { CString::new(n.as_bytes()).unwrap() } 73 | -------------------------------------------------------------------------------- /dbus/examples/properties.rs: -------------------------------------------------------------------------------- 1 | use dbus::{blocking::Connection, arg}; 2 | use std::time::Duration; 3 | 4 | fn print_refarg(value: &dyn arg::RefArg) { 5 | // We don't know what type the value is. We'll try a few and fall back to 6 | // debug printing if the value is more complex than that. 7 | if let Some(s) = value.as_str() { println!("{}", s); } 8 | else if let Some(i) = value.as_i64() { println!("{}", i); } 9 | else { println!("{:?}", value); } 10 | } 11 | 12 | fn main() -> Result<(), Box> { 13 | // Connect to server and create a proxy object. A proxy implements several interfaces, 14 | // in this case we'll use OrgFreedesktopDBusProperties, which allows us to call "get". 15 | let c = Connection::new_session()?; 16 | let p = c.with_proxy("org.mpris.MediaPlayer2.rhythmbox", "/org/mpris/MediaPlayer2", Duration::from_millis(5000)); 17 | use dbus::blocking::stdintf::org_freedesktop_dbus::Properties; 18 | 19 | // The Metadata property is a Dict. 20 | 21 | // Option 1: we can get the dict straight into a hashmap, like this: 22 | 23 | let metadata: arg::PropMap = p.get("org.mpris.MediaPlayer2.Player", "Metadata")?; 24 | 25 | println!("Option 1:"); 26 | 27 | // We now iterate over the hashmap. 28 | for (key, value) in metadata.iter() { 29 | print!(" {}: ", key); 30 | print_refarg(&value); 31 | } 32 | 33 | // As an alternative, if we just want a specific property and know the type of it, we can use 34 | // prop_cast: 35 | let title: Option<&String> = arg::prop_cast(&metadata, "xesam:title"); 36 | if let Some(title) = title { 37 | println!("The title is: {}", title); 38 | } 39 | 40 | // Option 2: we can get the entire dict as a RefArg and get the values out by iterating over it. 41 | 42 | let metadata: Box = p.get("org.mpris.MediaPlayer2.Player", "Metadata")?; 43 | 44 | // When using "as_iter()" for a dict, we'll get one key, it's value, next key, it's value, etc. 45 | let mut iter = metadata.as_iter().unwrap(); 46 | 47 | println!("Option 2:"); 48 | while let Some(key) = iter.next() { 49 | // Printing the key is easy, since we know it's a String. 50 | print!(" {}: ", key.as_str().unwrap()); 51 | let value = iter.next().unwrap(); 52 | print_refarg(&value); 53 | } 54 | 55 | Ok(()) 56 | } 57 | -------------------------------------------------------------------------------- /dbus-codegen-tests/tests/test1.rs: -------------------------------------------------------------------------------- 1 | #[allow(dead_code)] 2 | mod policykit; 3 | 4 | #[allow(dead_code)] 5 | #[deny(trivial_casts)] 6 | mod policykit_client; 7 | 8 | #[allow(dead_code)] 9 | #[deny(trivial_casts)] 10 | mod policykit_blocking; 11 | 12 | #[allow(dead_code)] 13 | #[deny(trivial_casts)] 14 | mod policykit_nonblock; 15 | 16 | 17 | use std::sync::atomic::*; 18 | 19 | impl policykit::OrgFreedesktopDBusIntrospectable for () { 20 | fn introspect(&self) -> Result { Ok("I feel so introspected right now".into()) } 21 | } 22 | 23 | #[test] 24 | fn test_main() { 25 | let f = dbus_tree::Factory::new_fn::<()>(); 26 | let i1 = policykit::org_freedesktop_dbus_introspectable_server(&f, (), |minfo| minfo.path.get_data()); 27 | let t = f.tree(()).add(f.object_path("/test", ()).add(i1)); 28 | let c = dbus::ffidisp::Connection::new_session().unwrap(); 29 | t.set_registered(&c, true).unwrap(); 30 | let cname = c.unique_name(); 31 | let quit = std::sync::Arc::new(AtomicBool::new(false)); 32 | let quit2 = quit.clone(); 33 | let _ = std::thread::spawn(move || { 34 | let c2 = dbus::ffidisp::Connection::new_session().unwrap(); 35 | // Older way 36 | { 37 | let m = dbus::Message::new_method_call(&cname, "/test", "org.freedesktop.DBus.Introspectable", "Introspect").unwrap(); 38 | let mut mrep = c2.send_with_reply_and_block(m, 1000).unwrap(); 39 | let m2 = mrep.as_result().unwrap(); 40 | assert_eq!(m2.read1(), Ok("I feel so introspected right now")); 41 | } 42 | 43 | // Old way 44 | { 45 | let p = c2.with_path(&cname, "/test", 1000); 46 | use policykit_client::OrgFreedesktopDBusIntrospectable; 47 | assert_eq!(p.introspect().unwrap(), "I feel so introspected right now"); 48 | } 49 | 50 | // New way 51 | { 52 | let c3 = dbus::blocking::Connection::new_session().unwrap(); 53 | let p = c3.with_proxy(cname, "/test", std::time::Duration::from_millis(1000)); 54 | use policykit_blocking::OrgFreedesktopDBusIntrospectable; 55 | assert_eq!(p.introspect().unwrap(), "I feel so introspected right now"); 56 | } 57 | 58 | quit2.store(true, Ordering::SeqCst); 59 | }); 60 | 61 | for _ in t.run(&c, c.iter(100)) { if quit.load(Ordering::SeqCst) { break; } } 62 | } 63 | -------------------------------------------------------------------------------- /dbus-codegen-tests/tests/test_asref.rs: -------------------------------------------------------------------------------- 1 | use std::sync::atomic::*; 2 | use std::rc::Rc; 3 | use std::convert::AsRef; 4 | 5 | #[allow(dead_code)] 6 | #[deny(trivial_casts)] 7 | mod policykit_asref; 8 | 9 | #[allow(dead_code)] 10 | #[deny(trivial_casts)] 11 | mod policykit_client; 12 | 13 | struct Whatever {} 14 | 15 | use dbus::arg; 16 | 17 | impl AsRef for Rc { 18 | fn as_ref(&self) -> &(dyn policykit_asref::OrgFreedesktopDBusProperties + 'static) { &**self } 19 | } 20 | 21 | impl policykit_asref::OrgFreedesktopDBusProperties for Whatever { 22 | fn get(&self, interfacename: &str, propertyname: &str) -> Result<::dbus::arg::Variant>, ::dbus::MethodErr> { 23 | assert_eq!(interfacename, "Interface.Name"); 24 | assert_eq!(propertyname, "Property.Name"); 25 | Ok(::dbus::arg::Variant(Box::new(5u8))) 26 | } 27 | 28 | fn get_all(&self, _interfacename: &str) -> 29 | Result<::std::collections::HashMap>>, ::dbus::MethodErr> { unimplemented!() } 30 | 31 | fn set(&self, _interfacename: &str, _propertyname: &str, value: ::dbus::arg::Variant>) -> Result<(), ::dbus::MethodErr> { 32 | assert_eq!(dbus::arg::RefArg::as_str(&value), Some("Hello")); 33 | Err(("A.B.C", "Error.Message").into()) 34 | } 35 | 36 | } 37 | 38 | 39 | #[test] 40 | fn test_asref() { 41 | let f = dbus_tree::Factory::new_fnmut::<()>(); 42 | let x = Rc::new(Whatever {}); 43 | let i1 = policykit_asref::org_freedesktop_dbus_properties_server(&f, (), move |_| { x.clone() }); 44 | let t = f.tree(()).add(f.object_path("/test", ()).add(i1)); 45 | let c = dbus::ffidisp::Connection::new_session().unwrap(); 46 | t.set_registered(&c, true).unwrap(); 47 | let cname = c.unique_name(); 48 | let quit = std::sync::Arc::new(AtomicBool::new(false)); 49 | let quit2 = quit.clone(); 50 | let _ = std::thread::spawn(move || { 51 | use policykit_client::OrgFreedesktopDBusProperties; 52 | use dbus::arg::RefArg; 53 | 54 | let c2 = dbus::ffidisp::Connection::new_session().unwrap(); 55 | let p = c2.with_path(cname, "/test", 1000); 56 | let v = p.get("Interface.Name", "Property.Name").unwrap(); 57 | assert_eq!(v.as_i64(), Some(5)); 58 | 59 | let vv = p.set("Interface.Name", "Property.Name", dbus::arg::Variant(Box::new("Hello".to_string()))); 60 | assert_eq!(vv.unwrap_err().message(), Some("Error.Message")); 61 | 62 | quit2.store(true, Ordering::SeqCst); 63 | }); 64 | for _ in t.run(&c, c.iter(100)) { if quit.load(Ordering::SeqCst) { break; } } 65 | } 66 | -------------------------------------------------------------------------------- /dbus-tokio/examples/tokio_client.rs: -------------------------------------------------------------------------------- 1 | use dbus_tokio::connection; 2 | use dbus::nonblock; 3 | use std::time::Duration; 4 | use dbus::message::MatchRule; 5 | 6 | #[tokio::main] 7 | pub async fn main() -> Result<(), Box> { 8 | 9 | // Connect to the D-Bus session bus (this is blocking, unfortunately). 10 | let (resource, conn) = connection::new_session_sync()?; 11 | 12 | // The resource is a task that should be spawned onto a tokio compatible 13 | // reactor ASAP. If the resource ever finishes, you lost connection to D-Bus. 14 | // 15 | // To shut down the connection, both call _handle.abort() and drop the connection. 16 | let _handle = tokio::spawn(async { 17 | let err = resource.await; 18 | panic!("Lost connection to D-Bus: {}", err); 19 | }); 20 | 21 | // Create interval - a Stream that will fire an event periodically 22 | let mut interval = tokio::time::interval(Duration::from_secs(2)); 23 | 24 | // Create a future calling D-Bus method each time the interval generates a tick 25 | let conn2 = conn.clone(); 26 | let calls = async move { 27 | loop { 28 | interval.tick().await; 29 | let conn = conn2.clone(); 30 | 31 | println!("Calling Hello..."); 32 | let proxy = nonblock::Proxy::new("com.example.dbustest", "/hello", Duration::from_secs(2), conn); 33 | // TODO: Handle timeouts and errors here 34 | let (x,): (String,) = proxy.method_call("com.example.dbustest", "Hello", ("Tokio async/await",)).await.unwrap(); 35 | println!("{}", x); 36 | } 37 | }; 38 | 39 | // To receive D-Bus signals we need to add a match that defines which signals should be forwarded 40 | // to our application. 41 | let mr = MatchRule::new_signal("com.example.dbustest", "HelloHappened"); 42 | let incoming_signal = conn.add_match(mr).await?.cb(|_, (source,): (String,)| { 43 | println!("Hello from {} happened on the bus!", source); 44 | true 45 | }); 46 | 47 | // This will never return (except on panic) as there's no exit condition in the calls loop 48 | calls.await; 49 | 50 | /* 51 | // ..or use the match as a stream if you prefer 52 | use futures_util::stream::StreamExt; 53 | let (incoming_signal, stream) = conn.add_match(mr).await?.stream(); 54 | let stream = stream.for_each(|(_, (source,)): (_, (String,))| { 55 | println!("Hello from {} happened on the bus!", source); 56 | async {} 57 | }); 58 | futures::join!(stream, calls) 59 | */ 60 | 61 | // Needed here to ensure the "incoming_signal" object is not dropped too early 62 | conn.remove_match(incoming_signal.token()).await?; 63 | 64 | unreachable!() 65 | } 66 | -------------------------------------------------------------------------------- /dbus-codegen-tests/tests/test_asref_mtsync.rs: -------------------------------------------------------------------------------- 1 | use std::sync::atomic::*; 2 | use std::sync::Arc; 3 | use std::convert::AsRef; 4 | 5 | #[allow(dead_code)] 6 | #[deny(trivial_casts)] 7 | mod policykit_asref_mtsync; 8 | 9 | #[allow(dead_code)] 10 | #[deny(trivial_casts)] 11 | mod policykit_client; 12 | 13 | struct Whatever {} 14 | 15 | use dbus::arg; 16 | 17 | impl AsRef for Arc { 18 | fn as_ref(&self) -> &(dyn policykit_asref_mtsync::OrgFreedesktopDBusProperties + 'static) { &**self } 19 | } 20 | 21 | impl policykit_asref_mtsync::OrgFreedesktopDBusProperties for Whatever { 22 | fn get(&self, interfacename: &str, propertyname: &str) -> Result<::dbus::arg::Variant>, ::dbus::MethodErr> { 23 | assert_eq!(interfacename, "Interface.Name"); 24 | assert_eq!(propertyname, "Property.Name"); 25 | Ok(::dbus::arg::Variant(Box::new(5u8))) 26 | } 27 | 28 | fn get_all(&self, _interfacename: &str) -> 29 | Result<::std::collections::HashMap>>, ::dbus::MethodErr> { unimplemented!() } 30 | 31 | fn set(&self, _interfacename: &str, _propertyname: &str, value: ::dbus::arg::Variant>) -> Result<(), ::dbus::MethodErr> { 32 | assert_eq!(dbus::arg::RefArg::as_str(&value), Some("Hello")); 33 | Err(("A.B.C", "Error.Message").into()) 34 | } 35 | 36 | } 37 | 38 | 39 | #[test] 40 | fn test_asref_mtsync() { 41 | let f = dbus_tree::Factory::new_sync::<()>(); 42 | let x = Arc::new(Whatever {}); 43 | let i1 = policykit_asref_mtsync::org_freedesktop_dbus_properties_server(&f, (), move |_| { x.clone() }); 44 | let t = f.tree(()).add(f.object_path("/test", ()).add(i1)); 45 | let c = dbus::ffidisp::Connection::new_session().unwrap(); 46 | t.set_registered(&c, true).unwrap(); 47 | let cname = c.unique_name(); 48 | let quit = std::sync::Arc::new(AtomicBool::new(false)); 49 | let quit2 = quit.clone(); 50 | let _ = std::thread::spawn(move || { 51 | use policykit_client::OrgFreedesktopDBusProperties; 52 | use dbus::arg::RefArg; 53 | 54 | let c2 = dbus::ffidisp::Connection::new_session().unwrap(); 55 | let p = c2.with_path(cname, "/test", 1000); 56 | let v = p.get("Interface.Name", "Property.Name").unwrap(); 57 | assert_eq!(v.as_i64(), Some(5)); 58 | 59 | let vv = p.set("Interface.Name", "Property.Name", dbus::arg::Variant(Box::new("Hello".to_string()))); 60 | assert_eq!(vv.unwrap_err().message(), Some("Error.Message")); 61 | 62 | quit2.store(true, Ordering::SeqCst); 63 | }); 64 | for _ in t.run(&c, c.iter(100)) { if quit.load(Ordering::SeqCst) { break; } } 65 | } 66 | -------------------------------------------------------------------------------- /dbus-native-channel/src/address.rs: -------------------------------------------------------------------------------- 1 | use std::os::unix::net::UnixStream; 2 | 3 | fn env_key(key: &str) -> Option { 4 | for (akey, value) in std::env::vars_os() { 5 | if akey == key { 6 | if let Ok(v) = value.into_string() { return Some(v) } 7 | } 8 | } 9 | None 10 | } 11 | 12 | pub fn read_session_address() -> Result> { 13 | Ok(env_key("DBUS_SESSION_BUS_ADDRESS").ok_or_else(|| "Environment variable not found")?) 14 | // TODO: according to the D-Bus spec, there are more ways to find the address, such 15 | // as asking the X window system. 16 | } 17 | 18 | pub fn read_system_address() -> Result> { 19 | Ok(env_key("DBUS_SYSTEM_BUS_ADDRESS").unwrap_or_else(|| 20 | "unix:path=/var/run/dbus/system_bus_socket".into() 21 | )) 22 | } 23 | 24 | pub fn read_starter_address() -> Result> { 25 | Ok(env_key("DBUS_SESSION_BUS_ADDRESS").ok_or_else(|| "Environment variable not found")?) 26 | } 27 | 28 | fn make_sockaddr_un(start: usize, s: &str) -> Result> { 29 | let bytes = s.as_bytes(); 30 | let mut r = libc::sockaddr_un { 31 | sun_family: libc::AF_UNIX as libc::sa_family_t, 32 | sun_path: [0; 108], 33 | }; 34 | if start+bytes.len()+1 >= r.sun_path.len() { Err("Address too long")? } 35 | for (i, &x) in bytes.into_iter().enumerate() { 36 | r.sun_path[i+start] = x as libc::c_char; 37 | } 38 | Ok(r) 39 | } 40 | 41 | pub fn address_to_sockaddr_un(s: &str) -> Result> { 42 | if !s.starts_with("unix:") { Err("Address is not a unix socket")? }; 43 | for pair in s["unix:".len()..].split(',') { 44 | let mut kv = pair.splitn(2, "="); 45 | if let Some(key) = kv.next() { 46 | if let Some(value) = kv.next() { 47 | if key == "path" { return make_sockaddr_un(0, value); } 48 | if key == "abstract" { return make_sockaddr_un(1, value); } 49 | } 50 | } 51 | } 52 | Err(format!("unsupported address type: {}", s))? 53 | } 54 | 55 | pub fn connect_blocking(addr: &str) -> Result> { 56 | let sockaddr = address_to_sockaddr_un(addr)?; 57 | crate::sys::connect_blocking(&sockaddr) 58 | } 59 | 60 | #[test] 61 | fn bus_exists() { 62 | let addr = read_session_address().unwrap(); 63 | println!("Bus address is: {:?}", addr); 64 | if addr.starts_with("unix:path=") { 65 | let path = std::path::Path::new(&addr["unix:path=".len()..]); 66 | assert!(path.exists()); 67 | } 68 | 69 | let addr = read_system_address().unwrap(); 70 | if addr.starts_with("unix:path=") { 71 | let path = std::path::Path::new(&addr["unix:path=".len()..]); 72 | assert!(path.exists()); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /dbus-codegen-tests/tests/test_user_types.rs: -------------------------------------------------------------------------------- 1 | mod user_types_dbus; 2 | 3 | use std::sync::atomic::{AtomicBool, Ordering}; 4 | use std::time::Duration; 5 | use dbus::blocking::Connection; 6 | use dbus::channel::MatchingReceiver; 7 | use dbus_crossroads::Crossroads; 8 | 9 | use codegen_tests::user_type::MyType; 10 | use user_types_dbus::*; 11 | 12 | struct MyServer { 13 | t: MyType, 14 | } 15 | 16 | impl MyServer { 17 | fn new() -> Self { 18 | MyServer{t: MyType::new("123".to_string(), "456".to_string())} 19 | } 20 | } 21 | 22 | impl user_types_cr::ComExampleMyService1InterestingInterface for MyServer { 23 | fn method1(&mut self, arg1: codegen_tests::user_type::MyType) -> Result{ 24 | return Ok(arg1); 25 | } 26 | fn bar(&self) -> Result { 27 | return Ok(self.t.clone()); 28 | } 29 | fn set_bar(&self, _value: codegen_tests::user_type::MyType) -> Result<(), dbus::MethodErr> { 30 | return Ok(()); 31 | } 32 | } 33 | 34 | 35 | #[test] 36 | fn test_cr() { 37 | let c = Connection::new_session().unwrap(); 38 | let cname = "com.example.dbustest"; 39 | c.request_name(cname, false, true, false).unwrap(); 40 | let mut cr = Crossroads::new(); 41 | let token = user_types_cr::register_com_example_my_service1_interesting_interface::(&mut cr); 42 | let server = MyServer::new(); 43 | cr.insert("/", &[token], server); 44 | c.start_receive(dbus::message::MatchRule::new_method_call(), Box::new(move |msg, conn| { 45 | cr.handle_message(msg, conn).unwrap(); 46 | true 47 | })); 48 | 49 | let quit = std::sync::Arc::new(AtomicBool::new(false)); 50 | let quit2 = quit.clone(); 51 | 52 | let _ = std::thread::spawn(move || { 53 | { 54 | let c2 = dbus::blocking::Connection::new_session().unwrap(); 55 | let p = c2.with_proxy(cname, "/", std::time::Duration::from_millis(1000)); 56 | use user_types_blocking::ComExampleMyService1InterestingInterface; 57 | let arg = MyType::new("abc".to_string(), "cdf".to_string()); 58 | assert_eq!(p.method1(&arg).unwrap(), arg); 59 | } 60 | 61 | let rt = tokio::runtime::Runtime::new().unwrap(); 62 | rt.block_on(async { 63 | let (resource, conn) = dbus_tokio::connection::new_session_sync().unwrap(); 64 | let _handle = tokio::spawn(async { 65 | let err = resource.await; 66 | panic!("Lost connection to D-Bus: {}", err); 67 | }); 68 | let p = dbus::nonblock::Proxy::new(cname, "/", Duration::from_secs(2), conn); 69 | use user_types_nonblock::ComExampleMyService1InterestingInterface; 70 | let arg = MyType::new("abc".to_string(), "cdf".to_string()); 71 | assert_eq!(p.method1(&arg).await.unwrap(), arg); 72 | }); 73 | 74 | quit2.store(true, Ordering::SeqCst); 75 | }); 76 | 77 | loop { 78 | c.process(std::time::Duration::from_millis(1000)).unwrap(); 79 | if quit.load(Ordering::SeqCst) { break; } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /dbus-tree/examples/server.rs: -------------------------------------------------------------------------------- 1 | /* This example creates a D-Bus server with the following functionality: 2 | It registers the "com.example.dbustest" name, creates a "/hello" object path, 3 | which has an "com.example.dbustest" interface. 4 | 5 | The interface has a "Hello" method (which takes no arguments and returns a string), 6 | and a "HelloHappened" signal (with a string argument) which is sent every time 7 | someone calls the "Hello" method. 8 | */ 9 | 10 | use std::sync::Arc; 11 | use dbus::blocking::LocalConnection; 12 | use dbus_tree::Factory; 13 | use std::error::Error; 14 | use std::time::Duration; 15 | 16 | fn main() -> Result<(), Box> { 17 | // Let's start by starting up a connection to the session bus and request a name. 18 | let c = LocalConnection::new_session()?; 19 | c.request_name("com.example.dbustest", false, true, false)?; 20 | 21 | // The choice of factory tells us what type of tree we want, 22 | // and if we want any extra data inside. We pick the simplest variant. 23 | let f = Factory::new_fn::<()>(); 24 | 25 | // We create the signal first, since we'll need it in both inside the method callback 26 | // and when creating the tree. 27 | let signal = Arc::new(f.signal("HelloHappened", ()).sarg::<&str,_>("sender")); 28 | let signal2 = signal.clone(); 29 | 30 | // We create a tree with one object path inside and make that path introspectable. 31 | let tree = f.tree(()).add(f.object_path("/hello", ()).introspectable().add( 32 | 33 | // We add an interface to the object path... 34 | f.interface("com.example.dbustest", ()).add_m( 35 | 36 | // ...and a method inside the interface. 37 | f.method("Hello", (), move |m| { 38 | 39 | // This is the callback that will be called when another peer on the bus calls our method. 40 | // the callback receives "MethodInfo" struct and can return either an error, or a list of 41 | // messages to send back. 42 | 43 | let name: &str = m.msg.read1()?; 44 | let s = format!("Hello {}!", name); 45 | let mret = m.msg.method_return().append1(s); 46 | 47 | let sig = signal.msg(m.path.get_name(), m.iface.get_name()) 48 | .append1(&*name); 49 | 50 | // Two messages will be returned - one is the method return (and should always be there), 51 | // and in our case we also have a signal we want to send at the same time. 52 | Ok(vec!(mret, sig)) 53 | 54 | // Our method has one output argument and one input argument. 55 | }).outarg::<&str,_>("reply") 56 | .inarg::<&str,_>("name") 57 | 58 | // We also add the signal to the interface. This is mainly for introspection. 59 | ).add_s(signal2) 60 | 61 | // Also add the root path, to help introspection from debugging tools. 62 | )).add(f.object_path("/", ()).introspectable()); 63 | 64 | // We add the tree to the connection so that incoming method calls will be handled. 65 | tree.start_receive(&c); 66 | 67 | // Serve clients forever. 68 | loop { c.process(Duration::from_millis(1000))?; } 69 | } 70 | -------------------------------------------------------------------------------- /dbus/examples/monitor.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use dbus::blocking::Connection; 4 | use dbus::channel::MatchingReceiver; 5 | use dbus::message::MatchRule; 6 | use dbus::Message; 7 | 8 | // This programs implements the equivalent of running the "dbus-monitor" tool 9 | fn main() { 10 | // Very simple argument parsing. 11 | let use_system_bus = std::env::args().into_iter().any(|a| a == "--system"); 12 | 13 | // First open up a connection to the desired bus. 14 | let conn = (if use_system_bus { Connection::new_system() } else { Connection::new_session() }).expect("D-Bus connection failed"); 15 | 16 | // Second create a rule to match messages we want to receive; in this example we add no 17 | // further requirements, so all messages will match 18 | let rule = MatchRule::new(); 19 | 20 | // Try matching using new scheme 21 | let proxy = conn.with_proxy("org.freedesktop.DBus", "/org/freedesktop/DBus", Duration::from_millis(5000)); 22 | let result: Result<(), dbus::Error> = 23 | proxy.method_call("org.freedesktop.DBus.Monitoring", "BecomeMonitor", (vec![rule.match_str()], 0u32)); 24 | 25 | match result { 26 | // BecomeMonitor was successful, start listening for messages 27 | Ok(_) => { 28 | conn.start_receive( 29 | rule, 30 | Box::new(|msg, _| { 31 | handle_message(&msg); 32 | true 33 | }), 34 | ); 35 | } 36 | // BecomeMonitor failed, fallback to using the old scheme 37 | Err(e) => { 38 | eprintln!("Failed to BecomeMonitor: '{}', falling back to eavesdrop", e); 39 | 40 | // First, we'll try "eavesdrop", which as the name implies lets us receive 41 | // *all* messages, not just ours. 42 | let rule_with_eavesdrop = { 43 | let mut rule = rule.clone(); 44 | rule.eavesdrop = true; 45 | rule 46 | }; 47 | 48 | let result = conn.add_match(rule_with_eavesdrop, |_: (), _, msg| { 49 | handle_message(&msg); 50 | true 51 | }); 52 | 53 | match result { 54 | Ok(_) => { 55 | // success, we're now listening 56 | } 57 | // This can sometimes fail, for example when listening to the system bus as a non-root user. 58 | // So, just like `dbus-monitor`, we attempt to fallback without `eavesdrop=true`: 59 | Err(e) => { 60 | eprintln!("Failed to eavesdrop: '{}', trying without it", e); 61 | conn.add_match(rule, |_: (), _, msg| { 62 | handle_message(&msg); 63 | true 64 | }) 65 | .expect("add_match failed"); 66 | } 67 | } 68 | } 69 | } 70 | 71 | // Loop and print out all messages received (using handle_message()) as they come. 72 | // Some can be quite large, e.g. if they contain embedded images.. 73 | loop { 74 | conn.process(Duration::from_millis(1000)).unwrap(); 75 | } 76 | } 77 | 78 | fn handle_message(msg: &Message) { 79 | println!("Got message: {:?}", msg); 80 | } 81 | -------------------------------------------------------------------------------- /dbus-crossroads/src_old/path.rs: -------------------------------------------------------------------------------- 1 | use super::handlers::Handlers; 2 | use dbus::strings::Path as PathName; 3 | use std::collections::HashMap; 4 | use std::any::{Any, TypeId}; 5 | use std::fmt; 6 | 7 | /// Internal helper trait, required due to orphan rules 8 | /// 9 | /// Just about anything implements this trait, i e, anything can be put inside a path. 10 | pub trait PathData: Sized { 11 | /// Internal helper trait 12 | fn pack(self) -> T; 13 | fn unpack(t: &T) -> Option<&Self>; 14 | fn unpack_mut(t: &mut T) -> Option<&mut Self>; 15 | } 16 | 17 | impl PathData> for S { 18 | fn pack(self) -> Box { Box::new(self) } 19 | fn unpack(t: &Box) -> Option<&S> { (&*t).downcast_ref() } 20 | fn unpack_mut(t: &mut Box) -> Option<&mut S> { (&mut *t).downcast_mut() } 21 | } 22 | 23 | impl PathData> for S { 24 | fn pack(self) -> Box { Box::new(self) } 25 | fn unpack(t: &Box) -> Option<&S> { (&*t).downcast_ref() } 26 | fn unpack_mut(t: &mut Box) -> Option<&mut S> { (&mut *t).downcast_mut() } 27 | } 28 | 29 | impl PathData> for S { 30 | fn pack(self) -> Box { Box::new(self) } 31 | fn unpack(t: &Box) -> Option<&S> { (&*t).downcast_ref() } 32 | fn unpack_mut(t: &mut Box) -> Option<&mut S> { (&mut *t).downcast_mut() } 33 | } 34 | 35 | pub struct Path { 36 | name: PathName<'static>, 37 | data: HashMap, 38 | } 39 | 40 | impl Path { 41 | pub fn new>>(n: N) -> Self { Path { name: n.into(), data: Default::default() } } 42 | 43 | pub fn name(&self) -> &PathName<'static> { &self.name } 44 | 45 | pub fn insert>(&mut self, i: I) { 46 | let id = TypeId::of::(); 47 | let t = i.pack(); 48 | self.data.insert(id, t); 49 | } 50 | 51 | pub fn with>(mut self, i: I) -> Self { 52 | self.insert(i); self 53 | } 54 | 55 | pub fn remove>(&mut self) { 56 | let id = TypeId::of::(); 57 | self.data.remove(&id); 58 | } 59 | 60 | pub fn get>(&self) -> Option<&I> { 61 | let id = TypeId::of::(); 62 | self.data.get(&id).and_then(|x| { PathData::unpack(x) }) 63 | } 64 | 65 | pub fn get_mut>(&mut self) -> Option<&mut I> { 66 | let id = TypeId::of::(); 67 | self.data.get_mut(&id).and_then(|x| { PathData::unpack_mut(x) }) 68 | } 69 | 70 | pub (super) fn get_from_typeid(&self, id: TypeId) -> Option<&H::Iface> { 71 | self.data.get(&id) 72 | } 73 | 74 | pub (super) fn get_from_typeid_mut(&mut self, id: TypeId) -> Option<&mut H::Iface> { 75 | self.data.get_mut(&id) 76 | } 77 | } 78 | 79 | impl fmt::Debug for Path { 80 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Path({:?})", self.name) } 81 | } 82 | 83 | #[test] 84 | fn test_path() { 85 | let mut p: Path = Path::new("/hello"); 86 | p.insert(7u16); 87 | *p.get_mut::().unwrap() += 1; 88 | let x: u16 = *p.get().unwrap(); 89 | assert_eq!(x, 8u16); 90 | } 91 | -------------------------------------------------------------------------------- /dbus-native-channel/src/authentication.rs: -------------------------------------------------------------------------------- 1 | 2 | #[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd, Hash, Debug)] 3 | pub enum Authentication { 4 | WaitingForOK(bool), 5 | WaitingForAgreeUnixFD, 6 | Error, 7 | Begin(bool), 8 | } 9 | 10 | impl Authentication { 11 | pub fn new(do_unix_fd: bool) -> (Self, String) { 12 | let uid = crate::sys::getuid(); 13 | let uid = uid.to_string(); 14 | let mut s = String::from("\0AUTH EXTERNAL "); 15 | for c in uid.as_bytes() { 16 | s.push_str(&format!("{:2x}", c)); 17 | } 18 | s.push_str("\r\n"); 19 | (Authentication::WaitingForOK(do_unix_fd), s) 20 | } 21 | pub fn handle(&mut self, data: &[u8]) -> Result<&'static str, Box> { 22 | let old_state = *self; 23 | *self = Authentication::Error; 24 | let s = std::str::from_utf8(data)?; 25 | if !s.ends_with("\r\n") { Err("D-Bus authentication error (no newline)")? }; 26 | let s = s.trim(); 27 | match old_state { 28 | Authentication::Error | Authentication::Begin(_) => Err("D-Bus invalid authentication state")?, 29 | Authentication::WaitingForOK(b) => if s.starts_with("OK ") || s == "OK" { 30 | if b { 31 | *self = Authentication::WaitingForAgreeUnixFD; 32 | Ok("NEGOTIATE_UNIX_FD\r\n") 33 | } else { 34 | *self = Authentication::Begin(false); 35 | Ok("BEGIN\r\n") 36 | } 37 | } else { 38 | Err(format!("D-Bus authentication error ({})", s))? 39 | }, 40 | Authentication::WaitingForAgreeUnixFD => if s == "AGREE_UNIX_FD" { 41 | *self = Authentication::Begin(true); 42 | Ok("BEGIN\r\n") 43 | } else if s.starts_with("ERROR ") || s == "ERROR" { 44 | *self = Authentication::Begin(false); 45 | Ok("BEGIN\r\n") 46 | } else { 47 | Err(format!("D-Bus invalid response ({})", s))? 48 | }, 49 | } 50 | } 51 | 52 | pub fn blocking(r: &mut R, w: &mut W, do_unix_fd: bool) -> Result> { 53 | let (mut a, s) = Authentication::new(do_unix_fd); 54 | w.write_all(s.as_bytes())?; 55 | 56 | let mut b = vec![]; 57 | r.read_until(b'\n', &mut b)?; 58 | let s = a.handle(&b)?; 59 | w.write_all(s.as_bytes())?; 60 | if a == Authentication::WaitingForAgreeUnixFD { 61 | let mut b = vec![]; 62 | r.read_until(b'\n', &mut b)?; 63 | let s = a.handle(&b)?; 64 | w.write_all(s.as_bytes())?; 65 | } 66 | if let Authentication::Begin(ufd) = a { Ok(ufd) } else { unreachable!() } 67 | } 68 | } 69 | 70 | 71 | #[test] 72 | fn session_auth() { 73 | let addr = crate::address::read_session_address().unwrap(); 74 | // dbus-deamon (not the systemd variant) has abstract sockets, which rust does not 75 | // support. https://github.com/rust-lang/rust/issues/42048 76 | if !addr.starts_with("unix:path=") { return; } 77 | let path = std::path::Path::new(&addr["unix:path=".len()..]); 78 | let stream = std::os::unix::net::UnixStream::connect(&path).unwrap(); 79 | 80 | let mut reader = std::io::BufReader::new(&stream); 81 | assert!(Authentication::blocking(&mut reader, &mut &stream, true).unwrap()); 82 | } 83 | -------------------------------------------------------------------------------- /dbus/changes-in-0.7.md: -------------------------------------------------------------------------------- 1 | D-Bus crate 0.7 overview 2 | ======================== 3 | 4 | In 0.7 the `Connection` struct has been rewritten (but the old one is still there). The new struct(s) have the following advantages: 5 | 6 | * It makes it possible to make a connection `Send`/`Sync`, see `blocking::SyncConnection`. 7 | * The call to `dbus_connection_dispatch` has been removed and what it did has been rewritten in Rust. This means better performance and panic safety. 8 | * The two-layer design (with `Channel` as a low-level connection) makes it easier to write a custom `Connection`, should that be necessary. 9 | * Preparation for a first class async/non-blocking `Connection`. A `nonblock` module is in the works. 10 | * Some preparations for interfacing the `dbus` crate with a native backend instead of relying on `libdbus`, should someone want to write such a backend. There is a lot more to do before that becomes a reality though. 11 | 12 | Things that have moved 13 | ====================== 14 | 15 | If you want the quickest upgrade experience, then you can just update your imports: 16 | 17 | * The old `Connection` is now at `ffidisp::Connection`, likewise for `ConnPath`, `BusType` and many others. Have a look at the `ffidisp` module to see if your struct has moved there. 18 | * The `stdintf` module is at `ffidisp::stdintf`. 19 | * The old `MessageItem`, should you still need it, is now under `arg::messageitem`. But do use the generic functions instead of `MessageItem` whenever possible. `MessageItem::DictEntry` has changed to `MessageItem::Dict` (which contains the entire dict, not just one entry) to make it less error prone. 20 | 21 | Migrating / upgrading 22 | ===================== 23 | 24 | On a long term, consider migrating / upgrading to `blocking::Connection` or `blocking::SyncConnection`. You would need to make the following adjustments: 25 | 26 | * Create and connect your connection easily with just `Connection::new_session()` or `Connection::new_system()`. 27 | * Instead of `ConnPath`, use a `Proxy`. It works approximately the same way. 28 | * `blocking::stdintf` can be helpful to make standard method calls, such as getting and setting properties on a remote peer. 29 | * `Connection::register_name` has been renamed to `request_name`. (This was just a misnaming.) 30 | * Instead of `incoming()` to process incoming messages, use `process()` (which takes a std `Duration`). This will not hand out any messages though, instead register callbacks using (in order from most convenient to most generic): `Proxy::match_signal`, `Proxy::match_start` or `channel::MatchingReceiver::start_receive`. 31 | * For `tree`s, you must now make sure the root path (`/`) is always part of your tree. 32 | * For `tree`s, call `Tree::start_receive()` to attach the tree and the connection, then call `Connection::process()` to process incoming messages. 33 | 34 | Have a look at the `client`, `server` and `match_signal` examples to get started. 35 | 36 | ReadAll / AppendAll 37 | =================== 38 | 39 | The `ReadAll` and `AppendAll` traits were present in later 0.6.x versions as well, but are worthy a mention because they can make code more ergonomic in many cases. They are implemented for tuples: the empty tuple `()`, the single-tuple `(TYPE,)`, and usual tuples `(TYPE1, TYPE2)` up to 11. So if you have a method call that takes `STRING arg1, INT32 arg2` and returns a `BOOLEAN`, you can call it like this: 40 | 41 | ``` 42 | let (r,): (bool,) = myProxy.method_call(interface, name, (arg1, arg2))?; 43 | ``` 44 | 45 | ...where `arg1` is a `&str` and `arg2` is a `i32`. 46 | -------------------------------------------------------------------------------- /.github/workflows/dbus-rs-github-ci.yml: -------------------------------------------------------------------------------- 1 | name: Dbus-rs-ci 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | # Taken from rust-lang/libz-sys CI/CD example. 14 | # 15 | # This job downloads and stores `cross` as an artifact, so that it can be 16 | # redownloaded across all of the jobs. 17 | install-cross: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: XAMPPRocky/get-github-release@v1 21 | id: cross 22 | with: 23 | owner: rust-embedded 24 | repo: cross 25 | matches: ${{ matrix.platform }} 26 | token: ${{ secrets.GITHUB_TOKEN }} 27 | - uses: actions/upload-artifact@v4 28 | with: 29 | name: cross-${{ matrix.platform }} 30 | path: ${{ steps.cross.outputs.install_path }} 31 | strategy: 32 | matrix: 33 | platform: [linux-musl] 34 | 35 | # Cross compile vendored libdbus for multiple architectures. 36 | # 37 | # Cannot run the full dbus test suite since the cross docker image doesn't 38 | # have dbus-daemon running. 39 | libdbus-sys-linux: 40 | runs-on: ubuntu-latest 41 | needs: install-cross 42 | steps: 43 | - uses: actions/checkout@v3 44 | with: 45 | submodules: 'recursive' 46 | - uses: dtolnay/rust-toolchain@stable 47 | with: 48 | toolchain: stable 49 | - name: Download Cross 50 | uses: actions/download-artifact@v4 51 | with: 52 | name: cross-linux-musl 53 | path: /tmp 54 | - run: rustup toolchain install ${{ matrix.channel }} 55 | - run: chmod +x /tmp/cross 56 | - name: Build 57 | run: /tmp/cross build --package dbus --features vendored --target ${{ matrix.target }} 58 | - name: Run tests 59 | run: /tmp/cross test --package libdbus-sys --features vendored --target ${{ matrix.target }} 60 | strategy: 61 | fail-fast: false 62 | matrix: 63 | channel: [stable] 64 | target: 65 | - aarch64-unknown-linux-musl 66 | - arm-unknown-linux-musleabihf 67 | - i686-unknown-linux-musl 68 | - x86_64-unknown-linux-musl 69 | - x86_64-unknown-linux-gnu 70 | 71 | build: 72 | 73 | runs-on: ubuntu-latest 74 | 75 | steps: 76 | - uses: actions/checkout@v3 77 | - name: Install dependencies 78 | run: | 79 | sudo apt-get update 80 | sudo apt-get install --no-install-recommends -y libdbus-1-dev dbus at-spi2-core 81 | - name: Check 82 | run: cargo check --verbose --all 83 | - name: Run tests 84 | run: | 85 | export DBUS_SESSION_BUS_ADDRESS=`dbus-daemon --session --print-address --fork` 86 | cargo test --all -- --nocapture --color always 87 | cargo test --all-features --all -- --nocapture --color always 88 | cd dbus-codegen && cargo test --all --no-default-features -- --nocapture --color always 89 | 90 | build-mingw: 91 | 92 | runs-on: windows-latest 93 | 94 | defaults: 95 | run: 96 | shell: msys2 {0} 97 | 98 | steps: 99 | - uses: msys2/setup-msys2@v2 100 | with: 101 | install: >- 102 | git 103 | mingw-w64-x86_64-dbus 104 | mingw-w64-x86_64-pkgconf 105 | mingw-w64-x86_64-rust 106 | - uses: actions/checkout@v3 107 | - name: Run tests 108 | run: | 109 | # dbus-daemon has no '--fork' option on windows. But it will autolaunch 110 | cd dbus && cargo test --lib -- --nocapture --color always 111 | -------------------------------------------------------------------------------- /dbus-crossroads/examples/server_cr.rs: -------------------------------------------------------------------------------- 1 | /* 2 | This example is a WIP demo of the "Crossroads" module, successor of the "Tree" module. 3 | 4 | This example creates a D-Bus server with the following functionality: 5 | It registers the "com.example.dbustest" name, creates a "/hello" object path, 6 | which has an "com.example.dbustest" interface. 7 | 8 | The interface has a "Hello" method (which takes no arguments and returns a string), 9 | and a "HelloHappened" signal (with a string argument) which is sent every time 10 | someone calls the "Hello" method. 11 | 12 | When you run this example, you can test it with this command: 13 | dbus-send --print-reply --dest=com.example.dbustest /hello com.example.dbustest.Hello string:MyName 14 | 15 | You should see output like this: 16 | method return time=1667227809.834235 sender=:1.1620 -> destination=:1.1628 serial=7 reply_serial=2 17 | string "Hello MyName! This API has been used 3 times." 18 | */ 19 | use dbus::blocking::Connection; 20 | use dbus_crossroads::{Crossroads, Context}; 21 | use std::error::Error; 22 | 23 | // This is our "Hello" object that we are going to store inside the crossroads instance. 24 | struct Hello { called_count: u32 } 25 | 26 | 27 | fn main() -> Result<(), Box> { 28 | // Let's start by starting up a connection to the session bus and request a name. 29 | let c = Connection::new_session()?; 30 | c.request_name("com.example.dbustest", false, true, false)?; 31 | 32 | // Create a new crossroads instance. 33 | // The instance is configured so that introspection and properties interfaces 34 | // are added by default on object path additions. 35 | let mut cr = Crossroads::new(); 36 | 37 | // Let's build a new interface, which can be used for "Hello" objects. 38 | let iface_token = cr.register("com.example.dbustest", |b| { 39 | // This row advertises (when introspected) that we can send a HelloHappened signal. 40 | // We use the single-tuple to say that we have one single argument, named "sender" of type "String". 41 | // The msg_fn returns a boxed function, which when called constructs the message to be emitted. 42 | let hello_happened = b.signal::<(String,), _>("HelloHappened", ("sender",)).msg_fn(); 43 | 44 | // Let's add a method to the interface. We have the method name, followed by 45 | // names of input and output arguments (used for introspection). The closure then controls 46 | // the types of these arguments. The last argument to the closure is a tuple of the input arguments. 47 | b.method("Hello", ("name",), ("reply",), move |ctx: &mut Context, hello: &mut Hello, (name,): (String,)| { 48 | 49 | // And here's what happens when the method is called. 50 | println!("Incoming hello call from {}!", name); 51 | hello.called_count += 1; 52 | let reply = format!("Hello {}! This API has been used {} times.", name, hello.called_count); 53 | 54 | // Now call the function we got earlier to get a signal message. 55 | // The function takes all its arguments as the second parameter, so we must again 56 | // tuple our single argument into a single-tuple. 57 | let signal_msg = hello_happened(ctx.path(), &(name,)); 58 | // The ctx parameter can be used to conveniently send extra messages. 59 | ctx.push_msg(signal_msg); 60 | 61 | // And the return value from the method call is a tuple of the output arguments. 62 | Ok((reply,)) 63 | }); 64 | }); 65 | 66 | // Let's add the "/hello" path, which implements the com.example.dbustest interface, 67 | // to the crossroads instance. 68 | cr.insert("/hello", &[iface_token], Hello { called_count: 0}); 69 | 70 | // Serve clients forever. 71 | cr.serve(&c)?; 72 | unreachable!() 73 | } 74 | -------------------------------------------------------------------------------- /dbus-tree/src/utils.rs: -------------------------------------------------------------------------------- 1 | // Small structs that don't have their own unit. 2 | 3 | use dbus::strings::{Signature, Member, Path, Interface as IfaceName}; 4 | use std::collections::{BTreeMap, btree_map}; 5 | use std::sync::Arc; 6 | 7 | pub type ArcMap = BTreeMap>; 8 | 9 | #[derive(Clone, Debug)] 10 | pub enum IterE<'a, V: 'a> { 11 | Path(btree_map::Values<'a, Arc>, Arc>), 12 | Iface(btree_map::Values<'a, Arc>, Arc>), 13 | Member(btree_map::Values<'a, Member<'static>, Arc>), 14 | String(btree_map::Values<'a, String, Arc>), 15 | } 16 | 17 | #[derive(Clone, Debug)] 18 | /// Iterator struct, returned from iterator methods on Tree, Objectpath and Interface. 19 | pub struct Iter<'a, V: 'a>(IterE<'a, V>); 20 | 21 | impl<'a, V: 'a> From> for Iter<'a, V> { fn from(x: IterE<'a, V>) -> Iter<'a, V> { Iter(x) }} 22 | 23 | impl<'a, V: 'a> Iterator for Iter<'a, V> { 24 | type Item = &'a Arc; 25 | fn next(&mut self) -> Option { 26 | match self.0 { 27 | IterE::Path(ref mut x) => x.next(), 28 | IterE::Iface(ref mut x) => x.next(), 29 | IterE::Member(ref mut x) => x.next(), 30 | IterE::String(ref mut x) => x.next(), 31 | } 32 | } 33 | } 34 | 35 | #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] 36 | /// A D-Bus Argument. 37 | pub struct Argument(Option, Signature<'static>); 38 | 39 | impl Argument { 40 | /// Create a new Argument. 41 | pub fn new(name: Option, sig: Signature<'static>) -> Argument { Argument(name, sig) } 42 | 43 | /// Descriptive name (if any). 44 | pub fn name(&self) -> Option<&str> { self.0.as_ref().map(|s| &**s) } 45 | 46 | /// Type signature of argument. 47 | pub fn signature(&self) -> &Signature<'static> { &self.1 } 48 | 49 | fn introspect(&self, indent: &str, dir: &str) -> String { 50 | let n = self.0.as_ref().map(|n| format!("name=\"{}\" ", n)).unwrap_or_default(); 51 | format!("{}\n", indent, n, self.1, dir) 52 | } 53 | 54 | } 55 | 56 | pub fn introspect_args(args: &[Argument], indent: &str, dir: &str) -> String { 57 | args.iter().fold("".to_string(), |aa, az| format!("{}{}", aa, az.introspect(indent, dir))) 58 | } 59 | 60 | // Small helper struct to reduce memory somewhat for objects without annotations 61 | #[derive(Clone, Debug, Default)] 62 | pub struct Annotations(Option>); 63 | 64 | impl Annotations { 65 | pub fn new() -> Annotations { Annotations(None) } 66 | 67 | pub fn insert, V: Into>(&mut self, n: N, v: V) { 68 | if self.0.is_none() { self.0 = Some(BTreeMap::new()) } 69 | self.0.as_mut().unwrap().insert(n.into(), v.into()); 70 | } 71 | 72 | pub fn introspect(&self, indent: &str) -> String { 73 | self.0.as_ref().map(|s| s.iter().fold("".into(), |aa, (ak, av)| { 74 | format!("{}{}\n", aa, indent, ak, av) 75 | })).unwrap_or_default() 76 | } 77 | } 78 | 79 | // Doesn't work, conflicting impls 80 | // impl> From for Argument 81 | 82 | impl From> for Argument { 83 | fn from(t: Signature<'static>) -> Argument { Argument(None, t) } 84 | } 85 | 86 | impl<'a> From<&'a str> for Argument { 87 | fn from(t: &'a str) -> Argument { Argument(None, String::from(t).into()) } 88 | } 89 | 90 | impl, S: Into>> From<(N, S)> for Argument { 91 | fn from((n, s): (N, S)) -> Argument { Argument(Some(n.into()), s.into()) } 92 | } 93 | 94 | pub trait Introspect { 95 | // At some point we might want to switch to fmt::Write / fmt::Formatter for performance... 96 | fn xml_name(&self) -> &'static str; 97 | fn xml_params(&self) -> String; 98 | fn xml_contents(&self) -> String; 99 | } 100 | -------------------------------------------------------------------------------- /dbus-tokio/examples/tokio_server_cr.rs: -------------------------------------------------------------------------------- 1 | use dbus_tokio::connection; 2 | use futures::future; 3 | use tokio::time::sleep; 4 | use dbus::channel::MatchingReceiver; 5 | use dbus::message::MatchRule; 6 | use dbus_crossroads::Crossroads; 7 | use std::time::Duration; 8 | 9 | // This is our "Hello" object that we are going to store inside the crossroads instance. 10 | struct Hello { called_count: u32 } 11 | 12 | #[tokio::main] 13 | pub async fn main() -> Result<(), Box> { 14 | // Create a new crossroads instance. 15 | // The instance is configured so that introspection and properties interfaces 16 | // are added by default on object path additions. 17 | let mut cr = Crossroads::new(); 18 | // Connect to the D-Bus session bus (this is blocking, unfortunately). 19 | let (resource, c) = connection::new_session_sync()?; 20 | 21 | 22 | // Enable async support for the crossroads instance. 23 | cr.set_async_support(Some((c.clone(), Box::new(|x| { tokio::spawn(x); })))); 24 | 25 | // Let's build a new interface, which can be used for "Hello" objects. 26 | let iface_token = cr.register("com.example.dbustest", |b| { 27 | // This row is just for introspection: It advertises that we can send a 28 | // HelloHappened signal. We use the single-tuple to say that we have one single argument, 29 | // named "sender" of type "String". 30 | b.signal::<(String,), _>("HelloHappened", ("sender",)); 31 | // Let's add a method to the interface. We have the method name, followed by 32 | // names of input and output arguments (used for introspection). The closure then controls 33 | // the types of these arguments. The last argument to the closure is a tuple of the input arguments. 34 | b.method_with_cr_async("Hello", ("name",), ("reply",), |mut ctx, cr, (name,): (String,)| { 35 | let hello: &mut Hello = cr.data_mut(ctx.path()).unwrap(); // ok_or_else(|| MethodErr::no_path(ctx.path()))?; 36 | // And here's what happens when the method is called. 37 | println!("Incoming hello call from {}!", name); 38 | hello.called_count += 1; 39 | let s = format!("Hello {}! This API has been used {} times.", name, hello.called_count); 40 | async move { 41 | // Let's wait half a second just to show off how async we are. 42 | sleep(Duration::from_millis(500)).await; 43 | // The ctx parameter can be used to conveniently send extra messages. 44 | let signal_msg = ctx.make_signal("HelloHappened", (name,)); 45 | ctx.push_msg(signal_msg); 46 | // And the return value is a tuple of the output arguments. 47 | ctx.reply(Ok((s,))) 48 | // The reply is sent when ctx is dropped / goes out of scope. 49 | } 50 | }); 51 | }); 52 | 53 | // Let's add the "/hello" path, which implements the com.example.dbustest interface, 54 | // to the crossroads instance. 55 | cr.insert("/hello", &[iface_token], Hello { called_count: 0}); 56 | 57 | 58 | 59 | // We add the Crossroads instance to the connection so that incoming method calls will be handled. 60 | c.start_receive(MatchRule::new_method_call(), Box::new(move |msg, conn| { 61 | cr.handle_message(msg, conn).unwrap(); 62 | true 63 | })); 64 | 65 | // The resource is a task that should be spawned onto a tokio compatible 66 | // reactor ASAP. If the resource ever finishes, you lost connection to D-Bus. 67 | // 68 | // To shut down the connection, both call _handle.abort() and drop the connection. 69 | let _handle = tokio::spawn(async { 70 | let err = resource.await; 71 | panic!("Lost connection to D-Bus: {}", err); 72 | }); 73 | 74 | // Let's request a name on the bus, so that clients can find us. 75 | c.request_name("com.example.dbustest", false, true, false).await?; 76 | 77 | // Run forever. 78 | future::pending::<()>().await; 79 | unreachable!() 80 | } 81 | -------------------------------------------------------------------------------- /dbus-codegen/data/org.freedesktop.DBus.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /dbus/src/message/signalargs.rs: -------------------------------------------------------------------------------- 1 | use crate::arg; 2 | use crate::{Message, MessageType}; 3 | use crate::message::MatchRule; 4 | use crate::strings::{BusName, Path, Interface, Member}; 5 | 6 | /// Helper methods for structs representing a Signal 7 | /// 8 | /// # Example 9 | /// 10 | /// Listen to InterfacesRemoved signal from org.bluez.obex. 11 | /// 12 | /// ```rust,no_run 13 | /// use dbus::blocking::Connection; 14 | /// use dbus::message::SignalArgs; 15 | /// use dbus::blocking::stdintf::org_freedesktop_dbus::ObjectManagerInterfacesRemoved as IR; 16 | /// use std::time::Duration; 17 | /// 18 | /// let c = Connection::new_session().unwrap(); 19 | /// // Add a match for this signal 20 | /// let mr = IR::match_rule(Some(&"org.bluez.obex".into()), None).static_clone(); 21 | /// c.add_match(mr, |ir: IR, _, _| { 22 | /// println!("Interfaces {:?} have been removed from bluez on path {}.", ir.interfaces, ir.object); 23 | /// true 24 | /// }); 25 | /// 26 | /// // Wait for the signal to arrive. 27 | /// loop { c.process(Duration::from_millis(1000)).unwrap(); } 28 | /// ``` 29 | 30 | pub trait SignalArgs { 31 | /// D-Bus name of signal 32 | const NAME: &'static str; 33 | 34 | /// D-Bus name of interface this signal belongs to 35 | const INTERFACE: &'static str; 36 | 37 | /// Returns a message that emits the signal. 38 | fn to_emit_message(&self, path: &Path) -> Message where Self: arg::AppendAll { 39 | let mut m = Message::signal(path, &Interface::from(Self::INTERFACE), &Member::from(Self::NAME)); 40 | arg::AppendAll::append(self, &mut arg::IterAppend::new(&mut m)); 41 | m 42 | } 43 | 44 | /// If the message is a signal of the correct type, return its arguments, otherwise return None. 45 | /// 46 | /// This does not check sender and path of the message, which is likely relevant to you as well. 47 | #[allow(clippy::if_same_then_else)] 48 | fn from_message(m: &Message) -> Option where Self: Sized + arg::ReadAll { 49 | if m.msg_type() != MessageType::Signal { None } 50 | else if m.interface().as_ref().map(|x| &**x) != Some(Self::INTERFACE) { None } 51 | else if m.member().as_ref().map(|x| &**x) != Some(Self::NAME) { None } 52 | else { 53 | arg::ReadAll::read(&mut m.iter_init()).ok() 54 | } 55 | } 56 | 57 | /// Returns a match rule matching this signal. 58 | /// 59 | /// If sender and/or path is None, matches all senders and/or paths. 60 | fn match_rule<'a>(sender: Option<&'a BusName>, path: Option<&'a Path>) -> MatchRule<'a> { 61 | let mut m: MatchRule = Default::default(); 62 | m.sender = sender.cloned(); 63 | m.path = path.cloned(); 64 | m.msg_type = Some(MessageType::Signal); 65 | m.interface = Some(Self::INTERFACE.into()); 66 | m.member = Some(Self::NAME.into()); 67 | m 68 | } 69 | 70 | 71 | /// Returns a string that can be sent to `Connection::add_match`. 72 | /// 73 | /// If sender and/or path is None, matches all senders and/or paths. 74 | fn match_str(sender: Option<&BusName>, path: Option<&Path>) -> String { 75 | Self::match_rule(sender, path).match_str() 76 | } 77 | } 78 | 79 | #[test] 80 | fn intf_removed() { 81 | use crate::blocking::LocalConnection; 82 | use crate::blocking::stdintf::org_freedesktop_dbus::ObjectManagerInterfacesRemoved as IR; 83 | use std::{time::Duration, cell::Cell, rc::Rc}; 84 | let c = LocalConnection::new_session().unwrap(); 85 | 86 | let mr = IR::match_rule(Some(&c.unique_name().into()), Some(&"/hello".into())).static_clone(); 87 | println!("Match: {:?}", mr); 88 | 89 | let ir = IR { object: "/hello".into(), interfaces: vec!("ABC.DEF".into(), "GHI.JKL".into()) }; 90 | let ir_msg = ir.to_emit_message(&"/hello".into()); 91 | let done = Rc::new(Cell::new(false)); 92 | let done2 = done.clone(); 93 | 94 | c.add_match(mr, move |ir2: IR, _, _| { 95 | assert_eq!(ir2.object, ir.object); 96 | assert_eq!(ir2.interfaces, ir.interfaces); 97 | done2.set(true); 98 | false 99 | }).unwrap(); 100 | use crate::channel::Sender; 101 | c.send(ir_msg).expect("Failed to send message"); 102 | while !done.get() { c.process(Duration::from_millis(1000)).unwrap(); } 103 | } 104 | -------------------------------------------------------------------------------- /dbus-crossroads/src_old/context.rs: -------------------------------------------------------------------------------- 1 | use dbus::strings::{Path as PathName, Interface as IfaceName, Member as MemberName, Signature}; 2 | use dbus::{Message, MessageType}; 3 | use super::info::IfaceInfo; 4 | use super::crossroads::Crossroads; 5 | use super::path::Path; 6 | use super::handlers::Handlers; 7 | use super::MethodErr; 8 | use super::stdimpl::DBusSignals; 9 | use std::ffi::CStr; 10 | use dbus::arg::{AppendAll, IterAppend}; 11 | 12 | #[derive(Debug)] 13 | enum MsgCow<'a> { 14 | Borrowed(&'a Message), 15 | Owned(Message), 16 | } 17 | 18 | #[derive(Debug)] 19 | pub struct MsgCtx<'a> { 20 | msg: Option>, 21 | member: MemberName<'a>, 22 | iface: Option>, 23 | path: PathName<'a>, 24 | pub (super) signals: DBusSignals, 25 | pub (super) reply: Option, 26 | pub (super) send_extra: Vec, 27 | } 28 | 29 | impl MsgCtx<'static> { 30 | pub fn new(msg: Message) -> Option { 31 | if msg.msg_type() != MessageType::MethodCall { return None }; 32 | let path = msg.path()?.into_static(); 33 | let iface = msg.interface().map(|x| x.into_static()); 34 | let member = msg.member()?.into_static(); 35 | Some(MsgCtx { msg: Some(MsgCow::Owned(msg)), member, iface, path, 36 | send_extra: vec!(), 37 | signals: Default::default(), 38 | reply: None 39 | }) 40 | } 41 | 42 | pub fn new_without_message(path: PathName<'static>, iface: Option>, member: MemberName<'static>) -> Self { 43 | MsgCtx { msg: None, member, iface, path, send_extra: vec!(), signals: Default::default(), reply: None } 44 | } 45 | } 46 | 47 | 48 | impl<'a> MsgCtx<'a> { 49 | 50 | pub fn message(&self) -> &Message { 51 | match self.msg { 52 | Some(MsgCow::Owned(ref r)) => r, 53 | Some(MsgCow::Borrowed(r)) => r, 54 | None => panic!("No message"), 55 | } 56 | } 57 | 58 | pub fn member(&self) -> &MemberName<'a> { &self.member } 59 | pub fn path(&self) -> &PathName<'a> { &self.path } 60 | // FIXME 61 | pub fn interface(&self) -> &IfaceName<'a> { self.iface.as_ref().unwrap() } 62 | 63 | pub fn send_msg(&mut self, msg: Message) { self.send_extra.push(msg); } 64 | 65 | pub fn make_signal<'b, A: AppendAll, N: Into>>(&self, name: N, args: A) -> Message { 66 | let mut msg = Message::signal(&self.path, self.iface.as_ref().unwrap(), &name.into()); 67 | args.append(&mut IterAppend::new(&mut msg)); 68 | msg 69 | } 70 | 71 | pub fn dbus_signals_mut(&mut self) -> &mut DBusSignals { &mut self.signals } 72 | 73 | pub (crate) fn do_reply(&mut self, f: F) { 74 | if self.message().get_no_reply() { return; } 75 | let mut m = self.message().method_return(); 76 | f(&mut m, self); 77 | self.reply = Some(m); 78 | } 79 | } 80 | 81 | pub struct AsyncMsgCtx { 82 | 83 | } 84 | 85 | pub struct AsyncMsgReply { 86 | 87 | } 88 | 89 | impl AsyncMsgCtx { 90 | pub fn send_msg(&mut self, msg: Message) { unimplemented!() } 91 | 92 | pub fn make_signal<'b, A: AppendAll, N: Into>>(&self, name: N, args: A) -> Message { 93 | unimplemented!() 94 | } 95 | 96 | pub fn message(&self) -> &Message { unimplemented!() } 97 | 98 | pub fn iface(&self) -> Option> { unimplemented!() } 99 | 100 | pub fn custom_reply(self, r: Option) -> AsyncMsgReply { unimplemented!() } 101 | 102 | pub fn reply(self, a: A) -> AsyncMsgReply { unimplemented!() } 103 | 104 | pub fn err>(self, m: I) -> AsyncMsgReply { unimplemented!() } 105 | 106 | pub (crate) fn iface_typeid(&self) -> Option { todo!() } 107 | } 108 | 109 | #[derive(Debug, Clone)] 110 | pub struct RefCtx<'a, H: Handlers> { 111 | pub crossroads: &'a Crossroads, 112 | pub path: &'a Path, 113 | pub (super) iface: &'a H::Iface, 114 | pub (super) iinfo: &'a IfaceInfo<'static, H>, 115 | } 116 | 117 | impl<'a, H: Handlers> RefCtx<'a, H> { 118 | 119 | pub (super) fn new<'b>(cr: &'a Crossroads, ctx: &'b MsgCtx) -> Result { 120 | let path = cr.paths.get(ctx.path.as_cstr()).ok_or_else(|| { MethodErr::no_path(&ctx.path) })?; 121 | let entry = cr.reg.get(ctx.iface.as_ref().unwrap().as_cstr()).ok_or_else(|| { MethodErr::no_interface(&ctx.iface.as_ref().unwrap()) })?; 122 | let iface = path.get_from_typeid(entry.typeid).ok_or_else(|| { MethodErr::no_interface(&ctx.iface.as_ref().unwrap()) })?; 123 | Ok(RefCtx { crossroads: cr, path, iface, iinfo: &entry.info }) 124 | } 125 | 126 | pub (super) fn with_iface(&self, ifacename: &CStr) -> Option { 127 | let entry = self.crossroads.reg.get(ifacename)?; 128 | let iface = self.path.get_from_typeid(entry.typeid)?; 129 | Some(RefCtx { crossroads: self.crossroads, path: self.path, iface, iinfo: &entry.info }) 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /dbus-tree/src/factory.rs: -------------------------------------------------------------------------------- 1 | use super::{MethodType, DataType, MTFn, MTFnMut, MTSync, MethodResult, MethodInfo}; 2 | use super::{Tree, ObjectPath, Interface, Property, Signal, Method}; 3 | use super::objectpath::IfaceCache; 4 | use std::sync::Arc; 5 | use dbus::strings::{Interface as IfaceName, Member}; 6 | use dbus::{Path, arg}; 7 | use std::cell::RefCell; 8 | 9 | /// The factory is used to create object paths, interfaces, methods etc. 10 | /// 11 | /// There are three factories: 12 | /// 13 | /// **MTFn** - all methods are `Fn()`. 14 | /// 15 | /// **MTFnMut** - all methods are `FnMut()`. This means they can mutate their environment, 16 | /// which has the side effect that if you call it recursively, it will RefCell panic. 17 | /// 18 | /// **MTSync** - all methods are `Fn() + Send + Sync + 'static`. This means that the methods 19 | /// can be called from different threads in parallel. 20 | /// 21 | #[derive(Debug, Clone)] 22 | pub struct Factory, D: DataType=()>(Arc>); 23 | 24 | impl, D: DataType> From>> for Factory { 25 | fn from(f: Arc>) -> Self { Factory(f) } 26 | } 27 | 28 | impl Factory, ()> { 29 | /// Creates a new factory for single-thread use. 30 | pub fn new_fn() -> Factory, D> { Factory(IfaceCache::new()) } 31 | 32 | /// Creates a new factory for single-thread use, where callbacks can mutate their environment. 33 | pub fn new_fnmut() -> Factory, D> { Factory(IfaceCache::new()) } 34 | 35 | /// Creates a new factory for multi-thread use. 36 | pub fn new_sync() -> Factory, D> { Factory(IfaceCache::new()) } 37 | 38 | } 39 | 40 | impl Factory, D> { 41 | /// Creates a new method for single-thread use. 42 | pub fn method(&self, t: T, data: D::Method, handler: H) -> Method, D> 43 | where H: 'static + Fn(&MethodInfo, D>) -> MethodResult, T: Into> { 44 | super::leaves::new_method(t.into(), data, Box::new(handler) as Box<_>) 45 | } 46 | } 47 | 48 | impl Factory, D> { 49 | /// Creates a new method for single-thread use. 50 | pub fn method(&self, t: T, data: D::Method, handler: H) -> Method, D> 51 | where H: 'static + FnMut(&MethodInfo, D>) -> MethodResult, T: Into> { 52 | super::leaves::new_method(t.into(), data, Box::new(RefCell::new(handler)) as Box<_>) 53 | } 54 | } 55 | 56 | impl Factory, D> { 57 | /// Creates a new method for multi-thread use. 58 | pub fn method(&self, t: T, data: D::Method, handler: H) -> Method, D> 59 | where H: Fn(&MethodInfo, D>) -> MethodResult + Send + Sync + 'static, T: Into> { 60 | super::leaves::new_method(t.into(), data, Box::new(handler) as Box<_>) 61 | } 62 | } 63 | 64 | 65 | impl, D: DataType> Factory { 66 | 67 | /// Creates a new property. 68 | /// 69 | /// `A` is used to calculate the type signature of the property. 70 | pub fn property>(&self, name: T, data: D::Property) -> Property { 71 | let sig = A::signature(); 72 | super::leaves::new_property(name.into(), sig, data) 73 | } 74 | 75 | /// Creates a new signal. 76 | pub fn signal>>(&self, name: T, data: D::Signal) -> Signal { 77 | super::leaves::new_signal(name.into(), data) 78 | } 79 | 80 | /// Creates a new interface. 81 | pub fn interface>>(&self, name: T, data: D::Interface) -> Interface { 82 | super::objectpath::new_interface(name.into(), data) 83 | } 84 | 85 | /// Creates a new object path. 86 | pub fn object_path>>(&self, name: T, data: D::ObjectPath) -> ObjectPath { 87 | super::objectpath::new_objectpath(name.into(), data, self.0.clone()) 88 | } 89 | 90 | /// Creates a new tree. 91 | pub fn tree(&self, data: D::Tree) -> Tree { 92 | super::objectpath::new_tree(data) 93 | } 94 | 95 | /// Creates a new method - usually you'll use "method" instead. 96 | /// 97 | /// This is useful for being able to create methods in code which is generic over methodtype. 98 | pub fn method_sync(&self, t: T, data: D::Method, handler: H) -> Method 99 | where H: Fn(&MethodInfo) -> MethodResult + Send + Sync + 'static, T: Into> { 100 | super::leaves::new_method(t.into(), data, M::make_method(handler)) 101 | } 102 | } 103 | 104 | 105 | #[test] 106 | fn create_fnmut() { 107 | let f = Factory::new_fnmut::<()>(); 108 | let mut move_me = 5u32; 109 | let m = f.method("test", (), move |m| { 110 | move_me += 1; 111 | Ok(vec!(m.msg.method_return().append1(&move_me))) 112 | }); 113 | assert_eq!(&**m.get_name(), "test"); 114 | } 115 | 116 | 117 | #[test] 118 | fn fn_customdata() { 119 | #[derive(Default)] 120 | struct Custom; 121 | impl DataType for Custom { 122 | type Tree = (); 123 | type ObjectPath = Arc; 124 | type Interface = (); 125 | type Property = (); 126 | type Method = i32; 127 | type Signal = (); 128 | } 129 | 130 | let f = Factory::new_fn::(); 131 | 132 | let m = f.method("test", 789, |_| unimplemented!()); 133 | assert_eq!(*m.get_data(), 789); 134 | 135 | let o = f.object_path("/test/test", Arc::new(7)); 136 | assert_eq!(**o.get_data(), 7); 137 | } 138 | -------------------------------------------------------------------------------- /dbus/src/prop.rs: -------------------------------------------------------------------------------- 1 | use super::{Connection, Message, MessageItem, Error, Path, Interface, BusName}; 2 | use std::collections::BTreeMap; 3 | 4 | /// Client side properties - get and set properties on a remote application. 5 | pub struct Props<'a> { 6 | name: BusName<'a>, 7 | path: Path<'a>, 8 | interface: Interface<'a>, 9 | timeout_ms: i32, 10 | conn: &'a Connection, 11 | } 12 | 13 | impl<'a> Props<'a> { 14 | /// Create a new Props. 15 | pub fn new(conn: &'a Connection, name: N, path: P, interface: I, timeout_ms: i32) -> Props<'a> 16 | where N: Into>, P: Into>, I: Into> { 17 | Props { 18 | name: name.into(), 19 | path: path.into(), 20 | interface: interface.into(), 21 | timeout_ms: timeout_ms, 22 | conn: conn, 23 | } 24 | } 25 | 26 | /// Get a single property's value. 27 | pub fn get(&self, propname: &str) -> Result { 28 | let mut m = Message::method_call(&self.name, &self.path, 29 | &"org.freedesktop.DBus.Properties".into(), &"Get".into()); 30 | m.append_items(&[self.interface.to_string().into(), propname.to_string().into()]); 31 | let mut r = self.conn.send_with_reply_and_block(m, self.timeout_ms)?; 32 | let reply = r.as_result()?.get_items(); 33 | if reply.len() == 1 { 34 | if let &MessageItem::Variant(ref v) = &reply[0] { 35 | return Ok((**v).clone()) 36 | } 37 | } 38 | let f = format!("Invalid reply for property get {}: '{:?}'", propname, reply); 39 | return Err(Error::new_custom("InvalidReply", &f)); 40 | } 41 | 42 | /// Set a single property's value. 43 | pub fn set(&self, propname: &str, value: MessageItem) -> Result<(), Error> { 44 | let mut m = Message::method_call(&self.name, &self.path, 45 | &"org.freedesktop.DBus.Properties".into(), &"Set".into()); 46 | m.append_items(&[self.interface.to_string().into(), propname.to_string().into(), Box::new(value).into()]); 47 | let mut r = self.conn.send_with_reply_and_block(m, self.timeout_ms)?; 48 | r.as_result()?; 49 | Ok(()) 50 | } 51 | 52 | /// Get a map of all the properties' names and their values. 53 | pub fn get_all(&self) -> Result, Error> { 54 | let mut m = Message::method_call(&self.name, &self.path, 55 | &"org.freedesktop.DBus.Properties".into(), &"GetAll".into()); 56 | m.append_items(&[self.interface.to_string().into()]); 57 | let mut r = self.conn.send_with_reply_and_block(m, self.timeout_ms)?; 58 | let reply = r.as_result()?.get_items(); 59 | 60 | (|| { 61 | if reply.len() != 1 { return Err(()) }; 62 | let mut t = BTreeMap::new(); 63 | let a: &[MessageItem] = reply[0].inner()?; 64 | for p in a.iter() { 65 | let (k, v) = p.inner()?; 66 | let (k, v): (&String, &MessageItem) = (k.inner()?, v.inner()?); 67 | t.insert(k.clone(), v.clone()); 68 | } 69 | Ok(t) 70 | })().map_err(|_| { 71 | let f = format!("Invalid reply for property GetAll: '{:?}'", reply); 72 | Error::new_custom("InvalidReply", &f) 73 | }) 74 | } 75 | } 76 | 77 | /// Wrapper around Props that keeps a map of fetched properties. 78 | pub struct PropHandler<'a> { 79 | p: Props<'a>, 80 | map: BTreeMap, 81 | } 82 | 83 | impl<'a> PropHandler<'a> { 84 | /// Create a new PropHandler from a Props. 85 | pub fn new(p: Props) -> PropHandler { 86 | PropHandler { p: p, map: BTreeMap::new() } 87 | } 88 | 89 | /// Get a map of all the properties' names and their values. 90 | pub fn get_all(&mut self) -> Result<(), Error> { 91 | self.map = self.p.get_all()?; 92 | Ok(()) 93 | } 94 | 95 | /// Get a mutable reference to the PropHandler's fetched properties. 96 | pub fn map_mut(&mut self) -> &mut BTreeMap { &mut self.map } 97 | 98 | /// Get a reference to the PropHandler's fetched properties. 99 | pub fn map(&self) -> &BTreeMap { &self.map } 100 | 101 | /// Get a single property's value. 102 | pub fn get(&mut self, propname: &str) -> Result<&MessageItem, Error> { 103 | let v = self.p.get(propname)?; 104 | self.map.insert(propname.to_string(), v); 105 | Ok(self.map.get(propname).unwrap()) 106 | } 107 | 108 | /// Set a single property's value. 109 | pub fn set(&mut self, propname: &str, value: MessageItem) -> Result<(), Error> { 110 | self.p.set(propname, value.clone())?; 111 | self.map.insert(propname.to_string(), value); 112 | Ok(()) 113 | } 114 | } 115 | 116 | 117 | /* Unfortunately org.freedesktop.DBus has no properties we can use for testing, but PolicyKit should be around on most distros. */ 118 | #[test] 119 | fn test_get_policykit_version() { 120 | use super::BusType; 121 | let c = Connection::get_private(BusType::System).unwrap(); 122 | let p = Props::new(&c, "org.freedesktop.PolicyKit1", "/org/freedesktop/PolicyKit1/Authority", 123 | "org.freedesktop.PolicyKit1.Authority", 10000); 124 | 125 | /* Let's use both the get and getall methods and see if we get the same result */ 126 | let v = p.get("BackendVersion").unwrap(); 127 | let vall = p.get_all().unwrap(); 128 | let v2 = vall.get("BackendVersion").unwrap(); 129 | 130 | assert_eq!(&v, &*v2); 131 | match v { 132 | MessageItem::Str(ref s) => { println!("Policykit Backend version is {}", s); } 133 | _ => { panic!("Invalid Get: {:?}", v); } 134 | }; 135 | } 136 | 137 | -------------------------------------------------------------------------------- /dbus-tokio/examples/tokio_adv_server_cr.rs: -------------------------------------------------------------------------------- 1 | // More advanced server example, tokio + crossroads version 2 | 3 | // This is supposed to look like a D-Bus service that allows the user to manipulate storage devices. 4 | 5 | use dbus_tokio::connection; 6 | use futures::future; 7 | use tokio::time::sleep; 8 | use dbus::channel::{MatchingReceiver, Sender}; 9 | use dbus::message::MatchRule; 10 | use dbus::nonblock::SyncConnection; 11 | use std::time::Duration; 12 | use dbus::{Path, Message}; 13 | use dbus_crossroads::{MethodErr, Crossroads, IfaceToken, IfaceBuilder}; 14 | use std::sync::{Arc, Mutex}; 15 | 16 | // Our storage device 17 | #[derive(Debug)] 18 | struct Device { 19 | description: String, 20 | path: Path<'static>, 21 | online: bool, 22 | checking: bool, 23 | } 24 | 25 | impl Device { 26 | // Creates a "test" device (not a real one, since this is an example). 27 | fn new_bogus(index: i32) -> Device { 28 | Device { 29 | description: format!("This is device {}, which is {}.", index, 30 | ["totally awesome", "really fancy", "still going strong"][(index as usize) % 3]), 31 | path: format!("/Device{}", index).into(), 32 | online: index % 2 == 0, 33 | checking: false, 34 | } 35 | } 36 | } 37 | 38 | fn register_iface(cr: &Arc>, conn: Arc) -> IfaceToken { 39 | let cr2 = cr.clone(); 40 | let mut cr_lock = cr.lock().unwrap(); 41 | cr_lock.register("com.example.dbus.rs.device", |b: &mut IfaceBuilder| { 42 | // The online property can be both set and get 43 | b.property("online") 44 | .get(|_, device| Ok(device.online)) 45 | .set(|_, device, value| { 46 | if value && device.checking { 47 | Err(MethodErr::failed(&"Device currently under check, cannot bring online"))? 48 | } 49 | device.online = value; 50 | Ok(Some(value)) 51 | }); 52 | // The "checking" property is read only 53 | b.property("checking") 54 | .emits_changed_false() 55 | .get(|_, device| Ok(device.checking)); 56 | // ...and so is the "description" property 57 | b.property("description") 58 | .emits_changed_const() 59 | .get(|_, device| Ok(device.description.clone())); 60 | 61 | // Add a method for starting a device check. 62 | // This method has no input or output arguments. 63 | b.method("check", (), (), move |_, device, _: ()| { 64 | if device.checking { 65 | Err(MethodErr::failed(&"Device currently under check, cannot start another check"))? 66 | } 67 | if device.online { 68 | Err(MethodErr::failed(&"Device is currently online, cannot start check"))? 69 | } 70 | device.checking = true; 71 | 72 | let path = device.path.clone(); 73 | let cr_clone = cr2.clone(); 74 | let conn_clone = conn.clone(); 75 | tokio::spawn(async move { 76 | // Let's pretend we're doing a 15 second check of the device. 77 | sleep(Duration::from_secs(15)).await; 78 | 79 | // Now we need to set checking to false again. 80 | // However, at this point we have no longer access to the "device" variable, 81 | // so we have to do this the manual way. 82 | let mut cr = cr_clone.lock().unwrap(); 83 | let device: &mut Device = cr.data_mut(&path).unwrap(); 84 | device.checking = false; 85 | 86 | // Send a signal that the check completed. 87 | let msg = Message::signal(&path, &"com.example.dbus.rs.device".into(), &"CheckComplete".into()); 88 | let _ = conn_clone.send(msg); 89 | }); 90 | Ok(()) 91 | }); 92 | // Advertise that we send a signal when check completes. 93 | b.signal::<(), _>("CheckComplete", ()); 94 | }) 95 | } 96 | 97 | #[tokio::main] 98 | pub async fn main() -> Result<(), Box> { 99 | 100 | // Connect to the D-Bus session bus (this is blocking, unfortunately). 101 | let (resource, c) = connection::new_session_sync()?; 102 | 103 | // Create a new crossroads instance. 104 | // 105 | // We have to wrap it inside an arc/mutex because we need to modify it outside message handling, 106 | // i e, when a check is completed. 107 | let cr = Arc::new(Mutex::new(Crossroads::new())); 108 | 109 | // Build and register our "com.example.dbus.rs.device" interface. 110 | let token = register_iface(&cr, c.clone()); 111 | 112 | // Create devices and register them in the tree 113 | { 114 | let mut cr_lock = cr.lock().unwrap(); 115 | for i in 0..10 { 116 | let d = Device::new_bogus(i); 117 | cr_lock.insert(d.path.clone(), &[token], d); 118 | } 119 | } 120 | 121 | // We add the Crossroads instance to the connection so that incoming method calls will be handled. 122 | c.start_receive(MatchRule::new_method_call(), Box::new(move |msg, conn| { 123 | let mut cr_lock = cr.lock().unwrap(); 124 | cr_lock.handle_message(msg, conn).unwrap(); 125 | true 126 | })); 127 | 128 | // The resource is a task that should be spawned onto a tokio compatible 129 | // reactor ASAP. If the resource ever finishes, you lost connection to D-Bus. 130 | // 131 | // To shut down the connection, both call _handle.abort() and drop the connection. 132 | let _handle = tokio::spawn(async { 133 | let err = resource.await; 134 | panic!("Lost connection to D-Bus: {}", err); 135 | }); 136 | 137 | c.request_name("com.example.dbus.rs.advancedserverexample", false, true, false).await?; 138 | 139 | // Run forever. 140 | future::pending::<()>().await; 141 | unreachable!() 142 | } 143 | -------------------------------------------------------------------------------- /dbus-crossroads/src/context.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use dbus::arg::AppendAll; 3 | use dbus::channel::Sender; 4 | use std::sync::Arc; 5 | use crate::{MethodErr, utils::Dbg}; 6 | 7 | /// Context is the struct that accompanies you through your method call handler, 8 | /// providing helpful information about the message sent from the client, as well as 9 | /// some methods to send extra messages (typically signals) in return. 10 | #[derive(Debug)] 11 | pub struct Context { 12 | path: dbus::Path<'static>, 13 | interface: Option>, 14 | method: dbus::strings::Member<'static>, 15 | message: dbus::Message, 16 | 17 | has_error: bool, 18 | reply: Option, 19 | send_extra: Vec, 20 | send_on_drop: Option>>, 21 | } 22 | 23 | impl Context { 24 | /// Creates a new Context. 25 | /// 26 | /// Usually you're not creating your own context, as the crossroads instance is creating one for you. 27 | pub fn new(msg: dbus::Message) -> Option { 28 | if msg.msg_type() != dbus::MessageType::MethodCall { return None; } 29 | let p = msg.path()?.into_static(); 30 | let i = msg.interface().map(|i| i.into_static()); 31 | let m = msg.member()?.into_static(); 32 | Some(Context { 33 | path: p, 34 | interface: i, 35 | method: m, 36 | message: msg, 37 | reply: None, 38 | send_on_drop: None, 39 | send_extra: vec!(), 40 | has_error: false, 41 | }) 42 | } 43 | 44 | /// Convenience method that sets an error reply if the closure returns an error. 45 | pub fn check Result>(&mut self, f: F) -> Result { 46 | f(self).map_err(|e| { self.reply_err(e); }) 47 | } 48 | 49 | /// If the reply is not already set, creates a new method return message and calls the closure 50 | /// so that the closure can fill in the arguments. 51 | pub fn do_reply(&mut self, f: F) { 52 | if self.message.get_no_reply() { return; } 53 | if self.reply.is_some() { return; } 54 | let mut msg = self.message.method_return(); 55 | f(&mut msg); 56 | self.reply = Some(msg); 57 | } 58 | 59 | /// Replies ok to the incoming message, if the reply is not already set. 60 | /// This is what you'll normally have last in your async method. 61 | /// 62 | /// Returns PhantomData just to aid the type system. 63 | pub (crate) fn reply_ok(&mut self, oa: OA) -> PhantomData { 64 | self.do_reply(|msg| { msg.append_all(oa); }); 65 | PhantomData 66 | } 67 | 68 | /// Replies to the incoming message, if the reply is not already set. 69 | /// This is what you'll normally have last in your async method. 70 | /// 71 | /// Returns PhantomData just to aid the type system. 72 | pub fn reply(&mut self, result: Result) -> PhantomData { 73 | match result { 74 | Ok(oa) => { self.reply_ok(oa); }, 75 | Err(e) => { self.reply_err(e); }, 76 | }; 77 | PhantomData 78 | } 79 | 80 | /// Replies to the incoming message with an error. 81 | pub (crate) fn reply_err(&mut self, err: MethodErr) { 82 | self.has_error = true; 83 | if !self.message.get_no_reply() { 84 | self.reply = Some(err.to_message(&self.message)) 85 | }; 86 | } 87 | 88 | /// Low-level function to set a reply 89 | /// 90 | /// You should probably prefer do_reply, or reply_ok / reply_err for async methods. 91 | pub fn set_reply(&mut self, msg: Option, check_no_reply: bool, check_set: bool) { 92 | if check_no_reply && self.message.get_no_reply() { return; } 93 | if check_set && self.reply.is_some() { return; } 94 | self.reply = msg; 95 | } 96 | 97 | /// Low-level function to flush set messages 98 | /// 99 | /// This is called internally, you should probably not use it. 100 | pub fn flush_messages(&mut self, conn: &S) -> Result<(), ()> { 101 | if let Some(msg) = self.reply.take() { 102 | conn.send(msg)?; 103 | } 104 | for msg in self.send_extra.drain(..) { 105 | conn.send(msg)?; 106 | } 107 | Ok(()) 108 | } 109 | 110 | /// Makes a new signal with the current interface and path 111 | pub fn make_signal<'b, A, N>(&self, name: N, args: A) -> dbus::Message 112 | where A: dbus::arg::AppendAll, N: Into> { 113 | let mut msg = dbus::Message::signal(&self.path, self.interface.as_ref().unwrap(), &name.into()); 114 | msg.append_all(args); 115 | msg 116 | } 117 | 118 | /// Adds an extra message to send together with the message reply, e g, a custom signal. 119 | pub fn push_msg(&mut self, msg: dbus::Message) { self.send_extra.push(msg); } 120 | 121 | /// The current object path. 122 | pub fn path(&self) -> &dbus::Path<'static> { &self.path } 123 | 124 | /// The current interface name. 125 | /// 126 | /// The D-Bus specfication allows for the interface to be unspecified, hence this returns an 127 | /// option. This is very rarely used in practice. 128 | pub fn interface(&self) -> Option<&dbus::strings::Interface<'static>> { self.interface.as_ref() } 129 | 130 | /// The current method name. 131 | pub fn method(&self) -> &dbus::strings::Member<'static> { &self.method } 132 | 133 | /// The message that caused this method to be called. 134 | pub fn message(&self) -> &dbus::Message { &self.message } 135 | 136 | /// True if a reply (error or method return) has been set. 137 | pub fn has_reply(&self) -> bool { self.reply.is_some() } 138 | 139 | /// Returns true is "reply_err" has been called, or "check" ever returned an error 140 | pub fn has_error(&self) -> bool { self.has_error } 141 | 142 | pub (crate) fn set_send_on_drop(&mut self, value: Arc) { 143 | self.send_on_drop = Some(Dbg(value)); 144 | } 145 | } 146 | 147 | impl Drop for Context { 148 | fn drop(&mut self) { 149 | if let Some(sender) = self.send_on_drop.take() { 150 | let _ = self.flush_messages(&*sender.0); 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /dbus/src/blocking/generated_org_freedesktop_standard_interfaces.rs: -------------------------------------------------------------------------------- 1 | // This code was autogenerated with `dbus-codegen-rust -m none -c blocking -g -i org.freedesktop.DBus --dbuscrate crate --file ./data/standard_interfaces.xml -o ../dbus/src/blocking/generated_org_freedesktop_standard_interfaces.rs`, see https://github.com/diwic/dbus-rs 2 | use crate as dbus; 3 | #[allow(unused_imports)] 4 | use crate::arg; 5 | use crate::blocking; 6 | 7 | pub trait Properties { 8 | fn get arg::Get<'b> + 'static>(&self, interface_name: &str, property_name: &str) -> Result; 9 | fn get_all(&self, interface_name: &str) -> Result; 10 | fn set(&self, interface_name: &str, property_name: &str, value: I2) -> Result<(), dbus::Error>; 11 | } 12 | 13 | #[derive(Debug)] 14 | pub struct PropertiesPropertiesChanged { 15 | pub interface_name: String, 16 | pub changed_properties: arg::PropMap, 17 | pub invalidated_properties: Vec, 18 | } 19 | 20 | impl arg::AppendAll for PropertiesPropertiesChanged { 21 | fn append(&self, i: &mut arg::IterAppend) { 22 | arg::RefArg::append(&self.interface_name, i); 23 | arg::RefArg::append(&self.changed_properties, i); 24 | arg::RefArg::append(&self.invalidated_properties, i); 25 | } 26 | } 27 | 28 | impl arg::ReadAll for PropertiesPropertiesChanged { 29 | fn read(i: &mut arg::Iter) -> Result { 30 | Ok(PropertiesPropertiesChanged { 31 | interface_name: i.read()?, 32 | changed_properties: i.read()?, 33 | invalidated_properties: i.read()?, 34 | }) 35 | } 36 | } 37 | 38 | impl dbus::message::SignalArgs for PropertiesPropertiesChanged { 39 | const NAME: &'static str = "PropertiesChanged"; 40 | const INTERFACE: &'static str = "org.freedesktop.DBus.Properties"; 41 | } 42 | 43 | impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref> Properties for blocking::Proxy<'a, C> { 44 | 45 | fn get arg::Get<'b> + 'static>(&self, interface_name: &str, property_name: &str) -> Result { 46 | self.method_call("org.freedesktop.DBus.Properties", "Get", (interface_name, property_name, )) 47 | .and_then(|r: (arg::Variant, )| Ok((r.0).0, )) 48 | } 49 | 50 | fn get_all(&self, interface_name: &str) -> Result { 51 | self.method_call("org.freedesktop.DBus.Properties", "GetAll", (interface_name, )) 52 | .and_then(|r: (arg::PropMap, )| Ok(r.0, )) 53 | } 54 | 55 | fn set(&self, interface_name: &str, property_name: &str, value: I2) -> Result<(), dbus::Error> { 56 | self.method_call("org.freedesktop.DBus.Properties", "Set", (interface_name, property_name, arg::Variant(value), )) 57 | } 58 | } 59 | 60 | pub trait Introspectable { 61 | fn introspect(&self) -> Result; 62 | } 63 | 64 | impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref> Introspectable for blocking::Proxy<'a, C> { 65 | 66 | fn introspect(&self) -> Result { 67 | self.method_call("org.freedesktop.DBus.Introspectable", "Introspect", ()) 68 | .and_then(|r: (String, )| Ok(r.0, )) 69 | } 70 | } 71 | 72 | pub trait Peer { 73 | fn ping(&self) -> Result<(), dbus::Error>; 74 | fn get_machine_id(&self) -> Result; 75 | } 76 | 77 | impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref> Peer for blocking::Proxy<'a, C> { 78 | 79 | fn ping(&self) -> Result<(), dbus::Error> { 80 | self.method_call("org.freedesktop.DBus.Peer", "Ping", ()) 81 | } 82 | 83 | fn get_machine_id(&self) -> Result { 84 | self.method_call("org.freedesktop.DBus.Peer", "GetMachineId", ()) 85 | .and_then(|r: (String, )| Ok(r.0, )) 86 | } 87 | } 88 | 89 | pub trait ObjectManager { 90 | fn get_managed_objects(&self) -> Result<::std::collections::HashMap, ::std::collections::HashMap>, dbus::Error>; 91 | } 92 | 93 | #[derive(Debug)] 94 | pub struct ObjectManagerInterfacesAdded { 95 | pub object: dbus::Path<'static>, 96 | pub interfaces: ::std::collections::HashMap, 97 | } 98 | 99 | impl arg::AppendAll for ObjectManagerInterfacesAdded { 100 | fn append(&self, i: &mut arg::IterAppend) { 101 | arg::RefArg::append(&self.object, i); 102 | arg::RefArg::append(&self.interfaces, i); 103 | } 104 | } 105 | 106 | impl arg::ReadAll for ObjectManagerInterfacesAdded { 107 | fn read(i: &mut arg::Iter) -> Result { 108 | Ok(ObjectManagerInterfacesAdded { 109 | object: i.read()?, 110 | interfaces: i.read()?, 111 | }) 112 | } 113 | } 114 | 115 | impl dbus::message::SignalArgs for ObjectManagerInterfacesAdded { 116 | const NAME: &'static str = "InterfacesAdded"; 117 | const INTERFACE: &'static str = "org.freedesktop.DBus.ObjectManager"; 118 | } 119 | 120 | #[derive(Debug)] 121 | pub struct ObjectManagerInterfacesRemoved { 122 | pub object: dbus::Path<'static>, 123 | pub interfaces: Vec, 124 | } 125 | 126 | impl arg::AppendAll for ObjectManagerInterfacesRemoved { 127 | fn append(&self, i: &mut arg::IterAppend) { 128 | arg::RefArg::append(&self.object, i); 129 | arg::RefArg::append(&self.interfaces, i); 130 | } 131 | } 132 | 133 | impl arg::ReadAll for ObjectManagerInterfacesRemoved { 134 | fn read(i: &mut arg::Iter) -> Result { 135 | Ok(ObjectManagerInterfacesRemoved { 136 | object: i.read()?, 137 | interfaces: i.read()?, 138 | }) 139 | } 140 | } 141 | 142 | impl dbus::message::SignalArgs for ObjectManagerInterfacesRemoved { 143 | const NAME: &'static str = "InterfacesRemoved"; 144 | const INTERFACE: &'static str = "org.freedesktop.DBus.ObjectManager"; 145 | } 146 | 147 | impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref> ObjectManager for blocking::Proxy<'a, C> { 148 | 149 | fn get_managed_objects(&self) -> Result<::std::collections::HashMap, ::std::collections::HashMap>, dbus::Error> { 150 | self.method_call("org.freedesktop.DBus.ObjectManager", "GetManagedObjects", ()) 151 | .and_then(|r: (::std::collections::HashMap, ::std::collections::HashMap>, )| Ok(r.0, )) 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /dbus/src/nonblock/generated_org_freedesktop_standard_interfaces.rs: -------------------------------------------------------------------------------- 1 | // This code was autogenerated with `dbus-codegen-rust -m none -c nonblock -g -i org.freedesktop.DBus --dbuscrate crate --file ./data/standard_interfaces.xml -o ../dbus/src/nonblock/generated_org_freedesktop_standard_interfaces.rs`, see https://github.com/diwic/dbus-rs 2 | use crate as dbus; 3 | #[allow(unused_imports)] 4 | use crate::arg; 5 | use crate::nonblock; 6 | 7 | pub trait Properties { 8 | fn get arg::Get<'b> + 'static>(&self, interface_name: &str, property_name: &str) -> nonblock::MethodReply; 9 | fn get_all(&self, interface_name: &str) -> nonblock::MethodReply; 10 | fn set(&self, interface_name: &str, property_name: &str, value: I2) -> nonblock::MethodReply<()>; 11 | } 12 | 13 | #[derive(Debug)] 14 | pub struct PropertiesPropertiesChanged { 15 | pub interface_name: String, 16 | pub changed_properties: arg::PropMap, 17 | pub invalidated_properties: Vec, 18 | } 19 | 20 | impl arg::AppendAll for PropertiesPropertiesChanged { 21 | fn append(&self, i: &mut arg::IterAppend) { 22 | arg::RefArg::append(&self.interface_name, i); 23 | arg::RefArg::append(&self.changed_properties, i); 24 | arg::RefArg::append(&self.invalidated_properties, i); 25 | } 26 | } 27 | 28 | impl arg::ReadAll for PropertiesPropertiesChanged { 29 | fn read(i: &mut arg::Iter) -> Result { 30 | Ok(PropertiesPropertiesChanged { 31 | interface_name: i.read()?, 32 | changed_properties: i.read()?, 33 | invalidated_properties: i.read()?, 34 | }) 35 | } 36 | } 37 | 38 | impl dbus::message::SignalArgs for PropertiesPropertiesChanged { 39 | const NAME: &'static str = "PropertiesChanged"; 40 | const INTERFACE: &'static str = "org.freedesktop.DBus.Properties"; 41 | } 42 | 43 | impl<'a, T: nonblock::NonblockReply, C: ::std::ops::Deref> Properties for nonblock::Proxy<'a, C> { 44 | 45 | fn get arg::Get<'b> + 'static>(&self, interface_name: &str, property_name: &str) -> nonblock::MethodReply { 46 | self.method_call("org.freedesktop.DBus.Properties", "Get", (interface_name, property_name, )) 47 | .and_then(|r: (arg::Variant, )| Ok((r.0).0, )) 48 | } 49 | 50 | fn get_all(&self, interface_name: &str) -> nonblock::MethodReply { 51 | self.method_call("org.freedesktop.DBus.Properties", "GetAll", (interface_name, )) 52 | .and_then(|r: (arg::PropMap, )| Ok(r.0, )) 53 | } 54 | 55 | fn set(&self, interface_name: &str, property_name: &str, value: I2) -> nonblock::MethodReply<()> { 56 | self.method_call("org.freedesktop.DBus.Properties", "Set", (interface_name, property_name, arg::Variant(value), )) 57 | } 58 | } 59 | 60 | pub trait Introspectable { 61 | fn introspect(&self) -> nonblock::MethodReply; 62 | } 63 | 64 | impl<'a, T: nonblock::NonblockReply, C: ::std::ops::Deref> Introspectable for nonblock::Proxy<'a, C> { 65 | 66 | fn introspect(&self) -> nonblock::MethodReply { 67 | self.method_call("org.freedesktop.DBus.Introspectable", "Introspect", ()) 68 | .and_then(|r: (String, )| Ok(r.0, )) 69 | } 70 | } 71 | 72 | pub trait Peer { 73 | fn ping(&self) -> nonblock::MethodReply<()>; 74 | fn get_machine_id(&self) -> nonblock::MethodReply; 75 | } 76 | 77 | impl<'a, T: nonblock::NonblockReply, C: ::std::ops::Deref> Peer for nonblock::Proxy<'a, C> { 78 | 79 | fn ping(&self) -> nonblock::MethodReply<()> { 80 | self.method_call("org.freedesktop.DBus.Peer", "Ping", ()) 81 | } 82 | 83 | fn get_machine_id(&self) -> nonblock::MethodReply { 84 | self.method_call("org.freedesktop.DBus.Peer", "GetMachineId", ()) 85 | .and_then(|r: (String, )| Ok(r.0, )) 86 | } 87 | } 88 | 89 | pub trait ObjectManager { 90 | fn get_managed_objects(&self) -> nonblock::MethodReply<::std::collections::HashMap, ::std::collections::HashMap>>; 91 | } 92 | 93 | #[derive(Debug)] 94 | pub struct ObjectManagerInterfacesAdded { 95 | pub object: dbus::Path<'static>, 96 | pub interfaces: ::std::collections::HashMap, 97 | } 98 | 99 | impl arg::AppendAll for ObjectManagerInterfacesAdded { 100 | fn append(&self, i: &mut arg::IterAppend) { 101 | arg::RefArg::append(&self.object, i); 102 | arg::RefArg::append(&self.interfaces, i); 103 | } 104 | } 105 | 106 | impl arg::ReadAll for ObjectManagerInterfacesAdded { 107 | fn read(i: &mut arg::Iter) -> Result { 108 | Ok(ObjectManagerInterfacesAdded { 109 | object: i.read()?, 110 | interfaces: i.read()?, 111 | }) 112 | } 113 | } 114 | 115 | impl dbus::message::SignalArgs for ObjectManagerInterfacesAdded { 116 | const NAME: &'static str = "InterfacesAdded"; 117 | const INTERFACE: &'static str = "org.freedesktop.DBus.ObjectManager"; 118 | } 119 | 120 | #[derive(Debug)] 121 | pub struct ObjectManagerInterfacesRemoved { 122 | pub object: dbus::Path<'static>, 123 | pub interfaces: Vec, 124 | } 125 | 126 | impl arg::AppendAll for ObjectManagerInterfacesRemoved { 127 | fn append(&self, i: &mut arg::IterAppend) { 128 | arg::RefArg::append(&self.object, i); 129 | arg::RefArg::append(&self.interfaces, i); 130 | } 131 | } 132 | 133 | impl arg::ReadAll for ObjectManagerInterfacesRemoved { 134 | fn read(i: &mut arg::Iter) -> Result { 135 | Ok(ObjectManagerInterfacesRemoved { 136 | object: i.read()?, 137 | interfaces: i.read()?, 138 | }) 139 | } 140 | } 141 | 142 | impl dbus::message::SignalArgs for ObjectManagerInterfacesRemoved { 143 | const NAME: &'static str = "InterfacesRemoved"; 144 | const INTERFACE: &'static str = "org.freedesktop.DBus.ObjectManager"; 145 | } 146 | 147 | impl<'a, T: nonblock::NonblockReply, C: ::std::ops::Deref> ObjectManager for nonblock::Proxy<'a, C> { 148 | 149 | fn get_managed_objects(&self) -> nonblock::MethodReply<::std::collections::HashMap, ::std::collections::HashMap>> { 150 | self.method_call("org.freedesktop.DBus.ObjectManager", "GetManagedObjects", ()) 151 | .and_then(|r: (::std::collections::HashMap, ::std::collections::HashMap>, )| Ok(r.0, )) 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /dbus-tree/examples/adv_server.rs: -------------------------------------------------------------------------------- 1 | // More advanced server example. 2 | 3 | // This is supposed to look like a D-Bus service that allows the user to manipulate storage devices. 4 | 5 | // Note: in the dbus-codegen/example directory, there is a version of this example where dbus-codegen 6 | // was used to create some boilerplate code - feel free to compare the two examples. 7 | 8 | use std::sync::Arc; 9 | use std::sync::mpsc; 10 | use std::cell::Cell; 11 | use std::thread; 12 | 13 | use dbus_tree as tree; 14 | use dbus::Path; 15 | use dbus_tree::{Interface, Signal, MTFn, Access, MethodErr, EmitsChangedSignal}; 16 | use dbus::ffidisp::Connection; 17 | 18 | // Our storage device 19 | #[derive(Debug)] 20 | struct Device { 21 | description: String, 22 | path: Path<'static>, 23 | index: i32, 24 | online: Cell, 25 | checking: Cell, 26 | } 27 | 28 | // Every storage device has its own object path. 29 | // We therefore create a link from the object path to the Device. 30 | #[derive(Copy, Clone, Default, Debug)] 31 | struct TData; 32 | impl tree::DataType for TData { 33 | type Tree = (); 34 | type ObjectPath = Arc; 35 | type Property = (); 36 | type Interface = (); 37 | type Method = (); 38 | type Signal = (); 39 | } 40 | 41 | 42 | impl Device { 43 | // Creates a "test" device (not a real one, since this is an example). 44 | fn new_bogus(index: i32) -> Device { 45 | Device { 46 | description: format!("This is device {}, which is {}.", index, 47 | ["totally awesome", "really fancy", "still going strong"][(index as usize) % 3]), 48 | path: format!("/Device{}", index).into(), 49 | index: index, 50 | online: Cell::new(index % 2 == 0), 51 | checking: Cell::new(false), 52 | } 53 | } 54 | } 55 | 56 | // Here's where we implement the code for our interface. 57 | fn create_iface(check_complete_s: mpsc::Sender) -> (Interface, TData>, Arc>) { 58 | let f = tree::Factory::new_fn(); 59 | 60 | let check_complete = Arc::new(f.signal("CheckComplete", ())); 61 | 62 | (f.interface("com.example.dbus.rs.device", ()) 63 | // The online property can be both set and get 64 | .add_p(f.property::("online", ()) 65 | .access(Access::ReadWrite) 66 | .on_get(|i, m| { 67 | let dev: &Arc = m.path.get_data(); 68 | i.append(dev.online.get()); 69 | Ok(()) 70 | }) 71 | .on_set(|i, m| { 72 | let dev: &Arc = m.path.get_data(); 73 | let b: bool = i.read()?; 74 | if b && dev.checking.get() { 75 | return Err(MethodErr::failed(&"Device currently under check, cannot bring online")) 76 | } 77 | dev.online.set(b); 78 | Ok(()) 79 | }) 80 | ) 81 | // The "checking" property is read only 82 | .add_p(f.property::("checking", ()) 83 | .emits_changed(EmitsChangedSignal::False) 84 | .on_get(|i, m| { 85 | let dev: &Arc = m.path.get_data(); 86 | i.append(dev.checking.get()); 87 | Ok(()) 88 | }) 89 | ) 90 | // ...and so is the "description" property 91 | .add_p(f.property::<&str,_>("description", ()) 92 | .emits_changed(EmitsChangedSignal::Const) 93 | .on_get(|i, m| { 94 | let dev: &Arc = m.path.get_data(); 95 | i.append(&dev.description); 96 | Ok(()) 97 | }) 98 | ) 99 | // ...add a method for starting a device check... 100 | .add_m(f.method("check", (), move |m| { 101 | let dev: &Arc = m.path.get_data(); 102 | if dev.checking.get() { 103 | return Err(MethodErr::failed(&"Device currently under check, cannot start another check")) 104 | } 105 | if dev.online.get() { 106 | return Err(MethodErr::failed(&"Device is currently online, cannot start check")) 107 | } 108 | dev.checking.set(true); 109 | 110 | // Start some lengthy processing in a separate thread... 111 | let devindex = dev.index; 112 | let ch = check_complete_s.clone(); 113 | thread::spawn(move || { 114 | 115 | // Bogus check of device 116 | use std::time::Duration; 117 | thread::sleep(Duration::from_secs(15)); 118 | 119 | // Tell main thread that we finished 120 | ch.send(devindex).unwrap(); 121 | }); 122 | Ok(vec!(m.msg.method_return())) 123 | })) 124 | // Indicate that we send a special signal once checking has completed. 125 | .add_s(check_complete.clone()) 126 | , check_complete) 127 | } 128 | 129 | fn create_tree(devices: &[Arc], iface: &Arc, TData>>) 130 | -> tree::Tree, TData> { 131 | 132 | let f = tree::Factory::new_fn(); 133 | let mut tree = f.tree(()); 134 | for dev in devices { 135 | tree = tree.add(f.object_path(dev.path.clone(), dev.clone()) 136 | .introspectable() 137 | .add(iface.clone()) 138 | ); 139 | } 140 | tree 141 | } 142 | 143 | fn run() -> Result<(), Box> { 144 | // Create our bogus devices 145 | let devices: Vec> = (0..10).map(|i| Arc::new(Device::new_bogus(i))).collect(); 146 | 147 | // Create tree 148 | let (check_complete_s, check_complete_r) = mpsc::channel::(); 149 | let (iface, sig) = create_iface(check_complete_s); 150 | let tree = create_tree(&devices, &Arc::new(iface)); 151 | 152 | // Setup DBus connection 153 | let c = Connection::new_session()?; 154 | c.register_name("com.example.dbus.rs.advancedserverexample", 0)?; 155 | tree.set_registered(&c, true)?; 156 | 157 | // ...and serve incoming requests. 158 | c.add_handler(tree); 159 | loop { 160 | // Wait for incoming messages. This will block up to one second. 161 | // Discard the result - relevant messages have already been handled. 162 | c.incoming(1000).next(); 163 | 164 | // Do all other things we need to do in our main loop. 165 | if let Ok(idx) = check_complete_r.try_recv() { 166 | let dev = &devices[idx as usize]; 167 | dev.checking.set(false); 168 | c.send(sig.msg(&dev.path, &"com.example.dbus.rs.device".into())).map_err(|_| "Sending DBus signal failed")?; 169 | } 170 | } 171 | } 172 | 173 | fn main() { 174 | if let Err(e) = run() { 175 | println!("{}", e); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /dbus-codegen/README.md: -------------------------------------------------------------------------------- 1 | # dbus-codegen-rust 2 | 3 | This program takes D-Bus XML Introspection data and generates Rust code 4 | for calling and implementing the interfaces in the introspection data. 5 | 6 | ## Example 7 | 8 | From a D-Bus interface like this: 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | You can choose to generate one of three things: 23 | 24 | * Client-side code (for calling the interface) 25 | * Server-side code for implementing the interface using [dbus-crossroads](https://docs.rs/dbus-crossroads) 26 | * Server-side code for implementing the interface using [dbus-tree](https://docs.rs/dbus-tree) 27 | 28 | ## Common for client and server sides 29 | 30 | * A trait for calling/implementing the methods of the interfaces, like this: 31 | 32 | ```rust 33 | pub trait OrgExampleTest { 34 | fn foo(&self, bar: i32) -> Result; 35 | } 36 | ``` 37 | 38 | ```rust 39 | pub trait OrgExampleTest { 40 | fn foo(&self, bar: i32) -> Result; 41 | } 42 | ``` 43 | 44 | For properties, `get_xx` and `set_xx` methods will be generated. There is currently no `get_all` method. 45 | 46 | * A struct for each signal, like this: 47 | 48 | ```rust 49 | #[derive(Debug, Default)] 50 | pub struct OrgExampleTestLaundry { 51 | pub eaten: bool, 52 | } 53 | 54 | impl dbus::SignalArgs for OrgExampleTestLaundry { /* code here */ } 55 | ``` 56 | 57 | ## Client side 58 | 59 | * The trait will be implemented for `blocking::Proxy`, `nonblock::Proxy` or `ffidisp::ConnPath`, 60 | which makes methods easy to call for a client, like this: 61 | 62 | ```rust 63 | use OrgExampleTest; 64 | let myString = myProxy.foo(myInteger)?; 65 | ``` 66 | 67 | * To catch signals emitted from the server, do like this: 68 | 69 | ```rust 70 | use dbus::SignalArgs; 71 | myConnection.add_match(OrgExampleTestLaundry::match_rule(None, None).into_static(), |laundrySignal| { 72 | println!("Laundry was eaten: {:?}", laundrySignal.eaten); 73 | }) 74 | ``` 75 | 76 | ## Server side - dbus-crossroads 77 | 78 | * A method will be generated that registers an `IfaceToken`, like this: 79 | 80 | ```rust 81 | let token = register_org_example_test(&mut myCrossroads); 82 | myCrossroads.insert("/", &[token], myData); 83 | ``` 84 | 85 | Where myData must be of a type that implements `OrgExampleTest`. 86 | 87 | ## Server side - dbus-tree 88 | 89 | * A method will be generated, which you can call to get a `tree::Interface`, like this: 90 | 91 | ```rust 92 | myInterface = orgexampletest_server(&myFactory, ()); 93 | ``` 94 | 95 | This interface can then be added to a `tree::ObjectPath`, as shown in the [main page](../README.md#server). 96 | 97 | 98 | In addition, you also need to implement the interface's methods, like this: 99 | 100 | ```rust 101 | impl OrgExampleTest for MyStruct { 102 | type Err = tree::MethodErr; 103 | fn foo(&self, bar: i32) -> Result { 104 | /* Your code here */ 105 | } 106 | } 107 | ``` 108 | 109 | I've been experimenting with different ways of how to make the generated server function reach the implementing struct, 110 | this is controlled by the command line parameter `methodaccess`. 111 | 112 | 1. If `methodaccess` is `MethodInfo`, then you need to implement the interface for the `MethodInfo` struct, like this: 113 | 114 | ```rust 115 | impl, D> OrgExampleTest for tree::MethodInfo { 116 | type Err = tree::MethodErr; 117 | fn foo(&self, bar: i32) -> Result { 118 | /* Your code here */ 119 | } 120 | } 121 | ``` 122 | 123 | 2. If `methodaccess` is `RefClosure`, then you need to supply a closure that returns a reference to the implementing struct. 124 | This is a good option if the struct is stored in tree (this means implementing `tree::DataType`). 125 | 126 | ```rust 127 | myInterface = orgexampletest_server(&myFactory, (), |m| m.path.get_data()); 128 | ``` 129 | 130 | 3. If `methodaccess` is `AsRefClosure`, then you need to supply a closure that returns an object which can reference to the implementing struct. 131 | The object is dropped after the method is called. This works well with `Arc`/`Rc`, like this: 132 | 133 | ```rust 134 | impl AsRef for Rc { 135 | fn as_ref(&self) -> &(dyn OrgExampleTest + 'static) { &**self } 136 | } 137 | 138 | let myRc = Rc::new(myStruct); 139 | myInterface = orgexampletest_server(&myFactory, (), move |_| myRc.clone()); 140 | ``` 141 | 142 | There is also a `methodtype` parameter that controls whether the server function will work well with `MTFn`, `MTFnMut` or `MTSync` trees, 143 | or all three (called `Generic`). Or not generate a server function at all (`None`). 144 | 145 | * To emit a signal, you can call `SignalArgs::to_emit_message` or `ConnPath::emit` to get a message which can be sent over the connection. 146 | 147 | # Usage 148 | 149 | This code can be used both as a library and as a binary executable. 150 | 151 | ## Binary executable 152 | 153 | Once you have installed dbus-codegen-rust (`cargo install dbus-codegen`), use the following command to import your XML: 154 | 155 | ``` 156 | dbus-codegen-rust < mydefinition.xml 157 | ``` 158 | 159 | This will print the generated Rust code to stdout, so you can pipe it into another file if you want: 160 | 161 | ``` 162 | dbus-codegen-rust < mydefinition.xml > mod.rs 163 | ``` 164 | 165 | Dbus-codegen-rust can also fetch the xml definition for you. Here's an example that generates client definitions for PolicyKit: 166 | 167 | ``` 168 | dbus-codegen-rust -s -d org.freedesktop.PolicyKit1 -p "/org/freedesktop/PolicyKit1/Authority" > policykit.rs 169 | ``` 170 | 171 | Dbus-codegen-rust defaults to generating client definitions. Use the `--crossroads` switch to 172 | generate dbus-crossroads server definitions and `--methodtype` to generate dbus-tree definitions. 173 | 174 | To see available options: 175 | 176 | ``` 177 | dbus-codegen-rust --help 178 | ``` 179 | 180 | ## Library usage 181 | 182 | ``` 183 | let opts = Default::default(); 184 | let code = dbus_codegen::generate(xml_str, &opts)?; 185 | ``` 186 | 187 | See [documentation](https://docs.rs/dbus-codegen/) for what options are available. 188 | 189 | # Features 190 | 191 | The `dbus` feature is enabled by default. If you turn it off (with the `--no-default-features` argument to cargo), 192 | this program (or library) no longer binds to the D-Bus C development headers, meaning you don't need these to be installed. 193 | This also means you can no longer fetch the xml definition from other programs when you run the binary. 194 | -------------------------------------------------------------------------------- /dbus/src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::arg::TypeMismatchError; 2 | use std::ffi::CString; 3 | use std::{ptr, fmt}; 4 | use crate::{arg, to_c_str, c_str_to_slice, init_dbus, Message}; 5 | use crate::strings::ErrorName; 6 | use std::error::Error as stdError; 7 | use std::result::Result as stdResult; 8 | 9 | /// Alias for a [`Result`](stdResult) containing [`dbus::Error`](Error) by default. 10 | /// 11 | /// Can still be used with different error types. 12 | pub type Result = stdResult; 13 | 14 | /// D-Bus Error wrapper. 15 | /// 16 | /// This is a wrapper around the libc dbus error object. 17 | pub struct Error { 18 | e: ffi::DBusError, 19 | } 20 | 21 | unsafe impl Send for Error {} 22 | 23 | // Note! For this Sync impl to be safe, it requires that no functions that take &self, 24 | // actually calls into FFI. All functions that call into FFI with a ffi::DBusError 25 | // must take &mut self. 26 | 27 | unsafe impl Sync for Error {} 28 | 29 | impl Error { 30 | 31 | /// Create a new custom D-Bus Error. 32 | pub fn new_custom<'a, N: Into>>(name: N, message: &str) -> Error { 33 | let n = to_c_str(&name.into()); 34 | let m = to_c_str(&message.replace("%","%%")); 35 | let mut e = Error::empty(); 36 | 37 | unsafe { ffi::dbus_set_error(e.get_mut(), n.as_ptr(), m.as_ptr()) }; 38 | e 39 | } 40 | 41 | /// Create a new generic D-Bus Error with "org.freedesktop.DBus.Error.Failed" as the Error name. 42 | pub fn new_failed(message: &str) -> Error { 43 | Error::new_custom("org.freedesktop.DBus.Error.Failed", message) 44 | } 45 | 46 | pub (crate) fn empty() -> Error { 47 | init_dbus(); 48 | let mut e = ffi::DBusError { 49 | name: ptr::null(), 50 | message: ptr::null(), 51 | dummy: 0, 52 | padding1: ptr::null() 53 | }; 54 | unsafe { ffi::dbus_error_init(&mut e); } 55 | Error{ e: e } 56 | } 57 | 58 | /// Error name/type, e g 'org.freedesktop.DBus.Error.Failed' 59 | pub fn name(&self) -> Option<&str> { 60 | c_str_to_slice(&self.e.name) 61 | } 62 | 63 | /// Custom message, e g 'Could not find a matching object path' 64 | pub fn message(&self) -> Option<&str> { 65 | c_str_to_slice(&self.e.message) 66 | } 67 | 68 | pub (crate) fn get_mut(&mut self) -> &mut ffi::DBusError { &mut self.e } 69 | } 70 | 71 | impl Drop for Error { 72 | fn drop(&mut self) { 73 | unsafe { ffi::dbus_error_free(&mut self.e); } 74 | } 75 | } 76 | 77 | impl fmt::Debug for Error { 78 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 79 | write!(f, "D-Bus error: {} ({})", self.message().unwrap_or(""), 80 | self.name().unwrap_or("")) 81 | } 82 | } 83 | 84 | impl stdError for Error { 85 | fn description(&self) -> &str { "D-Bus error" } 86 | } 87 | 88 | impl fmt::Display for Error { 89 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 90 | if let Some(x) = self.message() { 91 | write!(f, "{}", x) 92 | } else { Ok(()) } 93 | } 94 | } 95 | 96 | impl From for Error { 97 | fn from(t: arg::TypeMismatchError) -> Error { 98 | Error::new_custom("org.freedesktop.DBus.Error.Failed", &format!("{}", t)) 99 | } 100 | } 101 | 102 | 103 | impl From for Error { 104 | fn from(t: MethodErr) -> Error { 105 | Error::new_custom(t.errorname(), t.description()) 106 | } 107 | } 108 | 109 | 110 | #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] 111 | /// A D-Bus Method Error, containing an error name and a description. 112 | /// 113 | /// Unlike the "Error" struct, this is a Rust native struct. 114 | pub struct MethodErr(ErrorName<'static>, String); 115 | 116 | impl MethodErr { 117 | /// Create an Invalid Args MethodErr. 118 | pub fn invalid_arg(a: &T) -> MethodErr { 119 | ("org.freedesktop.DBus.Error.InvalidArgs", format!("Invalid argument {:?}", a)).into() 120 | } 121 | /// Create a MethodErr that there are not enough arguments given. 122 | pub fn no_arg() -> MethodErr { 123 | ("org.freedesktop.DBus.Error.InvalidArgs", "Not enough arguments").into() 124 | } 125 | /// Create a MethodErr that the method failed in the way specified. 126 | pub fn failed(a: &T) -> MethodErr { 127 | ("org.freedesktop.DBus.Error.Failed", a.to_string()).into() 128 | } 129 | 130 | /// Create a MethodErr that the Object path was unknown. 131 | pub fn no_path(a: &T) -> MethodErr { 132 | ("org.freedesktop.DBus.Error.UnknownObject", format!("Unknown object path {}", a)).into() 133 | } 134 | 135 | /// Create a MethodErr that the Interface was unknown. 136 | pub fn no_interface(a: &T) -> MethodErr { 137 | ("org.freedesktop.DBus.Error.UnknownInterface", format!("Unknown interface {}", a)).into() 138 | } 139 | /// Create a MethodErr that the Method was unknown. 140 | pub fn no_method(a: &T) -> MethodErr { 141 | ("org.freedesktop.DBus.Error.UnknownMethod", format!("Unknown method {}", a)).into() 142 | } 143 | /// Create a MethodErr that the Property was unknown. 144 | pub fn no_property(a: &T) -> MethodErr { 145 | ("org.freedesktop.DBus.Error.UnknownProperty", format!("Unknown property {}", a)).into() 146 | } 147 | /// Create a MethodErr that the Property was read-only. 148 | pub fn ro_property(a: &T) -> MethodErr { 149 | ("org.freedesktop.DBus.Error.PropertyReadOnly", format!("Property {} is read only", a)).into() 150 | } 151 | 152 | /// Error name accessor 153 | pub fn errorname(&self) -> &ErrorName<'static> { &self.0 } 154 | /// Description accessor 155 | pub fn description(&self) -> &str { &self.1 } 156 | 157 | /// Creates an error reply from a method call message. 158 | /// 159 | /// Note: You normally don't need to use this function, 160 | /// as it is called internally from Tree::handle. 161 | pub fn to_message(&self, msg: &Message) -> Message { 162 | msg.error(&self.0, &CString::new(&*self.1).unwrap()) 163 | } 164 | } 165 | 166 | impl fmt::Display for MethodErr { 167 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 168 | write!(f, "{}", self.description()) 169 | } 170 | } 171 | 172 | impl stdError for MethodErr {} 173 | 174 | impl From for MethodErr { 175 | fn from(t: TypeMismatchError) -> MethodErr { ("org.freedesktop.DBus.Error.Failed", format!("{}", t)).into() } 176 | } 177 | 178 | impl>, M: Into> From<(T, M)> for MethodErr { 179 | fn from((t, m): (T, M)) -> MethodErr { MethodErr(t.into(), m.into()) } 180 | } 181 | 182 | impl From for MethodErr { 183 | fn from(t: Error) -> MethodErr { 184 | let n = t.name().unwrap_or("org.freedesktop.DBus.Error.Failed"); 185 | let m = t.message().unwrap_or("Unknown error"); 186 | MethodErr(String::from(n).into(), m.into()) 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /dbus/src/channel.rs: -------------------------------------------------------------------------------- 1 | //! Connection base / building block. 2 | //! 3 | //! Contains some helper structs and traits common to all Connection types.- 4 | 5 | use crate::{Message, to_c_str, c_str_to_slice, MessageType}; 6 | use crate::message::MatchRule; 7 | 8 | #[cfg(not(feature = "native-channel"))] 9 | mod ffichannel; 10 | #[cfg(not(feature = "native-channel"))] 11 | pub use ffichannel::Channel; 12 | 13 | #[cfg(feature = "native-channel")] 14 | mod nativechannel; 15 | #[cfg(feature = "native-channel")] 16 | pub use nativechannel::Channel; 17 | 18 | 19 | /// Which bus to connect to 20 | #[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)] 21 | pub enum BusType { 22 | /// The Session bus - local to every logged in session 23 | Session = ffi::DBusBusType::Session as isize, 24 | /// The system wide bus 25 | System = ffi::DBusBusType::System as isize, 26 | /// The bus that started us, if any 27 | Starter = ffi::DBusBusType::Starter as isize, 28 | } 29 | 30 | /// Platform-specific file descriptor type 31 | #[cfg(unix)] 32 | pub type WatchFd = std::os::unix::io::RawFd; 33 | 34 | /// Platform-specific file descriptor type 35 | #[cfg(windows)] 36 | pub type WatchFd = std::os::windows::io::RawSocket; 37 | 38 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] 39 | /// A file descriptor, and an indication whether it should be read from, written to, or both. 40 | pub struct Watch { 41 | /// File descriptor 42 | pub fd: WatchFd, 43 | /// True if wakeup should happen when the file descriptor is ready for reading 44 | pub read: bool, 45 | /// True if wakeup should happen when the file descriptor is ready for writing 46 | pub write: bool, 47 | } 48 | 49 | /// Abstraction over different connections that send data 50 | pub trait Sender { 51 | /// Schedules a message for sending. 52 | /// 53 | /// Returns a serial number than can be used to match against a reply. 54 | fn send(&self, msg: Message) -> Result; 55 | } 56 | 57 | /// Use in case you don't want the send the message, but just collect it instead. 58 | impl Sender for std::cell::RefCell> { 59 | fn send(&self, msg: Message) -> Result { 60 | self.borrow_mut().push(msg); 61 | Ok(0) 62 | } 63 | } 64 | 65 | /// Use in case you don't want the send the message, but just collect it instead. 66 | impl Sender for std::sync::Mutex> { 67 | fn send(&self, msg: Message) -> Result { 68 | self.lock().unwrap().push(msg); 69 | Ok(0) 70 | } 71 | } 72 | 73 | /// Token used to identify a callback in the MatchingReceiver trait 74 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] 75 | pub struct Token(pub usize); 76 | 77 | /// Abstraction over different connections that receive data 78 | pub trait MatchingReceiver { 79 | /// Type of callback 80 | type F; 81 | /// Add a callback to be called in case a message matches. 82 | /// 83 | /// Returns an id that can be used to remove the callback. 84 | fn start_receive(&self, m: MatchRule<'static>, f: Self::F) -> Token; 85 | /// Remove a previously added callback. 86 | fn stop_receive(&self, id: Token) -> Option<(MatchRule<'static>, Self::F)>; 87 | } 88 | 89 | impl Sender for Channel { 90 | fn send(&self, msg: Message) -> Result { Channel::send(self, msg) } 91 | } 92 | 93 | /// Handles what we need to be a good D-Bus citizen. 94 | /// 95 | /// Call this if you have not handled the message yourself: 96 | /// * It handles calls to org.freedesktop.DBus.Peer. 97 | /// * For other method calls, it sends an error reply back that the method was unknown. 98 | pub fn default_reply(m: &Message) -> Option { 99 | peer(&m).or_else(|| unknown_method(&m)) 100 | } 101 | 102 | /// Replies if this is a call to org.freedesktop.DBus.Peer, otherwise returns None. 103 | fn peer(m: &Message) -> Option { 104 | if let Some(intf) = m.interface() { 105 | if &*intf != "org.freedesktop.DBus.Peer" { return None; } 106 | if let Some(method) = m.member() { 107 | if &*method == "Ping" { return Some(m.method_return()) } 108 | if &*method == "GetMachineId" { 109 | let mut r = m.method_return(); 110 | unsafe { 111 | let id = ffi::dbus_get_local_machine_id(); 112 | if !id.is_null() { 113 | r = r.append1(c_str_to_slice(&(id as *const _)).unwrap()); 114 | ffi::dbus_free(id as *mut _); 115 | return Some(r) 116 | } 117 | } 118 | return Some(m.error(&"org.freedesktop.DBus.Error.Failed".into(), &to_c_str("Failed to retrieve UUID"))) 119 | } 120 | } 121 | Some(m.error(&"org.freedesktop.DBus.Error.UnknownMethod".into(), &to_c_str("Method does not exist"))) 122 | } else { None } 123 | } 124 | 125 | /// For method calls, it replies that the method was unknown, otherwise returns None. 126 | fn unknown_method(m: &Message) -> Option { 127 | if m.msg_type() != MessageType::MethodCall { return None; } 128 | // if m.get_no_reply() { return None; } // The reference implementation does not do this? 129 | Some(m.error(&"org.freedesktop.DBus.Error.UnknownMethod".into(), &to_c_str("Path, Interface, or Method does not exist"))) 130 | } 131 | 132 | #[test] 133 | fn test_channel_send_sync() { 134 | fn is_send(_: &T) {} 135 | fn is_sync(_: &T) {} 136 | let c = Channel::get_private(BusType::Session).unwrap(); 137 | is_send(&c); 138 | is_sync(&c); 139 | } 140 | 141 | #[test] 142 | fn channel_simple_test() { 143 | let mut c = Channel::get_private(BusType::Session).unwrap(); 144 | assert!(c.is_connected()); 145 | c.set_watch_enabled(true); 146 | let fd = c.watch(); 147 | println!("{:?}", fd); 148 | let m = Message::new_method_call("org.freedesktop.DBus", "/", "org.freedesktop.DBus", "ListNames").unwrap(); 149 | let reply = c.send(m).unwrap(); 150 | let my_name = c.unique_name().unwrap(); 151 | loop { 152 | while let Some(mut msg) = c.pop_message() { 153 | println!("{:?}", msg); 154 | if msg.get_reply_serial() == Some(reply) { 155 | let r = msg.as_result().unwrap(); 156 | let z: crate::arg::Array<&str, _> = r.get1().unwrap(); 157 | for n in z { 158 | println!("{}", n); 159 | if n == my_name { return; } // Hooray, we found ourselves! 160 | } 161 | assert!(false); 162 | } else if let Some(r) = default_reply(&msg) { 163 | c.send(r).unwrap(); 164 | } 165 | } 166 | c.read_write(Some(std::time::Duration::from_millis(100))).unwrap(); 167 | } 168 | } 169 | 170 | #[test] 171 | fn test_bus_type_is_compatible_with_set() { 172 | use std::collections::HashSet; 173 | 174 | let mut set: HashSet = HashSet::new(); 175 | set.insert(BusType::Starter); 176 | set.insert(BusType::Starter); 177 | 178 | assert_eq!(set.len(), 1); 179 | assert!(!set.contains(&BusType::Session)); 180 | assert!(!set.contains(&BusType::System)); 181 | assert!(set.contains(&BusType::Starter)); 182 | } 183 | 184 | 185 | #[test] 186 | fn watchmap() { 187 | let mut c = Channel::get_private(BusType::Session).unwrap(); 188 | c.set_watch_enabled(true); 189 | let w = c.watch(); 190 | assert_eq!(w.write, false); 191 | assert_eq!(w.read, true); 192 | c.set_watch_enabled(false); 193 | println!("{:?}", w); 194 | c.set_watch_enabled(true); 195 | } 196 | -------------------------------------------------------------------------------- /dbus/src/message/matchrule.rs: -------------------------------------------------------------------------------- 1 | use crate::{Message, MessageType}; 2 | use crate::strings::{BusName, Path, Interface, Member}; 3 | use crate::message::parser; 4 | 5 | #[derive(Clone, Debug, Default)] 6 | /// A "match rule", that can match Messages on its headers. 7 | /// 8 | /// A field set to "None" means no filter for that header, 9 | /// a field set to "Some(_)" must match exactly. 10 | pub struct MatchRule<'a> { 11 | /// Match on message type (you typically want to do this) 12 | pub msg_type: Option, 13 | /// Match on message sender 14 | pub sender: Option>, 15 | /// If false (the default), match if sender could possibly match, due to mismatch between unique names and taken bus names 16 | pub strict_sender: bool, 17 | /// Match on message object path 18 | pub path: Option>, 19 | /// If true, will match all subpaths to the path as well as the path itself. Defaults to false. 20 | pub path_is_namespace: bool, 21 | /// Match on message interface 22 | pub interface: Option>, 23 | /// Match on message member (signal or method name) 24 | pub member: Option>, 25 | /// If true, also receive messages not intended for us. Defaults to false. 26 | pub eavesdrop: bool, 27 | _more_fields_may_come: (), 28 | } 29 | 30 | fn msg_type_str(m: MessageType) -> &'static str { 31 | use crate::MessageType::*; 32 | match m { 33 | Signal => "signal", 34 | MethodCall => "method_call", 35 | MethodReturn => "method_return", 36 | Error => "error", 37 | } 38 | } 39 | 40 | 41 | impl<'a> MatchRule<'a> { 42 | /// Make a string which you can use in the call to "add_match". 43 | pub fn match_str(&self) -> String { 44 | let mut v = vec!(); 45 | if let Some(x) = self.msg_type { v.push(("type", msg_type_str(x))) }; 46 | if let Some(ref x) = self.sender { v.push(("sender", &x)) }; 47 | let pn = if self.path_is_namespace { "path_namespace" } else { "path" }; 48 | if let Some(ref x) = self.path { v.push((pn, &x)) }; 49 | if let Some(ref x) = self.interface { v.push(("interface", &x)) }; 50 | if let Some(ref x) = self.member { v.push(("member", &x)) }; 51 | if self.eavesdrop { v.push(("eavesdrop", "true")) }; 52 | 53 | // For now we don't need to worry about internal quotes in strings as those are not valid names. 54 | // If we start matching against arguments, we need to worry. 55 | let v: Vec<_> = v.into_iter().map(|(k, v)| format!("{}='{}'", k, v)).collect(); 56 | v.join(",") 57 | } 58 | 59 | fn path_match(&self, msg: &Message) -> bool { 60 | if let Some(ref x) = self.path { 61 | if let Some(ref p) = msg.path() { 62 | if x != p { 63 | if self.path_is_namespace { 64 | p.starts_with(&**x) && &p[x.len()..x.len() + 1] == "/" 65 | } else { false } 66 | } else { true } 67 | } else { false } 68 | } else { true } 69 | } 70 | 71 | /// Returns whether or not the message matches the rule. 72 | pub fn matches(&self, msg: &Message) -> bool { 73 | if let Some(x) = self.msg_type { if x != msg.msg_type() { return false; } }; 74 | 75 | if let Some(ref x) = self.sender { 76 | if let Some(s) = msg.sender() { 77 | let check = self.strict_sender || (s.starts_with(":") == x.starts_with(":")); 78 | if check && s != *x { return false; } 79 | } else if self.strict_sender { return false; } 80 | }; 81 | if !self.path_match(msg) { return false; } 82 | if self.interface.is_some() && msg.interface() != self.interface { return false; }; 83 | if self.member.is_some() && msg.member() != self.member { return false; }; 84 | true 85 | } 86 | 87 | /// Create a new struct which matches every message. 88 | pub fn new() -> Self { Default::default() } 89 | 90 | /// Create a new struct which matches every incoming method call message. 91 | pub fn new_method_call() -> Self { 92 | let mut m = Self::new(); 93 | m.msg_type = Some(MessageType::MethodCall); 94 | m 95 | } 96 | 97 | /// Create a new struct which matches signals on the interface and member name. 98 | pub fn new_signal>, N: Into>>(intf: I, name: N) -> Self { 99 | let mut m = Self::new(); 100 | m.msg_type = Some(MessageType::Signal); 101 | m.interface = Some(intf.into()); 102 | m.member = Some(name.into()); 103 | m 104 | } 105 | 106 | /// Returns a clone with no borrowed references 107 | pub fn static_clone(&self) -> MatchRule<'static> { 108 | MatchRule { 109 | msg_type: self.msg_type, 110 | sender: self.sender.as_ref().map(|x| x.clone().into_static()), 111 | strict_sender: self.strict_sender, 112 | path: self.path.as_ref().map(|x| x.clone().into_static()), 113 | interface: self.interface.as_ref().map(|x| x.clone().into_static()), 114 | member: self.member.as_ref().map(|x| x.clone().into_static()), 115 | path_is_namespace: self.path_is_namespace, 116 | eavesdrop: self.eavesdrop, 117 | _more_fields_may_come: (), 118 | } 119 | } 120 | 121 | /// Enables eavesdropping for the generated message. 122 | /// You probably want to use [BecomeMonitor](https://dbus.freedesktop.org/doc/dbus-specification.html#bus-messages-become-monitor) instead 123 | pub fn with_eavesdrop(mut self) -> Self { 124 | self.eavesdrop = true; 125 | self 126 | } 127 | 128 | /// Sets the MatchRule to match on the message sender 129 | pub fn with_sender(mut self, sender: impl Into>) -> Self { 130 | self.sender = Some(sender.into()); 131 | self 132 | } 133 | 134 | /// Sets the MatchRule to match on the message sender and be strict 135 | pub fn with_strict_sender(mut self, sender: impl Into>) -> Self { 136 | self.sender = Some(sender.into()); 137 | self.strict_sender = true; 138 | self 139 | } 140 | 141 | /// Sets the MatchRule to match on the message path and treat it as a namespace 142 | pub fn with_namespaced_path(mut self, path: impl Into>) -> Self { 143 | self.path = Some(path.into()); 144 | self.path_is_namespace = true; 145 | self 146 | } 147 | 148 | /// Sets the MatchRule to match on the message path 149 | pub fn with_path(mut self, path: impl Into>) -> Self { 150 | self.path = Some(path.into()); 151 | self 152 | } 153 | 154 | /// Sets the MatchRule to match on the message interface 155 | pub fn with_interface(mut self, intf: impl Into>) -> Self { 156 | self.interface = Some(intf.into()); 157 | self 158 | } 159 | 160 | /// Sets the MatchRule to match on the message member 161 | pub fn with_member(mut self, member: impl Into>) -> Self { 162 | self.member = Some(member.into()); 163 | self 164 | } 165 | 166 | /// Sets the MatchRule to match on the message type. This will usually be `"signal"` 167 | pub fn with_type(mut self, ty: MessageType) -> Self { 168 | self.msg_type = Some(ty); 169 | self 170 | } 171 | 172 | /// Tries parsing a MatchRule from a String. Please note however that not all features supported 173 | /// by DBus are supported by dbus-rs (yet). args and destinations are not supported yet. 174 | pub fn parse(text: &'a str) -> Result { 175 | parser::Parser::new(text)?.parse() 176 | } 177 | } -------------------------------------------------------------------------------- /dbus-codegen/src/main.rs: -------------------------------------------------------------------------------- 1 | mod generate; 2 | 3 | use crate::generate::{ServerAccess, ConnectionType}; 4 | 5 | #[cfg(feature = "dbus")] 6 | mod connect_to_dbus { 7 | 8 | use dbus::blocking; 9 | 10 | // This code was copy-pasted from the output of this program. :-) 11 | pub trait OrgFreedesktopDBusIntrospectable { 12 | fn introspect(&self) -> Result; 13 | } 14 | 15 | impl<'a, C: ::std::ops::Deref> OrgFreedesktopDBusIntrospectable for blocking::Proxy<'a, C> { 16 | 17 | fn introspect(&self) -> Result { 18 | self.method_call("org.freedesktop.DBus.Introspectable", "Introspect", ()) 19 | .and_then(|r: (String, )| Ok(r.0, )) 20 | } 21 | } 22 | 23 | pub fn do_introspect(dest: &str, path: &str, systembus: bool) -> String { 24 | let c = if systembus { blocking::Connection::new_system() } else { blocking::Connection::new_session() }; 25 | let c = c.unwrap(); 26 | let p = c.with_proxy(dest, path, std::time::Duration::from_secs(10)); 27 | p.introspect().unwrap() 28 | } 29 | } 30 | 31 | // Unwrapping is fine here, this is just a test program. 32 | 33 | fn main() { 34 | let app = clap::Command::new("D-Bus Rust code generator").about("Generates Rust code from xml introspection data") 35 | .arg(clap::Arg::new("interfaces").short('f').long("interfaces").value_name("FILTER") 36 | .help("Comma separated list of filter strings. Only matching interfaces are generated if set.")) 37 | .arg(clap::Arg::new("genericvariant").short('g').long("generic-variant").action(clap::ArgAction::SetTrue) 38 | .help("If present, will try to make variant arguments generic instead of Variant>. \ 39 | Experimental, does not work with dbus-tree.")) 40 | .arg(clap::Arg::new("methodtype").short('m').long("methodtype").value_name("Fn") 41 | .help("Type of server method for dbus-tree; valid values are: 'Fn', 'FnMut', 'Sync', 'Generic', and 'None'. Defaults to 'None'.")) 42 | .arg(clap::Arg::new("methodaccess").short('a').long("methodaccess").value_name("RefClosure") 43 | .help("Specifies how to access the type implementing the interface for dbus-tree (experimental). Valid values are: 'RefClosure', 'AsRefClosure', 'MethodInfo'. \ 44 | Defaults to 'RefClosure'.")) 45 | .arg(clap::Arg::new("dbuscrate").long("dbuscrate").value_name("dbus") 46 | .help("Name of dbus crate, defaults to 'dbus'.")) 47 | .arg(clap::Arg::new("skipprefix").short('i').long("skipprefix").value_name("PREFIX") 48 | .help("If present, skips a specific prefix for interface names, e g 'org.freedesktop.DBus.'.")) 49 | .arg(clap::Arg::new("client").short('c').long("client").value_name("client") 50 | .help("Type of client connection. Valid values are: 'blocking', 'nonblock', 'ffidisp'.")) 51 | .arg(clap::Arg::new("propnewtype").short('n').long("prop-newtype").action(clap::ArgAction::SetTrue) 52 | .help("If present, will generate a struct wrapping PropMap to get properties from it with their expected types.")) 53 | .arg(clap::Arg::new("crossroads").short('r').long("crossroads").action(clap::ArgAction::SetTrue) 54 | .help("Generate dbus-crossroads server code.")) 55 | .arg(clap::Arg::new("output").short('o').long("output").value_name("FILE") 56 | .help("Write output into the specified file")) 57 | .arg(clap::Arg::new("file").long("file").required(false).value_name("FILE") 58 | .help("D-Bus XML Introspection file")); 59 | 60 | #[cfg(feature = "dbus")] 61 | let app = app 62 | .arg(clap::Arg::new("destination").short('d').long("destination").value_name("BUSNAME") 63 | .help("If present, connects to the supplied service to get introspection data. Reads from stdin otherwise.")) 64 | .arg(clap::Arg::new("path").short('p').long("path").value_name("PATH") 65 | .help("The path to ask for introspection data. Defaults to '/'. (Ignored if destination is not specified.)")) 66 | .arg(clap::Arg::new("systembus").short('s').long("system-bus").action(clap::ArgAction::SetTrue) 67 | .help("Connects to system bus, if not specified, the session bus will be used. (Ignored if destination is not specified.)")); 68 | 69 | let matches = app.get_matches(); 70 | 71 | let s = match (matches.get_one::("destination"), matches.get_one::("file")) { 72 | (Some(_), Some(_)) => panic!("'destination' and 'file' are mutually exclusive arguments - you can't provide both"), 73 | (None, Some(file_path)) => std::fs::read_to_string(file_path.clone()).unwrap(), 74 | #[cfg(feature = "dbus")] 75 | (Some(dest), None) => { 76 | let path = matches.get_one::("path").map(|s| &**s).unwrap_or("/"); 77 | connect_to_dbus::do_introspect(dest, path, matches.get_flag("systembus")) 78 | }, 79 | #[cfg(not(feature = "dbus"))] 80 | (Some(_), None) => unreachable!(), 81 | (None, None) => { 82 | let mut s = String::new(); 83 | std::io::Read::read_to_string(&mut std::io::stdin(),&mut s).unwrap(); 84 | s 85 | } 86 | }; 87 | 88 | let dbuscrate = matches.get_one::("dbuscrate").map(|s| &**s).unwrap_or("dbus"); 89 | 90 | let mtype = matches.get_one::("methodtype").map(|s| s.to_lowercase()); 91 | let mtype = match mtype.as_ref().map(|s| &**s) { 92 | Some("fn") => Some("MTFn"), 93 | Some("fnmut") => Some("MTFnMut"), 94 | Some("sync") => Some("MTSync"), 95 | Some("generic") => Some("MethodType"), 96 | None | Some("none") => None, 97 | _ => panic!("Invalid methodtype specified"), 98 | }; 99 | 100 | let maccess = matches.get_one::("methodaccess").map(|s| s.to_lowercase()); 101 | let maccess = match maccess.as_ref().map(|s| &**s) { 102 | None | Some("refclosure") => ServerAccess::RefClosure, 103 | Some("asrefclosure") => ServerAccess::AsRefClosure, 104 | Some("methodinfo") => ServerAccess::MethodInfo, 105 | _ => panic!("Invalid methodaccess specified"), 106 | }; 107 | 108 | let client = matches.get_one::("client").map(|s| s.to_lowercase()); 109 | let client = match client.as_ref().map(|s| &**s) { 110 | None | Some("blocking") => ConnectionType::Blocking, 111 | Some("nonblock") => ConnectionType::Nonblock, 112 | Some("ffidisp") => ConnectionType::Ffidisp, 113 | _ => panic!("Invalid client connection type specified"), 114 | }; 115 | 116 | let interfaces = matches.get_one::("interfaces").map(|s| s.split(",").map(|e| e.trim().to_owned()).collect()); 117 | 118 | let opts = generate::GenOpts { 119 | methodtype: mtype.map(|x| x.into()), 120 | dbuscrate: dbuscrate.into(), 121 | skipprefix: matches.get_one::("skipprefix").map(|x| x.into()), 122 | serveraccess: maccess, 123 | genericvariant: matches.get_flag("genericvariant"), 124 | connectiontype: client, 125 | propnewtype: matches.get_flag("propnewtype"), 126 | crossroads: matches.get_flag("crossroads"), 127 | interfaces, 128 | command_line: std::env::args().skip(1).collect::>().join(" ") 129 | }; 130 | 131 | let mut h: Box = match matches.get_one::("output") { 132 | Some(file_path) => Box::new(std::fs::File::create(file_path) 133 | .unwrap_or_else(|e| { 134 | panic!("Failed to open {}", e); 135 | })), 136 | None => Box::new(std::io::stdout()), 137 | }; 138 | 139 | h.write(generate::generate(&s, &opts).unwrap().as_bytes()).unwrap(); 140 | h.flush().unwrap(); 141 | } 142 | -------------------------------------------------------------------------------- /libdbus-sys/cross_compile.md: -------------------------------------------------------------------------------- 1 | Cross compiling dbus 2 | ==================== 3 | 4 | There are (at least) three different approaches to cross compiling dbus. Choose which one fits you best: 5 | 6 | * Using rust-embedded/cross, see below 7 | * There is an alternative guide at [issue 184](https://github.com/diwic/dbus-rs/issues/184#issuecomment-520228758), which also contains a [powershell script](https://github.com/diwic/dbus-rs/issues/184#issuecomment-520791888) that set things up for you. 8 | * Setting things up manually, see below 9 | 10 | The examples below all assume you're trying to compile for Raspberry Pi 2 or 3 running Raspbian. Adjust target triples accordingly if your target is something else. 11 | 12 | 13 | Cross compiling using rust-embedded/cross 14 | ========================================= 15 | 16 | The `vendored` feature is the current recommended way to cross compile dbus-rs: 17 | 18 | ``` 19 | dbus = {version = "0.9.7", features = ["vendored"]} 20 | ``` 21 | 22 | Then simply build your project with cross: 23 | 24 | ``` 25 | cross build --target arm-unknown-linux-musleabihf 26 | ``` 27 | 28 | #### Legacy Instructions: 29 | 30 | Thanks to [jobale](https://github.com/jobale) for providing these instructions 31 | (taken from [issue 292](https://github.com/diwic/dbus-rs/issues/292)). 32 | 33 | Tested on Ubuntu 20.04 | rustc 1.47.0 34 | 35 | - Install [rust-embedded/cross](https://github.com/rust-embedded/cross) 36 | - In your project directory, create a **Cross.toml** file: `touch Cross.toml` 37 | Add this code in it: 38 | 39 | ``` 40 | [target.armv7-unknown-linux-gnueabihf] 41 | image = "rustcross:dbus-armhf" 42 | 43 | [build.env] 44 | passthrough = [ 45 | "RUSTFLAGS", 46 | ] 47 | ``` 48 | 49 | 50 | - In your project directory create a **Dockerfile**: `touch Dockerfile` 51 | Put this code in it: 52 | 53 | ``` 54 | # Base image for rapsberrypi 3 target 55 | FROM rustembedded/cross:armv7-unknown-linux-gnueabihf 56 | 57 | # Install libdbus libraries and pkg-config 58 | RUN dpkg --add-architecture armhf && \ 59 | apt-get update && \ 60 | apt-get install --assume-yes libdbus-1-dev libdbus-1-dev:armhf pkg-config 61 | ``` 62 | 63 | For whatever reason, _in the docker image_, armhf libraries are installed in at least 2 locations. That's the reason of all my troubles: 64 | - /usr/arm-linux-gnueabihf/lib/ 65 | - /usr/lib/arm-linux-gnueabihf/ 66 | 67 | - Cross needs to know those locations. We pass them to the compiler through a command flag. For convenience I put it in a bash script: 68 | 69 | ``` 70 | #!/bin/bash 71 | set -o errexit 72 | set -o nounset 73 | set -o pipefail 74 | set -o xtrace 75 | 76 | readonly TARGET_ARCH=armv7-unknown-linux-gnueabihf 77 | readonly LINK_FLAGS='-L /usr/arm-linux-gnueabihf/lib/ -L /usr/lib/arm-linux-gnueabihf/' 78 | 79 | RUSTFLAGS=${LINK_FLAGS} cross build --release --target=${TARGET_ARCH} 80 | 81 | ``` 82 | 83 | 84 | Some more explanations for newcomers like me: 85 | 86 | - Cross command act as cargo command (e.g: `cross build` is the same as `cargo build` but for cross-compiling). 87 | - In Cross.toml, `passthrough = [ "RUSTFLAGS",]` is what enable us to pass flags/parameters to the compiler in the docker image. 88 | 89 | 90 | 91 | Setting up cross compiling manually 92 | =================================== 93 | 94 | A cross linker 95 | -------------- 96 | 97 | Apparently, `rustc` in itself can generate code for many archs but not assemble the generated code into the final executable. Hence you need a cross linker. 98 | 99 | **Install it** - here follow whatever guide you have for the target arch. Distributions may also ship with cross toolchains. Example for Ubuntu 18.04: 100 | 101 | `sudo apt install gcc-8-multilib-arm-linux-gnueabihf` 102 | 103 | **Tell rustc where to find it** - in [.cargo/config](https://doc.rust-lang.org/cargo/reference/config.html) add the following: 104 | 105 | ``` 106 | [target.armv7-unknown-linux-gnueabihf] 107 | linker = "arm-linux-gnueabihf-gcc-8" 108 | ``` 109 | 110 | Target rust std 111 | --------------- 112 | 113 | This one's easy, just run rustup: 114 | 115 | `rustup target add armv7-unknown-linux-gnueabihf` 116 | 117 | 118 | Target dbus libraries 119 | --------------------- 120 | 121 | **Installing the libraries** 122 | 123 | Now to the more challenging part. Since we link to a C library `libdbus-1.so`, we also need the target version of that library. However, `libdbus-1.so` in itself links to a systemd library (at least it does so here) which in turn links to other libraries etc, so we need target versions of those libraries too. 124 | 125 | Getting an entire rootfs/image is probably the easiest option. The rootfs needs to have `libdbus-1-dev` installed. I e: 126 | 127 | * Boot your target (i e, a raspberry), install `libdbus-1-dev` on it, turn it off and put the SD card into your computer's SD card reader. Mount the partition. 128 | * If the above is not an option, you could download an image, mount it at the right offset, like this (ugly hack!) `sudo mount -o loop,offset=50331648 2019-04-08-raspbian-stretch-lite.img /tmp/mnt` and then, to manually make the symlink that `libdbus-1-dev` does for you: `cd /tmp/mnt/usr/lib/arm-linux-gnueabihf && ln -s ../../../lib/arm-linux-gnueabihf/libdbus-1.so.3 libdbus-1.so`. 129 | * Or you can use the alternative guide's approach to download, extract and post-process the relevant `.deb` files manually. This might be a preferrable option if an entire image/rootfs would be too large. 130 | 131 | **Finding the libraries** 132 | 133 | When not cross compiling, finding the right library is done by a `build.rs` script which calls `pkg-config`. This will not work when cross compiling because it will point to the `libdbus-1.so` on the host, not the `libdbus-1.so` of the target. 134 | Maybe it is possible to teach `pkg-config` how to return the target library instead, but I have not tried this approach. Instead we can override build script altogether and provide the same info manually. This is possible because `libdbus-sys` has a `links = dbus` line. 135 | 136 | For the example below we assume that we have mounted a Raspbian rootfs on `/tmp/mnt`, and that the cross linker came with some basic libraries (libc, libpthread etc) that are installed on `/usr/arm-linux-gnueabihf/lib`. Unfortunately, we can't use the basic libraries that are present on the image, because they might contain absolute paths. 137 | 138 | And so we add the following to [.cargo/config](https://doc.rust-lang.org/cargo/reference/config.html): 139 | 140 | ``` 141 | [target.armv7-unknown-linux-gnueabihf.dbus] 142 | rustc-link-search = ["/usr/arm-linux-gnueabihf/lib", "/tmp/mnt/usr/lib/arm-linux-gnueabihf"] 143 | rustc-link-lib = ["dbus-1"] 144 | ``` 145 | 146 | 147 | Finally 148 | ------- 149 | 150 | If we are all set up, you should be able to successfully compile with: 151 | 152 | `cargo build --target=armv7-unknown-linux-gnueabihf` 153 | 154 | 155 | Docker compose 156 | ============== 157 | 158 | Dockerfile 159 | ```docker 160 | FROM rust:1.64.0-slim-bullseye 161 | 162 | RUN apt update && apt upgrade -y 163 | RUN apt install -y g++-arm-linux-gnueabihf libc6-dev-armhf-cross 164 | 165 | RUN rustup target add armv7-unknown-linux-gnueabihf 166 | RUN rustup toolchain install stable-armv7-unknown-linux-gnueabihf 167 | 168 | RUN dpkg --add-architecture armhf 169 | RUN apt update 170 | RUN apt install --assume-yes libdbus-1-dev libdbus-1-dev:armhf pkg-config 171 | 172 | WORKDIR /app 173 | 174 | ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc 175 | ENV CC_armv7_unknown_Linux_gnueabihf=arm-linux-gnueabihf-gcc 176 | ENV CXX_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-g++ 177 | ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=/usr/bin/arm-linux-gnueabihf-gcc 178 | ENV PKG_CONFIG_ALLOW_CROSS="true" 179 | ENV PKG_CONFIG_PATH="/usr/lib/arm-linux-gnueabihf/pkgconfig" 180 | ENV RUSTFLAGS="-L /usr/arm-linux-gnueabihf/lib/ -L /usr/lib/arm-linux-gnueabihf/" 181 | ``` 182 | 183 | docker-compose.yml 184 | ```yaml 185 | version: "3" 186 | 187 | services: 188 | compilation: 189 | build: . 190 | volumes: 191 | - ./:/app 192 | command: "cargo build --release --target=armv7-unknown-linux-gnueabihf" 193 | ``` 194 | 195 | You just have to add these two files to your project root and run `docker-compose up`. 196 | -------------------------------------------------------------------------------- /dbus/src/arg/variantstruct_impl.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::Signature; 3 | use std::any; 4 | use std::collections::VecDeque; 5 | 6 | #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] 7 | /// A simple wrapper to specify a D-Bus variant. 8 | /// 9 | /// See the argument guide and module level documentation for details and examples. 10 | pub struct Variant(pub T); 11 | 12 | impl Variant> { 13 | /// Creates a new refarg from an Iter. Mainly for internal use. 14 | pub fn new_refarg<'a>(i: &mut Iter<'a>) -> Option { 15 | i.recurse(ArgType::Variant).and_then(|mut si| si.get_refarg()).map(Variant) 16 | } 17 | } 18 | 19 | impl Default for Variant { 20 | fn default() -> Self { Variant(T::default()) } 21 | } 22 | 23 | 24 | impl Arg for Variant { 25 | const ARG_TYPE: ArgType = ArgType::Variant; 26 | fn signature() -> Signature<'static> { unsafe { Signature::from_slice_unchecked("v\0") } } 27 | } 28 | 29 | impl Append for Variant { 30 | fn append_by_ref(&self, i: &mut IterAppend) { 31 | let z = &self.0; 32 | i.append_container(ArgType::Variant, Some(T::signature().as_cstr()), |s| z.append_by_ref(s)); 33 | } 34 | } 35 | 36 | impl Append for Variant> { 37 | fn append_by_ref(&self, i: &mut IterAppend) { 38 | let z = &self.0; 39 | i.append_container(ArgType::Variant, Some(z.signature().as_cstr()), |s| z.append(s)); 40 | } 41 | } 42 | 43 | impl<'a, T: Get<'a>> Get<'a> for Variant { 44 | fn get(i: &mut Iter<'a>) -> Option> { 45 | i.recurse(ArgType::Variant).and_then(|mut si| si.get().map(Variant)) 46 | } 47 | } 48 | 49 | impl<'a> Get<'a> for Variant> { 50 | fn get(i: &mut Iter<'a>) -> Option>> { 51 | i.recurse(ArgType::Variant).map(Variant) 52 | } 53 | } 54 | /* 55 | impl<'a> Get<'a> for Variant> { 56 | fn get(i: &mut Iter<'a>) -> Option>> { 57 | i.recurse(ArgType::Variant).and_then(|mut si| si.get_refarg().map(|v| Variant(v))) 58 | } 59 | } 60 | */ 61 | impl RefArg for Variant { 62 | fn arg_type(&self) -> ArgType { ArgType::Variant } 63 | fn signature(&self) -> Signature<'static> { unsafe { Signature::from_slice_unchecked("v\0") } } 64 | fn append(&self, i: &mut IterAppend) { 65 | let z = &self.0; 66 | i.append_container(ArgType::Variant, Some(z.signature().as_cstr()), |s| z.append(s)); 67 | } 68 | #[inline] 69 | fn as_any(&self) -> &dyn any::Any where T: 'static { self } 70 | #[inline] 71 | fn as_any_mut(&mut self) -> &mut dyn any::Any where T: 'static { self } 72 | #[inline] 73 | fn as_i64(&self) -> Option { self.0.as_i64() } 74 | #[inline] 75 | fn as_u64(&self) -> Option { self.0.as_u64() } 76 | #[inline] 77 | fn as_f64(&self) -> Option { self.0.as_f64() } 78 | #[inline] 79 | fn as_str(&self) -> Option<&str> { self.0.as_str() } 80 | #[inline] 81 | fn as_iter<'a>(&'a self) -> Option + 'a>> { 82 | use std::iter; 83 | let z: &dyn RefArg = &self.0; 84 | Some(Box::new(iter::once(z))) 85 | } 86 | #[inline] 87 | fn box_clone(&self) -> Box { Box::new(Variant(self.0.box_clone())) } 88 | #[inline] 89 | fn as_static_inner(&self, index: usize) -> Option<&(dyn RefArg + 'static)> where Self: 'static { 90 | if index == 0 { Some(&self.0) } else { None } 91 | } 92 | } 93 | 94 | macro_rules! struct_impl { 95 | ( $($n: ident $t: ident,)+ ) => { 96 | 97 | /// Tuples are represented as D-Bus structs. 98 | impl<$($t: Arg),*> Arg for ($($t,)*) { 99 | const ARG_TYPE: ArgType = ArgType::Struct; 100 | fn signature() -> Signature<'static> { 101 | let mut s = String::from("("); 102 | $( s.push_str(&$t::signature()); )* 103 | s.push_str(")"); 104 | Signature::from(s) 105 | } 106 | } 107 | 108 | impl<$($t: Append),*> Append for ($($t,)*) { 109 | fn append_by_ref(&self, i: &mut IterAppend) { 110 | let ( $($n,)*) = self; 111 | i.append_container(ArgType::Struct, None, |s| { $( $n.append_by_ref(s); )* }); 112 | } 113 | } 114 | 115 | impl<'a, $($t: Get<'a>),*> Get<'a> for ($($t,)*) { 116 | fn get(i: &mut Iter<'a>) -> Option { 117 | let si = i.recurse(ArgType::Struct); 118 | if si.is_none() { return None; } 119 | let mut si = si.unwrap(); 120 | let mut _valid_item = true; 121 | $( 122 | if !_valid_item { return None; } 123 | let $n: Option<$t> = si.get(); 124 | if $n.is_none() { return None; } 125 | _valid_item = si.next(); 126 | )* 127 | Some(($( $n.unwrap(), )* )) 128 | } 129 | } 130 | 131 | impl<$($t: RefArg),*> RefArg for ($($t,)*) { 132 | fn arg_type(&self) -> ArgType { ArgType::Struct } 133 | fn signature(&self) -> Signature<'static> { 134 | let &( $(ref $n,)*) = self; 135 | let mut s = String::from("("); 136 | $( s.push_str(&$n.signature()); )* 137 | s.push_str(")"); 138 | Signature::from(s) 139 | } 140 | fn append(&self, i: &mut IterAppend) { 141 | let &( $(ref $n,)*) = self; 142 | i.append_container(ArgType::Struct, None, |s| { $( $n.append(s); )* }); 143 | } 144 | fn as_any(&self) -> &dyn any::Any where Self: 'static { self } 145 | fn as_any_mut(&mut self) -> &mut dyn any::Any where Self: 'static { self } 146 | fn as_iter<'a>(&'a self) -> Option + 'a>> { 147 | let &( $(ref $n,)*) = self; 148 | let v = vec!( 149 | $( $n as &dyn RefArg, )* 150 | ); 151 | Some(Box::new(v.into_iter())) 152 | } 153 | fn as_static_inner(&self, index: usize) -> Option<&(dyn RefArg + 'static)> where Self: 'static { 154 | let &( $(ref $n,)*) = self; 155 | let arr = [ $($n as &dyn RefArg,)*]; 156 | arr.get(index).map(|x| *x) 157 | } 158 | #[inline] 159 | fn box_clone(&self) -> Box { 160 | let &( $(ref $n,)*) = self; 161 | let mut z = VecDeque::new(); 162 | $( z.push_back($n.box_clone()); )* 163 | Box::new(z) 164 | } 165 | } 166 | 167 | 168 | }} // macro_rules end 169 | 170 | struct_impl!(a A,); 171 | struct_impl!(a A, b B,); 172 | struct_impl!(a A, b B, c C,); 173 | struct_impl!(a A, b B, c C, d D,); 174 | struct_impl!(a A, b B, c C, d D, e E,); 175 | struct_impl!(a A, b B, c C, d D, e E, f F,); 176 | struct_impl!(a A, b B, c C, d D, e E, f F, g G,); 177 | struct_impl!(a A, b B, c C, d D, e E, f F, g G, h H,); 178 | struct_impl!(a A, b B, c C, d D, e E, f F, g G, h H, i I,); 179 | struct_impl!(a A, b B, c C, d D, e E, f F, g G, h H, i I, j J,); 180 | struct_impl!(a A, b B, c C, d D, e E, f F, g G, h H, i I, j J, k K,); 181 | struct_impl!(a A, b B, c C, d D, e E, f F, g G, h H, i I, j J, k K, l L,); 182 | 183 | impl RefArg for VecDeque> { 184 | fn arg_type(&self) -> ArgType { ArgType::Struct } 185 | fn signature(&self) -> Signature<'static> { 186 | let mut s = String::from("("); 187 | for z in self { 188 | s.push_str(&z.signature()); 189 | } 190 | s.push_str(")"); 191 | Signature::from(s) 192 | } 193 | fn append(&self, i: &mut IterAppend) { 194 | i.append_container(ArgType::Struct, None, |s| { 195 | for z in self { z.append(s); } 196 | }); 197 | } 198 | #[inline] 199 | fn as_any(&self) -> &dyn any::Any where Self: 'static { self } 200 | #[inline] 201 | fn as_any_mut(&mut self) -> &mut dyn any::Any where Self: 'static { self } 202 | fn as_iter<'a>(&'a self) -> Option + 'a>> { 203 | Some(Box::new(self.iter().map(|b| &**b))) 204 | } 205 | #[inline] 206 | fn as_static_inner(&self, index: usize) -> Option<&(dyn RefArg + 'static)> where Self: 'static { 207 | self.get(index).map(|x| x as &dyn RefArg) 208 | } 209 | #[inline] 210 | fn box_clone(&self) -> Box { 211 | let t: VecDeque> = self.iter().map(|x| x.box_clone()).collect(); 212 | Box::new(t) 213 | } 214 | } 215 | --------------------------------------------------------------------------------