dbus-0.2.3/.gitignore00006640001750000175000000000024126531174600012664 0ustar0000000000000000/target /Cargo.lock dbus-0.2.3/Cargo.toml00006640001750000175000000001706126567000760012640 0ustar0000000000000000# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "dbus" version = "0.2.3" authors = ["David Henningsson "] description = "Bindings to D-Bus, which is a bus commonly used on Linux for inter-process communication." documentation = "http://diwic.github.io/dbus-rs-docs/dbus" keywords = ["D-Bus", "DBus"] license = "Apache-2.0/MIT" repository = "https://github.com/diwic/dbus-rs" [dependencies.libc] version = "0.2" [dev-dependencies.tempdir] version = "0.3" dbus-0.2.3/Cargo.toml.orig00006640001750000175000000000655126567000760013601 0ustar0000000000000000[package] name = "dbus" version = "0.2.3" authors = ["David Henningsson "] description = "Bindings to D-Bus, which is a bus commonly used on Linux for inter-process communication." repository = "https://github.com/diwic/dbus-rs" documentation = "http://diwic.github.io/dbus-rs-docs/dbus" keywords = ["D-Bus", "DBus"] license = "Apache-2.0/MIT" [dependencies] libc = "0.2" [dev-dependencies] tempdir = "0.3" dbus-0.2.3/LICENSE00006640001750000175000000026075126531174600011717 0ustar0000000000000000Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. dbus-0.2.3/README.md00006640001750000175000000004551126531174600012164 0ustar0000000000000000A D-Bus binding for rust. ======================== Current state: WIP, but these things should be up and working: * Connect to system or session bus * Method calls send/receive * Signals send/receive * Properties, on both client and server sides (set/get/getall methods, but no signals) * Server side introspection * Server side method dispatch (boxed closures) and property get/set dispatch (trait objects) [API Documentation](http://diwic.github.io/dbus-rs-docs/dbus/) Examples ======== Client ------ This example opens a connection to the session bus and asks for a list of all names currently present. ```rust let c = Connection::get_private(BusType::Session).unwrap(); let m = Message::new_method_call("org.freedesktop.DBus", "/", "org.freedesktop.DBus", "ListNames").unwrap(); let r = c.send_with_reply_and_block(m, 2000).unwrap(); let reply = r.get_items(); println!("{}", reply); ``` You can try a similar example by running: cargo run --example client Server ------ This example grabs the com.example.dbustest bus name, registers the /hello path and adds a method which returns a string. It then listens for incoming D-Bus events and handles them accordingly. ```rust let c = Connection::get_private(BusType::Session).unwrap(); c.register_name("com.example.dbustest", NameFlag::ReplaceExisting as u32).unwrap(); let f = Factory::new_fn(); let tree = f.tree().add(f.object_path("/hello").introspectable().add( f.interface("com.example.dbustest").add_m( f.method("Hello", |m,_,_| { let s = format!("Hello {}!", m.sender().unwrap()); Ok(vec!(m.method_return().append(s))) }).out_arg(("reply", "s")) ) )); tree.set_registered(&c, true).unwrap(); for _ in tree.run(&c, c.iter(1000)) {} ``` You can try a similar example by running: cargo run --example server Properties ---------- This example gets the current version of the Policykit backend. ```rust let c = Connection::get_private(BusType::System).unwrap(); let p = Props::new(&c, "org.freedesktop.PolicyKit1", "/org/freedesktop/PolicyKit1/Authority", "org.freedesktop.PolicyKit1.Authority", 10000); let v = p.get("BackendVersion").unwrap(); ``` You can try a this example by running: cargo run --example properties For an extended example, which also uses non-panicing error handling, see examples/rtkit.rs License ======= Apache / MIT dual licensed. dbus-0.2.3/examples/client.rs00006640001750000175000000000567126531174600014352 0ustar0000000000000000extern crate dbus; use dbus::{Connection, BusType, Message}; fn main() { let c = Connection::get_private(BusType::Session).unwrap(); let m = Message::new_method_call("org.freedesktop.DBus", "/", "org.freedesktop.DBus", "ListNames").unwrap(); let r = c.send_with_reply_and_block(m, 2000).unwrap(); let reply = r.get_items(); println!("{:?}", reply); } dbus-0.2.3/examples/properties.rs00006640001750000175000000000553126531174600015263 0ustar0000000000000000extern crate dbus; use dbus::{Connection, BusType, Props}; fn main() { let c = Connection::get_private(BusType::System).unwrap(); let p = Props::new(&c, "org.freedesktop.PolicyKit1", "/org/freedesktop/PolicyKit1/Authority", "org.freedesktop.PolicyKit1.Authority", 10000); println!("BackendVersion: {:?}", p.get("BackendVersion").unwrap()) } dbus-0.2.3/examples/rtkit.rs00006640001750000175000000005516126531174600014230 0ustar0000000000000000/* This example asks the rtkit service to make our thread realtime priority. Rtkit puts a few limitations on us to let us become realtime, such as setting RLIMIT_RTTIME correctly, hence the syscalls. */ extern crate dbus; extern crate libc; use std::cmp; use libc::c_int; use dbus::{Connection, BusType, Props, MessageItem, Message}; /* External C stuff. Currently only working for x86_64 */ #[allow(non_upper_case_globals)] #[cfg(target_arch = "x86_64")] static SYS_gettid: c_int = 186; static RLIMIT_RTTIME: c_int = 15; #[repr(C)] #[cfg(target_arch = "x86_64")] #[derive(Copy, Clone)] struct rlimit { rlim_cur: u64, rlim_max: u64, } extern "C" { fn syscall(num: c_int, ...) -> c_int; fn getrlimit(resource: c_int, rlim: *mut rlimit) -> c_int; fn setrlimit(resource: c_int, rlim: *const rlimit) -> c_int; } fn item_as_i64(i: MessageItem) -> Result> { match i { MessageItem::Int32(i) => Ok(i as i64), MessageItem::Int64(i) => Ok(i), _ => Err(Box::from(&*format!("Property is not integer ({:?})", i))) } } fn rtkit_set_realtime(c: &Connection, thread: u64, prio: u32) -> Result<(), ::dbus::Error> { let mut m = Message::new_method_call("org.freedesktop.RealtimeKit1", "/org/freedesktop/RealtimeKit1", "org.freedesktop.RealtimeKit1", "MakeThreadRealtime").unwrap(); m.append_items(&[thread.into(), prio.into()]); let mut r = try!(c.send_with_reply_and_block(m, 10000)); r.as_result().map(|_| ()) } fn make_realtime(prio: u32) -> Result> { let c = try!(Connection::get_private(BusType::System)); let p = Props::new(&c, "org.freedesktop.RealtimeKit1", "/org/freedesktop/RealtimeKit1", "org.freedesktop.RealtimeKit1", 10000); // Make sure we don't fail by wanting too much let max_prio = try!(item_as_i64(try!(p.get("MaxRealtimePriority")))) as u32; let prio = cmp::min(prio, max_prio); // Enforce RLIMIT_RTPRIO, also a must before asking rtkit for rtprio let max_rttime = try!(item_as_i64(try!(p.get("RTTimeNSecMax")))) as u64; let new_limit = rlimit { rlim_cur: max_rttime, rlim_max: max_rttime }; let mut old_limit = new_limit; if unsafe { getrlimit(RLIMIT_RTTIME, &mut old_limit) } < 0 { return Err(Box::from("getrlimit failed")); } if unsafe { setrlimit(RLIMIT_RTTIME, &new_limit) } < 0 { return Err(Box::from("setrlimit failed")); } // Finally, let's ask rtkit to make us realtime let thread_id = unsafe { syscall(SYS_gettid) }; let r = rtkit_set_realtime(&c, thread_id as u64, prio); if r.is_err() { unsafe { setrlimit(RLIMIT_RTTIME, &old_limit) }; } try!(r); Ok(prio) } fn main() { match make_realtime(5) { Ok(n) => println!("Got rtprio, level {}", n), Err(e) => println!("No rtprio: {}", e), } } dbus-0.2.3/examples/server.rs00006640001750000175000000001371126531174600014374 0ustar0000000000000000extern crate dbus; use dbus::{Connection, BusType, NameFlag}; use dbus::tree::Factory; fn main() { let c = Connection::get_private(BusType::Session).unwrap(); c.register_name("com.example.dbustest", NameFlag::ReplaceExisting as u32).unwrap(); let f = Factory::new_fn(); let tree = f.tree().add(f.object_path("/hello").introspectable().add( f.interface("com.example.dbustest").add_m( f.method("Hello", |m,_,_| { let s = format!("Hello {}!", m.sender().unwrap()); Ok(vec!(m.method_return().append(s))) }).out_arg(("reply", "s")) // One output argument, no input arguments ) )); tree.set_registered(&c, true).unwrap(); for _ in tree.run(&c, c.iter(1000)) {} } dbus-0.2.3/src/ffi.rs00006640001750000175000000023566126566777300012633 0ustar0000000000000000#![allow(dead_code)] use libc::{c_void, c_char, c_uint, c_int}; pub type DBusConnection = c_void; pub type DBusMessage = c_void; pub type DBusCallback = extern "C" fn(*mut c_void, *mut c_void, *mut c_void) -> DBusHandlerResult; pub type DBusWatch = c_void; #[repr(C)] #[derive(Debug, PartialEq, Copy, Clone)] pub enum DBusBusType { Session = 0, System = 1, Starter = 2, } pub const DBUS_TYPE_ARRAY: c_int = 'a' as c_int; pub const DBUS_TYPE_VARIANT: c_int = 'v' as c_int; pub const DBUS_TYPE_BOOLEAN: c_int = 'b' as c_int; pub const DBUS_TYPE_INVALID: c_int = 0; pub const DBUS_TYPE_STRING: c_int = 's' as c_int; pub const DBUS_TYPE_DICT_ENTRY: c_int = 'e' as c_int; pub const DBUS_TYPE_BYTE: c_int = 'y' as c_int; pub const DBUS_TYPE_INT16: c_int = 'n' as c_int; pub const DBUS_TYPE_UINT16: c_int = 'q' as c_int; pub const DBUS_TYPE_INT32: c_int = 'i' as c_int; pub const DBUS_TYPE_UINT32: c_int = 'u' as c_int; pub const DBUS_TYPE_INT64: c_int = 'x' as c_int; pub const DBUS_TYPE_UINT64: c_int = 't' as c_int; pub const DBUS_TYPE_DOUBLE: c_int = 'd' as c_int; pub const DBUS_TYPE_UNIX_FD: c_int = 'h' as c_int; pub const DBUS_TYPE_STRUCT: c_int = 'r' as c_int; pub const DBUS_TYPE_OBJECT_PATH: c_int = 'o' as c_int; #[repr(C)] #[derive(Debug, PartialEq, Copy, Clone)] pub enum DBusNameFlag { AllowReplacement = 1, ReplaceExisting = 2, DoNotQueue = 4, } #[repr(C)] #[derive(Debug, PartialEq, Copy, Clone)] pub enum DBusRequestNameReply { PrimaryOwner = 1, InQueue = 2, Exists = 3, AlreadyOwner = 4, } #[repr(C)] #[derive(Debug, PartialEq, Copy, Clone)] pub enum DBusReleaseNameReply { Released = 1, NonExistent = 2, NotOwner = 3, } #[repr(C)] #[derive(Debug, PartialEq, Copy, Clone)] pub enum DBusHandlerResult { Handled = 0, NotYetHandled = 1, NeedMemory = 2, } #[repr(C)] #[derive(Debug, PartialEq, Copy, Clone)] pub enum DBusMessageType { Invalid = 0, MethodCall = 1, MethodReturn = 2, Error = 3, Signal = 4, } #[repr(C)] #[derive(Debug, PartialEq, Copy, Clone)] pub enum DBusDispatchStatus { DataRemains = 0, Complete = 1, NeedMemory = 2, } #[repr(C)] #[derive(Debug, PartialEq, Copy, Clone)] pub enum DBusWatchEvent { Readable = 1, Writable = 2, Error = 4, Hangup = 8, } #[repr(C)] pub struct DBusError { pub name: *const c_char, pub message: *const c_char, pub dummy: c_uint, pub padding1: *const c_void } #[repr(C)] pub struct DBusMessageIter { pub dummy1: *mut c_void, pub dummy2: *mut c_void, pub dummy3: u32, pub dummy4: c_int, pub dummy5: c_int, pub dummy6: c_int, pub dummy7: c_int, pub dummy8: c_int, pub dummy9: c_int, pub dummy10: c_int, pub dummy11: c_int, pub pad1: c_int, pub pad2: c_int, pub pad3: *mut c_void, } pub type DBusHandleMessageFunction = Option< extern fn(conn: *mut DBusConnection, msg: *mut DBusMessage, user_data: *mut c_void) -> DBusHandlerResult>; pub type DBusAddWatchFunction = Option u32>; pub type DBusRemoveWatchFunction = Option; pub type DBusWatchToggledFunction = Option; #[repr(C)] pub struct DBusObjectPathVTable { pub unregister_function: Option, pub message_function: DBusHandleMessageFunction, pub dbus_internal_pad1: Option, pub dbus_internal_pad2: Option, pub dbus_internal_pad3: Option, pub dbus_internal_pad4: Option, } #[link(name = "dbus-1")] extern "C" { pub fn dbus_bus_get_private(t: DBusBusType, error: *mut DBusError) -> *mut DBusConnection; pub fn dbus_bus_get_unique_name(conn: *mut DBusConnection) -> *const c_char; pub fn dbus_bus_request_name(conn: *mut DBusConnection, name: *const c_char, flags: c_uint, error: *mut DBusError) -> c_int; pub fn dbus_bus_release_name(conn: *mut DBusConnection, name: *const c_char, error: *mut DBusError) -> c_int; pub fn dbus_bus_add_match(conn: *mut DBusConnection, rule: *const c_char, error: *mut DBusError); pub fn dbus_bus_remove_match(conn: *mut DBusConnection, rule: *const c_char, error: *mut DBusError); pub fn dbus_connection_close(conn: *mut DBusConnection); pub fn dbus_connection_dispatch(conn: *mut DBusConnection) -> DBusDispatchStatus; pub fn dbus_connection_flush(conn: *mut DBusConnection); pub fn dbus_connection_unref(conn: *mut DBusConnection); pub fn dbus_connection_set_exit_on_disconnect(conn: *mut DBusConnection, enable: u32); pub fn dbus_connection_send_with_reply_and_block(conn: *mut DBusConnection, message: *mut DBusMessage, timeout_milliseconds: c_int, error: *mut DBusError) -> *mut DBusMessage; pub fn dbus_connection_send(conn: *mut DBusConnection, message: *mut DBusMessage, serial: *mut u32) -> u32; pub fn dbus_connection_read_write_dispatch(conn: *mut DBusConnection, timeout_milliseconds: c_int) -> u32; pub fn dbus_connection_try_register_object_path(conn: *mut DBusConnection, path: *const c_char, vtable: *const DBusObjectPathVTable, user_data: *mut c_void, error: *mut DBusError) -> u32; pub fn dbus_connection_unregister_object_path(conn: *mut DBusConnection, path: *const c_char) -> u32; pub fn dbus_connection_list_registered(conn: *mut DBusConnection, parent_path: *const c_char, child_entries: *mut *mut *mut c_char) -> u32; pub fn dbus_connection_add_filter(conn: *mut DBusConnection, function: DBusHandleMessageFunction, user_data: *mut c_void, free_data_function: Option) -> u32; pub fn dbus_connection_remove_filter(conn: *mut DBusConnection, function: DBusHandleMessageFunction, user_data: *mut c_void) -> u32; pub fn dbus_connection_set_watch_functions(conn: *mut DBusConnection, add_function: DBusAddWatchFunction, remove_function: DBusRemoveWatchFunction, toggled_function: DBusWatchToggledFunction, data: *mut c_void, free_data_function: Option) -> u32; pub fn dbus_error_init(error: *mut DBusError); pub fn dbus_error_free(error: *mut DBusError); pub fn dbus_set_error(error: *mut DBusError, name: *const c_char, message: *const c_char, ...); pub fn dbus_set_error_from_message(error: *mut DBusError, message: *mut DBusMessage) -> u32; pub fn dbus_message_new_method_call(destination: *const c_char, path: *const c_char, iface: *const c_char, method: *const c_char) -> *mut DBusMessage; pub fn dbus_message_new_method_return(message: *mut DBusMessage) -> *mut DBusMessage; pub fn dbus_message_new_error(message: *mut DBusMessage, error_name: *const c_char, error_message: *const c_char) -> *mut DBusMessage; pub fn dbus_message_new_signal(path: *const c_char, iface: *const c_char, name: *const c_char) -> *mut DBusMessage; pub fn dbus_message_ref(message: *mut DBusMessage) -> *mut DBusMessage; pub fn dbus_message_unref(message: *mut DBusMessage); pub fn dbus_message_get_type(message: *mut DBusMessage) -> c_int; pub fn dbus_message_is_method_call(message: *mut DBusMessage, iface: *const c_char, method: *const c_char) -> u32; pub fn dbus_message_is_signal(message: *mut DBusMessage, iface: *const c_char, signal_name: *const c_char) -> u32; pub fn dbus_message_get_reply_serial(message: *mut DBusMessage) -> u32; pub fn dbus_message_get_serial(message: *mut DBusMessage) -> u32; pub fn dbus_message_get_path(message: *mut DBusMessage) -> *const c_char; pub fn dbus_message_get_interface(message: *mut DBusMessage) -> *const c_char; pub fn dbus_message_get_member(message: *mut DBusMessage) -> *const c_char; pub fn dbus_message_get_sender(message: *mut DBusMessage) -> *const c_char; pub fn dbus_message_set_serial(message: *mut DBusMessage, serial: u32); pub fn dbus_message_iter_append_basic(iter: *mut DBusMessageIter, t: c_int, value: *const c_void) -> u32; pub fn dbus_message_iter_init(message: *mut DBusMessage, iter: *mut DBusMessageIter) -> u32; pub fn dbus_message_iter_init_append(message: *mut DBusMessage, iter: *mut DBusMessageIter); pub fn dbus_message_iter_get_arg_type(iter: *mut DBusMessageIter) -> c_int; pub fn dbus_message_iter_get_basic(iter: *mut DBusMessageIter, value: *mut c_void); pub fn dbus_message_iter_get_signature(iter: *mut DBusMessageIter) -> *mut c_char; pub fn dbus_message_iter_next(iter: *mut DBusMessageIter) -> u32; pub fn dbus_message_iter_recurse(iter: *mut DBusMessageIter, subiter: *mut DBusMessageIter); pub fn dbus_message_iter_open_container(iter: *mut DBusMessageIter, _type: c_int, contained_signature: *const c_char, sub: *mut DBusMessageIter) -> u32; pub fn dbus_message_iter_close_container(iter: *mut DBusMessageIter, sub: *mut DBusMessageIter) -> u32; pub fn dbus_free(memory: *mut c_void); pub fn dbus_free_string_array(str_array: *mut *mut c_char) -> c_void; pub fn dbus_signature_validate_single(signature: *const c_char, error: *mut DBusError) -> u32; pub fn dbus_threads_init_default() -> c_int; pub fn dbus_validate_bus_name(busname: *const c_char, error: *mut DBusError) -> u32; pub fn dbus_validate_error_name(errorname: *const c_char, error: *mut DBusError) -> u32; pub fn dbus_validate_interface(interface: *const c_char, error: *mut DBusError) -> u32; pub fn dbus_validate_member(member: *const c_char, error: *mut DBusError) -> u32; pub fn dbus_validate_path(path: *const c_char, error: *mut DBusError) -> u32; pub fn dbus_watch_get_enabled(watch: *mut DBusWatch) -> u32; pub fn dbus_watch_get_flags(watch: *mut DBusWatch) -> c_uint; pub fn dbus_watch_get_unix_fd(watch: *mut DBusWatch) -> c_int; pub fn dbus_watch_handle(watch: *mut DBusWatch, flags: c_uint) -> u32; } dbus-0.2.3/src/lib.rs00006640001750000175000000050115126566777300012623 0ustar0000000000000000//! D-Bus bindings for Rust //! //! [D-Bus](http://dbus.freedesktop.org/) is a message bus, and is mainly used in Linux //! for communication between processes. It is present by default on almost every //! Linux distribution out there, and runs in two instances - one per session, and one //! system-wide. //! //! See the examples directory for some examples to get you started. #![warn(missing_docs)] extern crate libc; pub use ffi::DBusBusType as BusType; pub use ffi::DBusNameFlag as NameFlag; pub use ffi::DBusRequestNameReply as RequestNameReply; pub use ffi::DBusReleaseNameReply as ReleaseNameReply; pub use ffi::DBusMessageType as MessageType; pub use ffi::DBusWatchEvent as WatchEvent; pub use message::{Message, MessageItem, FromMessageItem, OwnedFd, ArrayError}; pub use prop::PropHandler; pub use prop::Props; pub use watch::Watch; /// A TypeSig describes the type of a MessageItem. pub type TypeSig<'a> = std::borrow::Cow<'a, str>; use std::ffi::{CString, CStr}; use std::ptr::{self}; use std::collections::LinkedList; use std::cell::{Cell, RefCell}; use std::mem; use std::os::unix::io::RawFd; #[allow(missing_docs)] mod ffi; mod message; mod prop; mod objpath; mod watch; mod strings; pub use strings::{Signature, Path, Interface, Member, ErrorName, BusName}; /// Contains functionality for the "server" of a D-Bus object. A remote application can /// introspect this object and call methods on it. /// Deprecated - use the `tree` module instead. pub mod obj { pub use objpath::{ObjectPath, Interface, Property, Signal, Argument}; pub use objpath::{Method, MethodHandler, MethodResult}; pub use objpath::{PropertyROHandler, PropertyRWHandler, PropertyWOHandler, PropertyGetResult, PropertySetResult}; } mod methoddisp; /// Contains functionality for dispatching methods on a D-Bus "server". /// Supersedes the `obj` module. Properties are somewhat still WIP, /// but should in any case be better than `obj` already. /// /// # Example /// ``` /// use dbus::{tree, Connection, BusType}; /// let f = tree::Factory::new_fn(); /// /* Add a method returning "Thanks!" on interface "com.example.dbus.rs" /// on object path "/example". */ /// let t = f.tree().add(f.object_path("/example").introspectable() /// .add(f.interface("com.example.dbus.rs") /// .add_m(f.method("CallMe", |m,_,_| { /// Ok(vec!(m.method_return().append("Thanks!"))) } /// ).out_arg("s")) /// )); /// /// let c = Connection::get_private(BusType::Session).unwrap(); /// t.set_registered(&c, true).unwrap(); /// /* Run forever */ /// // for _ in t.run(&c, c.iter(1000)) {} /// ``` pub mod tree { pub use methoddisp::{Factory, Tree, TreeServer, ObjectPath, Interface, Signal}; pub use methoddisp::{Property, EmitsChangedSignal, Access}; pub use methoddisp::{Method, MethodErr, MethodResult, Argument}; pub use methoddisp::{MethodFn, MethodFnMut, MethodSync}; } static INITDBUS: std::sync::Once = std::sync::ONCE_INIT; fn init_dbus() { INITDBUS.call_once(|| { if unsafe { ffi::dbus_threads_init_default() } == 0 { panic!("Out of memory when trying to initialize D-Bus library!"); } }); } /// D-Bus Error wrapper pub struct Error { e: ffi::DBusError, } unsafe impl Send for Error {} fn c_str_to_slice(c: & *const libc::c_char) -> Option<&str> { if *c == ptr::null() { None } else { std::str::from_utf8( unsafe { CStr::from_ptr(*c).to_bytes() }).ok() } } fn to_c_str(n: &str) -> CString { CString::new(n.as_bytes()).unwrap() } impl Error { /// Create a new custom D-Bus Error. pub fn new_custom(name: &str, message: &str) -> Error { let n = to_c_str(name); let m = to_c_str(&message.replace("%","%%")); let mut e = Error::empty(); unsafe { ffi::dbus_set_error(e.get_mut(), n.as_ptr(), m.as_ptr()) }; e } fn empty() -> Error { init_dbus(); let mut e = ffi::DBusError { name: ptr::null(), message: ptr::null(), dummy: 0, padding1: ptr::null() }; unsafe { ffi::dbus_error_init(&mut e); } Error{ e: e } } /// Error name/type, e g 'org.freedesktop.DBus.Error.Failed' pub fn name(&self) -> Option<&str> { c_str_to_slice(&self.e.name) } /// Custom message, e g 'Could not find a matching object path' pub fn message(&self) -> Option<&str> { c_str_to_slice(&self.e.message) } fn get_mut(&mut self) -> &mut ffi::DBusError { &mut self.e } } impl Drop for Error { fn drop(&mut self) { unsafe { ffi::dbus_error_free(&mut self.e); } } } impl std::fmt::Debug for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { write!(f, "D-Bus error: {} ({})", self.message().unwrap_or(""), self.name().unwrap_or("")) } } impl std::error::Error for Error { fn description(&self) -> &str { "D-Bus error" } } impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(),std::fmt::Error> { if let Some(x) = self.message() { write!(f, "{:?}", x.to_string()) } else { Ok(()) } } } /// When listening for incoming events on the D-Bus, this enum will tell you what type /// of incoming event has happened. #[derive(Debug)] pub enum ConnectionItem { /// No event between now and timeout Nothing, /// Incoming method call MethodCall(Message), /// Incoming signal Signal(Message), /// Incoming method return (mostly used for Async I/O) MethodReturn(Message), /// Indicates whether a file descriptor should be monitored or not. /// Unless you're doing Async I/O, you can simply ignore this variant. WatchFd(Watch), } /// ConnectionItem iterator pub struct ConnectionItems<'a> { c: &'a Connection, timeout_ms: Option, } impl<'a> Iterator for ConnectionItems<'a> { type Item = ConnectionItem; fn next(&mut self) -> Option { loop { let i = self.c.i.pending_items.borrow_mut().pop_front(); if i.is_some() { return i; } match self.timeout_ms { Some(t) => { let r = unsafe { ffi::dbus_connection_read_write_dispatch(self.c.conn(), t as libc::c_int) }; if !self.c.i.pending_items.borrow().is_empty() { continue }; if r == 0 { return None; } return Some(ConnectionItem::Nothing); } None => { let r = unsafe { ffi::dbus_connection_dispatch(self.c.conn()) }; if !self.c.i.pending_items.borrow().is_empty() { continue }; if r == ffi::DBusDispatchStatus::DataRemains { continue }; if r == ffi::DBusDispatchStatus::Complete { return None }; panic!("dbus_connection_dispatch failed"); } } } } } /* Since we register callbacks with userdata pointers, we need to make sure the connection pointer does not move around. Hence this extra indirection. */ struct IConnection { conn: Cell<*mut ffi::DBusConnection>, pending_items: RefCell>, watches: Option>, } /// A D-Bus connection. Start here if you want to get on the D-Bus! pub struct Connection { i: Box, } extern "C" fn filter_message_cb(conn: *mut ffi::DBusConnection, msg: *mut ffi::DBusMessage, user_data: *mut libc::c_void) -> ffi::DBusHandlerResult { let m = message::message_from_ptr(msg, true); let i: &IConnection = unsafe { mem::transmute(user_data) }; assert!(i.conn.get() == conn); let mtype: ffi::DBusMessageType = unsafe { mem::transmute(ffi::dbus_message_get_type(msg)) }; let r = match mtype { ffi::DBusMessageType::Signal => { i.pending_items.borrow_mut().push_back(ConnectionItem::Signal(m)); ffi::DBusHandlerResult::Handled } ffi::DBusMessageType::MethodReturn => { i.pending_items.borrow_mut().push_back(ConnectionItem::MethodReturn(m)); ffi::DBusHandlerResult::NotYetHandled } _ => ffi::DBusHandlerResult::NotYetHandled, }; r } extern "C" fn object_path_message_cb(conn: *mut ffi::DBusConnection, msg: *mut ffi::DBusMessage, user_data: *mut libc::c_void) -> ffi::DBusHandlerResult { let m = message::message_from_ptr(msg, true); let i: &IConnection = unsafe { mem::transmute(user_data) }; assert!(i.conn.get() == conn); i.pending_items.borrow_mut().push_back(ConnectionItem::MethodCall(m)); ffi::DBusHandlerResult::Handled } impl Connection { #[inline(always)] fn conn(&self) -> *mut ffi::DBusConnection { self.i.conn.get() } /// Creates a new D-Bus connection. pub fn get_private(bus: BusType) -> Result { let mut e = Error::empty(); let conn = unsafe { ffi::dbus_bus_get_private(bus, e.get_mut()) }; if conn == ptr::null_mut() { return Err(e) } let mut c = Connection { i: Box::new(IConnection { conn: Cell::new(conn), pending_items: RefCell::new(LinkedList::new()), watches: None, })}; /* No, we don't want our app to suddenly quit if dbus goes down */ unsafe { ffi::dbus_connection_set_exit_on_disconnect(conn, 0) }; assert!(unsafe { ffi::dbus_connection_add_filter(c.conn(), Some(filter_message_cb as ffi::DBusCallback), mem::transmute(&*c.i), None) } != 0); let iconn: *const IConnection = &*c.i; c.i.watches = Some(watch::WatchList::new(&c, Box::new(move |w| { let i: &IConnection = unsafe { mem::transmute(iconn) }; i.pending_items.borrow_mut().push_back(ConnectionItem::WatchFd(w)); }))); Ok(c) } /// Sends a message over the D-Bus and waits for a reply. /// This is usually used for method calls. pub fn send_with_reply_and_block(&self, msg: Message, timeout_ms: i32) -> Result { let mut e = Error::empty(); let response = unsafe { ffi::dbus_connection_send_with_reply_and_block(self.conn(), message::get_message_ptr(&msg), timeout_ms as libc::c_int, e.get_mut()) }; if response == ptr::null_mut() { return Err(e); } Ok(message::message_from_ptr(response, false)) } /// Sends a message over the D-Bus without waiting. Useful for sending signals and method call replies. pub fn send(&self, msg: Message) -> Result { let mut serial = 0u32; let r = unsafe { ffi::dbus_connection_send(self.conn(), message::get_message_ptr(&msg), &mut serial) }; if r == 0 { return Err(()); } unsafe { ffi::dbus_connection_flush(self.conn()) }; Ok(serial) } /// Get the connection's unique name. pub fn unique_name(&self) -> String { let c = unsafe { ffi::dbus_bus_get_unique_name(self.conn()) }; c_str_to_slice(&c).unwrap_or("").to_string() } /// Check if there are new incoming events pub fn iter(&self, timeout_ms: i32) -> ConnectionItems { ConnectionItems { c: self, timeout_ms: Some(timeout_ms), } } /// Register an object path. pub fn register_object_path(&self, path: &str) -> Result<(), Error> { let mut e = Error::empty(); let p = to_c_str(path); let vtable = ffi::DBusObjectPathVTable { unregister_function: None, message_function: Some(object_path_message_cb as ffi::DBusCallback), dbus_internal_pad1: None, dbus_internal_pad2: None, dbus_internal_pad3: None, dbus_internal_pad4: None, }; let r = unsafe { let user_data: *mut libc::c_void = mem::transmute(&*self.i); ffi::dbus_connection_try_register_object_path(self.conn(), p.as_ptr(), &vtable, user_data, e.get_mut()) }; if r == 0 { Err(e) } else { Ok(()) } } /// Unregister an object path. pub fn unregister_object_path(&self, path: &str) { let p = to_c_str(path); let r = unsafe { ffi::dbus_connection_unregister_object_path(self.conn(), p.as_ptr()) }; if r == 0 { panic!("Out of memory"); } } /// List registered object paths. pub fn list_registered_object_paths(&self, path: &str) -> Vec { let p = to_c_str(path); let mut clist: *mut *mut libc::c_char = ptr::null_mut(); let r = unsafe { ffi::dbus_connection_list_registered(self.conn(), p.as_ptr(), &mut clist) }; if r == 0 { panic!("Out of memory"); } let mut v = Vec::new(); let mut i = 0; loop { let s = unsafe { let citer = clist.offset(i); if *citer == ptr::null_mut() { break }; mem::transmute(citer) }; v.push(format!("{}", c_str_to_slice(s).unwrap())); i += 1; } unsafe { ffi::dbus_free_string_array(clist) }; v } /// Register a name. pub fn register_name(&self, name: &str, flags: u32) -> Result { let mut e = Error::empty(); let n = to_c_str(name); let r = unsafe { ffi::dbus_bus_request_name(self.conn(), n.as_ptr(), flags, e.get_mut()) }; if r == -1 { Err(e) } else { Ok(unsafe { mem::transmute(r) }) } } /// Release a name. pub fn release_name(&self, name: &str) -> Result { let mut e = Error::empty(); let n = to_c_str(name); let r = unsafe { ffi::dbus_bus_release_name(self.conn(), n.as_ptr(), e.get_mut()) }; if r == -1 { Err(e) } else { Ok(unsafe { mem::transmute(r) }) } } /// Add a match rule to match messages on the message bus. pub fn add_match(&self, rule: &str) -> Result<(), Error> { let mut e = Error::empty(); let n = to_c_str(rule); unsafe { ffi::dbus_bus_add_match(self.conn(), n.as_ptr(), e.get_mut()) }; if e.name().is_some() { Err(e) } else { Ok(()) } } /// Remove a match rule to match messages on the message bus. pub fn remove_match(&self, rule: &str) -> Result<(), Error> { let mut e = Error::empty(); let n = to_c_str(rule); unsafe { ffi::dbus_bus_remove_match(self.conn(), n.as_ptr(), e.get_mut()) }; if e.name().is_some() { Err(e) } else { Ok(()) } } /// Async I/O: Get an up-to-date list of file descriptors to watch. pub fn watch_fds(&self) -> Vec { self.i.watches.as_ref().unwrap().get_enabled_fds() } /// Async I/O: Call this function whenever you detected an event on the Fd, /// Flags are a set of WatchEvent bits. /// The returned iterator will return pending items only, never block for new events. pub fn watch_handle(&self, fd: RawFd, flags: libc::c_uint) -> ConnectionItems { self.i.watches.as_ref().unwrap().watch_handle(fd, flags); ConnectionItems { c: self, timeout_ms: None } } } impl Drop for Connection { fn drop(&mut self) { unsafe { ffi::dbus_connection_close(self.conn()); ffi::dbus_connection_unref(self.conn()); } } } impl std::fmt::Debug for Connection { fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { write!(f, "D-Bus Connection({})", self.unique_name()) } } #[cfg(test)] mod test { use super::{Connection, Message, BusType, MessageItem, ConnectionItem, NameFlag, RequestNameReply, ReleaseNameReply}; #[test] fn connection() { let c = Connection::get_private(BusType::Session).unwrap(); let n = c.unique_name(); assert!(n.starts_with(":1.")); println!("Connected to DBus, unique name: {}", n); } #[test] fn invalid_message() { let c = Connection::get_private(BusType::Session).unwrap(); let m = Message::new_method_call("foo.bar", "/", "foo.bar", "FooBar").unwrap(); let e = c.send_with_reply_and_block(m, 2000).err().unwrap(); assert!(e.name().unwrap() == "org.freedesktop.DBus.Error.ServiceUnknown"); } #[test] fn message_listnames() { let c = Connection::get_private(BusType::Session).unwrap(); let m = Message::method_call(&"org.freedesktop.DBus".into(), &"/".into(), &"org.freedesktop.DBus".into(), &"ListNames".into()); let r = c.send_with_reply_and_block(m, 2000).unwrap(); let reply = r.get_items(); println!("{:?}", reply); } #[test] fn message_namehasowner() { let c = Connection::get_private(BusType::Session).unwrap(); let mut m = Message::new_method_call("org.freedesktop.DBus", "/", "org.freedesktop.DBus", "NameHasOwner").unwrap(); m.append_items(&[MessageItem::Str("org.freedesktop.DBus".to_string())]); let r = c.send_with_reply_and_block(m, 2000).unwrap(); let reply = r.get_items(); println!("{:?}", reply); assert_eq!(reply, vec!(MessageItem::Bool(true))); } #[test] fn object_path() { use std::sync::mpsc; let (tx, rx) = mpsc::channel(); let thread = ::std::thread::spawn(move || { let c = Connection::get_private(BusType::Session).unwrap(); c.register_object_path("/hello").unwrap(); // println!("Waiting..."); tx.send(c.unique_name()).unwrap(); for n in c.iter(1000) { // println!("Found message... ({})", n); match n { ConnectionItem::MethodCall(ref m) => { let reply = Message::new_method_return(m).unwrap(); c.send(reply).unwrap(); break; } _ => {} } } c.unregister_object_path("/hello"); }); let c = Connection::get_private(BusType::Session).unwrap(); let n = rx.recv().unwrap(); let m = Message::new_method_call(&n, "/hello", "com.example.hello", "Hello").unwrap(); println!("Sending..."); let r = c.send_with_reply_and_block(m, 8000).unwrap(); let reply = r.get_items(); println!("{:?}", reply); thread.join().unwrap(); } #[test] fn register_name() { let c = Connection::get_private(BusType::Session).unwrap(); let n = format!("com.example.hello.test.register_name"); assert_eq!(c.register_name(&n, NameFlag::ReplaceExisting as u32).unwrap(), RequestNameReply::PrimaryOwner); assert_eq!(c.release_name(&n).unwrap(), ReleaseNameReply::Released); } #[test] fn signal() { let c = Connection::get_private(BusType::Session).unwrap(); let iface = "com.example.signaltest"; let mstr = format!("interface='{}',member='ThisIsASignal'", iface); c.add_match(&mstr).unwrap(); let m = Message::new_signal("/mysignal", iface, "ThisIsASignal").unwrap(); let uname = c.unique_name(); c.send(m).unwrap(); for n in c.iter(1000) { match n { ConnectionItem::Signal(s) => { let (_, p, i, m) = s.headers(); match (&*p.unwrap(), &*i.unwrap(), &*m.unwrap()) { ("/mysignal", "com.example.signaltest", "ThisIsASignal") => { assert_eq!(s.sender().unwrap(), uname); break; }, (_, _, _) => println!("Other signal: {:?}", s.headers()), } } _ => {}, } } c.remove_match(&mstr).unwrap(); } #[test] fn watch() { let c = Connection::get_private(BusType::Session).unwrap(); let mut d = c.watch_fds(); assert!(d.len() > 0); println!("Fds to watch: {:?}", d); for n in c.iter(1000) { match n { ConnectionItem::WatchFd(w) => { assert!(w.readable() || w.writable()); assert!(d.contains(&w)); d.retain(|x| *x != w); if d.len() == 0 { break }; } _ => {}, } } } } dbus-0.2.3/src/message.rs00006640001750000175000000103004126566777300013475 0ustar0000000000000000use std::borrow::Cow; use std::{fmt, mem, ptr}; use super::{ffi, Error, MessageType, TypeSig, libc, to_c_str, c_str_to_slice, init_dbus}; use super::{BusName, Path, Interface, Member, ErrorName}; use std::os::unix::io::{RawFd, AsRawFd}; use std::ffi::CStr; #[derive(Debug,Copy,Clone)] /// Errors that can happen when creating a MessageItem::Array. pub enum ArrayError { /// The array is empty. EmptyArray, /// The array is composed of different element types. DifferentElementTypes, } fn new_dbus_message_iter() -> ffi::DBusMessageIter { ffi::DBusMessageIter { dummy1: ptr::null_mut(), dummy2: ptr::null_mut(), dummy3: 0, dummy4: 0, dummy5: 0, dummy6: 0, dummy7: 0, dummy8: 0, dummy9: 0, dummy10: 0, dummy11: 0, pad1: 0, pad2: 0, pad3: ptr::null_mut(), } } /// An RAII wrapper around Fd to ensure that file descriptor is closed /// when the scope ends. #[derive(Debug, PartialEq, PartialOrd)] pub struct OwnedFd { fd: RawFd } impl OwnedFd { /// Create a new OwnedFd from a RawFd. pub fn new(fd: RawFd) -> OwnedFd { OwnedFd { fd: fd } } /// Convert an OwnedFD back into a RawFd. pub fn into_fd(self) -> RawFd { let s = self.fd; ::std::mem::forget(self); s } } impl Drop for OwnedFd { fn drop(&mut self) { unsafe { libc::close(self.fd); } } } impl Clone for OwnedFd { fn clone(&self) -> OwnedFd { OwnedFd::new(unsafe { libc::dup(self.fd) } ) // FIXME: handle errors } } impl AsRawFd for OwnedFd { fn as_raw_fd(&self) -> RawFd { self.fd } } /// MessageItem - used as parameters and return values from /// method calls, or as data added to a signal. #[derive(Debug, PartialEq, PartialOrd, Clone)] pub enum MessageItem { /// A D-Bus array requires all elements to be of the same type. /// All elements must match the TypeSig. Array(Vec, TypeSig<'static>), /// A D-Bus struct allows for values of different types. Struct(Vec), /// A D-Bus variant is a wrapper around another `MessageItem`, which /// can be of any type. Variant(Box), /// A D-Bus dictionary is an Array of DictEntry items. DictEntry(Box, Box), /// A D-Bus objectpath requires its content to be a valid objectpath, /// so this cannot be any string. ObjectPath(Path), /// A D-Bus String is zero terminated, so no \0 s in the String, please. /// (D-Bus strings are also - like Rust strings - required to be valid UTF-8.) Str(String), /// A D-Bus boolean type. Bool(bool), /// A D-Bus unsigned 8 bit type. Byte(u8), /// A D-Bus signed 16 bit type. Int16(i16), /// A D-Bus signed 32 bit type. Int32(i32), /// A D-Bus signed 64 bit type. Int64(i64), /// A D-Bus unsigned 16 bit type. UInt16(u16), /// A D-Bus unsigned 32 bit type. UInt32(u32), /// A D-Bus unsigned 64 bit type. UInt64(u64), /// A D-Bus IEEE-754 double-precision floating point type. Double(f64), /// D-Bus allows for sending file descriptors, which can be used to /// set up SHM, unix pipes, or other communication channels. UnixFd(OwnedFd), } fn iter_get_basic(i: &mut ffi::DBusMessageIter) -> i64 { let mut c: i64 = 0; unsafe { let p: *mut libc::c_void = mem::transmute(&mut c); ffi::dbus_message_iter_get_basic(i, p); } c } fn iter_get_f64(i: &mut ffi::DBusMessageIter) -> f64 { let mut c: f64 = 0.0; unsafe { let p: *mut libc::c_void = mem::transmute(&mut c); ffi::dbus_message_iter_get_basic(i, p); } c } fn iter_append_f64(i: &mut ffi::DBusMessageIter, v: f64) { unsafe { let p: *const libc::c_void = mem::transmute(&v); ffi::dbus_message_iter_append_basic(i, ffi::DBUS_TYPE_DOUBLE, p); } } fn iter_append_array(i: &mut ffi::DBusMessageIter, a: &[MessageItem], t: TypeSig<'static>) { let mut subiter = new_dbus_message_iter(); let atype = to_c_str(&t); assert!(unsafe { ffi::dbus_message_iter_open_container(i, ffi::DBUS_TYPE_ARRAY, atype.as_ptr(), &mut subiter) } != 0); for item in a.iter() { // assert!(item.type_sig() == t); item.iter_append(&mut subiter); } assert!(unsafe { ffi::dbus_message_iter_close_container(i, &mut subiter) } != 0); } fn iter_append_struct(i: &mut ffi::DBusMessageIter, a: &[MessageItem]) { let mut subiter = new_dbus_message_iter(); let res = unsafe { ffi::dbus_message_iter_open_container(i, ffi::DBUS_TYPE_STRUCT, ptr::null(), &mut subiter) }; assert!(res != 0); for item in a.iter() { item.iter_append(&mut subiter); } let res2 = unsafe { ffi::dbus_message_iter_close_container(i, &mut subiter) }; assert!(res2 != 0); } fn iter_append_variant(i: &mut ffi::DBusMessageIter, a: &MessageItem) { let mut subiter = new_dbus_message_iter(); let atype = to_c_str(&a.type_sig()); assert!(unsafe { ffi::dbus_message_iter_open_container(i, ffi::DBUS_TYPE_VARIANT, atype.as_ptr(), &mut subiter) } != 0); a.iter_append(&mut subiter); assert!(unsafe { ffi::dbus_message_iter_close_container(i, &mut subiter) } != 0); } fn iter_append_dict(i: &mut ffi::DBusMessageIter, k: &MessageItem, v: &MessageItem) { let mut subiter = new_dbus_message_iter(); assert!(unsafe { ffi::dbus_message_iter_open_container(i, ffi::DBUS_TYPE_DICT_ENTRY, ptr::null(), &mut subiter) } != 0); k.iter_append(&mut subiter); v.iter_append(&mut subiter); assert!(unsafe { ffi::dbus_message_iter_close_container(i, &mut subiter) } != 0); } impl MessageItem { /// Get the D-Bus ASCII type-code for this MessageItem. pub fn type_sig(&self) -> TypeSig<'static> { match self { // TODO: Can we make use of the ffi constants here instead of duplicating them? &MessageItem::Str(_) => Cow::Borrowed("s"), &MessageItem::Bool(_) => Cow::Borrowed("b"), &MessageItem::Byte(_) => Cow::Borrowed("y"), &MessageItem::Int16(_) => Cow::Borrowed("n"), &MessageItem::Int32(_) => Cow::Borrowed("i"), &MessageItem::Int64(_) => Cow::Borrowed("x"), &MessageItem::UInt16(_) => Cow::Borrowed("q"), &MessageItem::UInt32(_) => Cow::Borrowed("u"), &MessageItem::UInt64(_) => Cow::Borrowed("t"), &MessageItem::Double(_) => Cow::Borrowed("d"), &MessageItem::Array(_, ref s) => Cow::Owned(format!("a{}", s)), &MessageItem::Struct(ref s) => Cow::Owned(format!("({})", s.iter().fold(String::new(), |s, i| s + &*i.type_sig()))), &MessageItem::Variant(_) => Cow::Borrowed("v"), &MessageItem::DictEntry(ref k, ref v) => Cow::Owned(format!("{{{}{}}}", k.type_sig(), v.type_sig())), &MessageItem::ObjectPath(_) => Cow::Borrowed("o"), &MessageItem::UnixFd(_) => Cow::Borrowed("h"), } } /// Get the integer value for this MessageItem's type-code. pub fn array_type(&self) -> i32 { let s = match self { &MessageItem::Str(_) => ffi::DBUS_TYPE_STRING, &MessageItem::Bool(_) => ffi::DBUS_TYPE_BOOLEAN, &MessageItem::Byte(_) => ffi::DBUS_TYPE_BYTE, &MessageItem::Int16(_) => ffi::DBUS_TYPE_INT16, &MessageItem::Int32(_) => ffi::DBUS_TYPE_INT32, &MessageItem::Int64(_) => ffi::DBUS_TYPE_INT64, &MessageItem::UInt16(_) => ffi::DBUS_TYPE_UINT16, &MessageItem::UInt32(_) => ffi::DBUS_TYPE_UINT32, &MessageItem::UInt64(_) => ffi::DBUS_TYPE_UINT64, &MessageItem::Double(_) => ffi::DBUS_TYPE_DOUBLE, &MessageItem::Array(_,_) => ffi::DBUS_TYPE_ARRAY, &MessageItem::Struct(_) => ffi::DBUS_TYPE_STRUCT, &MessageItem::Variant(_) => ffi::DBUS_TYPE_VARIANT, &MessageItem::DictEntry(_,_) => ffi::DBUS_TYPE_DICT_ENTRY, &MessageItem::ObjectPath(_) => ffi::DBUS_TYPE_OBJECT_PATH, &MessageItem::UnixFd(_) => ffi::DBUS_TYPE_UNIX_FD, }; s as i32 } /// Creates a (String, Variant) dictionary from an iterator with Result passthrough (an Err will abort and return that Err) pub fn from_dict>>(i: I) -> Result { let mut v = Vec::new(); for r in i { let (s, vv) = try!(r); v.push((s.into(), Box::new(vv).into()).into()); } Ok(MessageItem::Array(v, Cow::Borrowed("{sv}"))) } /// Creates an MessageItem::Array from a list of MessageItems. /// /// Note: This requires `v` to be non-empty. See also /// `MessageItem::from(&[T])`, which can handle empty arrays as well. pub fn new_array(v: Vec) -> Result { if v.len() == 0 { return Err(ArrayError::EmptyArray); } let t = v[0].type_sig(); for i in &v { if i.type_sig() != t { return Err(ArrayError::DifferentElementTypes); } } Ok(MessageItem::Array(v, t)) } fn new_array2(i: I) -> MessageItem where D: Into, D: Default, I: Iterator { let v: Vec = i.map(|ii| ii.into()).collect(); let t = if v.len() == 0 { D::default().into().type_sig() } else { v[0].type_sig() }; MessageItem::Array(v, t) } fn new_array3<'b, D: 'b, I>(i: I) -> MessageItem where D: Into + Default + Clone, I: Iterator { MessageItem::new_array2(i.map(|ii| ii.clone())) } fn from_iter(i: &mut ffi::DBusMessageIter) -> Vec { let mut v = Vec::new(); loop { let t = unsafe { ffi::dbus_message_iter_get_arg_type(i) }; match t { ffi::DBUS_TYPE_INVALID => { return v }, ffi::DBUS_TYPE_DICT_ENTRY => { let mut subiter = new_dbus_message_iter(); unsafe { ffi::dbus_message_iter_recurse(i, &mut subiter) }; let a = MessageItem::from_iter(&mut subiter); if a.len() != 2 { panic!("D-Bus dict entry error"); } let mut a = a.into_iter(); let key = Box::new(a.next().unwrap()); let value = Box::new(a.next().unwrap()); v.push(MessageItem::DictEntry(key, value)); } ffi::DBUS_TYPE_VARIANT => { let mut subiter = new_dbus_message_iter(); unsafe { ffi::dbus_message_iter_recurse(i, &mut subiter) }; let a = MessageItem::from_iter(&mut subiter); if a.len() != 1 { panic!("D-Bus variant error"); } v.push(MessageItem::Variant(Box::new(a.into_iter().next().unwrap()))); } ffi::DBUS_TYPE_ARRAY => { let mut subiter = new_dbus_message_iter(); unsafe { ffi::dbus_message_iter_recurse(i, &mut subiter) }; let a = MessageItem::from_iter(&mut subiter); let t = if a.len() > 0 { a[0].type_sig() } else { let c = unsafe { ffi::dbus_message_iter_get_signature(&mut subiter) }; let s = c_str_to_slice(&(c as *const libc::c_char)).unwrap().to_string(); unsafe { ffi::dbus_free(c as *mut libc::c_void) }; Cow::Owned(s) }; v.push(MessageItem::Array(a, t)); }, ffi::DBUS_TYPE_STRUCT => { let mut subiter = new_dbus_message_iter(); unsafe { ffi::dbus_message_iter_recurse(i, &mut subiter) }; v.push(MessageItem::Struct(MessageItem::from_iter(&mut subiter))); }, ffi::DBUS_TYPE_STRING => { let mut c: *const libc::c_char = ptr::null(); unsafe { let p: *mut libc::c_void = mem::transmute(&mut c); ffi::dbus_message_iter_get_basic(i, p); }; v.push(MessageItem::Str(c_str_to_slice(&c).expect("D-Bus string error").to_string())); }, ffi::DBUS_TYPE_OBJECT_PATH => { let mut c: *const libc::c_char = ptr::null(); unsafe { let p: *mut libc::c_void = mem::transmute(&mut c); ffi::dbus_message_iter_get_basic(i, p); }; let o = Path::new(c_str_to_slice(&c).expect("D-Bus object path error")).ok().expect("D-Bus object path error"); v.push(MessageItem::ObjectPath(o)); }, ffi::DBUS_TYPE_UNIX_FD => v.push(MessageItem::UnixFd(OwnedFd::new(iter_get_basic(i) as libc::c_int))), ffi::DBUS_TYPE_BOOLEAN => v.push(MessageItem::Bool((iter_get_basic(i) as u32) != 0)), ffi::DBUS_TYPE_BYTE => v.push(MessageItem::Byte(iter_get_basic(i) as u8)), ffi::DBUS_TYPE_INT16 => v.push(MessageItem::Int16(iter_get_basic(i) as i16)), ffi::DBUS_TYPE_INT32 => v.push(MessageItem::Int32(iter_get_basic(i) as i32)), ffi::DBUS_TYPE_INT64 => v.push(MessageItem::Int64(iter_get_basic(i) as i64)), ffi::DBUS_TYPE_UINT16 => v.push(MessageItem::UInt16(iter_get_basic(i) as u16)), ffi::DBUS_TYPE_UINT32 => v.push(MessageItem::UInt32(iter_get_basic(i) as u32)), ffi::DBUS_TYPE_UINT64 => v.push(MessageItem::UInt64(iter_get_basic(i) as u64)), ffi::DBUS_TYPE_DOUBLE => v.push(MessageItem::Double(iter_get_f64(i))), _ => { panic!("D-Bus unsupported message type {} ({})", t, t as u8 as char); } } unsafe { ffi::dbus_message_iter_next(i) }; } } fn iter_append_basic(&self, i: &mut ffi::DBusMessageIter, v: i64) { let t = self.array_type(); unsafe { let p: *const libc::c_void = mem::transmute(&v); ffi::dbus_message_iter_append_basic(i, t as libc::c_int, p); } } fn iter_append(&self, i: &mut ffi::DBusMessageIter) { match self { &MessageItem::Str(ref s) => unsafe { let c = to_c_str(s); let p = mem::transmute(&c); ffi::dbus_message_iter_append_basic(i, ffi::DBUS_TYPE_STRING, p); }, &MessageItem::Bool(b) => self.iter_append_basic(i, b as i64), &MessageItem::Byte(b) => self.iter_append_basic(i, b as i64), &MessageItem::Int16(b) => self.iter_append_basic(i, b as i64), &MessageItem::Int32(b) => self.iter_append_basic(i, b as i64), &MessageItem::Int64(b) => self.iter_append_basic(i, b as i64), &MessageItem::UInt16(b) => self.iter_append_basic(i, b as i64), &MessageItem::UInt32(b) => self.iter_append_basic(i, b as i64), &MessageItem::UInt64(b) => self.iter_append_basic(i, b as i64), &MessageItem::UnixFd(ref b) => self.iter_append_basic(i, b.as_raw_fd() as i64), &MessageItem::Double(b) => iter_append_f64(i, b), &MessageItem::Array(ref b, ref t) => iter_append_array(i, &**b, t.clone()), &MessageItem::Struct(ref v) => iter_append_struct(i, &**v), &MessageItem::Variant(ref b) => iter_append_variant(i, &**b), &MessageItem::DictEntry(ref k, ref v) => iter_append_dict(i, &**k, &**v), &MessageItem::ObjectPath(ref s) => unsafe { let c: *const libc::c_char = s.as_ref().as_ptr(); let p = mem::transmute(&c); ffi::dbus_message_iter_append_basic(i, ffi::DBUS_TYPE_OBJECT_PATH, p); } } } fn copy_to_iter(i: &mut ffi::DBusMessageIter, v: &[MessageItem]) { for item in v.iter() { item.iter_append(i); } } /// Conveniently get the inner value of a `MessageItem` /// /// # Example /// ``` /// use dbus::MessageItem; /// let m: MessageItem = 5i64.into(); /// let s: i64 = m.inner().unwrap(); /// assert_eq!(s, 5i64); /// ``` pub fn inner<'a, T: FromMessageItem<'a>>(&'a self) -> Result { T::from(self) } } macro_rules! msgitem_convert { ($t: ty, $s: ident) => { impl From<$t> for MessageItem { fn from(i: $t) -> MessageItem { MessageItem::$s(i) } } impl<'a> FromMessageItem<'a> for $t { fn from(i: &'a MessageItem) -> Result<$t,()> { if let &MessageItem::$s(ref b) = i { Ok(*b) } else { Err(()) } } } } } msgitem_convert!(u8, Byte); msgitem_convert!(u64, UInt64); msgitem_convert!(u32, UInt32); msgitem_convert!(u16, UInt16); msgitem_convert!(i16, Int16); msgitem_convert!(i32, Int32); msgitem_convert!(i64, Int64); msgitem_convert!(f64, Double); msgitem_convert!(bool, Bool); /// Create a `MessageItem::Array`. impl<'a, T> From<&'a [T]> for MessageItem where T: Into + Clone + Default { fn from(i: &'a [T]) -> MessageItem { MessageItem::new_array3(i.iter()) } } impl<'a> From<&'a str> for MessageItem { fn from(i: &str) -> MessageItem { MessageItem::Str(i.to_string()) } } impl From for MessageItem { fn from(i: String) -> MessageItem { MessageItem::Str(i) } } impl From for MessageItem { fn from(i: Path) -> MessageItem { MessageItem::ObjectPath(i) } } impl From for MessageItem { fn from(i: OwnedFd) -> MessageItem { MessageItem::UnixFd(i) } } /// Create a `MessageItem::Variant` impl From> for MessageItem { fn from(i: Box) -> MessageItem { MessageItem::Variant(i) } } /// Create a `MessageItem::DictEntry` impl From<(MessageItem, MessageItem)> for MessageItem { fn from(i: (MessageItem, MessageItem)) -> MessageItem { MessageItem::DictEntry(Box::new(i.0), Box::new(i.1)) } } /// Helper trait for `MessageItem::inner()` pub trait FromMessageItem<'a> :Sized { /// Allows converting from a MessageItem into the type it contains. fn from(i: &'a MessageItem) -> Result; } impl<'a> FromMessageItem<'a> for &'a str { fn from(i: &'a MessageItem) -> Result<&'a str,()> { match i { &MessageItem::Str(ref b) => Ok(&b), &MessageItem::ObjectPath(ref b) => Ok(&b), _ => Err(()), } } } impl<'a> FromMessageItem<'a> for &'a String { fn from(i: &'a MessageItem) -> Result<&'a String,()> { if let &MessageItem::Str(ref b) = i { Ok(&b) } else { Err(()) } } } impl<'a> FromMessageItem<'a> for &'a Path { fn from(i: &'a MessageItem) -> Result<&'a Path,()> { if let &MessageItem::ObjectPath(ref b) = i { Ok(&b) } else { Err(()) } } } impl<'a> FromMessageItem<'a> for &'a MessageItem { fn from(i: &'a MessageItem) -> Result<&'a MessageItem,()> { if let &MessageItem::Variant(ref b) = i { Ok(&**b) } else { Err(()) } } } impl<'a> FromMessageItem<'a> for &'a Vec { fn from(i: &'a MessageItem) -> Result<&'a Vec,()> { match i { &MessageItem::Array(ref b, _) => Ok(&b), &MessageItem::Struct(ref b) => Ok(&b), _ => Err(()), } } } impl<'a> FromMessageItem<'a> for &'a [MessageItem] { fn from(i: &'a MessageItem) -> Result<&'a [MessageItem],()> { i.inner::<&Vec>().map(|s| &**s) } } impl<'a> FromMessageItem<'a> for &'a OwnedFd { fn from(i: &'a MessageItem) -> Result<&'a OwnedFd,()> { if let &MessageItem::UnixFd(ref b) = i { Ok(b) } else { Err(()) } } } impl<'a> FromMessageItem<'a> for (&'a MessageItem, &'a MessageItem) { fn from(i: &'a MessageItem) -> Result<(&'a MessageItem, &'a MessageItem),()> { if let &MessageItem::DictEntry(ref k, ref v) = i { Ok((&**k, &**v)) } else { Err(()) } } } /// A D-Bus message. A message contains some headers (e g sender and destination address) /// and a list of MessageItems. pub struct Message { msg: *mut ffi::DBusMessage, } unsafe impl Send for Message {} impl Message { /// Creates a new method call message. pub fn new_method_call(destination: D, path: P, iface: I, method: M) -> Result where D: Into, P: Into, I: Into, M: Into { init_dbus(); let (d, p, i, m) = (destination.into(), path.into(), iface.into(), method.into()); let ptr = unsafe { ffi::dbus_message_new_method_call(d.as_ref().as_ptr(), p.as_ref().as_ptr(), i.as_ref().as_ptr(), m.as_ref().as_ptr()) }; if ptr == ptr::null_mut() { Err("D-Bus error: dbus_message_new_method_call failed".into()) } else { Ok(Message { msg: ptr}) } } /// Creates a new method call message. pub fn method_call(destination: &BusName, path: &Path, iface: &Interface, name: &Member) -> Message { init_dbus(); let ptr = unsafe { ffi::dbus_message_new_method_call(destination.as_ref().as_ptr(), path.as_ref().as_ptr(), iface.as_ref().as_ptr(), name.as_ref().as_ptr()) }; if ptr == ptr::null_mut() { panic!("D-Bus error: dbus_message_new_signal failed") } Message { msg: ptr} } /// Creates a new signal message. pub fn new_signal(path: P, iface: I, name: M) -> Result where P: Into>, I: Into>, M: Into> { init_dbus(); let p = try!(Path::new(path)); let i = try!(Interface::new(iface)); let m = try!(Member::new(name)); let ptr = unsafe { ffi::dbus_message_new_signal(p.as_ref().as_ptr(), i.as_ref().as_ptr(), m.as_ref().as_ptr()) }; if ptr == ptr::null_mut() { Err("D-Bus error: dbus_message_new_signal failed".into()) } else { Ok(Message { msg: ptr}) } } /// Creates a new signal message. pub fn signal(path: &Path, iface: &Interface, name: &Member) -> Message { init_dbus(); let ptr = unsafe { ffi::dbus_message_new_signal(path.as_ref().as_ptr(), iface.as_ref().as_ptr(), name.as_ref().as_ptr()) }; if ptr == ptr::null_mut() { panic!("D-Bus error: dbus_message_new_signal failed") } Message { msg: ptr} } /// Creates a method reply for this method call. pub fn new_method_return(m: &Message) -> Option { let ptr = unsafe { ffi::dbus_message_new_method_return(m.msg) }; if ptr == ptr::null_mut() { None } else { Some(Message { msg: ptr} ) } } /// Creates a method return (reply) for this method call. pub fn method_return(&self) -> Message { let ptr = unsafe { ffi::dbus_message_new_method_return(self.msg) }; if ptr == ptr::null_mut() { panic!("D-Bus error: dbus_message_new_method_return failed") } Message {msg: ptr} } /// The old way to create a new error reply pub fn new_error(m: &Message, error_name: &str, error_message: &str) -> Option { let (en, em) = (to_c_str(error_name), to_c_str(error_message)); let ptr = unsafe { ffi::dbus_message_new_error(m.msg, en.as_ptr(), em.as_ptr()) }; if ptr == ptr::null_mut() { None } else { Some(Message { msg: ptr} ) } } /// Creates a new error reply pub fn error(&self, error_name: &ErrorName, error_message: &CStr) -> Message { let ptr = unsafe { ffi::dbus_message_new_error(self.msg, error_name.as_ref().as_ptr(), error_message.as_ptr()) }; if ptr == ptr::null_mut() { panic!("D-Bus error: dbus_message_new_error failed") } Message { msg: ptr} } /// Get the MessageItems that make up the message. pub fn get_items(&self) -> Vec { let mut i = new_dbus_message_iter(); match unsafe { ffi::dbus_message_iter_init(self.msg, &mut i) } { 0 => Vec::new(), _ => MessageItem::from_iter(&mut i) } } /// Get the D-Bus serial of a message, if one was specified. pub fn get_serial(&self) -> u32 { unsafe { ffi::dbus_message_get_serial(self.msg) } } /// Get the serial of the message this message is a reply to, if present. pub fn get_reply_serial(&self) -> Option { let s = unsafe { ffi::dbus_message_get_reply_serial(self.msg) }; if s == 0 { None } else { Some(s) } } /// Add one or more MessageItems to this Message. pub fn append_items(&mut self, v: &[MessageItem]) { let mut i = new_dbus_message_iter(); unsafe { ffi::dbus_message_iter_init_append(self.msg, &mut i) }; MessageItem::copy_to_iter(&mut i, v); } /// Appends one MessageItem to a message. /// Use in builder style: e g `m.method_return().append(7i32)` pub fn append>(self, v: I) -> Self { let mut i = new_dbus_message_iter(); unsafe { ffi::dbus_message_iter_init_append(self.msg, &mut i) }; MessageItem::copy_to_iter(&mut i, &[v.into()]); self } /// Gets the MessageType of the Message. pub fn msg_type(&self) -> MessageType { unsafe { mem::transmute(ffi::dbus_message_get_type(self.msg)) } } /// Gets the name of the connection that originated this message. pub fn sender(&self) -> Option { let s = unsafe { ffi::dbus_message_get_sender(self.msg) }; c_str_to_slice(&s).map(|s| s.to_string()) } /// Returns a tuple of (Message type, Path, Interface, Member) of the current message. pub fn headers(&self) -> (MessageType, Option, Option, Option) { let p = unsafe { ffi::dbus_message_get_path(self.msg) }; let i = unsafe { ffi::dbus_message_get_interface(self.msg) }; let m = unsafe { ffi::dbus_message_get_member(self.msg) }; (self.msg_type(), c_str_to_slice(&p).map(|s| s.to_string()), c_str_to_slice(&i).map(|s| s.to_string()), c_str_to_slice(&m).map(|s| s.to_string())) } /// Gets the object path this Message is being sent to. pub fn path(&self) -> Option { let p = unsafe { ffi::dbus_message_get_path(self.msg) }; c_str_to_slice(&p).map(|s| s.into()) } /// Gets the interface this Message is being sent to. pub fn interface(&self) -> Option { let p = unsafe { ffi::dbus_message_get_interface(self.msg) }; c_str_to_slice(&p).map(|s| s.into()) } /// Gets the interface member being called. pub fn member(&self) -> Option { let p = unsafe { ffi::dbus_message_get_member(self.msg) }; c_str_to_slice(&p).map(|s| s.into()) } /// When the remote end returns an error, the message itself is /// correct but its contents is an error. This method will /// transform such an error to a D-Bus Error or otherwise return /// the original message. pub fn as_result(&mut self) -> Result<&mut Message, Error> { let mut e = Error::empty(); if unsafe { ffi::dbus_set_error_from_message(e.get_mut(), self.msg) } != 0 { Err(e) } else { Ok(self) } } } impl Drop for Message { fn drop(&mut self) { unsafe { ffi::dbus_message_unref(self.msg); } } } impl fmt::Debug for Message { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "{:?}", self.headers()) } } pub fn message_from_ptr(ptr: *mut ffi::DBusMessage, add_ref: bool) -> Message { if add_ref { unsafe { ffi::dbus_message_ref(ptr) }; } Message { msg: ptr } } pub fn get_message_ptr<'a>(m: &Message) -> *mut ffi::DBusMessage { m.msg } // For purpose of testing the library only. #[cfg(test)] pub fn message_set_serial(m: &mut Message, s: u32) { unsafe { ffi::dbus_message_set_serial(m.msg, s) }; } #[cfg(test)] mod test { extern crate tempdir; use super::super::{Connection, ConnectionItem, Message, BusType, MessageItem, OwnedFd, libc, Path}; #[test] fn unix_fd() { use std::io::prelude::*; use std::io::SeekFrom; use std::fs::OpenOptions; use std::os::unix::io::AsRawFd; let c = Connection::get_private(BusType::Session).unwrap(); c.register_object_path("/hello").unwrap(); let mut m = Message::new_method_call(&c.unique_name(), "/hello", "com.example.hello", "Hello").unwrap(); let tempdir = tempdir::TempDir::new("dbus-rs-test").unwrap(); let mut filename = tempdir.path().to_path_buf(); filename.push("test"); println!("Creating file {:?}", filename); let mut file = OpenOptions::new().create(true).read(true).write(true).open(&filename).unwrap(); file.write_all(b"z").unwrap(); file.seek(SeekFrom::Start(0)).unwrap(); let ofd = OwnedFd::new(file.as_raw_fd()); m.append_items(&[MessageItem::UnixFd(ofd.clone())]); println!("Sending {:?}", m.get_items()); c.send(m).unwrap(); for n in c.iter(1000) { match n { ConnectionItem::MethodCall(m) => { if let Some(&MessageItem::UnixFd(ref z)) = m.get_items().get(0) { println!("Got {:?}", m.get_items()); let mut q: libc::c_char = 100; assert_eq!(1, unsafe { libc::read(z.as_raw_fd(), &mut q as *mut i8 as *mut libc::c_void, 1) }); assert_eq!(q, 'z' as libc::c_char); break; } else { panic!("Expected UnixFd, got {:?}", m.get_items()); } } _ => println!("Got {:?}", n), } } } #[test] fn message_types() { let c = Connection::get_private(BusType::Session).unwrap(); c.register_object_path("/hello").unwrap(); let mut m = Message::new_method_call(&c.unique_name(), "/hello", "com.example.hello", "Hello").unwrap(); m.append_items(&[ 2000u16.into(), MessageItem::new_array(vec!(129u8.into())).unwrap(), ["Hello", "world"][..].into(), 987654321u64.into(), (-1i32).into(), format!("Hello world").into(), (-3.14f64).into(), MessageItem::Struct(vec!(256i16.into())), Path::new("/some/path").unwrap().into(), MessageItem::new_array(vec!((123543u32.into(), true.into()).into())).unwrap() ]); let sending = format!("{:?}", m.get_items()); println!("Sending {}", sending); c.send(m).unwrap(); for n in c.iter(1000) { match n { ConnectionItem::MethodCall(m) => { let receiving = format!("{:?}", m.get_items()); println!("Receiving {}", receiving); assert_eq!(sending, receiving); break; } _ => println!("Got {:?}", n), } } } #[test] fn dict_of_dicts() { use std::collections::BTreeMap; let officeactions: BTreeMap<&'static str, MessageItem> = BTreeMap::new(); let mut officethings = BTreeMap::new(); officethings.insert("pencil", 2u16.into()); officethings.insert("paper", 5u16.into()); let mut homethings = BTreeMap::new(); homethings.insert("apple", 11u16.into()); let mut homeifaces = BTreeMap::new(); homeifaces.insert("getThings", homethings); let mut officeifaces = BTreeMap::new(); officeifaces.insert("getThings", officethings); officeifaces.insert("getActions", officeactions); let mut paths = BTreeMap::new(); paths.insert("/hello/office", officeifaces); paths.insert("/hello/home", homeifaces); println!("Original treemap: {:?}", paths); let m = MessageItem::new_array(paths.iter().map( |(path, ifaces)| (MessageItem::ObjectPath(Path::new(*path).unwrap()), MessageItem::new_array(ifaces.iter().map( |(iface, props)| (iface.to_string().into(), MessageItem::from_dict::<(),_>(props.iter().map( |(name, value)| Ok((name.to_string(), value.clone())) )).unwrap() ).into() ).collect()).unwrap() ).into() ).collect()).unwrap(); println!("As MessageItem: {:?}", m); assert_eq!(m.type_sig(), "a{oa{sa{sv}}}"); let c = Connection::get_private(BusType::Session).unwrap(); c.register_object_path("/hello").unwrap(); let mut msg = Message::new_method_call(&c.unique_name(), "/hello", "org.freedesktop.DBusObjectManager", "GetManagedObjects").unwrap(); msg.append_items(&[m]); let sending = format!("{:?}", msg.get_items()); println!("Sending {}", sending); c.send(msg).unwrap(); for n in c.iter(1000) { match n { ConnectionItem::MethodCall(m) => { let receiving = format!("{:?}", m.get_items()); println!("Receiving {}", receiving); assert_eq!(sending, receiving); break; } _ => println!("Got {:?}", n), } } } #[test] fn issue24() { let c = Connection::get_private(BusType::Session).unwrap(); let mut m = Message::new_method_call("org.test.rust", "/", "org.test.rust", "Test").unwrap(); let a = MessageItem::from("test".to_string()); let b = MessageItem::from("test".to_string()); let foo = MessageItem::Struct(vec!(a, b)); let bar = foo.clone(); let args = [MessageItem::new_array(vec!(foo, bar)).unwrap()]; println!("{:?}", args); m.append_items(&args); c.send(m).unwrap(); } } dbus-0.2.3/src/methoddisp.rs00006640001750000175000000120310126566777300014210 0ustar0000000000000000#![allow(dead_code)] use {MessageItem, Message, MessageType, Connection, ConnectionItem, Error, ErrorName}; use {Signature, Member, Path}; use Interface as IfaceName; use std::cell::RefCell; use std::sync::{Arc, Mutex}; use std::collections::BTreeMap; use std::marker::PhantomData; use std::ffi::CString; use std::fmt; type ArcMap = BTreeMap, Arc>; #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] /// A D-Bus Argument. pub struct Argument(Option, Signature); impl Argument { /// Create a new Argument. pub fn new(name: Option, sig: Signature) -> Argument { Argument(name, sig) } fn introspect(&self, indent: &str, dir: &str) -> String { let n = self.0.as_ref().map(|n| format!("name=\"{}\" ", n)).unwrap_or("".into()); format!("{}\n", indent, n, self.1, dir) } fn introspect_all(args: &[Argument], indent: &str, dir: &str) -> String { args.iter().fold("".to_string(), |aa, az| format!("{}{}", aa, az.introspect(indent, dir))) } } // Doesn't work, conflicting impls // impl> From for Argument impl From for Argument { fn from(t: Signature) -> Argument { Argument(None, t) } } impl<'a> From<&'a str> for Argument { fn from(t: &str) -> Argument { Argument(None, t.into()) } } impl, S: Into> From<(N, S)> for Argument { fn from((n, s): (N, S)) -> Argument { Argument(Some(n.into()), s.into()) } } #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] /// A D-Bus Method Error. pub struct MethodErr(ErrorName, String); impl MethodErr { /// Create an Invalid Args MethodErr. pub fn invalid_arg(a: &T) -> MethodErr { ("org.freedesktop.DBus.Error.InvalidArgs", format!("Invalid argument {:?}", a)).into() } /// Create a MethodErr that there are not enough arguments given. pub fn no_arg() -> MethodErr { ("org.freedesktop.DBus.Error.InvalidArgs", "Not enough arguments").into() } /// Create a MethodErr that the method failed in the way specified. pub fn failed(a: &T) -> MethodErr { ("org.freedesktop.DBus.Error.Failed", a.to_string()).into() } /// Create a MethodErr that the Interface was unknown. pub fn no_interface(a: &T) -> MethodErr { ("org.freedesktop.DBus.Error.UnknownInterface", format!("Unknown interface {}", a)).into() } /// Create a MethodErr that the Property was unknown. pub fn no_property(a: &T) -> MethodErr { ("org.freedesktop.DBus.Error.UnknownProperty", format!("Unknown property {}", a)).into() } /// Create a MethodErr that the Property was read-only. pub fn ro_property(a: &T) -> MethodErr { ("org.freedesktop.DBus.Error.PropertyReadOnly", format!("Property {} is read only", a)).into() } } impl, M: Into> From<(T, M)> for MethodErr { fn from((t, m): (T, M)) -> MethodErr { MethodErr(t.into(), m.into()) } } /// Result containing the Messages returned from the Method, or a MethodErr. pub type MethodResult = Result, MethodErr>; /// A MethodType that wraps an Fn function pub struct MethodFn<'a>(Box>, &Tree>) -> MethodResult + 'a>); /// A MethodType that wraps an FnMut function. Calling this recursively will cause a refcell panic. pub struct MethodFnMut<'a>(Box>, &Tree>) -> MethodResult + 'a>>); /// A MethodType that wraps an Fn+Send+Sync function, so it can be called from several threads in parallel. pub struct MethodSync(Box, &Tree) -> MethodResult + Send + Sync + 'static>); impl<'a> fmt::Debug for MethodFn<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "") } } impl<'a> fmt::Debug for MethodFnMut<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "") } } impl fmt::Debug for MethodSync { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "") } } /// A helper trait used internally to make the tree generic over MethodFn, MethodFnMut and MethodSync. pub trait MethodType: Sized { fn call_method(&self, m: &Message, o: &ObjectPath, i: &Tree) -> MethodResult; fn box_method(h: H) -> Self where H: Fn(&Message, &ObjectPath, &Tree) -> MethodResult + Send + Sync + 'static; } impl<'a> MethodType for MethodFn<'a> { fn call_method(&self, m: &Message, o: &ObjectPath>, i: &Tree>) -> MethodResult { self.0(m, o, i) } fn box_method(h: H) -> Self where H: Fn(&Message, &ObjectPath>, &Tree>) -> MethodResult + Send + Sync + 'static { MethodFn(Box::new(h)) } } impl MethodType for MethodSync { fn call_method(&self, m: &Message, o: &ObjectPath, i: &Tree) -> MethodResult { self.0(m, o, i) } fn box_method(h: H) -> Self where H: Fn(&Message, &ObjectPath, &Tree) -> MethodResult + Send + Sync + 'static { MethodSync(Box::new(h)) } } impl<'a> MethodType for MethodFnMut<'a> { fn call_method(&self, m: &Message, o: &ObjectPath>, i: &Tree>) -> MethodResult { let mut z = self.0.borrow_mut(); (&mut *z)(m, o, i) } fn box_method(h: H) -> Self where H: Fn(&Message, &ObjectPath>, &Tree>) -> MethodResult + Send + Sync + 'static { MethodFnMut(Box::new(RefCell::new(h))) } } #[derive(Debug)] /// A D-Bus Method. pub struct Method { cb: M, name: Arc, i_args: Vec, o_args: Vec, anns: BTreeMap, } impl Method { /// Builder method that adds an "in" Argument to this Method. pub fn in_arg>(mut self, a: A) -> Self { self.i_args.push(a.into()); self } /// Builder method that adds multiple "in" Arguments to this Method. pub fn in_args, A: IntoIterator>(mut self, a: A) -> Self { self.i_args.extend(a.into_iter().map(|b| b.into())); self } /// Builder method that adds an "out" Argument to this Method. pub fn out_arg>(mut self, a: A) -> Self { self.o_args.push(a.into()); self } /// Builder method that adds multiple "out" Arguments to this Method. pub fn out_args, A: IntoIterator>(mut self, a: A) -> Self { self.o_args.extend(a.into_iter().map(|b| b.into())); self } /// Add an annotation to the method. pub fn annotate, V: Into>(mut self, name: N, value: V) -> Self { self.anns.insert(name.into(), value.into()); self } /// Add an annotation that this entity is deprecated. pub fn deprecated(self) -> Self { self.annotate("org.freedesktop.DBus.Deprecated", "true") } } impl Method { /// Call the Method. pub fn call(&self, m: &Message, o: &ObjectPath, i: &Tree) -> MethodResult { self.cb.call_method(m, o, i) } fn new(n: Member, cb: M) -> Self { Method { name: Arc::new(n), i_args: vec!(), o_args: vec!(), anns: BTreeMap::new(), cb: cb } } } #[derive(Debug)] /// Represents a D-Bus interface. pub struct Interface { name: Arc, methods: ArcMap>, signals: ArcMap, properties: ArcMap>, anns: BTreeMap, } impl Interface { /// Adds a method to the interface. pub fn add_m(mut self, m: Method) -> Self { self.methods.insert(m.name.clone(), Arc::new(m)); self } /// Adds a signal to the interface. pub fn add_s(mut self, s: Signal) -> Self { self.signals.insert(s.name.clone(), Arc::new(s)); self } /// Adds a signal to the interface. Returns a reference to the signal /// (which you can use to emit the signal, once it belongs to an object path). pub fn add_s_ref(&mut self, s: Signal) -> Arc { let s = Arc::new(s); self.signals.insert(s.name.clone(), s.clone()); s } /// Adds a property to the interface. pub fn add_p(mut self, p: Property) -> Self { self.properties.insert(p.name.clone(), Arc::new(p)); self } /// Adds a property to the interface. Returns a reference to the property /// (which you can use to get and set the current value of the property). pub fn add_p_ref(&mut self, p: Property) -> Arc> { let p = Arc::new(p); self.properties.insert(p.name.clone(), p.clone()); p } /// Add an annotation to this Inteface. pub fn annotate, V: Into>(mut self, name: N, value: V) -> Self { self.anns.insert(name.into(), value.into()); self } /// Add an annotation that this entity is deprecated. pub fn deprecated(self) -> Self { self.annotate("org.freedesktop.DBus.Deprecated", "true") } fn new(t: IfaceName) -> Interface { Interface { name: Arc::new(t), methods: BTreeMap::new(), signals: BTreeMap::new(), properties: BTreeMap::new(), anns: BTreeMap::new() } } } #[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Debug)] /// Enumerates the different signaling behaviors a Property can have /// to being changed. pub enum EmitsChangedSignal { /// The Property emits a signal that includes the new value. True, /// The Property emits a signal that does not include the new value. Invalidates, /// The Property cannot be changed. Const, /// The Property does not emit a signal when changed. False, } #[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Debug)] /// The possible access characteristics a Property can have. pub enum Access { /// The Property can only be read (Get). Read, /// The Property can be read or written. ReadWrite, /// The Property can only be written (Set). Write, } impl Access { fn introspect(&self) -> &'static str { match self { &Access::Read => "read", &Access::ReadWrite => "readwrite", &Access::Write => "write", } } } #[derive(Debug)] /// A D-Bus Property. pub struct Property { name: Arc, value: Mutex, emits: EmitsChangedSignal, rw: Access, set_cb: Option, owner: Mutex, Arc)>>, anns: BTreeMap, } impl Property { /// Gets the value of the Property. pub fn get_value(&self) -> MessageItem { self.value.lock().unwrap().clone() } /// Gets the signal (if any) associated with the Property. pub fn get_signal(&self) -> Option { self.owner.lock().unwrap().as_ref().map(|&(ref p, ref i)| { Message::signal(&p, &"org.freedesktop.DBus.Properties".into(), &"PropertiesChanged".into()) .append(String::from(&***i)) }) } /// Returns error if "emits" is "Const", and the property is in a /// tree. Returns messages to be sent over a connection, this /// could be the PropertiesChanged signal. pub fn set_value(&self, m: MessageItem) -> Result,()> { let ss = match self.emits { EmitsChangedSignal::False => None, EmitsChangedSignal::Const => if self.get_signal().is_some() { return Err(()) } else { None }, EmitsChangedSignal::True => self.get_signal().map(|mut s| { let m = MessageItem::Array(vec!(((&*self.name).clone().into(), Box::new(m.clone()).into()).into()), "{sv}".into()); s.append_items(&[m]); s }), EmitsChangedSignal::Invalidates => self.get_signal().map(|mut s| { let m2 = [(&*self.name).clone()][..].into(); s.append_items(&[MessageItem::Array(vec!(), "{sv}".into()), m2]); s }), }; *self.value.lock().unwrap() = m; Ok(ss.map(|s| vec!(s)).unwrap_or(vec!())) } /// Builder method that allows setting the Property's signal /// behavior when changed. pub fn emits_changed(mut self, e: EmitsChangedSignal) -> Self { self.emits = e; assert!(self.rw == Access::Read || self.emits != EmitsChangedSignal::Const); self } /// Builder method that allows setting the Property as readable, /// writable, or both. pub fn access(mut self, e: Access) -> Self { self.rw = e; assert!(self.rw == Access::Read || self.emits != EmitsChangedSignal::Const); self } /// Helper method to check accessibility before getting a value. pub fn remote_get(&self, _: &Message) -> Result { // TODO: We should be able to call a user-defined callback here instead... if self.rw == Access::Write { return Err(MethodErr::failed(&format!("Property {} is write only", &self.name))) } Ok(self.get_value()) } /// Helper method to verify and extract a MessageItem from a Set message pub fn verify_remote_set(&self, m: &Message) -> Result { let items = m.get_items(); let s: &MessageItem = try!(items.get(2).ok_or_else(|| MethodErr::no_arg()) .and_then(|i| i.inner().map_err(|_| MethodErr::invalid_arg(&i)))); if self.rw == Access::Read { Err(MethodErr::ro_property(&self.name)) } else if s.type_sig() != self.value.lock().unwrap().type_sig() { Err(MethodErr::failed(&format!("Property {} cannot change type to {}", &self.name, s.type_sig()))) } else { Ok(s.clone()) } } fn remote_set(&self, m: &Message, o: &ObjectPath, t: &Tree) -> Result, MethodErr> { if let Some(ref cb) = self.set_cb { cb.call_method(m, o, t) } else { let s = try!(self.verify_remote_set(m)); self.set_value(s).map_err(|_| MethodErr::ro_property(&self.name)) } } /// Add an annotation to this Property. pub fn annotate, V: Into>(mut self, name: N, value: V) -> Self { self.anns.insert(name.into(), value.into()); self } /// Add an annotation that this entity is deprecated. pub fn deprecated(self) -> Self { self.annotate("org.freedesktop.DBus.Deprecated", "true") } fn new(s: String, i: MessageItem) -> Property { Property { name: Arc::new(s), emits: EmitsChangedSignal::True, rw: Access::Read, value: Mutex::new(i), owner: Mutex::new(None), anns: BTreeMap::new(), set_cb: None } } } impl Property { /// Sets a callback to be called when a "Set" call is coming in from the remote side. /// Might change to something more ergonomic. /// For multi-thread use. pub fn on_set(mut self, m: H) -> Self where H: Fn(&Message, &ObjectPath, &Tree) -> MethodResult + Send + Sync + 'static { self.set_cb = Some(MethodSync::box_method(m)); self } } impl<'a> Property> { /// Sets a callback to be called when a "Set" call is coming in from the remote side. /// Might change to something more ergonomic. /// For single-thread use. pub fn on_set(mut self, m: H) -> Self where H: Fn(&Message, &ObjectPath>, &Tree>) -> MethodResult { self.set_cb = Some(MethodFn(Box::new(m))); self } } impl<'a> Property> { /// Sets a callback to be called when a "Set" call is coming in from the remote side. /// Might change to something more ergonomic. /// For single-thread use. pub fn on_set(mut self, m: H) -> Self where H: FnMut(&Message, &ObjectPath>, &Tree>) -> MethodResult { self.set_cb = Some(MethodFnMut(Box::new(RefCell::new(m)))); self } } #[derive(Debug)] /// A D-Bus Signal. pub struct Signal { name: Arc, arguments: Vec, owner: Mutex, Arc)>>, anns: BTreeMap, } impl Signal { /// Returns a message which emits the signal when sent. /// Panics if the signal is not inserted in an object path. pub fn emit(&self, items: &[MessageItem]) -> Message { let mut m = { let lock = self.owner.lock().unwrap(); let &(ref p, ref i) = lock.as_ref().unwrap(); Message::signal(p, i, &self.name) }; m.append_items(items); m } /// Builder method that adds an Argument to the Signal. pub fn arg>(mut self, a: A) -> Self { self.arguments.push(a.into()); self } /// Builder method that adds multiple "rguments to the Signel. pub fn args, A: IntoIterator>(mut self, a: A) -> Self { self.arguments.extend(a.into_iter().map(|b| b.into())); self } /// Add an annotation to this Signal. pub fn annotate, V: Into>(mut self, name: N, value: V) -> Self { self.anns.insert(name.into(), value.into()); self } /// Add an annotation that this entity is deprecated. pub fn deprecated(self) -> Self { self.annotate("org.freedesktop.DBus.Deprecated", "true") } } fn introspect_anns(anns: &BTreeMap, indent: &str) -> String { anns.iter().fold("".into(), |aa, (ak, av)| { format!("{}{}\n", aa, indent, ak, av) }) } fn introspect_map (String, String)> (h: &ArcMap, name: &str, indent: &str, func: C) -> String { h.iter().fold("".into(), |a, (k, v)| { let (params, contents) = func(v); format!("{}{}<{} name=\"{}\"{}{}>\n", a, indent, name, &**k, params, if contents.len() > 0 { format!(">\n{}{} { name: Arc, ifaces: ArcMap>, } impl ObjectPath { fn new(p: Path) -> ObjectPath { ObjectPath { name: Arc::new(p), ifaces: BTreeMap::new() } } fn prop_set(&self, m: &Message, o: &ObjectPath, t: &Tree) -> MethodResult { let items = m.get_items(); let iface_name: &String = try!(items.get(0).ok_or_else(|| MethodErr::no_arg()) .and_then(|i| i.inner().map_err(|_| MethodErr::invalid_arg(&i)))); let prop_name: &String = try!(items.get(1).ok_or_else(|| MethodErr::no_arg()) .and_then(|i| i.inner().map_err(|_| MethodErr::invalid_arg(&i)))); let iface: &Interface = try!(IfaceName::new(&**iface_name).map_err(|e| MethodErr::invalid_arg(&e)) .and_then(|i| self.ifaces.get(&i).ok_or_else(|| MethodErr::no_interface(&i)))); let prop: &Property = try!(iface.properties.get(prop_name).ok_or_else(|| MethodErr::no_property(prop_name))); let mut r = try!(prop.remote_set(m, o, t)); r.push(m.method_return()); Ok(r) } fn prop_get(&self, m: &Message) -> MethodResult { let items = m.get_items(); let iface_name: &String = try!(items.get(0).ok_or_else(|| MethodErr::no_arg()) .and_then(|i| i.inner().map_err(|_| MethodErr::invalid_arg(&i)))); let prop_name: &String = try!(items.get(1).ok_or_else(|| MethodErr::no_arg()) .and_then(|i| i.inner().map_err(|_| MethodErr::invalid_arg(&i)))); let iface: &Interface = try!(IfaceName::new(&**iface_name).map_err(|e| MethodErr::invalid_arg(&e)) .and_then(|i| self.ifaces.get(&i).ok_or_else(|| MethodErr::no_interface(&i)))); let prop: &Property = try!(iface.properties.get(prop_name).ok_or_else(|| MethodErr::no_property(prop_name))); let r = try!(prop.remote_get(m)); Ok(vec!(m.method_return().append(Box::new(r)))) } fn prop_get_all(&self, m: &Message) -> MethodResult { let items = m.get_items(); let iface_name: &String = try!(items.get(0).ok_or_else(|| MethodErr::no_arg()) .and_then(|i| i.inner().map_err(|_| MethodErr::invalid_arg(&i)))); let iface: &Interface = try!(IfaceName::new(&**iface_name).map_err(|e| MethodErr::invalid_arg(&e)) .and_then(|i| self.ifaces.get(&i).ok_or_else(|| MethodErr::no_interface(&i)))); let mut q: Vec = vec!(); for v in iface.properties.values() { q.push(((&**v.name).into(), try!(v.remote_get(m))).into()) } Ok(vec!(m.method_return().append(MessageItem::Array(q, "{sv}".into())))) } fn add_property_handler(&mut self) { let ifname = IfaceName::from("org.freedesktop.DBus.Properties"); if self.ifaces.contains_key(&ifname) { return }; let f: Factory = Factory(PhantomData); let i = Interface::::new(ifname) .add_m(f.method_sync("Get", |m,o,_| o.prop_get(m) ) .in_arg(("interface_name", "s")).in_arg(("property_name", "s")).out_arg(("value", "v"))) .add_m(f.method_sync("GetAll", |m,o,_| o.prop_get_all(m)) .in_arg(("interface_name", "s")).out_arg(("props", "a{sv}"))) .add_m(f.method_sync("Set", |m,o,t| o.prop_set(m, o, t)) .in_args(vec!(("interface_name", "s"), ("property_name", "s"), ("value", "v")))); self.ifaces.insert(i.name.clone(), Arc::new(i)); } /// Add an Interface to this Object Path. pub fn add(mut self, p: Interface) -> Self { for s in p.signals.values() { *s.owner.lock().unwrap() = Some((self.name.clone(), p.name.clone())) }; for s in p.properties.values() { *s.owner.lock().unwrap() = Some((self.name.clone(), p.name.clone())) }; if !p.properties.is_empty() { self.add_property_handler(); } self.ifaces.insert(p.name.clone(), Arc::new(p)); self } /// Adds introspection support for this object path. pub fn introspectable(self) -> Self { let ifname: IfaceName = "org.freedesktop.DBus.Introspectable".into(); if self.ifaces.contains_key(&ifname) { return self }; let f: Factory = Factory(PhantomData); self.add(Interface::::new(ifname) .add_m(f.method_sync("Introspect", |m,o,t| Ok(vec!(m.method_return().append(o.introspect(t))))) .out_arg(("xml_data", "s")))) } fn handle(&self, m: &Message, t: &Tree) -> MethodResult { let i = try!(m.interface().and_then(|i| self.ifaces.get(&i)).ok_or( ("org.freedesktop.DBus.Error.UnknownInterface", "Unknown interface"))); let me = try!(m.member().and_then(|me| i.methods.get(&me)).ok_or( ("org.freedesktop.DBus.Error.UnknownMethod", "Unknown method"))); me.call(m, &self, t) } fn introspect(&self, tree: &Tree) -> String { let ifacestr = introspect_map(&self.ifaces, "interface", " ", |iv| (format!(""), format!("{}{}{}{}", introspect_map(&iv.methods, "method", " ", |m| (format!(""), format!("{}{}{}", Argument::introspect_all(&m.i_args, " ", " direction=\"in\""), Argument::introspect_all(&m.o_args, " ", " direction=\"out\""), introspect_anns(&m.anns, " ") ))), introspect_map(&iv.properties, "property", " ", |p| ( format!(" type=\"{}\" access=\"{}\"", p.get_value().type_sig(), p.rw.introspect()), introspect_anns(&p.anns, " ") )), introspect_map(&iv.signals, "signal", " ", |s| (format!(""), format!("{}{}", Argument::introspect_all(&s.arguments, " ", ""), introspect_anns(&s.anns, " ") ))), introspect_anns(&iv.anns, " ") )) ); let olen = self.name.len()+1; let childstr = tree.children(&self, true).iter().fold("".to_string(), |na, n| format!("{} \n", na, &n.name[olen..]) ); let nodestr = format!(r##" {}{}"##, self.name, ifacestr, childstr); nodestr } fn get_managed_objects(&self, t: &Tree) -> MessageItem { let mut paths = t.children(&self, false); paths.push(&self); MessageItem::Array( paths.iter().map(|p| ((&*p.name).clone().into(), MessageItem::Array( p.ifaces.values().map(|i| ((&**i.name).into(), MessageItem::Array( i.properties.values().map(|pp| ((&**pp.name).into(), Box::new(pp.get_value() ).into()).into()).collect(), "{sv}".into() )).into()).collect(), "{sa{sv}}".into() )).into()).collect(), "{oa{sa{sv}}}".into() ) } /// Adds ObjectManager support for this object path. /// /// It is not possible to add/remove interfaces while the object path belongs to a tree, /// hence no InterfacesAdded / InterfacesRemoved signals are sent. pub fn object_manager(self) -> Self { let ifname: IfaceName = "org.freedesktop.DBus.ObjectManager".into(); if self.ifaces.contains_key(&ifname) { return self }; let f: Factory = Factory(PhantomData); self.add(Interface::::new(ifname) .add_m(f.method_sync("GetManagedObjects", |m,o,t| Ok(vec!(m.method_return().append(o.get_managed_objects(t))))) .out_arg("a{oa{sa{sv}}}"))) } } /// An iterator adapter that handles incoming method calls. /// /// Method calls that match an object path in the tree are handled and consumed by this /// iterator. Other messages are passed through. pub struct TreeServer<'a, I, M: 'a> { iter: I, conn: &'a Connection, tree: &'a Tree, } impl<'a, I: Iterator, M: 'a + MethodType> Iterator for TreeServer<'a, I, M> { type Item = ConnectionItem; fn next(&mut self) -> Option { loop { let n = self.iter.next(); if let &Some(ConnectionItem::MethodCall(ref msg)) = &n { if let Some(v) = self.tree.handle(&msg) { // Probably the wisest is to ignore any send errors here - // maybe the remote has disconnected during our processing. for m in v { let _ = self.conn.send(m); }; continue; } } return n; } } } /// A collection of object paths. #[derive(Debug)] pub struct Tree { paths: ArcMap> } impl Tree { fn children(&self, o: &ObjectPath, direct_only: bool) -> Vec<&ObjectPath> { let parent: &str = &o.name; let plen = parent.len()+1; self.paths.values().filter_map(|v| { let k: &str = &v.name; if !k.starts_with(parent) || k.len() <= plen || &k[plen-1..plen] != "/" {None} else { let child = &k[plen..]; if direct_only && child.contains("/") {None} else {Some(&**v)} } }).collect() } /// Add a Object Path to this Tree. pub fn add(mut self, p: ObjectPath) -> Self { self.paths.insert(p.name.clone(), Arc::new(p)); self } /// Registers or unregisters all object paths in the tree. pub fn set_registered(&self, c: &Connection, b: bool) -> Result<(), Error> { let mut regd_paths = Vec::new(); for p in self.paths.keys() { if b { match c.register_object_path(p) { Ok(()) => regd_paths.push(p.clone()), Err(e) => { while let Some(rp) = regd_paths.pop() { c.unregister_object_path(&rp); } return Err(e) } } } else { c.unregister_object_path(p); } } Ok(()) } /// Handles a message. Will return None in case the object path was not /// found, or otherwise a list of messages to be sent back. pub fn handle(&self, m: &Message) -> Option> { if m.msg_type() != MessageType::MethodCall { None } else { m.path().and_then(|p| self.paths.get(&p).map(|s| s.handle(m, &self) .unwrap_or_else(|e| vec!(m.error(&e.0, &CString::new(e.1).unwrap()))))) } } /// This method takes an `ConnectionItem` iterator (you get it from `Connection::iter()`) /// and handles all matching items. Non-matching items (e g signals) are passed through. pub fn run<'a, I: Iterator>(&'a self, c: &'a Connection, i: I) -> TreeServer<'a, I, M> { TreeServer { iter: i, tree: &self, conn: c } } } /// The factory is used to create object paths, interfaces, methods etc. /// /// There are three factories: /// /// **Fn** - all methods are `Fn()`. /// /// **FnMut** - all methods are `FnMut()`. This means they can mutate their environment, /// which has the side effect that if you call it recursively, it will RefCell panic. /// /// **Sync** - all methods are `Fn() + Send + Sync + 'static`. This means that the methods /// can be called from different threads in parallel. #[derive(Debug, Copy, Clone)] pub struct Factory(PhantomData); impl<'a> Factory> { /// Creates a new factory for single-thread use. pub fn new_fn() -> Self { Factory(PhantomData) } /// Creates a new method for single-thread use. pub fn method<'b, H: 'b, T>(&self, t: T, handler: H) -> Method> where H: Fn(&Message, &ObjectPath>, &Tree>) -> MethodResult, T: Into { Method::new(t.into(), MethodFn(Box::new(handler))) } /// Creates a new property for single-thread use. pub fn property<'b, T: Into, I: Into>(&self, t: T, i: I) -> Property> { Property::new(t.into(), i.into()) } /// Creates a new interface for single-thread use. pub fn interface<'b, T: Into>(&self, t: T) -> Interface> { Interface::new(t.into()) } /// Creates a new tree for single-thread use. pub fn tree<'b>(&self) -> Tree> { Tree { paths: BTreeMap::new() }} /// Creates a new object path for single-thread use. pub fn object_path<'b, T: Into>(&self, t: T) -> ObjectPath> { ObjectPath::new(t.into()) } } impl<'a> Factory> { /// Creates a new factory for single-thread + mutable fns use. pub fn new_fnmut() -> Self { Factory(PhantomData) } /// Creates a new method for single-thread use. /// This method can mutate its environment, so it will panic in case /// it is called recursively. pub fn method<'b, H: 'b, T>(&self, t: T, handler: H) -> Method> where H: FnMut(&Message, &ObjectPath>, &Tree>) -> MethodResult, T: Into { Method::new(t.into(), MethodFnMut(Box::new(RefCell::new(handler)))) } /// Creates a new mutable property for single-thread use. pub fn property<'b, T: Into, I: Into>(&self, t: T, i: I) -> Property> { Property::new(t.into(), i.into()) } /// Creates a new mutable interface for single-thread use. pub fn interface<'b, T: Into>(&self, t: T) -> Interface> { Interface::new(t.into()) } /// Creates a new mutable tree for single-thread use. pub fn tree<'b>(&self) -> Tree> { Tree { paths: BTreeMap::new() }} /// Creates a new mutable object path for single-thread use. pub fn object_path<'b, T: Into>(&self, t: T) -> ObjectPath> { ObjectPath::new(t.into()) } } impl Factory { /// Creates a new factory for multi-thread use. /// Trees created will be able to Send and Sync, i e, /// it can handle several messages in parallel. pub fn new_sync() -> Self { Factory(PhantomData) } /// Creates a new method for multi-thread use. /// This puts bounds on the callback to enable it to be called from several threads /// in parallel. pub fn method(&self, t: T, handler: H) -> Method where H: Fn(&Message, &ObjectPath, &Tree) -> MethodResult + Send + Sync + 'static, T: Into { Method::new(t.into(), MethodSync(Box::new(handler))) } /// Creates a new property for multi-threaded use. pub fn property, I: Into>(&self, t: T, i: I) -> Property { Property::new(t.into(), i.into()) } /// Creates a new interface for multi-threaded use. pub fn interface>(&self, t: T) -> Interface { Interface::new(t.into()) } /// Creates a new tree for multi-threaded use. pub fn tree(&self) -> Tree { Tree { paths: BTreeMap::new() }} /// Creates a new object path for multi-threaded use. pub fn object_path>(&self, t: T) -> ObjectPath { ObjectPath::new(t.into()) } } impl Factory { /// Create a Signal. pub fn signal>(&self, t: T) -> Signal { Signal { name: Arc::new(t.into()), arguments: vec!(), owner: Mutex::new(None), anns: BTreeMap::new() } } } impl Factory { /// Creates a new method with bounds enough to be used in all trees. pub fn method_sync(&self, t: T, handler: H) -> Method where H: Fn(&Message, &ObjectPath, &Tree) -> MethodResult + Send + Sync + 'static, T: Into { Method::new(t.into(), M::box_method(handler)) } } #[test] fn factory_test() { let f = Factory::new_fn(); f.interface("com.example.hello").deprecated(); let b = 5i32; f.method("GetSomething", |m,_,_| Ok(vec!({ let mut z = m.method_return(); z.append_items(&[b.into()]); z}))); let t = f.tree().add(f.object_path("/funghi").add(f.interface("a.b.c").deprecated())); let t = t.add(f.object_path("/ab")).add(f.object_path("/a")).add(f.object_path("/a/b/c")).add(f.object_path("/a/b")); assert_eq!(t.children(t.paths.get(&Path::from("/a")).unwrap(), true).len(), 1); } #[test] fn test_sync_prop() { let f = Factory::new_sync(); let mut i = f.interface("com.example.echo"); let p = i.add_p_ref(f.property("EchoCount", 7i32)); let tree1 = Arc::new(f.tree().add(f.object_path("/echo").introspectable().add(i))); let tree2 = tree1.clone(); println!("{:#?}", tree2); ::std::thread::spawn(move || { let r = p.set_value(9i32.into()).unwrap(); let signal = r.get(0).unwrap(); assert_eq!(signal.msg_type(), MessageType::Signal); let mut msg = Message::new_method_call("com.example.echoserver", "/echo", "com.example", "dummy").unwrap(); super::message::message_set_serial(&mut msg, 3); tree2.handle(&msg); }); let mut msg = Message::new_method_call("com.example.echoserver", "/echo", "org.freedesktop.DBus.Properties", "Get").unwrap() .append("com.example.echo").append("EchoCount"); super::message::message_set_serial(&mut msg, 4); let r = tree1.handle(&msg).unwrap(); let r1 = r.get(0).unwrap(); let ii = r1.get_items(); let vv: &MessageItem = ii.get(0).unwrap().inner().unwrap(); let v: i32 = vv.inner().unwrap(); assert!(v == 7 || v == 9); } /* This test case no longer works, for unknown reason, see https://github.com/diwic/dbus-rs/issues/27 #[test] fn prop_lifetime_simple() { let f = Factory::new_fnmut(); let count; let mut i = f.interface("com.example.dbus.rs"); count = i.add_p_ref(f.property("changes", 0i32)); let _setme = i.add_p_ref(f.property("setme", 0u8).access(Access::ReadWrite).on_set(|_,_,_| { let v: i32 = count.get_value().inner().unwrap(); count.set_value((v + 1).into()).unwrap(); Ok(vec!()) })); } */ /* This test case no longer works, for unknown reason, see https://github.com/diwic/dbus-rs/issues/27 #[test] fn prop_server() { let (count, setme): (_, RefCell>>>); setme = RefCell::new(None); let f = Factory::new_fnmut(); let mut i = f.interface("com.example.dbus.rs"); count = i.add_p_ref(f.property("changes", 0i32)); *setme.borrow_mut() = Some(i.add_p_ref(f.property("setme", 0u8).access(Access::ReadWrite).on_set(|m,_,_| { let ss2 = setme.borrow(); let ss = ss2.as_ref().unwrap(); let s = try!(ss.verify_remote_set(m)); let r = try!(ss.set_value(s).map_err(|_| MethodErr::ro_property(&ss.name))); let v: i32 = count.get_value().inner().unwrap(); count.set_value((v + 1).into()).unwrap(); Ok(r) }))); let tree = f.tree().add(f.object_path("/example").add(i)); let mut msg = Message::new_method_call("com.example.dbus.rs", "/example", "org.freedesktop.DBus.Properties", "Get").unwrap() .append("com.example.dbus.rs").append("changes"); super::message::message_set_serial(&mut msg, 10); let r = tree.handle(&msg).unwrap(); let r1 = r.get(0).unwrap(); let ii = r1.get_items(); let vv: &MessageItem = ii.get(0).unwrap().inner().unwrap(); let v: i32 = vv.inner().unwrap(); assert_eq!(v, 0); // Read-only let mut msg = Message::new_method_call("com.example.dbus.rs", "/example", "org.freedesktop.DBus.Properties", "Set").unwrap() .append("com.example.dbus.rs").append("changes").append(5i32); super::message::message_set_serial(&mut msg, 20); let mut r = tree.handle(&msg).unwrap(); assert!(r.get_mut(0).unwrap().as_result().is_err()); // Wrong type let mut msg = Message::new_method_call("com.example.dbus.rs", "/example", "org.freedesktop.DBus.Properties", "Set").unwrap() .append("com.example.dbus.rs").append("setme").append(8i32); super::message::message_set_serial(&mut msg, 30); let mut r = tree.handle(&msg).unwrap(); assert!(r.get_mut(0).unwrap().as_result().is_err()); // Correct! let mut msg = Message::new_method_call("com.example.dbus.rs", "/example", "org.freedesktop.DBus.Properties", "Set").unwrap() .append("com.example.dbus.rs").append("setme").append(Box::new(9u8.into())); super::message::message_set_serial(&mut msg, 30); let mut r = tree.handle(&msg).unwrap(); println!("{:?}", r[0].as_result()); let c: i32 = count.get_value().inner().unwrap(); assert_eq!(c, 1); } */ #[test] fn test_introspection() { let f = Factory::new_sync(); let t = f.object_path("/echo").introspectable() .add(f.interface("com.example.echo") .add_m(f.method("Echo", |_,_,_| unimplemented!()).in_arg(("request", "s")).out_arg(("reply", "s"))) .add_p(f.property("EchoCount", 7i32)) .add_s(f.signal("Echoed").arg(("data", "s"))) ); let actual_result = t.introspect(&f.tree().add(f.object_path("/echo/subpath"))); println!("\n=== Introspection XML start ===\n{}\n=== Introspection XML end ===", actual_result); let expected_result = r##" "##; assert_eq!(expected_result, actual_result); } dbus-0.2.3/src/objpath.rs00006640001750000175000000050225126531174600013470 0ustar0000000000000000use super::{Connection, Message, MessageItem, Error, TypeSig}; use std::collections::BTreeMap; use std::rc::Rc; use std::cell::{Cell, RefCell}; use std::borrow::Cow; /// a Method has a list of Arguments. pub struct Argument<'a> { name: &'a str, sig: TypeSig<'a>, } impl<'a> Argument<'a> { /// Create a new Argument. pub fn new>>(name: &'a str, sig: T) -> Argument<'a> { Argument { name: name, sig: sig.into() } } } struct Annotation { name: String, value: String, } struct ISignal<'a> { args: Vec>, anns: Vec, } /// Declares that an Interface can send this signal pub struct Signal<'a> { name: String, i: ISignal<'a>, } impl<'a> Signal<'a> { /// Create a new Signal. pub fn new(name: N, args: Vec>) -> Signal<'a> { Signal { name: name.to_string(), i: ISignal { args: args, anns: vec![] } } } /// Add an Annotation to the Signal. pub fn annotate(&mut self, name: N, value: V) { self.i.anns.push(Annotation { name: name.to_string(), value: value.to_string() }); } } /// A method returns either a list of MessageItems, or an error - the tuple /// represents the name and message of the Error. pub type MethodResult = Result, (&'static str, String)>; /// Contains the retrieved MessageItem or an error tuple containing the /// name and message of the error. pub type PropertyGetResult = Result; /// Contains () or an error tuple containing the name and message of /// the error. pub type PropertySetResult = Result<(), (&'static str, String)>; /// A boxed closure for dynamic dispatch. It is called when the method is /// called by a remote application. pub type MethodHandler<'a> = Box MethodResult + 'a>; struct IMethod<'a> { in_args: Vec>, out_args: Vec>, cb: Rc>>, anns: Vec, } /// a method that can be called from another application pub struct Method<'a> { name: String, i: IMethod<'a> } impl<'a> Method<'a> { /// Create a new Method. pub fn new(name: N, in_args: Vec>, out_args: Vec>, cb: MethodHandler<'a>) -> Method<'a> { Method { name: name.to_string(), i: IMethod { in_args: in_args, out_args: out_args, cb: Rc::new(RefCell::new(cb)), anns: vec![] } } } /// Add an Annotation to the Method. pub fn annotate(&mut self, name: N, value: V) { self.i.anns.push(Annotation { name: name.to_string(), value: value.to_string() }); } } /// A read/write property handler. pub trait PropertyRWHandler { /// Get a property's value. fn get(&self) -> PropertyGetResult; /// Set a property's value. fn set(&self, &MessageItem) -> PropertySetResult; } /// A read-only property handler. pub trait PropertyROHandler { /// Get a property's value. fn get(&self) -> PropertyGetResult; } /// A write-only property handler. pub trait PropertyWOHandler { /// Set a property's value. fn set(&self, &MessageItem) -> PropertySetResult; } /// Types of access to a Property. pub enum PropertyAccess<'a> { RO(Box), RW(Box), WO(Box), } struct IProperty<'a> { sig: TypeSig<'a>, access: PropertyAccess<'a>, anns: Vec, } /// Properties that a remote application can get/set. pub struct Property<'a> { name: String, i: IProperty<'a> } impl<'a> Property<'a> { fn new(name: N, sig: TypeSig<'a>, a: PropertyAccess<'a>) -> Property<'a> { Property { name: name.to_string(), i: IProperty { sig: sig, access: a, anns: vec![] } } } /// Creates a new read-only Property pub fn new_ro(name: N, sig: TypeSig<'a>, h: Box) -> Property<'a> { Property::new(name, sig, PropertyAccess::RO(h)) } /// Creates a new read-write Property pub fn new_rw(name: N, sig: TypeSig<'a>, h: Box) -> Property<'a> { Property::new(name, sig, PropertyAccess::RW(h)) } /// Creates a new write-only Property pub fn new_wo(name: N, sig: TypeSig<'a>, h: Box) -> Property<'a> { Property::new(name, sig, PropertyAccess::WO(h)) } /// Add an annotation to the Property pub fn annotate(&mut self, name: N, value: V) { self.i.anns.push(Annotation { name: name.to_string(), value: value.to_string() }) } } /// Interfaces can contain Methods, Properties, and Signals. pub struct Interface<'a> { methods: BTreeMap>, properties: BTreeMap>, signals: BTreeMap>, } impl<'a> Interface<'a> { /// Create a new Interface. pub fn new(m: Vec>, p: Vec>, s: Vec>) -> Interface<'a> { Interface { methods: m.into_iter().map(|m| (m.name, m.i)).collect(), properties: p.into_iter().map(|p| (p.name, p.i)).collect(), signals: s.into_iter().map(|s| (s.name, s.i)).collect(), } } } struct IObjectPath<'a> { conn: &'a Connection, path: String, registered: Cell, interfaces: RefCell>>, } /// Represents a D-Bus object path, which can in turn contain Interfaces. pub struct ObjectPath<'a> { // We need extra references for the introspector and property handlers, hence this extra boxing i: Rc>, } impl<'a> Drop for ObjectPath<'a> { fn drop(&mut self) { let _ = self.i.set_registered(false); self.i.interfaces.borrow_mut().clear(); // This should remove all the other references to i } } fn introspect_args(args: &Vec, indent: &str, dir: &str) -> String { args.iter().fold("".to_string(), |aa, az| { format!("{}{}\n", aa, indent, az.name, az.sig, dir) }) } fn introspect_anns(anns: &Vec, indent: &str) -> String { anns.iter().fold("".to_string(), |aa, az| { format!("{}{}\n", aa, indent, az.name, az.value) }) } fn introspect_map (String, String)> (h: &BTreeMap, name: &str, indent: &str, func: C) -> String { h.iter().fold("".to_string(), |a, (k, v)| { let (params, contents) = func(v); format!("{}{}<{} name=\"{}\"{}{}>\n", a, indent, name, k, params, if contents.len() > 0 { format!(">\n{}{} IObjectPath<'a> { fn set_registered(&self, register: bool) -> Result<(), Error> { if register == self.registered.get() { return Ok(()) }; if register { try!(self.conn.register_object_path(&self.path)); } else { self.conn.unregister_object_path(&self.path); } self.registered.set(register); Ok(()) } fn introspect(&self, _: &mut Message) -> MethodResult { let ifacestr = introspect_map(&self.interfaces.borrow(), "interface", " ", |iv| (format!(""), format!("{}{}{}", introspect_map(&iv.methods, "method", " ", |m| (format!(""), format!("{}{}{}", introspect_args(&m.in_args, " ", " direction=\"in\""), introspect_args(&m.out_args, " ", " direction=\"out\""), introspect_anns(&m.anns, " ") ))), introspect_map(&iv.properties, "property", " ", |p| ( format!(" type=\"{}\" access=\"{}\"", p.sig, match p.access { PropertyAccess::RO(_) => "read", PropertyAccess::RW(_) => "readwrite", PropertyAccess::WO(_) => "write", }), introspect_anns(&p.anns, " ") )), introspect_map(&iv.signals, "signal", " ", |s| (format!(""), format!("{}{}", introspect_args(&s.args, " ", ""), introspect_anns(&s.anns, " ") ))) )) ); let childstr = self.conn.list_registered_object_paths(&self.path).iter().fold("".to_string(), |na, n| format!(r##"{} "##, na, n) ); let nodestr = format!(r##" {}{}"##, self.path, ifacestr, childstr); Ok(vec!(MessageItem::Str(nodestr))) } fn property_get(&self, msg: &mut Message) -> MethodResult { let items = msg.get_items(); let iface_name = try!(parse_msg_str(items.get(0))); let prop_name = try!(parse_msg_str(items.get(1))); let is = self.interfaces.borrow(); let i = try!(is.get(iface_name).ok_or_else(|| ("org.freedesktop.DBus.Error.UnknownInterface", format!("Unknown interface {}", iface_name)))); let p = try!(i.properties.get(prop_name).ok_or_else(|| ("org.freedesktop.DBus.Error.UnknownProperty", format!("Unknown property {}", prop_name)))); let v = try!(match p.access { PropertyAccess::RO(ref cb) => cb.get(), PropertyAccess::RW(ref cb) => cb.get(), PropertyAccess::WO(_) => { return Err(("org.freedesktop.DBus.Error.Failed", format!("Property {} is write only", prop_name))) } }); Ok(vec!(MessageItem::Variant(Box::new(v)))) } fn property_getall(&self, msg: &mut Message) -> MethodResult { let items = msg.get_items(); let iface_name = try!(parse_msg_str(items.get(0))); let is = self.interfaces.borrow(); let i = try!(is.get(iface_name).ok_or_else(|| ("org.freedesktop.DBus.Error.UnknownInterface", format!("Unknown interface {}", iface_name)))); let mut result = Vec::new(); result.push(try!(MessageItem::from_dict(i.properties.iter().filter_map(|(pname, pv)| { let v = match pv.access { PropertyAccess::RO(ref cb) => cb.get(), PropertyAccess::RW(ref cb) => cb.get(), PropertyAccess::WO(_) => { return None } }; Some(v.map(|vv| (pname.clone(),vv))) })))); Ok(result) } fn property_set(&self, msg: &mut Message) -> MethodResult { let items = msg.get_items(); let iface_name = try!(parse_msg_str(items.get(0))); let prop_name = try!(parse_msg_str(items.get(1))); let value = try!(parse_msg_variant(items.get(2))); let is = self.interfaces.borrow(); let i = try!(is.get(iface_name).ok_or_else(|| ("org.freedesktop.DBus.Error.UnknownInterface", format!("Unknown interface {}", iface_name)))); let p = try!(i.properties.get(prop_name).ok_or_else(|| ("org.freedesktop.DBus.Error.UnknownProperty", format!("Unknown property {}", prop_name)))); try!(match p.access { PropertyAccess::WO(ref cb) => cb.set(value), PropertyAccess::RW(ref cb) => cb.set(value), PropertyAccess::RO(_) => { return Err(("org.freedesktop.DBus.Error.PropertyReadOnly", format!("Property {} is read only", prop_name))) } }); Ok(vec!()) } } fn parse_msg_str(a: Option<&MessageItem>) -> Result<&str,(&'static str, String)> { let name = try!(a.ok_or_else(|| ("org.freedesktop.DBus.Error.InvalidArgs", format!("Invalid argument {:?}", a)))); name.inner().map_err(|_| ("org.freedesktop.DBus.Error.InvalidArgs", format!("Invalid argument {:?}", a))) } fn parse_msg_variant(a: Option<&MessageItem>) -> Result<&MessageItem,(&'static str, String)> { let name = try!(a.ok_or_else(|| ("org.freedesktop.DBus.Error.InvalidArgs", format!("Invalid argument {:?}", a)))); name.inner().map_err(|_| ("org.freedesktop.DBus.Error.InvalidArgs", format!("Invalid argument {:?}", a))) } impl PropertyROHandler for MessageItem { fn get(&self) -> PropertyGetResult { Ok(self.clone()) } } impl<'a> ObjectPath<'a> { /// Create a new ObjectPath. pub fn new(conn: &'a Connection, path: &str, introspectable: bool) -> ObjectPath<'a> { let i = IObjectPath { conn: conn, path: path.to_string(), registered: Cell::new(false), interfaces: RefCell::new(BTreeMap::new()), }; let mut o = ObjectPath { i: Rc::new(i) }; if introspectable { let o_cl = o.i.clone(); let i = Interface::new(vec!( Method::new("Introspect", vec!(), vec!(Argument::new("xml_data", "s")), Box::new(move |m| { o_cl.introspect(m) }))), vec!(), vec!()); o.insert_interface("org.freedesktop.DBus.Introspectable", i); } o } fn add_property_handler(&mut self) { if self.i.interfaces.borrow().contains_key("org.freedesktop.DBus.Properties") { return }; let (cl1, cl2, cl3) = (self.i.clone(), self.i.clone(), self.i.clone()); let i = Interface::new(vec!( Method::new("Get", vec!(Argument::new("interface_name", "s"), Argument::new("property_name", "s")), vec!(Argument::new("value", "v")), Box::new(move |m| cl1.property_get(m))), Method::new("GetAll", vec!(Argument::new("interface_name", "s")), vec!(Argument::new("props", "a{sv}")), Box::new(move |m| cl2.property_getall(m))), Method::new("Set", vec!(Argument::new("interface_name", "s"), Argument::new("property_name", "s"), Argument::new("value", "v")), vec!(), Box::new(move |m| cl3.property_set(m)))), vec!(), vec!()); self.insert_interface("org.freedesktop.DBus.Properties", i); } /// Add an Interface to this ObjectPath. pub fn insert_interface(&mut self, name: N, i: Interface<'a>) { if !i.properties.is_empty() { self.add_property_handler(); } self.i.interfaces.borrow_mut().insert(name.to_string(), i); } /// Returns if the ObjectPath is registered. pub fn is_registered(&self) -> bool { self.i.registered.get() } /// Changes the registration status of the ObjectPath. pub fn set_registered(&mut self, register: bool) -> Result<(), Error> { self.i.set_registered(register) } /// Handles a method call if the object path matches. /// Return value: None => not handled (no match), /// Some(Err(())) => message reply send failed, /// Some(Ok()) => message reply send ok */ pub fn handle_message(&mut self, msg: &mut Message) -> Option> { let (_, path, iface, method) = msg.headers(); if path.is_none() || path.unwrap() != self.i.path { return None; } if iface.is_none() { return None; } let method = { // This is because we don't want to hold the refcell lock when we call the // callback - maximum flexibility for clients. if let Some(i) = self.i.interfaces.borrow().get(&iface.unwrap()) { if let Some(Some(m)) = method.map(|m| i.methods.get(&m)) { m.cb.clone() } else { return Some(self.i.conn.send(Message::new_error( msg, "org.freedesktop.DBus.Error.UnknownMethod", "Unknown method").unwrap()).map(|_| ())); } } else { return Some(self.i.conn.send(Message::new_error(msg, "org.freedesktop.DBus.Error.UnknownInterface", "Unknown interface").unwrap()).map(|_| ())); } }; let r = { // Now call it let mut m = method.borrow_mut(); (&mut **m)(msg) }; let reply = match r { Ok(r) => { let mut z = Message::new_method_return(msg).unwrap(); z.append_items(&r); z }, Err((aa,bb)) => Message::new_error(msg, aa, &bb).unwrap(), }; Some(self.i.conn.send(reply).map(|_| ())) } } #[cfg(test)] fn make_objpath<'a>(c: &'a Connection) -> ObjectPath<'a> { let mut o = ObjectPath::new(c, "/echo", true); o.insert_interface("com.example.echo", Interface::new( vec!(Method::new("Echo", vec!(Argument::new("request", "s")), vec!(Argument::new("reply", "s")), Box::new(|_| { Err(("dummy", "dummy".to_string())) } ))), vec!(Property::new_ro("EchoCount", MessageItem::Int32(7).type_sig(), Box::new(MessageItem::Int32(7)))), vec!(Signal::new("Echoed", vec!(Argument::new("data", "s")))))); o } #[test] fn test_objpath() { let c = Connection::get_private(super::BusType::Session).unwrap(); let mut o = make_objpath(&c); o.set_registered(true).unwrap(); let busname = format!("com.example.objpath.test.test_objpath"); assert_eq!(c.register_name(&busname, super::NameFlag::ReplaceExisting as u32).unwrap(), super::RequestNameReply::PrimaryOwner); let thread = ::std::thread::spawn(move || { let c = Connection::get_private(super::BusType::Session).unwrap(); let pr = super::Props::new(&c, &*busname, "/echo", "com.example.echo", 5000); assert_eq!(pr.get("EchoCount").unwrap(), 7i32.into()); let m = pr.get_all().unwrap(); assert_eq!(m.get("EchoCount").unwrap(), &7i32.into()); }); let mut i = 0; for n in c.iter(1000) { println!("objpath msg {:?}", n); if let super::ConnectionItem::MethodCall(mut m) = n { if let Some(msg) = o.handle_message(&mut m) { msg.unwrap(); i += 1; if i >= 2 { break }; } } } thread.join().unwrap(); } /// Currently commented out because it requires feature(alloc) /* #[test] fn test_refcount() { let c = Connection::get_private(super::BusType::Session).unwrap(); let i = { let o = make_objpath(&c); o.i.clone() }; assert!(::std::rc::is_unique(&i)); } */ #[test] fn test_introspect() { let c = Connection::get_private(super::BusType::Session).unwrap(); let mut o = make_objpath(&c); o.set_registered(true).unwrap(); let mut o2 = ObjectPath::new(&c, "/echo/subpath", true); o2.set_registered(true).unwrap(); let mut msg = Message::new_method_call("com.example.echoserver", "/echo", "org.freedesktop.DBus.Introspectable", "Introspect").unwrap(); println!("Introspect result: {}", parse_msg_str(o.i.introspect(&mut msg).unwrap().get(0)).unwrap()); let result = r##" "##; assert_eq!(result, parse_msg_str(o.i.introspect(&mut msg).unwrap().get(0)).unwrap()); } dbus-0.2.3/src/prop.rs00006640001750000175000000012167126566777300013042 0ustar0000000000000000use super::{Connection, Message, MessageItem, Error, Path, Interface, BusName}; use std::collections::BTreeMap; /// Client side properties - get and set properties on a remote application. pub struct Props<'a> { name: BusName, path: Path, interface: Interface, timeout_ms: i32, conn: &'a Connection, } impl<'a> Props<'a> { /// Create a new Props. pub fn new(conn: &'a Connection, name: N, path: P, interface: I, timeout_ms: i32) -> Props<'a> where N: Into, P: Into, I: Into { Props { name: name.into(), path: path.into(), interface: interface.into(), timeout_ms: timeout_ms, conn: conn, } } /// Get a single property's value. pub fn get(&self, propname: &str) -> Result { let mut m = Message::method_call(&self.name, &self.path, &"org.freedesktop.DBus.Properties".into(), &"Get".into()); m.append_items(&[self.interface.to_string().into(), propname.to_string().into()]); let mut r = try!(self.conn.send_with_reply_and_block(m, self.timeout_ms)); let reply = try!(r.as_result()).get_items(); if reply.len() == 1 { if let &MessageItem::Variant(ref v) = &reply[0] { return Ok((**v).clone()) } } let f = format!("Invalid reply for property get {}: '{:?}'", propname, reply); return Err(Error::new_custom("InvalidReply", &f)); } /// Set a single property's value. pub fn set(&self, propname: &str, value: MessageItem) -> Result<(), Error> { let mut m = Message::method_call(&self.name, &self.path, &"org.freedesktop.DBus.Properties".into(), &"Set".into()); m.append_items(&[self.interface.to_string().into(), propname.to_string().into(), Box::new(value).into()]); let mut r = try!(self.conn.send_with_reply_and_block(m, self.timeout_ms)); try!(r.as_result()); Ok(()) } /// Get a map of all the properties' names and their values. pub fn get_all(&self) -> Result, Error> { let mut m = Message::method_call(&self.name, &self.path, &"org.freedesktop.DBus.Properties".into(), &"GetAll".into()); m.append_items(&[self.interface.to_string().into()]); let mut r = try!(self.conn.send_with_reply_and_block(m, self.timeout_ms)); let reply = try!(r.as_result()).get_items(); (|| { if reply.len() != 1 { return Err(()) }; let mut t = BTreeMap::new(); let a: &[MessageItem] = try!(reply[0].inner()); for p in a.iter() { let (k, v) = try!(p.inner()); let (k, v): (&String, &MessageItem) = (try!(k.inner()), try!(v.inner())); t.insert(k.clone(), v.clone()); } Ok(t) })().map_err(|_| { let f = format!("Invalid reply for property GetAll: '{:?}'", reply); Error::new_custom("InvalidReply", &f) }) } } /// Wrapper around Props that keeps a map of fetched properties. pub struct PropHandler<'a> { p: Props<'a>, map: BTreeMap, } impl<'a> PropHandler<'a> { /// Create a new PropHandler from a Props. pub fn new(p: Props) -> PropHandler { PropHandler { p: p, map: BTreeMap::new() } } /// Get a map of all the properties' names and their values. pub fn get_all(&mut self) -> Result<(), Error> { self.map = try!(self.p.get_all()); Ok(()) } /// Get a mutable reference to the PropHandler's fetched properties. pub fn map_mut(&mut self) -> &mut BTreeMap { &mut self.map } /// Get a reference to the PropHandler's fetched properties. pub fn map(&self) -> &BTreeMap { &self.map } /// Get a single property's value. pub fn get(&mut self, propname: &str) -> Result<&MessageItem, Error> { let v = try!(self.p.get(propname)); self.map.insert(propname.to_string(), v); Ok(self.map.get(propname).unwrap()) } /// Set a single property's value. pub fn set(&mut self, propname: &str, value: MessageItem) -> Result<(), Error> { try!(self.p.set(propname, value.clone())); self.map.insert(propname.to_string(), value); Ok(()) } } /* Unfortunately org.freedesktop.DBus has no properties we can use for testing, but PolicyKit should be around on most distros. */ #[test] fn test_get_policykit_version() { use super::BusType; let c = Connection::get_private(BusType::System).unwrap(); let p = Props::new(&c, "org.freedesktop.PolicyKit1", "/org/freedesktop/PolicyKit1/Authority", "org.freedesktop.PolicyKit1.Authority", 10000); /* Let's use both the get and getall methods and see if we get the same result */ let v = p.get("BackendVersion").unwrap(); let vall = p.get_all().unwrap(); let v2 = vall.get("BackendVersion").unwrap(); assert_eq!(&v, &*v2); match v { MessageItem::Str(ref s) => { println!("Policykit Backend version is {}", s); } _ => { panic!("Invalid Get: {:?}", v); } }; } dbus-0.2.3/src/strings.rs00006640001750000175000000006302126566777300013545 0ustar0000000000000000// CString wrappers. use ffi; use std::{str, fmt, ops, default}; use std::ffi::{CStr, CString}; use Error; macro_rules! cstring_wrapper { ($t: ident, $s: ident) => { impl $t { /// Creates a new instance of this struct. pub fn new>>(s: S) -> Result<$t, String> { let c = try!(CString::new(s).map_err(|e| e.to_string())); let mut e = Error::empty(); let b = unsafe { ffi::$s(c.as_ptr(), e.get_mut()) }; if b != 0 { Ok($t(c)) } else { Err(e.message().unwrap().into()) } } } /* /// #Panics /// /// If given string is not valid. /// impl>> From for $t { fn from(s: S) -> $t { $t::new(s).unwrap() } } */ /// #Panics /// /// If given string is not valid. impl<'a> From for $t { fn from(s: String) -> $t { $t::new(s).unwrap() } } /// #Panics /// /// If given string is not valid. impl<'a> From<&'a String> for $t { fn from(s: &'a String) -> $t { $t::new(s.clone()).unwrap() } } /// #Panics /// /// If given string is not valid. impl<'a> From<&'a str> for $t { fn from(s: &'a str) -> $t { $t::new(s).unwrap() } } impl ops::Deref for $t { type Target = str; fn deref(&self) -> &str { str::from_utf8(self.0.to_bytes()).unwrap() } } impl fmt::Display for $t { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let s: &str = &self; (&s as &fmt::Display).fmt(f) } } impl AsRef for $t { fn as_ref(&self) -> &CStr { &self.0 } } }} /// A wrapper around a string that is guaranteed to be /// a valid (single) D-Bus type signature. Supersedes TypeSig. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] pub struct Signature(CString); cstring_wrapper!(Signature, dbus_signature_validate_single); /// A wrapper around a string that is guaranteed to be /// a valid D-Bus object path. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] pub struct Path(CString); cstring_wrapper!(Path, dbus_validate_path); // This is needed so one can make arrays of paths easily impl default::Default for Path { fn default() -> Path { Path(CString::new("/").unwrap()) } } /// A wrapper around a string that is guaranteed to be /// a valid D-Bus member, i e, a signal or method name. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] pub struct Member(CString); cstring_wrapper!(Member, dbus_validate_member); /// A wrapper around a string that is guaranteed to be /// a valid D-Bus interface name. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] pub struct Interface(CString); cstring_wrapper!(Interface, dbus_validate_interface); /// A wrapper around a string that is guaranteed to be /// a valid D-Bus bus name. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] pub struct BusName(CString); cstring_wrapper!(BusName, dbus_validate_bus_name); /// A wrapper around a string that is guaranteed to be /// a valid D-Bus bus name. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] pub struct ErrorName(CString); cstring_wrapper!(ErrorName, dbus_validate_error_name); #[test] fn some_path() { let p1: Path = "/valid".into(); let p2 = Path::new("##invalid##"); assert_eq!(p1, Path(CString::new("/valid").unwrap())); assert_eq!(p2, Err("Object path was not valid: '##invalid##'".into())); } dbus-0.2.3/src/watch.rs00006640001750000175000000017246126531174600013155 0ustar0000000000000000use ffi; use libc; use super::Connection; use std::mem; use std::cell::RefCell; use std::os::unix::io::{RawFd, AsRawFd}; /// A file descriptor to watch for incoming events (for async I/O) #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Watch { fd: RawFd, read: bool, write: bool, } impl Watch { /// Get the RawFd this Watch is for pub fn fd(&self) -> RawFd { self.fd } /// Add POLLIN to events to listen for pub fn readable(&self) -> bool { self.read } /// Add POLLOUT to events to listen for pub fn writable(&self) -> bool { self.write } } impl AsRawFd for Watch { fn as_raw_fd(&self) -> RawFd { self.fd } } /// Note - internal struct, not to be used outside API. Moving it outside its box will break things. pub struct WatchList { watches: RefCell>, enabled_fds: RefCell>, on_update: Box } impl WatchList { pub fn new(c: &Connection, on_update: Box) -> Box { let w = Box::new(WatchList { on_update: on_update, watches: RefCell::new(vec!()), enabled_fds: RefCell::new(vec!()) }); if unsafe { ffi::dbus_connection_set_watch_functions(c.i.conn.get(), Some(add_watch_cb), Some(remove_watch_cb), Some(toggled_watch_cb), &*w as *const _ as *mut _, None) } == 0 { panic!("dbus_connection_set_watch_functions failed"); } w } pub fn watch_handle(&self, fd: RawFd, flags: libc::c_uint) { // println!("watch_handle {} flags {}", fd, flags); for &q in self.watches.borrow().iter() { let w = self.get_watch(q); if w.fd != fd { continue }; if unsafe { ffi::dbus_watch_handle(q, flags) } == 0 { panic!("dbus_watch_handle failed"); } self.update(q); }; } pub fn get_enabled_fds(&self) -> Vec { self.enabled_fds.borrow().clone() } fn get_watch(&self, watch: *mut ffi::DBusWatch) -> Watch { let mut w = Watch { fd: unsafe { ffi::dbus_watch_get_unix_fd(watch) }, read: false, write: false}; let enabled = self.watches.borrow().contains(&watch) && unsafe { ffi::dbus_watch_get_enabled(watch) != 0 }; let flags = unsafe { ffi::dbus_watch_get_flags(watch) }; if enabled { w.read = (flags & ffi::DBusWatchEvent::Readable as libc::c_uint) != 0; w.write = (flags & ffi::DBusWatchEvent::Writable as libc::c_uint) != 0; } // println!("Get watch fd {:?} ptr {:?} enabled {:?} flags {:?}", w, watch, enabled, flags); w } fn update(&self, watch: *mut ffi::DBusWatch) { let mut w = self.get_watch(watch); for &q in self.watches.borrow().iter() { if q == watch { continue }; let ww = self.get_watch(q); if ww.fd != w.fd { continue }; w.read |= ww.read; w.write |= ww.write; } // println!("Updated sum: {:?}", w); { let mut fdarr = self.enabled_fds.borrow_mut(); if w.write || w.read { if fdarr.contains(&w) { return; } // Nothing changed } else if !fdarr.iter().any(|q| w.fd == q.fd) { return; } // Nothing changed fdarr.retain(|f| f.fd != w.fd); if w.write || w.read { fdarr.push(w) }; } (*self.on_update)(w); } } extern "C" fn add_watch_cb(watch: *mut ffi::DBusWatch, data: *mut libc::c_void) -> u32 { let wlist: &WatchList = unsafe { mem::transmute(data) }; // println!("Add watch {:?}", watch); wlist.watches.borrow_mut().push(watch); wlist.update(watch); 1 } extern "C" fn remove_watch_cb(watch: *mut ffi::DBusWatch, data: *mut libc::c_void) { let wlist: &WatchList = unsafe { mem::transmute(data) }; // println!("Removed watch {:?}", watch); wlist.watches.borrow_mut().retain(|w| *w != watch); wlist.update(watch); } extern "C" fn toggled_watch_cb(watch: *mut ffi::DBusWatch, data: *mut libc::c_void) { let wlist: &WatchList = unsafe { mem::transmute(data) }; // println!("Toggled watch {:?}", watch); wlist.update(watch); } #[cfg(test)] mod test { use libc; use super::super::{Connection, Message, BusType, WatchEvent, ConnectionItem, MessageType}; #[repr(C)] #[derive(Clone, Debug)] pub struct PollFd { fd: libc::c_int, events: libc::c_short, revents: libc::c_short, } const POLLIN: libc::c_short = 0x001; const POLLOUT: libc::c_short = 0x004; const POLLERR: libc::c_short = 0x008; const POLLHUP: libc::c_short = 0x010; extern "C" { pub fn poll(fds: *mut PollFd, nfds: libc::c_ulong, timeout: libc::c_int) -> libc::c_int; } fn build_pollfds(c: &Connection) -> Vec { c.watch_fds().iter().map(|w| PollFd { fd: w.fd(), events: POLLERR + POLLHUP + if w.readable() { POLLIN } else { 0 } + if w.writable() { POLLOUT } else { 0 }, revents: 0 }).collect() } #[test] fn async() { let c = Connection::get_private(BusType::Session).unwrap(); c.register_object_path("/test").unwrap(); let m = Message::new_method_call(&c.unique_name(), "/test", "com.example.asynctest", "AsyncTest").unwrap(); let serial = c.send(m).unwrap(); println!("Async: sent serial {}", serial); let mut fds: Vec = build_pollfds(&c); let mut new_fds = None; let mut i = 0; let mut success = false; while !success { i += 1; if let Some(q) = new_fds { fds = q; new_fds = None }; for f in fds.iter_mut() { f.revents = 0 }; assert!(unsafe { poll(fds.as_mut_ptr(), fds.len() as libc::c_ulong, 1000) } > 0); for f in fds.iter().filter(|pfd| pfd.revents != 0) { let m = if (f.revents & POLLIN) != 0 { WatchEvent::Readable as libc::c_uint } else { 0 } + if (f.revents & POLLOUT) != 0 { WatchEvent::Writable as libc::c_uint } else { 0 } + if (f.revents & POLLERR) != 0 { WatchEvent::Error as libc::c_uint } else { 0 } + if (f.revents & POLLHUP) != 0 { WatchEvent::Hangup as libc::c_uint } else { 0 }; println!("Async: fd {}, revents {} -> {}", f.fd, f.revents, m); assert!(f.revents & POLLIN != 0 || f.revents & POLLOUT != 0); for e in c.watch_handle(f.fd, m) { println!("Async: got {:?}", e); match e { ConnectionItem::WatchFd(_) => new_fds = Some(build_pollfds(&c)), ConnectionItem::MethodCall(m) => { assert_eq!(m.headers(), (MessageType::MethodCall, Some("/test".to_string()), Some("com.example.asynctest".into()), Some("AsyncTest".to_string()))); let mut mr = Message::new_method_return(&m).unwrap(); mr.append_items(&["Goodies".into()]); c.send(mr).unwrap(); } ConnectionItem::MethodReturn(m) => { assert_eq!(m.headers().0, MessageType::MethodReturn); assert_eq!(m.get_reply_serial().unwrap(), serial); let i = m.get_items(); let s: &str = i[0].inner().unwrap(); assert_eq!(s, "Goodies"); success = true; } _ => (), } } if i > 100 { panic!() }; } } } }