dbus-0.6.5/Cargo.toml.orig010064400017500001750000000013301351377132500135730ustar0000000000000000[package] name = "dbus" version = "0.6.5" 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://docs.rs/dbus" keywords = ["D-Bus", "DBus", "IPC"] license = "Apache-2.0/MIT" categories = ["os::unix-apis", "api-bindings"] readme = "../README.md" [dependencies] libc = "0.2.7" libdbus-sys = { path = "../libdbus-sys", version = "0.2" } [dev-dependencies] tempdir = "0.3" [features] no-string-validation = [] [badges] is-it-maintained-open-issues = { repository = "diwic/dbus-rs" } is-it-maintained-issue-resolution = { repository = "diwic/dbus-rs" } dbus-0.6.5/Cargo.toml0000644000000023370000000000000100430ustar00# 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.6.5" authors = ["David Henningsson "] description = "Bindings to D-Bus, which is a bus commonly used on Linux for inter-process communication." documentation = "http://docs.rs/dbus" readme = "../README.md" keywords = ["D-Bus", "DBus", "IPC"] categories = ["os::unix-apis", "api-bindings"] license = "Apache-2.0/MIT" repository = "https://github.com/diwic/dbus-rs" [dependencies.libc] version = "0.2.7" [dependencies.libdbus-sys] version = "0.2" [dev-dependencies.tempdir] version = "0.3" [features] no-string-validation = [] [badges.is-it-maintained-issue-resolution] repository = "diwic/dbus-rs" [badges.is-it-maintained-open-issues] repository = "diwic/dbus-rs" dbus-0.6.5/LICENSE-APACHE010066400017500001750000000261421323514667700126510ustar0000000000000000Apache 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 2014-2018 David Henningsson and other contributors 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.6.5/LICENSE-MIT010066400017500001750000000021231323514667700123520ustar0000000000000000Copyright (c) 2014-2018 David Henningsson and other contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.dbus-0.6.5/examples/adv_server.rs010064400017500001750000000136671351376776700152670ustar0000000000000000// More advanced server example. // This is supposed to look like a D-Bus service that allows the user to manipulate storage devices. // Note: in the dbus-codegen/example directory, there is a version of this example where dbus-codegen // was used to create some boilerplate code - feel free to compare the two examples. extern crate dbus; use std::sync::Arc; use std::sync::mpsc; use std::cell::Cell; use std::thread; use dbus::{Connection, BusType, tree, Path}; use dbus::tree::{Interface, Signal, MTFn, Access, MethodErr, EmitsChangedSignal}; // Our storage device #[derive(Debug)] struct Device { description: String, path: Path<'static>, index: i32, online: Cell, checking: Cell, } // Every storage device has its own object path. // We therefore create a link from the object path to the Device. #[derive(Copy, Clone, Default, Debug)] struct TData; impl tree::DataType for TData { type Tree = (); type ObjectPath = Arc; type Property = (); type Interface = (); type Method = (); type Signal = (); } impl Device { // Creates a "test" device (not a real one, since this is an example). fn new_bogus(index: i32) -> Device { Device { description: format!("This is device {}, which is {}.", index, ["totally awesome", "really fancy", "still going strong"][(index as usize) % 3]), path: format!("/Device{}", index).into(), index: index, online: Cell::new(index % 2 == 0), checking: Cell::new(false), } } } // Here's where we implement the code for our interface. fn create_iface(check_complete_s: mpsc::Sender) -> (Interface, TData>, Arc>) { let f = tree::Factory::new_fn(); let check_complete = Arc::new(f.signal("CheckComplete", ())); (f.interface("com.example.dbus.rs.device", ()) // The online property can be both set and get .add_p(f.property::("online", ()) .access(Access::ReadWrite) .on_get(|i, m| { let dev: &Arc = m.path.get_data(); i.append(dev.online.get()); Ok(()) }) .on_set(|i, m| { let dev: &Arc = m.path.get_data(); let b: bool = try!(i.read()); if b && dev.checking.get() { return Err(MethodErr::failed(&"Device currently under check, cannot bring online")) } dev.online.set(b); Ok(()) }) ) // The "checking" property is read only .add_p(f.property::("checking", ()) .emits_changed(EmitsChangedSignal::False) .on_get(|i, m| { let dev: &Arc = m.path.get_data(); i.append(dev.checking.get()); Ok(()) }) ) // ...and so is the "description" property .add_p(f.property::<&str,_>("description", ()) .emits_changed(EmitsChangedSignal::Const) .on_get(|i, m| { let dev: &Arc = m.path.get_data(); i.append(&dev.description); Ok(()) }) ) // ...add a method for starting a device check... .add_m(f.method("check", (), move |m| { let dev: &Arc = m.path.get_data(); if dev.checking.get() { return Err(MethodErr::failed(&"Device currently under check, cannot start another check")) } if dev.online.get() { return Err(MethodErr::failed(&"Device is currently online, cannot start check")) } dev.checking.set(true); // Start some lengthy processing in a separate thread... let devindex = dev.index; let ch = check_complete_s.clone(); thread::spawn(move || { // Bogus check of device use std::time::Duration; thread::sleep(Duration::from_secs(15)); // Tell main thread that we finished ch.send(devindex).unwrap(); }); Ok(vec!(m.msg.method_return())) })) // Indicate that we send a special signal once checking has completed. .add_s(check_complete.clone()) , check_complete) } fn create_tree(devices: &[Arc], iface: &Arc, TData>>) -> tree::Tree, TData> { let f = tree::Factory::new_fn(); let mut tree = f.tree(()); for dev in devices { tree = tree.add(f.object_path(dev.path.clone(), dev.clone()) .introspectable() .add(iface.clone()) ); } tree } fn run() -> Result<(), Box> { // Create our bogus devices let devices: Vec> = (0..10).map(|i| Arc::new(Device::new_bogus(i))).collect(); // Create tree let (check_complete_s, check_complete_r) = mpsc::channel::(); let (iface, sig) = create_iface(check_complete_s); let tree = create_tree(&devices, &Arc::new(iface)); // Setup DBus connection let c = try!(Connection::get_private(BusType::Session)); try!(c.register_name("com.example.dbus.rs.advancedserverexample", 0)); try!(tree.set_registered(&c, true)); // ...and serve incoming requests. c.add_handler(tree); loop { // Wait for incoming messages. This will block up to one second. // Discard the result - relevant messages have already been handled. c.incoming(1000).next(); // Do all other things we need to do in our main loop. if let Ok(idx) = check_complete_r.try_recv() { let dev = &devices[idx as usize]; dev.checking.set(false); try!(c.send(sig.msg(&dev.path, &"com.example.dbus.rs.device".into())).map_err(|_| "Sending DBus signal failed")); } } } fn main() { if let Err(e) = run() { println!("{}", e); } } dbus-0.6.5/examples/argument_guide.md010066400017500001750000000207161323552507200160530ustar0000000000000000Preamble -------- The different ways you can append and get message arguments can be a bit bewildering. I've iterated a few times on the design and didn't want to lose backwards compatibility. This guide is to help you on your way. In addition, many of the examples in the examples directory append and read arguments. Code generation --------------- First - if you can get D-Bus introspection data, you can use the the `dbus-codegen` tool to generate some boilerplate code for you. E g, if you want to talk to NetworkManager: ```rust cargo install dbus-codegen dbus-codegen-rust -s -g -m None -d org.freedesktop.NetworkManager -p /org/freedesktop/NetworkManager > networkmanager.rs ``` You would then use this code like: ```rust // main.rs mod networkmanager; /* ... */ // Start a connection to the system bus. let c = Connection::get_private(BusType::System)?; // Make a "ConnPath" struct that just contains a Connection, a destination and a path. let p = c.with_path("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager", 5000); // Bring our generated code into scope. use networkmanager::OrgFreedesktopNetworkManager; // Now we can call methods on our connpath from the "org.freedesktop.NetworkManager" interface. let devices = c.get_all_devices()?; ``` There is also pre-generated code for standard D-Bus interfaces in the `stdintf` module. A similar example: ```rust let c = Connection::get_private(BusType::Session)?; // Make a "ConnPath" struct that just contains a Connection, a destination and a path. let p = c.with_path("org.mpris.MediaPlayer2.rhythmbox", "/org/mpris/MediaPlayer2", 5000); // The ConnPath struct implements many traits, e g `org.freedesktop.DBus.Properties`. Bring the trait into scope. use stdintf::org_freedesktop_dbus::Properties; // Now we can call org.freedesktop.DBus.Properties.Get just like an ordinary method and get the result back. let metadata = p.get("org.mpris.MediaPlayer2.Player", "Metadata")?; ``` For more details, see `dbus-codegen-rust --help` and the `README.md` in the dbus-codegen directory. Now, if you want to make a service yourself, the generated code is more complex. And for some use cases, codegen isn't really an option, so let's move on: Append / get basic types ------------------------ If you just want to get/append simple types, just use `append1` / `append2` / `append3`, and `read1` / `read2` / `read3`. The imaginary method below takes one byte parameter and one string parameter, and returns one string parameter and one int parameter. ```rust let m = Message::new_method_call(dest, path, intf, member)?.append2(5u8, "Foo"); let r = c.send_with_reply_and_block(m, 2000)?; let (data1, data2): (&str, i32) = c.read2()?; ``` Arrays and dictionaries ----------------------- D-Bus arrays and dictionaries usually correspond to `Vec` and `HashMap`. You can just append and get them like basic types: ```rust let v = vec![3i32, 4i32, 5i32]; let mut map = HashMap::new(); map.insert("Funghi", 5u16); map.insert("Mold", 8u16); let m = Message::new_method_call(dest, path, intf, member)?.append2(v, map); let r = c.send_with_reply_and_block(m, 2000)?; let (data1, data2): (Vec, HashMap<&str, u16>) = r.read2()?; ``` Or combine them as you wish, e g, use a `Vec>`, a `HashMap>` or `HashMap>` to construct more difficult types. Slices can sometimes be used as arrays - e g, `&[&str]` can be appended, but only very simple types can be used with `get` and `read`, e g `&[u8]`. This is the easiest way to get started, but in case you want to avoid the overhead of creating `Vec` or `HashMap`s, the "Array and Dict types" and "Iter / IterAppend" sections offer useful alternatives. Variants -------- Things are getting slightly more complex with Variants, because they are not strongly typed and thus not fit as well into Rust's strongly typed as arrays and dicts. If you know the type beforehand, it's still easy: ```rust let v = Variant("This is a variant containing a &str"); let m = Message::new_method_call(dest, path, intf, member)?.append1(v); let r = c.send_with_reply_and_block(m, 2000)?; let z: Variant = r.read1()?; println!("Method returned {}", z.0); ``` The `Variant` struct is just a wrapper with a public interior, so you can easily both read from it and write to it with the `.0` accessor. Sometimes you don't know the type beforehand. We can solve this in two ways (choose whichever is more appropriate for your use case), either through the trait object `Box` or through `Iter` / `IterAppend` (see later sections). Through trait objects: ```rust let x = Box::new(5000i32) as Box; let m = Message::new_method_call(dest, path, intf, member)?.append1(Variant(x)); let r = c.send_with_reply_and_block(m, 2000)?; let z: Variant> = r.read1()?; ``` Ok, so we retrieved our `Box`. We now need to use the `RefArg` methods to probe it, to see what's inside. Easiest is to use `as_i64` or `as_str` if you want to test for integer or string types. Use `as_iter` if the variant contains a complex type you need to iterate over. For floating point values, use `arg::cast` (this requires that the RefArg is `static` though, due to Rust type system limitations). Match over `arg_type` if you need to know the exact type. ```rust let z: Variant> = r.read1()?; let value = &z.0; if let Some(s) = value.as_str() { println!("It's a string: {}", s); } else if let Some(i) = value.as_i64() { println!("It's an integer: {}", i); } else if let Some(f) = arg::cast::(value) { println!("It's a float: {}", f); } else { println!("Don't know how to handle a {:?}", value.arg_type()) } ``` Dicts and variants are sometimes combined, e g, you might need to read a D-Bus dictionary of String to Variants. You can then read these as `HashMap>>`. Structs ------- D-Bus structs are implemented as Rust tuples. You can append and get tuples like you do with other types of arguments. TODO: Example Declare method arguments ------------------------ When you make a `Tree`, you want to declare what input and output arguments your method expects - so that correct D-Bus introspection data can be generated. You'll use the same types as you learned earlier in this guide: ```rust factory.method( /* ... */ ) .inarg::>,_>("request") .outarg::<&str,_>("reply") ``` The types are just for generating a correct signature, they are never instantiated. Many different types can generate the same signature - e g, `Array`, `Vec` and `&[u8]` will all generate the same signature. `Variant` will generate the same type signature regardless of what's inside, so just write `Variant<()>` for simplicity. Iter / IterAppend ----------------- Iter and IterAppend are more low-level, direct methods to get and append arguments. They can, e g, come handy if you have more than five arguments to read. E g, for appending a variant with IterAppend you can use `IterAppend::new(&msg).append_variant(|i| i.append(5000i32))` to append what you need to your variant inside the closure. To read a variant you can use `let i = msg.read1::>::()?` and then examine the methods on `i.0` to probe the variant. Array and Dict types -------------------- These provide slightly better flexibility than using `Vec` and `HashMap` by instead integrating with `Iterator`. Here's an example where you can append and get a dictionary without having to create a HashMap: ```rust let x = &[("Hello", true), ("World", false)]; let m = Message::new_method_call(dest, path, intf, member)?.append1(Dict::new(x)); let r = c.send_with_reply_and_block(m, 2000)?; let z: Dict = r.read1()?; for (key, value) in z { /* do something */ } ``` An edge case where this is necessary is having floating point keys in a dictionary - this is supported in D-Bus but not in Rust's `HashMap`. I have never seen this in practice, though. Unusual types ------------- The types `Path`, `Signature` and `OwnedFd` are not often used, but they can be appended and read as other argument types. `Path` and `Signature` will return strings with a borrowed lifetime - use `.into_static()` if you want to untie that lifetime. For `OwnedFd`, which a wrapper around a file descriptor, remember that the file descriptor will be closed when it goes out of scope. MessageItem ----------- MessageItem was the first design - an enum representing a D-Bus argument. It still works, but I doubt you'll ever need to use it. Newer methods provide better type safety, speed, and ergonomics. dbus-0.6.5/examples/client.rs010064400017500001750000000007651351376776700144000ustar0000000000000000extern crate dbus; use dbus::{Connection, BusType, Message}; use dbus::arg::Array; 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(); // ListNames returns one argument, which is an array of strings. let arr: Array<&str, _> = r.get1().unwrap(); for name in arr { println!("{}", name); } } dbus-0.6.5/examples/properties.rs010064400017500001750000000035531351376776700153140ustar0000000000000000extern crate dbus; use dbus::{Connection, BusType, stdintf, arg}; use std::collections::HashMap; fn print_refarg(value: &arg::RefArg) { // We don't know what type the value is. We'll try a few and fall back to // debug printing if the value is more complex than that. if let Some(s) = value.as_str() { println!("{}", s); } else if let Some(i) = value.as_i64() { println!("{}", i); } else { println!("{:?}", value); } } fn main() { // Connect to server and create a ConnPath. A ConnPath implements several interfaces, // in this case we'll use OrgFreedesktopDBusProperties, which allows us to call "get". let c = Connection::get_private(BusType::Session).unwrap(); let p = c.with_path("org.mpris.MediaPlayer2.rhythmbox", "/org/mpris/MediaPlayer2", 5000); use stdintf::org_freedesktop_dbus::Properties; // The Metadata property is a Dict. // Option 1: we can get the dict straight into a hashmap, like this: let metadata: HashMap>> = p.get("org.mpris.MediaPlayer2.Player", "Metadata").unwrap(); println!("Option 1:"); // We now iterate over the hashmap. for (key, value) in metadata.iter() { print!(" {}: ", key); print_refarg(&value); } // Option 2: we can get the entire dict as a RefArg and get the values out by iterating over it. let metadata: Box = p.get("org.mpris.MediaPlayer2.Player", "Metadata").unwrap(); // When using "as_iter()" for a dict, we'll get one key, it's value, next key, it's value, etc. let mut iter = metadata.as_iter().unwrap(); println!("Option 2:"); while let Some(key) = iter.next() { // Printing the key is easy, since we know it's a String. print!(" {}: ", key.as_str().unwrap()); let value = iter.next().unwrap(); print_refarg(&value); } } dbus-0.6.5/examples/properties_msgitem.rs010064400017500001750000000005531351376776700170360ustar0000000000000000extern 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.6.5/examples/rtkit.rs010064400017500001750000000046071351376776700142560ustar0000000000000000/* 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 dbus::{Connection, BusType, Props, MessageItem, Message}; 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("RTTimeUSecMax")))) as u64; let new_limit = libc::rlimit64 { rlim_cur: max_rttime, rlim_max: max_rttime }; let mut old_limit = new_limit; if unsafe { libc::getrlimit64(libc::RLIMIT_RTTIME, &mut old_limit) } < 0 { return Err(Box::from("getrlimit failed")); } if unsafe { libc::setrlimit64(libc::RLIMIT_RTTIME, &new_limit) } < 0 { return Err(Box::from("setrlimit failed")); } // Finally, let's ask rtkit to make us realtime let thread_id = unsafe { libc::syscall(libc::SYS_gettid) }; let r = rtkit_set_realtime(&c, thread_id as u64, prio); if r.is_err() { unsafe { libc::setrlimit64(libc::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.6.5/examples/server.rs010064400017500001750000000056011351376776700144220ustar0000000000000000/* This example creates a D-Bus server with the following functionality: It registers the "com.example.dbustest" name, creates a "/hello" object path, which has an "com.example.dbustest" interface. The interface has a "Hello" method (which takes no arguments and returns a string), and a "HelloHappened" signal (with a string argument) which is sent every time someone calls the "Hello" method. */ extern crate dbus; use std::sync::Arc; use dbus::{Connection, BusType, NameFlag}; use dbus::tree::Factory; fn main() { // Let's start by starting up a connection to the session bus and register a name. let c = Connection::get_private(BusType::Session).unwrap(); c.register_name("com.example.dbustest", NameFlag::ReplaceExisting as u32).unwrap(); // The choice of factory tells us what type of tree we want, // and if we want any extra data inside. We pick the simplest variant. let f = Factory::new_fn::<()>(); // We create the signal first, since we'll need it in both inside the method callback // and when creating the tree. let signal = Arc::new(f.signal("HelloHappened", ()).sarg::<&str,_>("sender")); let signal2 = signal.clone(); // We create a tree with one object path inside and make that path introspectable. let tree = f.tree(()).add(f.object_path("/hello", ()).introspectable().add( // We add an interface to the object path... f.interface("com.example.dbustest", ()).add_m( // ...and a method inside the interface. f.method("Hello", (), move |m| { // This is the callback that will be called when another peer on the bus calls our method. // the callback receives "MethodInfo" struct and can return either an error, or a list of // messages to send back. let name: &str = m.msg.read1()?; let s = format!("Hello {}!", name); let mret = m.msg.method_return().append1(s); let sig = signal.msg(m.path.get_name(), m.iface.get_name()) .append1(&*name); // Two messages will be returned - one is the method return (and should always be there), // and in our case we also have a signal we want to send at the same time. Ok(vec!(mret, sig)) // Our method has one output argument and one input argument. }).outarg::<&str,_>("reply") .inarg::<&str,_>("name") // We also add the signal to the interface. This is mainly for introspection. ).add_s(signal2) )); // We register all object paths in the tree. tree.set_registered(&c, true).unwrap(); // We add the tree to the connection so that incoming method calls will be handled // automatically during calls to "incoming". c.add_handler(tree); // Serve other peers forever. loop { c.incoming(1000).next(); } } dbus-0.6.5/examples/unity_focused_window.rs010064400017500001750000000016151351376776700173640ustar0000000000000000extern crate dbus; // Tracks currently focused window under the Unity desktop by listening to the // FocusedWindowChanged signal. The signal contains "window_id", "app_id" and "stage", // we print only "app_id". use dbus::{Connection, BusType, ConnectionItem}; fn focus_msg(ci: &ConnectionItem) -> Option<&str> { let m = if let &ConnectionItem::Signal(ref s) = ci { s } else { return None }; if &*m.interface().unwrap() != "com.canonical.Unity.WindowStack" { return None }; if &*m.member().unwrap() != "FocusedWindowChanged" { return None }; let (_, app) = m.get2::(); app } fn main() { let c = Connection::get_private(BusType::Session).unwrap(); c.add_match("interface='com.canonical.Unity.WindowStack',member='FocusedWindowChanged'").unwrap(); for i in c.iter(1000) { if let Some(app) = focus_msg(&i) { println!("{} has now focus.", app) }; } } dbus-0.6.5/src/arg/array_impl.rs010064400017500001750000000457751351376776700150150ustar0000000000000000use super::*; use {Signature, Path, Message, ffi, OwnedFd}; use std::marker::PhantomData; use std::{ptr, mem, any, fmt}; use super::check; use std::ffi::{CString}; use std::os::raw::{c_void, c_int}; use std::collections::HashMap; use std::hash::Hash; // Map DBus-Type -> Alignment. Copied from _dbus_marshal_write_fixed_multi in // http://dbus.freedesktop.org/doc/api/html/dbus-marshal-basic_8c_source.html#l01020 // Note that Rust booleans are one byte, dbus booleans are four bytes! const FIXED_ARRAY_ALIGNMENTS: [(ArgType, usize); 9] = [ (ArgType::Byte, 1), (ArgType::Int16, 2), (ArgType::UInt16, 2), (ArgType::UInt32, 4), (ArgType::Int32, 4), (ArgType::Boolean, 4), (ArgType::Int64, 8), (ArgType::UInt64, 8), (ArgType::Double, 8) ]; /// Represents a D-Bus array. impl<'a, T: Arg> Arg for &'a [T] { const ARG_TYPE: ArgType = ArgType::Array; fn signature() -> Signature<'static> { Signature::from(format!("a{}", T::signature())) } } fn array_append(z: &[T], i: &mut IterAppend, mut f: F) { let zptr = z.as_ptr(); let zlen = z.len() as i32; // Can we do append_fixed_array? let a = (T::ARG_TYPE, mem::size_of::()); let can_fixed_array = (zlen > 1) && (z.len() == zlen as usize) && FIXED_ARRAY_ALIGNMENTS.iter().any(|&v| v == a); i.append_container(ArgType::Array, Some(T::signature().as_cstr()), |s| if can_fixed_array { unsafe { check("dbus_message_iter_append_fixed_array", ffi::dbus_message_iter_append_fixed_array(&mut s.0, a.0 as c_int, &zptr as *const _ as *const c_void, zlen)) }} else { for arg in z { f(arg, s); }} ); } /// Appends a D-Bus array. Note: In case you have a large array of a type that implements FixedArray, /// using this method will be more efficient than using an Array. impl<'a, T: Arg + Append + Clone> Append for &'a [T] { fn append(self, i: &mut IterAppend) { array_append(self, i, |arg, s| arg.clone().append(s)); } } impl<'a, T: Arg + RefArg> RefArg for &'a [T] { fn arg_type(&self) -> ArgType { ArgType::Array } fn signature(&self) -> Signature<'static> { Signature::from(format!("a{}", ::signature())) } fn append(&self, i: &mut IterAppend) { array_append(self, i, |arg, s| (arg as &RefArg).append(s)); } #[inline] fn as_any(&self) -> &any::Any where Self: 'static { self } #[inline] fn as_any_mut(&mut self) -> &mut any::Any where Self: 'static { self } fn box_clone(&self) -> Box { Box::new(InternalArray { inner_sig: ::signature(), data: self.iter().map(|x| x.box_clone()).collect(), }) } } impl RefArg for Vec { fn arg_type(&self) -> ArgType { ArgType::Array } fn signature(&self) -> Signature<'static> { Signature::from(format!("a{}", ::signature())) } fn append(&self, i: &mut IterAppend) { array_append(&self, i, |arg, s| (arg as &RefArg).append(s)); } #[inline] fn as_any(&self) -> &any::Any where Self: 'static { self } #[inline] fn as_any_mut(&mut self) -> &mut any::Any where Self: 'static { self } fn as_iter<'a>(&'a self) -> Option + 'a>> { Some(Box::new(self.iter().map(|b| b as &RefArg))) } #[inline] fn box_clone(&self) -> Box { (&**self).box_clone() } } impl<'a, T: FixedArray> Get<'a> for &'a [T] { fn get(i: &mut Iter<'a>) -> Option<&'a [T]> { debug_assert!(FIXED_ARRAY_ALIGNMENTS.iter().any(|&v| v == (T::ARG_TYPE, mem::size_of::()))); i.recurse(Self::ARG_TYPE).and_then(|mut si| unsafe { let etype = ffi::dbus_message_iter_get_element_type(&mut i.0); if etype != T::ARG_TYPE as c_int { return None }; let mut v = ptr::null_mut(); let mut i = 0; ffi::dbus_message_iter_get_fixed_array(&mut si.0, &mut v as *mut _ as *mut c_void, &mut i); if v == ptr::null_mut() { assert_eq!(i, 0); Some(&[][..]) } else { Some(::std::slice::from_raw_parts(v, i as usize)) } }) } } #[derive(Copy, Clone, Debug)] /// Append a D-Bus dict type (i e, an array of dict entries). /// /// See the argument guide and module level documentation for details and alternatives. pub struct Dict<'a, K: DictKey, V: Arg, I>(I, PhantomData<(&'a Message, *const K, *const V)>); impl<'a, K: DictKey, V: Arg, I> Dict<'a, K, V, I> { fn entry_sig() -> String { format!("{{{}{}}}", K::signature(), V::signature()) } } impl<'a, K: 'a + DictKey, V: 'a + Append + Arg, I: Iterator> Dict<'a, K, V, I> { /// Creates a new Dict from an iterator. The iterator is consumed when appended. pub fn new>(j: J) -> Dict<'a, K, V, I> { Dict(j.into_iter(), PhantomData) } } impl<'a, K: DictKey, V: Arg, I> Arg for Dict<'a, K, V, I> { const ARG_TYPE: ArgType = ArgType::Array; fn signature() -> Signature<'static> { Signature::from(format!("a{}", Self::entry_sig())) } } impl<'a, K: 'a + DictKey + Append, V: 'a + Append + Arg, I: Iterator> Append for Dict<'a, K, V, I> { fn append(self, i: &mut IterAppend) { let z = self.0; i.append_container(Self::ARG_TYPE, Some(&CString::new(Self::entry_sig()).unwrap()), |s| for (k, v) in z { s.append_container(ArgType::DictEntry, None, |ss| { k.append(ss); v.append(ss); }) }); } } impl<'a, K: DictKey + Get<'a>, V: Arg + Get<'a>> Get<'a> for Dict<'a, K, V, Iter<'a>> { fn get(i: &mut Iter<'a>) -> Option { i.recurse(Self::ARG_TYPE).map(|si| Dict(si, PhantomData)) // TODO: Verify full element signature? } } impl<'a, K: DictKey + Get<'a>, V: Arg + Get<'a>> Iterator for Dict<'a, K, V, Iter<'a>> { type Item = (K, V); fn next(&mut self) -> Option<(K, V)> { let i = self.0.recurse(ArgType::DictEntry).and_then(|mut si| { let k = si.get(); if k.is_none() { return None }; assert!(si.next()); let v = si.get(); if v.is_none() { return None }; Some((k.unwrap(), v.unwrap())) }); self.0.next(); i } } impl Arg for HashMap { const ARG_TYPE: ArgType = ArgType::Array; fn signature() -> Signature<'static> { Signature::from(format!("a{{{}{}}}", K::signature(), V::signature())) } } impl Append for HashMap { fn append(self, i: &mut IterAppend) { Dict::new(self.into_iter()).append(i); } } impl<'a, K: DictKey + Get<'a> + Eq + Hash, V: Arg + Get<'a>> Get<'a> for HashMap { fn get(i: &mut Iter<'a>) -> Option { // TODO: Full element signature is not verified. Dict::get(i).map(|d| d.into_iter().collect()) } } impl RefArg for HashMap { fn arg_type(&self) -> ArgType { ArgType::Array } fn signature(&self) -> Signature<'static> { format!("a{{{}{}}}", ::signature(), ::signature()).into() } fn append(&self, i: &mut IterAppend) { let sig = CString::new(format!("{{{}{}}}", ::signature(), ::signature())).unwrap(); i.append_container(ArgType::Array, Some(&sig), |s| for (k, v) in self { s.append_container(ArgType::DictEntry, None, |ss| { k.append(ss); v.append(ss); }) }); } #[inline] fn as_any(&self) -> &any::Any where Self: 'static { self } #[inline] fn as_any_mut(&mut self) -> &mut any::Any where Self: 'static { self } fn as_iter<'b>(&'b self) -> Option + 'b>> { Some(Box::new(self.iter().flat_map(|(k, v)| vec![k as &RefArg, v as &RefArg].into_iter()))) } #[inline] fn box_clone(&self) -> Box { Box::new(InternalDict { outer_sig: self.signature(), data: self.iter().map(|(k, v)| (k.box_clone(), v.box_clone())).collect(), }) } } impl Arg for Vec { const ARG_TYPE: ArgType = ArgType::Array; fn signature() -> Signature<'static> { Signature::from(format!("a{}", T::signature())) } } impl Append for Vec { fn append(self, i: &mut IterAppend) { Array::new(self).append(i); } } impl<'a, T: Arg + Get<'a>> Get<'a> for Vec { fn get(i: &mut Iter<'a>) -> Option { >>::get(i).map(|a| a.collect()) } } #[derive(Copy, Clone, Debug)] /// Represents a D-Bus Array. Maximum flexibility (wraps an iterator of items to append). /// /// See the argument guide and module level documentation for details and alternatives. pub struct Array<'a, T, I>(I, PhantomData<(*const T, &'a Message)>); impl<'a, T: 'a, I: Iterator> Array<'a, T, I> { /// Creates a new Array from an iterator. The iterator is consumed when appending. pub fn new>(j: J) -> Array<'a, T, I> { Array(j.into_iter(), PhantomData) } } impl<'a, T: Arg, I> Arg for Array<'a, T, I> { const ARG_TYPE: ArgType = ArgType::Array; fn signature() -> Signature<'static> { Signature::from(format!("a{}", T::signature())) } } impl<'a, T: 'a + Arg + Append, I: Iterator> Append for Array<'a, T, I> { fn append(self, i: &mut IterAppend) { let z = self.0; i.append_container(ArgType::Array, Some(T::signature().as_cstr()), |s| for arg in z { arg.append(s) }); } } impl<'a, T: Arg + Get<'a>> Get<'a> for Array<'a, T, Iter<'a>> { fn get(i: &mut Iter<'a>) -> Option>> { i.recurse(Self::ARG_TYPE).map(|si| Array(si, PhantomData)) // TODO: Verify full element signature? } } impl<'a, T: Get<'a>> Iterator for Array<'a, T, Iter<'a>> { type Item = T; fn next(&mut self) -> Option { let i = self.0.get(); self.0.next(); i } } // Due to the strong typing here; RefArg is implemented only for T's that are both Arg and RefArg. // We need Arg for this to work for empty arrays (we can't get signature from first element if there is no elements). // We need RefArg for non-consuming append. impl<'a, T: 'a + Arg + fmt::Debug + RefArg, I: fmt::Debug + Clone + Iterator> RefArg for Array<'static, T, I> { fn arg_type(&self) -> ArgType { ArgType::Array } fn signature(&self) -> Signature<'static> { Signature::from(format!("a{}", ::signature())) } fn append(&self, i: &mut IterAppend) { let z = self.0.clone(); i.append_container(ArgType::Array, Some(::signature().as_cstr()), |s| for arg in z { (arg as &RefArg).append(s) } ); } #[inline] fn as_any(&self) -> &any::Any where Self: 'static { self } #[inline] fn as_any_mut(&mut self) -> &mut any::Any where Self: 'static { self } fn box_clone(&self) -> Box { Box::new(InternalArray { inner_sig: ::signature(), data: self.0.clone().map(|x| x.box_clone()).collect(), }) } } fn get_fixed_array_refarg<'a, T: FixedArray + Clone + RefArg>(i: &mut Iter<'a>) -> Box { let s = <&[T]>::get(i).unwrap(); Box::new(s.to_vec()) } fn get_var_array_refarg<'a, T: 'static + RefArg + Arg, F: FnMut(&mut Iter<'a>) -> Option> (i: &mut Iter<'a>, mut f: F) -> Box { let mut v: Vec = vec!(); // dbus_message_iter_get_element_count might be O(n), better not use it let mut si = i.recurse(ArgType::Array).unwrap(); while let Some(q) = f(&mut si) { v.push(q); si.next(); } Box::new(v) } #[derive(Debug)] struct InternalDict { data: Vec<(K, Box)>, outer_sig: Signature<'static>, } fn get_dict_refarg<'a, K, F: FnMut(&mut Iter<'a>) -> Option>(i: &mut Iter<'a>, mut f: F) -> Box where K: DictKey + 'static + RefArg + Clone { let mut data = vec!(); let outer_sig = i.signature(); let mut si = i.recurse(ArgType::Array).unwrap(); while let Some(mut d) = si.recurse(ArgType::DictEntry) { let k = f(&mut d).unwrap(); d.next(); data.push((k, d.get_refarg().unwrap())); si.next(); } Box::new(InternalDict { data, outer_sig }) } // This only happens from box_clone impl RefArg for InternalDict> { fn arg_type(&self) -> ArgType { ArgType::Array } fn signature(&self) -> Signature<'static> { self.outer_sig.clone() } fn append(&self, i: &mut IterAppend) { let inner_sig = &self.outer_sig.as_cstr().to_bytes_with_nul()[1..]; let inner_sig = CStr::from_bytes_with_nul(inner_sig).unwrap(); i.append_container(ArgType::Array, Some(inner_sig), |s| for (k, v) in &self.data { s.append_container(ArgType::DictEntry, None, |ss| { k.append(ss); v.append(ss); }) }); } #[inline] fn as_any(&self) -> &any::Any where Self: 'static { self } #[inline] fn as_any_mut(&mut self) -> &mut any::Any where Self: 'static { self } fn as_iter<'b>(&'b self) -> Option + 'b>> { Some(Box::new(self.data.iter().flat_map(|(k, v)| vec![k as &RefArg, v as &RefArg].into_iter()))) } #[inline] fn box_clone(&self) -> Box { Box::new(InternalDict { data: self.data.iter().map(|(k, v)| (k.box_clone(), v.box_clone())).collect(), outer_sig: self.outer_sig.clone(), }) } } impl RefArg for InternalDict { fn arg_type(&self) -> ArgType { ArgType::Array } fn signature(&self) -> Signature<'static> { self.outer_sig.clone() } fn append(&self, i: &mut IterAppend) { let inner_sig = &self.outer_sig.as_cstr().to_bytes_with_nul()[1..]; let inner_sig = CStr::from_bytes_with_nul(inner_sig).unwrap(); i.append_container(ArgType::Array, Some(inner_sig), |s| for (k, v) in &self.data { s.append_container(ArgType::DictEntry, None, |ss| { k.append(ss); v.append(ss); }) }); } #[inline] fn as_any(&self) -> &any::Any where Self: 'static { self } #[inline] fn as_any_mut(&mut self) -> &mut any::Any where Self: 'static { self } fn as_iter<'b>(&'b self) -> Option + 'b>> { Some(Box::new(self.data.iter().flat_map(|(k, v)| vec![k as &RefArg, v as &RefArg].into_iter()))) } #[inline] fn box_clone(&self) -> Box { Box::new(InternalDict { data: self.data.iter().map(|(k, v)| (k.clone(), v.box_clone())).collect(), outer_sig: self.outer_sig.clone(), }) } } // Fallback for Arrays of Arrays and Arrays of Structs. // We store the signature manually here and promise that it is correct for all elements // has that signature. #[derive(Debug)] struct InternalArray { data: Vec>, inner_sig: Signature<'static>, } fn get_internal_array<'a>(i: &mut Iter<'a>) -> Box { let mut si = i.recurse(ArgType::Array).unwrap(); let inner_sig = si.signature(); let data = si.collect::>(); Box::new(InternalArray { data, inner_sig }) } impl RefArg for InternalArray { fn arg_type(&self) -> ArgType { ArgType::Array } fn signature(&self) -> Signature<'static> { Signature::from(format!("a{}", self.inner_sig)) } fn append(&self, i: &mut IterAppend) { i.append_container(ArgType::Array, Some(self.inner_sig.as_cstr()), |s| for arg in &self.data { (arg as &RefArg).append(s) } ); } #[inline] fn as_any(&self) -> &any::Any where Self: 'static { self } #[inline] fn as_any_mut(&mut self) -> &mut any::Any where Self: 'static { self } fn as_iter<'a>(&'a self) -> Option + 'a>> { Some(Box::new(self.data.iter().map(|b| b as &RefArg))) } #[inline] fn box_clone(&self) -> Box { Box::new(InternalArray { data: self.data.iter().map(|x| x.box_clone()).collect(), inner_sig: self.inner_sig.clone(), }) } } pub fn get_array_refarg<'a>(i: &mut Iter<'a>) -> Box { debug_assert!(i.arg_type() == ArgType::Array); let etype = ArgType::from_i32(unsafe { ffi::dbus_message_iter_get_element_type(&mut i.0) } as i32).unwrap(); let x = match etype { ArgType::Byte => get_fixed_array_refarg::(i), ArgType::Int16 => get_fixed_array_refarg::(i), ArgType::UInt16 => get_fixed_array_refarg::(i), ArgType::Int32 => get_fixed_array_refarg::(i), ArgType::UInt32 => get_fixed_array_refarg::(i), ArgType::Int64 => get_fixed_array_refarg::(i), ArgType::UInt64 => get_fixed_array_refarg::(i), ArgType::Double => get_fixed_array_refarg::(i), ArgType::String => get_var_array_refarg::(i, |si| si.get()), ArgType::ObjectPath => get_var_array_refarg::, _>(i, |si| si.get::().map(|s| s.into_static())), ArgType::Signature => get_var_array_refarg::, _>(i, |si| si.get::().map(|s| s.into_static())), ArgType::Variant => get_var_array_refarg::>, _>(i, |si| Variant::new_refarg(si)), ArgType::Boolean => get_var_array_refarg::(i, |si| si.get()), ArgType::Invalid => panic!("Array with Invalid ArgType"), ArgType::Array => get_internal_array(i), ArgType::DictEntry => { let key = ArgType::from_i32(i.signature().as_bytes()[2] as i32).unwrap(); // The third character, after "a{", is our key. match key { ArgType::Byte => get_dict_refarg::(i, |si| si.get()), ArgType::Int16 => get_dict_refarg::(i, |si| si.get()), ArgType::UInt16 => get_dict_refarg::(i, |si| si.get()), ArgType::Int32 => get_dict_refarg::(i, |si| si.get()), ArgType::UInt32 => get_dict_refarg::(i, |si| si.get()), ArgType::Int64 => get_dict_refarg::(i, |si| si.get()), ArgType::UInt64 => get_dict_refarg::(i, |si| si.get()), ArgType::Double => get_dict_refarg::(i, |si| si.get()), ArgType::Boolean => get_dict_refarg::(i, |si| si.get()), // ArgType::UnixFd => get_dict_refarg::(i, |si| si.get()), ArgType::String => get_dict_refarg::(i, |si| si.get()), ArgType::ObjectPath => get_dict_refarg::, _>(i, |si| si.get::().map(|s| s.into_static())), ArgType::Signature => get_dict_refarg::, _>(i, |si| si.get::().map(|s| s.into_static())), _ => panic!("Array with invalid dictkey ({:?})", key), } } ArgType::UnixFd => get_var_array_refarg::(i, |si| si.get()), ArgType::Struct => get_internal_array(i), }; debug_assert_eq!(i.signature(), x.signature()); x } dbus-0.6.5/src/arg/basic_impl.rs010064400017500001750000000240341351376776700147410ustar0000000000000000use ffi; use super::*; use super::check; use {Signature, Path, OwnedFd}; use std::{ptr, any, mem}; use std::ffi::CStr; use std::os::raw::{c_void, c_char, c_int}; fn arg_append_basic(i: *mut ffi::DBusMessageIter, arg_type: ArgType, v: T) { let p = &v as *const _ as *const c_void; unsafe { check("dbus_message_iter_append_basic", ffi::dbus_message_iter_append_basic(i, arg_type as c_int, p)); }; } fn arg_get_basic(i: *mut ffi::DBusMessageIter, arg_type: ArgType) -> Option { unsafe { let mut c: T = mem::zeroed(); if ffi::dbus_message_iter_get_arg_type(i) != arg_type as c_int { return None }; ffi::dbus_message_iter_get_basic(i, &mut c as *mut _ as *mut c_void); Some(c) } } fn arg_append_f64(i: *mut ffi::DBusMessageIter, arg_type: ArgType, v: f64) { let p = &v as *const _ as *const c_void; unsafe { check("dbus_message_iter_append_basic", ffi::dbus_message_iter_append_basic(i, arg_type as c_int, p)); }; } fn arg_get_f64(i: *mut ffi::DBusMessageIter, arg_type: ArgType) -> Option { let mut c = 0f64; unsafe { if ffi::dbus_message_iter_get_arg_type(i) != arg_type as c_int { return None }; ffi::dbus_message_iter_get_basic(i, &mut c as *mut _ as *mut c_void); } Some(c) } fn arg_append_str(i: *mut ffi::DBusMessageIter, arg_type: ArgType, v: &CStr) { let p = v.as_ptr(); let q = &p as *const _ as *const c_void; unsafe { check("dbus_message_iter_append_basic", ffi::dbus_message_iter_append_basic(i, arg_type as c_int, q)); }; } unsafe fn arg_get_str<'a>(i: *mut ffi::DBusMessageIter, arg_type: ArgType) -> Option<&'a CStr> { if ffi::dbus_message_iter_get_arg_type(i) != arg_type as c_int { return None }; let mut p = ptr::null_mut(); ffi::dbus_message_iter_get_basic(i, &mut p as *mut _ as *mut c_void); Some(CStr::from_ptr(p as *const c_char)) } // Implementation for basic types. macro_rules! integer_impl { ($t: ident, $s: ident, $f: expr, $i: ident, $ii: expr, $u: ident, $uu: expr, $fff: ident, $ff: expr) => { impl Arg for $t { const ARG_TYPE: ArgType = ArgType::$s; #[inline] fn signature() -> Signature<'static> { unsafe { Signature::from_slice_unchecked($f) } } } impl Append for $t { fn append(self, i: &mut IterAppend) { arg_append_basic(&mut i.0, ArgType::$s, self) } } impl<'a> Get<'a> for $t { fn get(i: &mut Iter) -> Option { arg_get_basic(&mut i.0, ArgType::$s) } } impl RefArg for $t { #[inline] fn arg_type(&self) -> ArgType { ArgType::$s } #[inline] fn signature(&self) -> Signature<'static> { unsafe { Signature::from_slice_unchecked($f) } } #[inline] fn append(&self, i: &mut IterAppend) { arg_append_basic(&mut i.0, ArgType::$s, *self) } #[inline] fn as_any(&self) -> &any::Any { self } #[inline] fn as_any_mut(&mut self) -> &mut any::Any { self } #[inline] fn as_i64(&self) -> Option { let $i = *self; $ii } #[inline] fn as_u64(&self) -> Option { let $u = *self; $uu } #[inline] fn as_f64(&self) -> Option { let $fff = *self; $ff } #[inline] fn box_clone(&self) -> Box { Box::new(self.clone()) } } impl DictKey for $t {} unsafe impl FixedArray for $t {} }} // End of macro_rules integer_impl!(u8, Byte, b"y\0", i, Some(i as i64), u, Some(u as u64), f, Some(f as f64)); integer_impl!(i16, Int16, b"n\0", i, Some(i as i64), _u, None, f, Some(f as f64)); integer_impl!(u16, UInt16, b"q\0", i, Some(i as i64), u, Some(u as u64), f, Some(f as f64)); integer_impl!(i32, Int32, b"i\0", i, Some(i as i64), _u, None, f, Some(f as f64)); integer_impl!(u32, UInt32, b"u\0", i, Some(i as i64), u, Some(u as u64), f, Some(f as f64)); integer_impl!(i64, Int64, b"x\0", i, Some(i), _u, None, _f, None); integer_impl!(u64, UInt64, b"t\0", _i, None, u, Some(u as u64), _f, None); macro_rules! refarg_impl { ($t: ty, $i: ident, $ii: expr, $ss: expr, $uu: expr, $ff: expr) => { impl RefArg for $t { #[inline] fn arg_type(&self) -> ArgType { <$t as Arg>::ARG_TYPE } #[inline] fn signature(&self) -> Signature<'static> { <$t as Arg>::signature() } #[inline] fn append(&self, i: &mut IterAppend) { <$t as Append>::append(self.clone(), i) } #[inline] fn as_any(&self) -> &any::Any { self } #[inline] fn as_any_mut(&mut self) -> &mut any::Any { self } #[inline] fn as_i64(&self) -> Option { let $i = self; $ii } #[inline] fn as_u64(&self) -> Option { let $i = self; $uu } #[inline] fn as_f64(&self) -> Option { let $i = self; $ff } #[inline] fn as_str(&self) -> Option<&str> { let $i = self; $ss } #[inline] fn box_clone(&self) -> Box { Box::new(self.clone()) } } } } impl Arg for bool { const ARG_TYPE: ArgType = ArgType::Boolean; fn signature() -> Signature<'static> { unsafe { Signature::from_slice_unchecked(b"b\0") } } } impl Append for bool { fn append(self, i: &mut IterAppend) { arg_append_basic(&mut i.0, ArgType::Boolean, if self {1} else {0}) } } impl DictKey for bool {} impl<'a> Get<'a> for bool { fn get(i: &mut Iter) -> Option { arg_get_basic::(&mut i.0, ArgType::Boolean).map(|q| q != 0) } } refarg_impl!(bool, _i, Some(if *_i { 1 } else { 0 }), None, Some(if *_i { 1 as u64 } else { 0 as u64 }), Some(if *_i { 1 as f64 } else { 0 as f64 })); impl Arg for f64 { const ARG_TYPE: ArgType = ArgType::Double; fn signature() -> Signature<'static> { unsafe { Signature::from_slice_unchecked(b"d\0") } } } impl Append for f64 { fn append(self, i: &mut IterAppend) { arg_append_f64(&mut i.0, ArgType::Double, self) } } impl DictKey for f64 {} impl<'a> Get<'a> for f64 { fn get(i: &mut Iter) -> Option { arg_get_f64(&mut i.0, ArgType::Double) } } unsafe impl FixedArray for f64 {} refarg_impl!(f64, _i, None, None, None, Some(*_i)); /// Represents a D-Bus string. impl<'a> Arg for &'a str { const ARG_TYPE: ArgType = ArgType::String; fn signature() -> Signature<'static> { unsafe { Signature::from_slice_unchecked(b"s\0") } } } impl<'a> Append for &'a str { fn append(self, i: &mut IterAppend) { use std::borrow::Cow; let b: &[u8] = self.as_bytes(); let v: Cow<[u8]> = if b.len() > 0 && b[b.len()-1] == 0 { Cow::Borrowed(b) } else { let mut bb: Vec = b.into(); bb.push(0); Cow::Owned(bb) }; let z = unsafe { CStr::from_ptr(v.as_ptr() as *const c_char) }; arg_append_str(&mut i.0, ArgType::String, &z) } } impl<'a> DictKey for &'a str {} impl<'a> Get<'a> for &'a str { fn get(i: &mut Iter<'a>) -> Option<&'a str> { unsafe { arg_get_str(&mut i.0, ArgType::String) } .and_then(|s| s.to_str().ok()) } } impl<'a> Arg for String { const ARG_TYPE: ArgType = ArgType::String; fn signature() -> Signature<'static> { unsafe { Signature::from_slice_unchecked(b"s\0") } } } impl<'a> Append for String { fn append(mut self, i: &mut IterAppend) { self.push_str("\0"); let s: &str = &self; s.append(i) } } impl<'a> DictKey for String {} impl<'a> Get<'a> for String { fn get(i: &mut Iter<'a>) -> Option { <&str>::get(i).map(|s| String::from(s)) } } refarg_impl!(String, _i, None, Some(&_i), None, None); /// Represents a D-Bus string. impl<'a> Arg for &'a CStr { const ARG_TYPE: ArgType = ArgType::String; fn signature() -> Signature<'static> { unsafe { Signature::from_slice_unchecked(b"s\0") } } } /* /// Note: Will give D-Bus errors in case the CStr is not valid UTF-8. impl<'a> Append for &'a CStr { fn append(self, i: &mut IterAppend) { arg_append_str(&mut i.0, Self::arg_type(), &self) } } */ impl<'a> DictKey for &'a CStr {} impl<'a> Get<'a> for &'a CStr { fn get(i: &mut Iter<'a>) -> Option<&'a CStr> { unsafe { arg_get_str(&mut i.0, Self::ARG_TYPE) }} } impl Arg for OwnedFd { const ARG_TYPE: ArgType = ArgType::UnixFd; fn signature() -> Signature<'static> { unsafe { Signature::from_slice_unchecked(b"h\0") } } } impl Append for OwnedFd { fn append(self, i: &mut IterAppend) { use std::os::unix::io::AsRawFd; arg_append_basic(&mut i.0, ArgType::UnixFd, self.as_raw_fd()) } } impl DictKey for OwnedFd {} impl<'a> Get<'a> for OwnedFd { fn get(i: &mut Iter) -> Option { arg_get_basic(&mut i.0, ArgType::UnixFd).map(|q| OwnedFd::new(q)) } } refarg_impl!(OwnedFd, _i, { use std::os::unix::io::AsRawFd; Some(_i.as_raw_fd() as i64) }, None, None, None); macro_rules! string_impl { ($t: ident, $s: ident, $f: expr) => { impl<'a> Arg for $t<'a> { const ARG_TYPE: ArgType = ArgType::$s; fn signature() -> Signature<'static> { unsafe { Signature::from_slice_unchecked($f) } } } impl RefArg for $t<'static> { fn arg_type(&self) -> ArgType { ArgType::$s } fn signature(&self) -> Signature<'static> { unsafe { Signature::from_slice_unchecked($f) } } fn append(&self, i: &mut IterAppend) { arg_append_str(&mut i.0, ArgType::$s, self.as_cstr()) } #[inline] fn as_any(&self) -> &any::Any { self } #[inline] fn as_any_mut(&mut self) -> &mut any::Any { self } #[inline] fn as_str(&self) -> Option<&str> { Some(self) } #[inline] fn box_clone(&self) -> Box { Box::new(self.clone().into_static()) } } impl<'a> DictKey for $t<'a> {} impl<'a> Append for $t<'a> { fn append(self, i: &mut IterAppend) { arg_append_str(&mut i.0, ArgType::$s, self.as_cstr()) } } /* Unfortunately, this does not work because it conflicts with getting a $t<'static>. impl<'a> Get<'a> for $t<'a> { fn get(i: &mut Iter<'a>) -> Option<$t<'a>> { unsafe { arg_get_str(&mut i.0, ArgType::$s) } .map(|s| unsafe { $t::from_slice_unchecked(s.to_bytes_with_nul()) } ) } } */ impl<'a> Get<'a> for $t<'static> { fn get(i: &mut Iter<'a>) -> Option<$t<'static>> { unsafe { arg_get_str(&mut i.0, ArgType::$s).map(|s| $t::from_slice_unchecked(s.to_bytes_with_nul()).into_static()) }} } } } string_impl!(Path, ObjectPath, b"o\0"); string_impl!(Signature, Signature, b"g\0"); dbus-0.6.5/src/arg/mod.rs010064400017500001750000000415721351376776700134240ustar0000000000000000//! Types and traits for easily getting a message's arguments, or appening a message with arguments. //! //! Also see the arguments guide (in the examples directory). //! //! A message has `read1`, `read2` etc, and `append1`, `append2` etc, which is your //! starting point into this module's types. //! //! **Append a**: //! //! `bool, u8, u16, u32, u64, i16, i32, i64, f64` - the corresponding D-Bus basic type //! //! `&str` - a D-Bus string. D-Bus strings do not allow null characters, so //! if the string contains null characters, it will be cropped //! to only include the data before the null character. (Tip: This allows for skipping an //! allocation by writing a string literal which ends with a null character.) //! //! `&[T] where T: Append` - a D-Bus array. Note: can use an efficient fast-path in case of //! T being an FixedArray type. //! //! `Array where T: Append, I: Iterator` - a D-Bus array, maximum flexibility. //! //! `Variant where T: Append` - a D-Bus variant. //! //! `(T1, T2) where T1: Append, T2: Append` - tuples are D-Bus structs. Implemented up to 12. //! //! `Dict where K: Append + DictKey, V: Append, I: Iterator` - A D-Bus dict (array of dict entries). //! //! `Path` - a D-Bus object path. //! //! `Signature` - a D-Bus signature. //! //! `OwnedFd` - shares the file descriptor with the remote side. //! //! **Get / read a**: //! //! `bool, u8, u16, u32, u64, i16, i32, i64, f64` - the corresponding D-Bus basic type //! //! `&str`, `&CStr` - a D-Bus string. D-Bus strings are always UTF-8 and do not contain null characters. //! //! `&[T] where T: FixedArray` - a D-Bus array of integers or f64. //! //! `Array where T: Get` - a D-Bus array, maximum flexibility. Implements Iterator so you can easily //! collect it into, e g, a `Vec`. //! //! `Variant where T: Get` - a D-Bus variant. Use this type of Variant if you know the inner type. //! //! `Variant` - a D-Bus variant. This type of Variant allows you to examine the inner type. //! //! `(T1, T2) where T1: Get, T2: Get` - tuples are D-Bus structs. Implemented up to 12. //! //! `Dict where K: Get + DictKey, V: Get` - A D-Bus dict (array of dict entries). Implements Iterator so you can easily //! collect it into, e g, a `HashMap`. //! //! `Path` - a D-Bus object path. //! //! `Signature` - a D-Bus signature. //! //! `OwnedFd` - a file descriptor sent from the remote side. //! mod msgarg; mod basic_impl; mod variantstruct_impl; mod array_impl; pub use self::msgarg::{Arg, FixedArray, Get, DictKey, Append, RefArg, AppendAll, ReadAll, cast, cast_mut}; pub use self::array_impl::{Array, Dict}; pub use self::variantstruct_impl::Variant; use std::{fmt, mem, ptr, error}; use {ffi, Message, Signature, Path, OwnedFd}; use std::ffi::{CStr, CString}; use std::os::raw::{c_void, c_int}; fn check(f: &str, i: u32) { if i == 0 { panic!("D-Bus error: '{}' failed", f) }} fn ffi_iter() -> ffi::DBusMessageIter { unsafe { mem::zeroed() }} #[derive(Clone, Copy)] /// Helper struct for appending one or more arguments to a Message. pub struct IterAppend<'a>(ffi::DBusMessageIter, &'a Message); impl<'a> IterAppend<'a> { /// Creates a new IterAppend struct. pub fn new(m: &'a mut Message) -> IterAppend<'a> { let mut i = ffi_iter(); unsafe { ffi::dbus_message_iter_init_append(m.ptr(), &mut i) }; IterAppend(i, m) } /// Appends the argument. pub fn append(&mut self, a: T) { a.append(self) } fn append_container)>(&mut self, arg_type: ArgType, sig: Option<&CStr>, f: F) { let mut s = IterAppend(ffi_iter(), self.1); let p = sig.map(|s| s.as_ptr()).unwrap_or(ptr::null()); check("dbus_message_iter_open_container", unsafe { ffi::dbus_message_iter_open_container(&mut self.0, arg_type as c_int, p, &mut s.0) }); f(&mut s); check("dbus_message_iter_close_container", unsafe { ffi::dbus_message_iter_close_container(&mut self.0, &mut s.0) }); } /// Low-level function to append a variant. /// /// Use in case the `Variant` struct is not flexible enough - /// the easier way is to just call e g "append1" on a message and supply a `Variant` parameter. /// /// In order not to get D-Bus errors: during the call to "f" you need to call "append" on /// the supplied `IterAppend` exactly once, /// and with a value which has the same signature as inner_sig. pub fn append_variant)>(&mut self, inner_sig: &Signature, f: F) { self.append_container(ArgType::Variant, Some(inner_sig.as_cstr()), f) } /// Low-level function to append an array. /// /// Use in case the `Array` struct is not flexible enough - /// the easier way is to just call e g "append1" on a message and supply an `Array` parameter. /// /// In order not to get D-Bus errors: during the call to "f", you should only call "append" on /// the supplied `IterAppend` with values which has the same signature as inner_sig. pub fn append_array)>(&mut self, inner_sig: &Signature, f: F) { self.append_container(ArgType::Array, Some(inner_sig.as_cstr()), f) } /// Low-level function to append a struct. /// /// Use in case tuples are not flexible enough - /// the easier way is to just call e g "append1" on a message and supply a tuple parameter. pub fn append_struct)>(&mut self, f: F) { self.append_container(ArgType::Struct, None, f) } /// Low-level function to append a dict entry. /// /// Use in case the `Dict` struct is not flexible enough - /// the easier way is to just call e g "append1" on a message and supply a `Dict` parameter. /// /// In order not to get D-Bus errors: during the call to "f", you should call "append" once /// for the key, then once for the value. You should only call this function for a subiterator /// you got from calling "append_dict", and signatures need to match what you specified in "append_dict". pub fn append_dict_entry)>(&mut self, f: F) { self.append_container(ArgType::DictEntry, None, f) } /// Low-level function to append a dict. /// /// Use in case the `Dict` struct is not flexible enough - /// the easier way is to just call e g "append1" on a message and supply a `Dict` parameter. /// /// In order not to get D-Bus errors: during the call to "f", you should only call "append_dict_entry" /// for the subiterator - do this as many times as the number of dict entries. pub fn append_dict)>(&mut self, key_sig: &Signature, value_sig: &Signature, f: F) { let sig = format!("{{{}{}}}", key_sig, value_sig); self.append_container(Array::::ARG_TYPE, Some(&CString::new(sig).unwrap()), f); } } #[derive(Clone, Copy)] /// Helper struct for retrieve one or more arguments from a Message. pub struct Iter<'a>(ffi::DBusMessageIter, &'a Message, u32); impl<'a> Iter<'a> { /// Creates a new struct for iterating over the arguments of a message, starting with the first argument. pub fn new(m: &'a Message) -> Iter<'a> { let mut i = ffi_iter(); unsafe { ffi::dbus_message_iter_init(m.ptr(), &mut i) }; Iter(i, m, 0) } /// Returns the current argument, if T is the argument type. Otherwise returns None. pub fn get>(&mut self) -> Option { T::get(self) } /// Returns the current argument as a trait object (experimental). /// /// Note: For the more complex arguments (arrays / dicts / structs, and especially /// combinations thereof), their internal representations are still a bit in flux. /// Instead, use as_iter() to read the values of those. /// /// The rest are unlikely to change - Variants are `Variant>`, strings are `String`, /// paths are `Path<'static>`, signatures are `Signature<'static>`, Int32 are `i32s` and so on. pub fn get_refarg(&mut self) -> Option> { Some(match self.arg_type() { ArgType::Array => array_impl::get_array_refarg(self), ArgType::Variant => Box::new(Variant::new_refarg(self).unwrap()), ArgType::Boolean => Box::new(self.get::().unwrap()), ArgType::Invalid => return None, ArgType::String => Box::new(self.get::().unwrap()), ArgType::DictEntry => unimplemented!(), ArgType::Byte => Box::new(self.get::().unwrap()), ArgType::Int16 => Box::new(self.get::().unwrap()), ArgType::UInt16 => Box::new(self.get::().unwrap()), ArgType::Int32 => Box::new(self.get::().unwrap()), ArgType::UInt32 => Box::new(self.get::().unwrap()), ArgType::Int64 => Box::new(self.get::().unwrap()), ArgType::UInt64 => Box::new(self.get::().unwrap()), ArgType::Double => Box::new(self.get::().unwrap()), ArgType::UnixFd => Box::new(self.get::().unwrap()), ArgType::Struct => Box::new(self.recurse(ArgType::Struct).unwrap().collect::>()), ArgType::ObjectPath => Box::new(self.get::().unwrap().into_static()), ArgType::Signature => Box::new(self.get::().unwrap().into_static()), }) } /// Returns the type signature for the current argument. pub fn signature(&mut self) -> Signature<'static> { unsafe { let c = ffi::dbus_message_iter_get_signature(&mut self.0); assert!(c != ptr::null_mut()); let cc = CStr::from_ptr(c); let r = Signature::new(cc.to_bytes()); ffi::dbus_free(c as *mut c_void); r.unwrap() } } /// The raw arg_type for the current item. /// /// Unlike Arg::arg_type, this requires access to self and is not a static method. /// You can match this against Arg::arg_type for different types to understand what type the current item is. /// In case you're past the last argument, this function will return 0. pub fn arg_type(&mut self) -> ArgType { let s = unsafe { ffi::dbus_message_iter_get_arg_type(&mut self.0) }; ArgType::from_i32(s as i32).unwrap() } /// Returns false if there are no more items. pub fn next(&mut self) -> bool { self.2 += 1; unsafe { ffi::dbus_message_iter_next(&mut self.0) != 0 } } /// Wrapper around `get` and `next`. Calls `get`, and then `next` if `get` succeeded. /// /// Also returns a `Result` rather than an `Option` to give an error if successful. /// /// # Example /// ```ignore /// struct ServiceBrowserItemNew { /// interface: i32, /// protocol: i32, /// name: String, /// item_type: String, /// domain: String, /// flags: u32, /// } /// /// fn service_browser_item_new_msg(m: &Message) -> Result { /// let mut iter = m.iter_init(); /// Ok(ServiceBrowserItemNew { /// interface: iter.read()?, /// protocol: iter.read()?, /// name: iter.read()?, /// item_type: iter.read()?, /// domain: iter.read()?, /// flags: iter.read()?, /// }) /// } /// ``` pub fn read>(&mut self) -> Result { let r = try!(self.get().ok_or_else(|| TypeMismatchError { expected: T::ARG_TYPE, found: self.arg_type(), position: self.2 })); self.next(); Ok(r) } /// If the current argument is a container of the specified arg_type, then a new /// Iter is returned which is for iterating over the contents inside the container. /// /// Primarily for internal use (the "get" function is more ergonomic), but could be /// useful for recursing into containers with unknown types. pub fn recurse(&mut self, arg_type: ArgType) -> Option> { let containers = [ArgType::Array, ArgType::DictEntry, ArgType::Struct, ArgType::Variant]; if !containers.iter().any(|&t| t == arg_type) { return None; } let mut subiter = ffi_iter(); unsafe { if ffi::dbus_message_iter_get_arg_type(&mut self.0) != arg_type as c_int { return None }; ffi::dbus_message_iter_recurse(&mut self.0, &mut subiter) } Some(Iter(subiter, self.1, 0)) } } impl<'a> fmt::Debug for Iter<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut z = self.clone(); let mut t = f.debug_tuple("Iter"); loop { t.field(&z.arg_type()); if !z.next() { break } } t.finish() } } impl<'a> Iterator for Iter<'a> { type Item = Box; fn next(&mut self) -> Option { let r = self.get_refarg(); if r.is_some() { self.next(); } r } } /// Type of Argument /// /// use this to figure out, e g, which type of argument is at the current position of Iter. #[repr(u8)] #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] pub enum ArgType { /// Dicts are Arrays of dict entries, so Dict types will have Array as ArgType. Array = ffi::DBUS_TYPE_ARRAY as u8, /// Variant Variant = ffi::DBUS_TYPE_VARIANT as u8, /// bool Boolean = ffi::DBUS_TYPE_BOOLEAN as u8, /// Invalid arg type - this is also the ArgType returned when there are no more arguments available. Invalid = ffi::DBUS_TYPE_INVALID as u8, /// String String = ffi::DBUS_TYPE_STRING as u8, /// Dict entry; you'll usually not encounter this one as dicts are arrays of dict entries. DictEntry = ffi::DBUS_TYPE_DICT_ENTRY as u8, /// u8 Byte = ffi::DBUS_TYPE_BYTE as u8, /// i16 Int16 = ffi::DBUS_TYPE_INT16 as u8, /// u16 UInt16 = ffi::DBUS_TYPE_UINT16 as u8, /// i32 Int32 = ffi::DBUS_TYPE_INT32 as u8, /// u32 UInt32 = ffi::DBUS_TYPE_UINT32 as u8, /// i64 Int64 = ffi::DBUS_TYPE_INT64 as u8, /// u64 UInt64 = ffi::DBUS_TYPE_UINT64 as u8, /// f64 Double = ffi::DBUS_TYPE_DOUBLE as u8, /// OwnedFd UnixFd = ffi::DBUS_TYPE_UNIX_FD as u8, /// Use tuples or Vec> to read/write structs. Struct = ffi::DBUS_TYPE_STRUCT as u8, /// Path ObjectPath = ffi::DBUS_TYPE_OBJECT_PATH as u8, /// Signature Signature = ffi::DBUS_TYPE_SIGNATURE as u8, } const ALL_ARG_TYPES: [(ArgType, &'static str); 18] = [(ArgType::Variant, "Variant"), (ArgType::Array, "Array/Dict"), (ArgType::Struct, "Struct"), (ArgType::String, "String"), (ArgType::DictEntry, "Dict entry"), (ArgType::ObjectPath, "Path"), (ArgType::Signature, "Signature"), (ArgType::UnixFd, "OwnedFd"), (ArgType::Boolean, "bool"), (ArgType::Byte, "u8"), (ArgType::Int16, "i16"), (ArgType::Int32, "i32"), (ArgType::Int64, "i64"), (ArgType::UInt16, "u16"), (ArgType::UInt32, "u32"), (ArgType::UInt64, "u64"), (ArgType::Double, "f64"), (ArgType::Invalid, "nothing")]; impl ArgType { /// A str corresponding to the name of a Rust type. pub fn as_str(self) -> &'static str { ALL_ARG_TYPES.iter().skip_while(|a| a.0 != self).next().unwrap().1 } /// Converts an i32 to an ArgType (or an error). pub fn from_i32(i: i32) -> Result { for &(a, _) in &ALL_ARG_TYPES { if a as i32 == i { return Ok(a); } } Err(format!("Invalid ArgType {} ({})", i, i as u8 as char)) } } /// Error struct to indicate a D-Bus argument type mismatch. /// /// Might be returned from `iter::read()`. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct TypeMismatchError { expected: ArgType, found: ArgType, position: u32, } impl TypeMismatchError { /// The ArgType we were trying to read, but failed pub fn expected_arg_type(&self) -> ArgType { self.expected } /// The ArgType we should have been trying to read, if we wanted the read to succeed pub fn found_arg_type(&self) -> ArgType { self.found } /// At what argument was the error found? /// /// Returns 0 for first argument, 1 for second argument, etc. pub fn pos(&self) -> u32 { self.position } } impl error::Error for TypeMismatchError { fn description(&self) -> &str { "D-Bus argument type mismatch" } fn cause(&self) -> Option<&error::Error> { None } } impl fmt::Display for TypeMismatchError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} at position {}: expected {}, found {}", (self as &error::Error).description(), self.position, self.expected.as_str(), if self.expected == self.found { "same but still different somehow" } else { self.found.as_str() } ) } } #[allow(dead_code)] fn test_compile() { let mut q = IterAppend::new(unsafe { mem::transmute(0usize) }); q.append(5u8); q.append(Array::new(&[5u8, 6, 7])); q.append((8u8, &[9u8, 6, 7][..])); q.append(Variant((6u8, 7u8))); } dbus-0.6.5/src/arg/msgarg.rs010064400017500001750000000407631351376776700141260ustar0000000000000000#![allow(dead_code)] use {Signature, Message, arg::TypeMismatchError}; use std::{fmt, any}; use std::sync::Arc; use std::rc::Rc; use super::{Iter, IterAppend, ArgType}; /// Types that can represent a D-Bus message argument implement this trait. /// /// Types should also implement either Append or Get to be useful. pub trait Arg { /// The corresponding D-Bus argument type code. const ARG_TYPE: ArgType; /// The corresponding D-Bus argument type code; just returns ARG_TYPE. /// /// For backwards compatibility. #[deprecated(note = "Use associated constant ARG_TYPE instead")] fn arg_type() -> ArgType { return Self::ARG_TYPE; } /// The corresponding D-Bus type signature for this type. fn signature() -> Signature<'static>; } /// Types that can be appended to a message as arguments implement this trait. pub trait Append: Sized { /// Performs the append operation. fn append(self, &mut IterAppend); } /// Helper trait to append many arguments to a message. pub trait AppendAll: Sized { /// Performs the append operation. fn append(self, &mut IterAppend); } /// Types that can be retrieved from a message as arguments implement this trait. pub trait Get<'a>: Sized { /// Performs the get operation. fn get(i: &mut Iter<'a>) -> Option; } /// Helper trait to read all arguments from a message. pub trait ReadAll: Sized { /// Performs the read operation. fn read(i: &mut Iter) -> Result; } /// Object safe version of Arg + Append + Get. pub trait RefArg: fmt::Debug { /// The corresponding D-Bus argument type code. fn arg_type(&self) -> ArgType; /// The corresponding D-Bus type signature for this type. fn signature(&self) -> Signature<'static>; /// Performs the append operation. fn append(&self, &mut IterAppend); /// Transforms this argument to Any (which can be downcasted to read the current value). /// /// Note: The internal representation of complex types (Array, Dict, Struct) is unstable /// and as_any should not be relied upon for these types. Use as_iter instead. fn as_any(&self) -> &any::Any where Self: 'static; /// Transforms this argument to Any (which can be downcasted to read the current value). /// /// Note: The internal representation of complex types (Array, Dict, Struct) is unstable /// and as_any should not be relied upon for these types. Use as_iter instead. /// /// # Panic /// Will panic if the interior cannot be made mutable, e g, if encapsulated /// inside a Rc with a reference count > 1. fn as_any_mut(&mut self) -> &mut any::Any where Self: 'static; /// Try to read the argument as an i64. /// /// Works for: Boolean, Byte, Int16, UInt16, Int32, UInt32, Int64, UnixFd. #[inline] fn as_i64(&self) -> Option { None } /// Try to read the argument as an u64. /// /// Works for: Boolean, Byte, Int16, UInt16, Int32, UInt32, UInt64. #[inline] fn as_u64(&self) -> Option { None } /// Try to read the argument as an f64. /// /// Works for: Boolean, Byte, Int16, UInt16, Int32, UInt32, Double. #[inline] fn as_f64(&self) -> Option { None } /// Try to read the argument as a str. /// /// Works for: String, ObjectPath, Signature. #[inline] fn as_str(&self) -> Option<&str> { None } /// Try to read the argument as an iterator. /// /// Works for: Array/Dict, Struct, Variant. #[inline] fn as_iter<'a>(&'a self) -> Option + 'a>> { None } /// Deep clone of the RefArg, causing the result to be 'static. /// /// Usable as an escape hatch in case of lifetime problems with RefArg. /// /// In case of complex types (Array, Dict, Struct), the clone is not guaranteed /// to have the same internal representation as the original. fn box_clone(&self) -> Box { unimplemented!() /* Needed for backwards comp */ } } impl<'a> Get<'a> for Box { fn get(i: &mut Iter<'a>) -> Option { i.get_refarg() } } /// Cast a RefArg as a specific type (shortcut for any + downcast) #[inline] pub fn cast<'a, T: 'static>(a: &'a (RefArg + 'static)) -> Option<&'a T> { a.as_any().downcast_ref() } /// Cast a RefArg as a specific type (shortcut for any_mut + downcast_mut) /// /// # Panic /// Will panic if the interior cannot be made mutable, e g, if encapsulated /// inside a Rc with a reference count > 1. #[inline] pub fn cast_mut<'a, T: 'static>(a: &'a mut (RefArg + 'static)) -> Option<&'a mut T> { a.as_any_mut().downcast_mut() } /// If a type implements this trait, it means the size and alignment is the same /// as in D-Bus. This means that you can quickly append and get slices of this type. /// /// Note: Booleans do not implement this trait because D-Bus booleans are 4 bytes and Rust booleans are 1 byte. pub unsafe trait FixedArray: Arg + 'static + Clone + Copy {} /// Types that can be used as keys in a dict type implement this trait. pub trait DictKey: Arg {} /// Simple lift over reference to value - this makes some iterators more ergonomic to use impl<'a, T: Arg> Arg for &'a T { const ARG_TYPE: ArgType = T::ARG_TYPE; fn signature() -> Signature<'static> { T::signature() } } impl<'a, T: Append + Clone> Append for &'a T { fn append(self, i: &mut IterAppend) { self.clone().append(i) } } impl<'a, T: DictKey> DictKey for &'a T {} impl<'a, T: RefArg + ?Sized> RefArg for &'a T { #[inline] fn arg_type(&self) -> ArgType { (&**self).arg_type() } #[inline] fn signature(&self) -> Signature<'static> { (&**self).signature() } #[inline] fn append(&self, i: &mut IterAppend) { (&**self).append(i) } #[inline] fn as_any(&self) -> &any::Any where T: 'static { (&**self).as_any() } #[inline] fn as_any_mut(&mut self) -> &mut any::Any where T: 'static { unreachable!() } #[inline] fn as_i64(&self) -> Option { (&**self).as_i64() } #[inline] fn as_u64(&self) -> Option { (&**self).as_u64() } #[inline] fn as_f64(&self) -> Option { (&**self).as_f64() } #[inline] fn as_str(&self) -> Option<&str> { (&**self).as_str() } #[inline] fn as_iter<'b>(&'b self) -> Option + 'b>> { (&**self).as_iter() } #[inline] fn box_clone(&self) -> Box { (&**self).box_clone() } } macro_rules! deref_impl { ($t: ident, $ss: ident, $make_mut: expr) => { impl RefArg for $t { #[inline] fn arg_type(&self) -> ArgType { (&**self).arg_type() } #[inline] fn signature(&self) -> Signature<'static> { (&**self).signature() } #[inline] fn append(&self, i: &mut IterAppend) { (&**self).append(i) } #[inline] fn as_any(&self) -> &any::Any where T: 'static { (&**self).as_any() } #[inline] fn as_any_mut<'a>(&'a mut $ss) -> &'a mut any::Any where T: 'static { $make_mut.as_any_mut() } #[inline] fn as_i64(&self) -> Option { (&**self).as_i64() } #[inline] fn as_u64(&self) -> Option { (&**self).as_u64() } #[inline] fn as_f64(&self) -> Option { (&**self).as_f64() } #[inline] fn as_str(&self) -> Option<&str> { (&**self).as_str() } #[inline] fn as_iter<'a>(&'a self) -> Option + 'a>> { (&**self).as_iter() } #[inline] fn box_clone(&self) -> Box { (&**self).box_clone() } } impl DictKey for $t {} impl Arg for $t { const ARG_TYPE: ArgType = T::ARG_TYPE; fn signature() -> Signature<'static> { T::signature() } } impl<'a, T: Get<'a>> Get<'a> for $t { fn get(i: &mut Iter<'a>) -> Option { T::get(i).map(|v| $t::new(v)) } } } } impl Append for Box { fn append(self, i: &mut IterAppend) { let q: T = *self; q.append(i) } } deref_impl!(Box, self, &mut **self ); deref_impl!(Rc, self, Rc::get_mut(self).unwrap()); deref_impl!(Arc, self, Arc::get_mut(self).unwrap()); /// Internal trait to help generics. Implemented for (), (A1), (A1, A2) and so on (where A1: Arg, A2: Arg etc). /// /// You would probably not use this trait directly, instead use generic functions which /// take ArgBuilder as an argument. It helps reading and appending multiple arguments /// to/from a message in one go. pub trait ArgBuilder: Sized { /// A tuple of &static str. Used for introspection. type strs; /// Low-level introspection helper method. fn strs_sig)>(a: Self::strs, f: F); /// Low-level method to read arguments from a message. fn read(msg: &Message) -> Result; /// Low-level method to append arguments to a message. fn append(self, msg: &mut Message); } impl ArgBuilder for () { type strs = (); fn strs_sig)>(_: Self::strs, _: F) {} fn read(_: &Message) -> Result { Ok(()) } fn append(self, _: &mut Message) {} } macro_rules! argbuilder_impl { ($($n: ident $t: ident $s: ty,)+) => { impl<$($t: Arg + Append + for<'z> Get<'z>),*> ArgBuilder for ($($t,)*) { type strs = ($(&'static $s,)*); fn strs_sig)>(z: Self::strs, mut q: Q) { let ( $($n,)*) = z; $( q($n, $t::signature()); )* } fn read(msg: &Message) -> Result { let mut ii = msg.iter_init(); $( let $n = ii.read()?; )* Ok(($( $n, )* )) } fn append(self, msg: &mut Message) { let ( $($n,)*) = self; let mut ia = IterAppend::new(msg); $( ia.append($n); )* } } impl<$($t: Append),*> AppendAll for ($($t,)*) { fn append(self, ia: &mut IterAppend) { let ( $($n,)*) = self; $( ia.append($n); )* } } impl<$($t: Arg + for<'z> Get<'z>),*> ReadAll for ($($t,)*) { fn read(ii: &mut Iter) -> Result { $( let $n = ii.read()?; )* Ok(($( $n, )* )) } } } } argbuilder_impl!(a A str,); argbuilder_impl!(a A str, b B str,); argbuilder_impl!(a A str, b B str, c C str,); argbuilder_impl!(a A str, b B str, c C str, d D str,); argbuilder_impl!(a A str, b B str, c C str, d D str, e E str,); argbuilder_impl!(a A str, b B str, c C str, d D str, e E str, f F str,); argbuilder_impl!(a A str, b B str, c C str, d D str, e E str, f F str, g G str,); argbuilder_impl!(a A str, b B str, c C str, d D str, e E str, f F str, g G str, h H str,); argbuilder_impl!(a A str, b B str, c C str, d D str, e E str, f F str, g G str, h H str, i I str,); argbuilder_impl!(a A str, b B str, c C str, d D str, e E str, f F str, g G str, h H str, i I str, j J str,); #[cfg(test)] mod test { extern crate tempdir; use {Connection, ConnectionItem, Message, BusType, Path, Signature}; use arg::{Array, Variant, Dict, Iter, ArgType, TypeMismatchError, RefArg, cast}; use std::collections::HashMap; #[test] fn refarg() { let c = Connection::get_private(BusType::Session).unwrap(); c.register_object_path("/mooh").unwrap(); let m = Message::new_method_call(&c.unique_name(), "/mooh", "com.example.hello", "Hello").unwrap(); let mut vv: Vec>> = vec!(); vv.push(Variant(Box::new(5i32))); vv.push(Variant(Box::new(String::from("Hello world")))); let m = m.append_ref(&vv); let (f1, f2) = (false, 7u64); let mut v: Vec<&RefArg> = vec!(); v.push(&f1); v.push(&f2); let m = m.append_ref(&v); let vi32 = vec![7i32, 9i32]; let vstr: Vec = ["This", "is", "dbus", "rs"].iter().map(|&s| s.into()).collect(); let m = m.append_ref(&[&vi32 as &RefArg, &vstr as &RefArg]); let mut map = HashMap::new(); map.insert(true, String::from("Yes")); map.insert(false, String::from("No")); let m = m.append_ref(&[&map as &RefArg, &1.5f64 as &RefArg]); c.send(m).unwrap(); for n in c.iter(1000) { if let ConnectionItem::MethodCall(m) = n { let rv: Vec> = m.iter_init().collect(); println!("Receiving {:?}", rv); let rv0: &Variant> = cast(&rv[0]).unwrap(); let rv00: &i32 = cast(&rv0.0).unwrap(); assert_eq!(rv00, &5i32); assert_eq!(Some(&false), rv[2].as_any().downcast_ref::()); assert_eq!(Some(&vi32), rv[4].as_any().downcast_ref::>()); assert_eq!(Some(&vstr), rv[5].as_any().downcast_ref::>()); let mut diter = rv[6].as_iter().unwrap(); { let mut mmap: HashMap = HashMap::new(); while let Some(k) = diter.next() { let x: String = diter.next().unwrap().as_str().unwrap().into(); mmap.insert(*cast::(&k.box_clone()).unwrap(), x); } assert_eq!(mmap[&true], "Yes"); } let mut iter = rv[6].as_iter().unwrap(); assert!(iter.next().unwrap().as_i64().is_some()); assert!(iter.next().unwrap().as_str().is_some()); assert!(iter.next().unwrap().as_str().is_none()); assert!(iter.next().unwrap().as_i64().is_none()); assert!(iter.next().is_none()); assert!(rv[7].as_f64().unwrap() > 1.0); assert!(rv[7].as_f64().unwrap() < 2.0); break; } } } #[test] fn message_types() { let c = Connection::get_private(BusType::Session).unwrap(); c.register_object_path("/hello").unwrap(); let m = Message::new_method_call(&c.unique_name(), "/hello", "com.example.hello", "Hello").unwrap(); let m = m.append1(2000u16); let m = m.append1(Array::new(&vec![129u8, 5, 254])); let m = m.append2(Variant(&["Hello", "world"][..]), &[32768u16, 16u16, 12u16][..]); let m = m.append3(-1i32, &*format!("Hello world"), -3.14f64); let m = m.append1((256i16, Variant(18_446_744_073_709_551_615u64))); let m = m.append2(Path::new("/a/valid/path").unwrap(), Signature::new("a{sv}").unwrap()); let mut z = HashMap::new(); z.insert(123543u32, true); z.insert(0u32, false); let m = m.append1(Dict::new(&z)); let sending = format!("{:?}", m.iter_init()); println!("Sending {}", sending); c.send(m).unwrap(); for n in c.iter(1000) { match n { ConnectionItem::MethodCall(m) => { use super::Arg; let receiving = format!("{:?}", m.iter_init()); println!("Receiving {}", receiving); assert_eq!(sending, receiving); assert_eq!(2000u16, m.get1().unwrap()); assert_eq!(m.get2(), (Some(2000u16), Some(&[129u8, 5, 254][..]))); assert_eq!(m.read2::().unwrap_err(), TypeMismatchError { position: 1, found: ArgType::Array, expected: ArgType::Boolean }); let mut g = m.iter_init(); let e = g.read::().unwrap_err(); assert_eq!(e.pos(), 0); assert_eq!(e.expected_arg_type(), ArgType::UInt32); assert_eq!(e.found_arg_type(), ArgType::UInt16); assert!(g.next() && g.next()); let v: Variant = g.get().unwrap(); let mut viter = v.0; assert_eq!(viter.arg_type(), Array::<&str,()>::ARG_TYPE); let a: Array<&str, _> = viter.get().unwrap(); assert_eq!(a.collect::>(), vec!["Hello", "world"]); assert!(g.next()); assert_eq!(g.get::(), None); // It's an array, not a single u16 assert!(g.next() && g.next() && g.next() && g.next()); assert_eq!(g.get(), Some((256i16, Variant(18_446_744_073_709_551_615u64)))); assert!(g.next()); assert_eq!(g.get(), Some(Path::new("/a/valid/path").unwrap())); assert!(g.next()); assert_eq!(g.get(), Some(Signature::new("a{sv}").unwrap())); assert!(g.next()); let d: Dict = g.get().unwrap(); let z2: HashMap<_, _> = d.collect(); assert_eq!(z, z2); break; } _ => println!("Got {:?}", n), } } } } dbus-0.6.5/src/arg/variantstruct_impl.rs010064400017500001750000000176531351376776700166020ustar0000000000000000use super::*; use {message, Signature}; use std::any; #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] /// A simple wrapper to specify a D-Bus variant. /// /// See the argument guide and module level documentation for details and examples. pub struct Variant(pub T); impl Variant> { /// Creates a new refarg from an Iter. Mainly for internal use. pub fn new_refarg<'a>(i: &mut Iter<'a>) -> Option { i.recurse(ArgType::Variant).and_then(|mut si| si.get_refarg()).map(|v| Variant(v)) } } impl Default for Variant> { // This is a bit silly, because there is no such thing as a default argument. // Unfortunately due to a design mistake while making the SignalArgs trait, we'll // have to work around that by adding a default implementation here. // https://github.com/diwic/dbus-rs/issues/136 fn default() -> Self { Variant(Box::new(0u8) as Box) } } impl Default for Variant { fn default() -> Self { Variant(T::default()) } } impl Arg for Variant { const ARG_TYPE: ArgType = ArgType::Variant; fn signature() -> Signature<'static> { unsafe { Signature::from_slice_unchecked(b"v\0") } } } impl Append for Variant { fn append(self, i: &mut IterAppend) { let z = self.0; i.append_container(ArgType::Variant, Some(T::signature().as_cstr()), |s| z.append(s)); } } impl Append for Variant { fn append(self, i: &mut IterAppend) { let z = self.0; let asig = z.signature(); let sig = asig.as_cstr(); i.append_container(ArgType::Variant, Some(&sig), |s| z.append(s)); } } impl Append for Variant> { fn append(self, i: &mut IterAppend) { let z = self.0; i.append_container(ArgType::Variant, Some(z.signature().as_cstr()), |s| z.append(s)); } } impl<'a, T: Get<'a>> Get<'a> for Variant { fn get(i: &mut Iter<'a>) -> Option> { i.recurse(ArgType::Variant).and_then(|mut si| si.get().map(|v| Variant(v))) } } impl<'a> Get<'a> for Variant> { fn get(i: &mut Iter<'a>) -> Option>> { i.recurse(ArgType::Variant).map(|v| Variant(v)) } } /* impl<'a> Get<'a> for Variant> { fn get(i: &mut Iter<'a>) -> Option>> { i.recurse(ArgType::Variant).and_then(|mut si| si.get_refarg().map(|v| Variant(v))) } } */ impl RefArg for Variant { fn arg_type(&self) -> ArgType { ArgType::Variant } fn signature(&self) -> Signature<'static> { unsafe { Signature::from_slice_unchecked(b"v\0") } } fn append(&self, i: &mut IterAppend) { let z = &self.0; i.append_container(ArgType::Variant, Some(z.signature().as_cstr()), |s| z.append(s)); } #[inline] fn as_any(&self) -> &any::Any where T: 'static { self } #[inline] fn as_any_mut(&mut self) -> &mut any::Any where T: 'static { self } #[inline] fn as_i64(&self) -> Option { self.0.as_i64() } #[inline] fn as_u64(&self) -> Option { self.0.as_u64() } #[inline] fn as_f64(&self) -> Option { self.0.as_f64() } #[inline] fn as_str(&self) -> Option<&str> { self.0.as_str() } #[inline] fn as_iter<'a>(&'a self) -> Option + 'a>> { use std::iter; let z: &RefArg = &self.0; Some(Box::new(iter::once(z))) } #[inline] fn box_clone(&self) -> Box { Box::new(Variant(self.0.box_clone())) } } macro_rules! struct_impl { ( $($n: ident $t: ident,)+ ) => { /// Tuples are represented as D-Bus structs. impl<$($t: Arg),*> Arg for ($($t,)*) { const ARG_TYPE: ArgType = ArgType::Struct; fn signature() -> Signature<'static> { let mut s = String::from("("); $( s.push_str(&$t::signature()); )* s.push_str(")"); Signature::from(s) } } impl<$($t: Append),*> Append for ($($t,)*) { fn append(self, i: &mut IterAppend) { let ( $($n,)*) = self; i.append_container(ArgType::Struct, None, |s| { $( $n.append(s); )* }); } } impl<'a, $($t: Get<'a>),*> Get<'a> for ($($t,)*) { fn get(i: &mut Iter<'a>) -> Option { let si = i.recurse(ArgType::Struct); if si.is_none() { return None; } let mut si = si.unwrap(); let mut _valid_item = true; $( if !_valid_item { return None; } let $n: Option<$t> = si.get(); if $n.is_none() { return None; } _valid_item = si.next(); )* Some(($( $n.unwrap(), )* )) } } impl<$($t: RefArg),*> RefArg for ($($t,)*) { fn arg_type(&self) -> ArgType { ArgType::Struct } fn signature(&self) -> Signature<'static> { let &( $(ref $n,)*) = self; let mut s = String::from("("); $( s.push_str(&$n.signature()); )* s.push_str(")"); Signature::from(s) } fn append(&self, i: &mut IterAppend) { let &( $(ref $n,)*) = self; i.append_container(ArgType::Struct, None, |s| { $( $n.append(s); )* }); } fn as_any(&self) -> &any::Any where Self: 'static { self } fn as_any_mut(&mut self) -> &mut any::Any where Self: 'static { self } fn as_iter<'a>(&'a self) -> Option + 'a>> { let &( $(ref $n,)*) = self; let v = vec!( $( $n as &RefArg, )* ); Some(Box::new(v.into_iter())) } #[inline] fn box_clone(&self) -> Box { let &( $(ref $n,)*) = self; let mut z = vec!(); $( z.push($n.box_clone()); )* Box::new(z) } } }} // macro_rules end struct_impl!(a A,); struct_impl!(a A, b B,); struct_impl!(a A, b B, c C,); struct_impl!(a A, b B, c C, d D,); struct_impl!(a A, b B, c C, d D, e E,); struct_impl!(a A, b B, c C, d D, e E, f F,); struct_impl!(a A, b B, c C, d D, e E, f F, g G,); struct_impl!(a A, b B, c C, d D, e E, f F, g G, h H,); struct_impl!(a A, b B, c C, d D, e E, f F, g G, h H, i I,); struct_impl!(a A, b B, c C, d D, e E, f F, g G, h H, i I, j J,); struct_impl!(a A, b B, c C, d D, e E, f F, g G, h H, i I, j J, k K,); struct_impl!(a A, b B, c C, d D, e E, f F, g G, h H, i I, j J, k K, l L,); impl RefArg for Vec> { fn arg_type(&self) -> ArgType { ArgType::Struct } fn signature(&self) -> Signature<'static> { let mut s = String::from("("); for z in self { s.push_str(&z.signature()); } s.push_str(")"); Signature::from(s) } fn append(&self, i: &mut IterAppend) { i.append_container(ArgType::Struct, None, |s| { for z in self { z.append(s); } }); } #[inline] fn as_any(&self) -> &any::Any where Self: 'static { self } #[inline] fn as_any_mut(&mut self) -> &mut any::Any where Self: 'static { self } fn as_iter<'a>(&'a self) -> Option + 'a>> { Some(Box::new(self.iter().map(|b| &**b))) } #[inline] fn box_clone(&self) -> Box { let t: Vec> = self.iter().map(|x| x.box_clone()).collect(); Box::new(t) } } impl Append for message::MessageItem { fn append(self, i: &mut IterAppend) { message::append_messageitem(&mut i.0, &self) } } impl<'a> Get<'a> for message::MessageItem { fn get(i: &mut Iter<'a>) -> Option { message::get_messageitem(&mut i.0) } } impl RefArg for message::MessageItem { fn arg_type(&self) -> ArgType { ArgType::from_i32(self.array_type()).unwrap() } fn signature(&self) -> Signature<'static> { message::MessageItem::signature(&self) } fn append(&self, i: &mut IterAppend) { message::append_messageitem(&mut i.0, self) } #[inline] fn as_any(&self) -> &any::Any where Self: 'static { self } #[inline] fn as_any_mut(&mut self) -> &mut any::Any where Self: 'static { self } #[inline] fn box_clone(&self) -> Box { Box::new(self.clone()) } } dbus-0.6.5/src/connection.rs010064400017500001750000000701551351376776700142320ustar0000000000000000use super::{Error, ffi, to_c_str, c_str_to_slice, Watch, Message, MessageType, BusName, Path, ConnPath}; use super::{RequestNameReply, ReleaseNameReply, BusType}; use super::watch::WatchList; use std::{fmt, mem, ptr, thread, panic, ops}; use std::collections::VecDeque; use std::cell::{Cell, RefCell}; use std::os::unix::io::RawFd; use std::os::raw::{c_void, c_char, c_int, c_uint}; /// The type of function to use for replacing the message callback. /// /// See the documentation for Connection::replace_message_callback for more information. pub type MessageCallback = Box bool + 'static>; #[repr(C)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] /// Flags to use for Connection::register_name. /// /// More than one flag can be specified, if so just add their values. pub enum DBusNameFlag { /// Allow another service to become the primary owner if requested AllowReplacement = ffi::DBUS_NAME_FLAG_ALLOW_REPLACEMENT as isize, /// Request to replace the current primary owner ReplaceExisting = ffi::DBUS_NAME_FLAG_REPLACE_EXISTING as isize, /// If we can not become the primary owner do not place us in the queue DoNotQueue = ffi::DBUS_NAME_FLAG_DO_NOT_QUEUE as isize, } impl DBusNameFlag { /// u32 value of flag. pub fn value(self) -> u32 { self as u32 } } /// 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, including method return errors (mostly used for Async I/O) MethodReturn(Message), } impl From for ConnectionItem { fn from(m: Message) -> Self { let mtype = m.msg_type(); match mtype { MessageType::Signal => ConnectionItem::Signal(m), MessageType::MethodReturn => ConnectionItem::MethodReturn(m), MessageType::Error => ConnectionItem::MethodReturn(m), MessageType::MethodCall => ConnectionItem::MethodCall(m), _ => panic!("unknown message type {:?} received from D-Bus", mtype), } } } /// ConnectionItem iterator pub struct ConnectionItems<'a> { c: &'a Connection, timeout_ms: Option, end_on_timeout: bool, handlers: MsgHandlerList, } impl<'a> ConnectionItems<'a> { /// Builder method that adds a new msg handler. /// /// Note: Likely to changed/refactored/removed in next release pub fn with(mut self, h: H) -> Self { self.handlers.push(Box::new(h)); self } // Returns true if processed, false if not fn process_handlers(&mut self, ci: &ConnectionItem) -> bool { let m = match *ci { ConnectionItem::MethodReturn(ref msg) => msg, ConnectionItem::Signal(ref msg) => msg, ConnectionItem::MethodCall(ref msg) => msg, ConnectionItem::Nothing => return false, }; msghandler_process(&mut self.handlers, m, &self.c) } /// Access and modify message handlers /// /// Note: Likely to changed/refactored/removed in next release pub fn msg_handlers(&mut self) -> &mut Vec> { &mut self.handlers } /// Creates a new ConnectionItems iterator /// /// For io_timeout, setting None means the fds will not be read/written. I e, only pending /// items in libdbus's internal queue will be processed. /// /// For end_on_timeout, setting false will means that the iterator will never finish (unless /// the D-Bus server goes down). Instead, ConnectionItem::Nothing will be returned in case no /// items are in queue. pub fn new(conn: &'a Connection, io_timeout: Option, end_on_timeout: bool) -> Self { ConnectionItems { c: conn, timeout_ms: io_timeout, end_on_timeout: end_on_timeout, handlers: Vec::new(), } } } impl<'a> Iterator for ConnectionItems<'a> { type Item = ConnectionItem; fn next(&mut self) -> Option { loop { if self.c.i.filter_cb.borrow().is_none() { panic!("ConnectionItems::next called recursively or with a MessageCallback set to None"); } let i: Option = self.c.next_msg().map(|x| x.into()); if let Some(ci) = i { if !self.process_handlers(&ci) { return Some(ci); } } if let Some(t) = self.timeout_ms { let r = unsafe { ffi::dbus_connection_read_write_dispatch(self.c.conn(), t as c_int) }; self.c.check_panic(); if !self.c.i.pending_items.borrow().is_empty() { continue }; if r == 0 { return None; } } let r = unsafe { ffi::dbus_connection_dispatch(self.c.conn()) }; self.c.check_panic(); if !self.c.i.pending_items.borrow().is_empty() { continue }; if r == ffi::DBusDispatchStatus::DataRemains { continue }; if r == ffi::DBusDispatchStatus::Complete { return if self.end_on_timeout { None } else { Some(ConnectionItem::Nothing) } }; panic!("dbus_connection_dispatch failed"); } } } /// Iterator over incoming messages on a connection. #[derive(Debug, Clone)] pub struct ConnMsgs { /// The connection or some reference to it. pub conn: C, /// How many ms dbus should block, waiting for incoming messages until timing out. /// /// If set to None, the dbus library will not read/write from file descriptors at all. /// Instead the iterator will end when there's nothing currently in the queue. pub timeout_ms: Option, } impl> Iterator for ConnMsgs { type Item = Message; fn next(&mut self) -> Option { loop { let iconn = &self.conn.i; if iconn.filter_cb.borrow().is_none() { panic!("ConnMsgs::next called recursively or with a MessageCallback set to None"); } let i = self.conn.next_msg(); if let Some(ci) = i { return Some(ci); } if let Some(t) = self.timeout_ms { let r = unsafe { ffi::dbus_connection_read_write_dispatch(self.conn.conn(), t as c_int) }; self.conn.check_panic(); if !iconn.pending_items.borrow().is_empty() { continue }; if r == 0 { return None; } } let r = unsafe { ffi::dbus_connection_dispatch(self.conn.conn()) }; self.conn.check_panic(); if !iconn.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>, handlers: RefCell, filter_cb: RefCell>, filter_cb_panic: RefCell>, } /// A D-Bus connection. Start here if you want to get on the D-Bus! pub struct Connection { i: Box, } pub fn conn_handle(c: &Connection) -> *mut ffi::DBusConnection { c.i.conn.get() } extern "C" fn filter_message_cb(conn: *mut ffi::DBusConnection, msg: *mut ffi::DBusMessage, user_data: *mut c_void) -> ffi::DBusHandlerResult { let i: &IConnection = unsafe { mem::transmute(user_data) }; let connref: panic::AssertUnwindSafe<&Connection> = unsafe { mem::transmute(&i) }; if i.conn.get() != conn || i.filter_cb_panic.try_borrow().is_err() { // This should never happen, but let's be extra sure // process::abort(); ?? return ffi::DBusHandlerResult::Handled; } if i.filter_cb_panic.borrow().is_err() { // We're in panic mode. Let's quit this ASAP return ffi::DBusHandlerResult::Handled; } let fcb = panic::AssertUnwindSafe(&i.filter_cb); let r = panic::catch_unwind(|| { let m = Message::from_ptr(msg, true); let mut cb = fcb.borrow_mut().take().unwrap(); // Take the callback out while we call it. let r = cb(connref.0, m); let mut cb2 = fcb.borrow_mut(); // If the filter callback has not been replaced, put it back in. if cb2.is_none() { *cb2 = Some(cb) }; r }); match r { Ok(false) => ffi::DBusHandlerResult::NotYetHandled, Ok(true) => ffi::DBusHandlerResult::Handled, Err(e) => { *i.filter_cb_panic.borrow_mut() = Err(e); ffi::DBusHandlerResult::Handled } } } fn default_filter_callback(c: &Connection, m: Message) -> bool { let b = m.msg_type() == MessageType::Signal; c.i.pending_items.borrow_mut().push_back(m); b } extern "C" fn object_path_message_cb(_conn: *mut ffi::DBusConnection, _msg: *mut ffi::DBusMessage, _user_data: *mut c_void) -> ffi::DBusHandlerResult { /* Already pushed in filter_message_cb, so we just set the handled flag here to disable the "default" handler. */ ffi::DBusHandlerResult::Handled } impl Connection { #[inline(always)] fn conn(&self) -> *mut ffi::DBusConnection { self.i.conn.get() } fn conn_from_ptr(conn: *mut ffi::DBusConnection) -> Result { let mut c = Connection { i: Box::new(IConnection { conn: Cell::new(conn), pending_items: RefCell::new(VecDeque::new()), watches: None, handlers: RefCell::new(vec!()), filter_cb: RefCell::new(Some(Box::new(default_filter_callback))), filter_cb_panic: RefCell::new(Ok(())), })}; /* 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), mem::transmute(&*c.i), None) } != 0); c.i.watches = Some(WatchList::new(&c, Box::new(|_| {}))); Ok(c) } /// 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) } Self::conn_from_ptr(conn) } /// Creates a new D-Bus connection to a remote address. /// /// Note: for all common cases (System / Session bus) you probably want "get_private" instead. pub fn open_private(address: &str) -> Result { let mut e = Error::empty(); let conn = unsafe { ffi::dbus_connection_open_private(to_c_str(address).as_ptr(), e.get_mut()) }; if conn == ptr::null_mut() { return Err(e) } Self::conn_from_ptr(conn) } /// Registers a new D-Bus connection with the bus. /// /// Note: `get_private` does this automatically, useful with `open_private` pub fn register(&self) -> Result<(), Error> { let mut e = Error::empty(); if unsafe { ffi::dbus_bus_register(self.conn(), e.get_mut()) == 0 } { Err(e) } else { Ok(()) } } /// Gets whether the connection is currently open. pub fn is_connected(&self) -> bool { unsafe { ffi::dbus_connection_get_is_connected(self.conn()) != 0 } } /// 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(), msg.ptr(), timeout_ms as c_int, e.get_mut()) }; if response == ptr::null_mut() { return Err(e); } Ok(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(), msg.ptr(), &mut serial) }; if r == 0 { return Err(()); } unsafe { ffi::dbus_connection_flush(self.conn()) }; Ok(serial) } /// Sends a message over the D-Bus, returning a MessageReply. /// /// Call add_handler on the result to start waiting for reply. This should be done before next call to `incoming` or `iter`. pub fn send_with_reply<'a, F: FnOnce(Result<&Message, Error>) + 'a>(&self, msg: Message, f: F) -> Result, ()> { let serial = self.send(msg)?; Ok(MessageReply(Some(f), serial)) } /// Adds a message handler to the connection. /// /// # Example /// /// ``` /// use std::{cell, rc}; /// use dbus::{Connection, Message, BusType}; /// /// let c = Connection::get_private(BusType::Session).unwrap(); /// let m = Message::new_method_call("org.freedesktop.DBus", "/", "org.freedesktop.DBus", "ListNames").unwrap(); /// /// let done: rc::Rc> = Default::default(); /// let done2 = done.clone(); /// c.add_handler(c.send_with_reply(m, move |reply| { /// let v: Vec<&str> = reply.unwrap().read1().unwrap(); /// println!("The names on the D-Bus are: {:?}", v); /// done2.set(true); /// }).unwrap()); /// while !done.get() { c.incoming(100).next(); } /// ``` pub fn add_handler(&self, h: H) { let h = Box::new(h); self.i.handlers.borrow_mut().push(h); } /// Removes a MsgHandler from the connection. /// /// If there are many MsgHandlers, it is not specified which one will be returned. /// /// There might be more methods added later on, which give better ways to deal /// with the list of MsgHandler currently on the connection. If this would help you, /// please [file an issue](https://github.com/diwic/dbus-rs/issues). pub fn extract_handler(&self) -> Option> { self.i.handlers.borrow_mut().pop() } /// 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 /// /// If there are no incoming events, ConnectionItems::Nothing will be returned. /// See ConnectionItems::new if you want to customize this behaviour. pub fn iter(&self, timeout_ms: i32) -> ConnectionItems { ConnectionItems::new(self, Some(timeout_ms), false) } /// Check if there are new incoming events /// /// Supersedes "iter". pub fn incoming(&self, timeout_ms: u32) -> ConnMsgs<&Self> { ConnMsgs { conn: &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), dbus_internal_pad1: None, dbus_internal_pad2: None, dbus_internal_pad3: None, dbus_internal_pad4: None, }; let r = unsafe { let user_data: *mut 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 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. /// /// See the `unity_focused_window` example for how to use this to catch signals. /// (The syntax of the "rule" string is specified in the [D-Bus specification](https://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing-match-rules).) 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. /// /// See the `Watch` struct for an example. 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. /// /// See the `Watch` struct for an example. pub fn watch_handle(&self, fd: RawFd, flags: c_uint) -> ConnectionItems { self.i.watches.as_ref().unwrap().watch_handle(fd, flags); ConnectionItems::new(self, None, true) } /// Create a convenience struct for easier calling of many methods on the same destination and path. pub fn with_path<'a, D: Into>, P: Into>>(&'a self, dest: D, path: P, timeout_ms: i32) -> ConnPath<'a, &'a Connection> { ConnPath { conn: self, dest: dest.into(), path: path.into(), timeout: timeout_ms } } /// Replace the default message callback. Returns the previously set callback. /// /// By default, when you call ConnectionItems::next, all relevant incoming messages /// are returned through the ConnectionItems iterator, and /// irrelevant messages are passed on to libdbus's default handler. /// If you need to customize this behaviour (i e, to handle all incoming messages yourself), /// you can set this message callback yourself. A few caveats apply: /// /// Return true from the callback to disable libdbus's internal handling of the message, or /// false to allow it. In other words, true and false correspond to /// `DBUS_HANDLER_RESULT_HANDLED` and `DBUS_HANDLER_RESULT_NOT_YET_HANDLED` respectively. /// /// Be sure to call the previously set callback from inside your callback, /// if you want, e.g. ConnectionItems::next to yield the message. /// /// You can unset the message callback (might be useful to satisfy the borrow checker), but /// you will get a panic if you call ConnectionItems::next while the message callback is unset. /// The message callback will be temporary unset while inside a message callback, so calling /// ConnectionItems::next recursively will also result in a panic. /// /// If your message callback panics, ConnectionItems::next will panic, too. /// /// # Examples /// /// Replace the default callback with our own: /// /// ```ignore /// use dbus::{Connection, BusType}; /// let c = Connection::get_private(BusType::Session).unwrap(); /// // Set our callback /// c.replace_message_callback(Some(Box::new(move |conn, msg| { /// println!("Got message: {:?}", msg.get_items()); /// // Let libdbus handle some things by default, /// // like "nonexistent object" error replies to method calls /// false /// }))); /// /// for _ in c.iter(1000) { /// // Only `ConnectionItem::Nothing` would be ever yielded here. /// } /// ``` /// /// Chain our callback to filter out some messages before `iter().next()`: /// /// ``` /// use dbus::{Connection, BusType, MessageType}; /// let c = Connection::get_private(BusType::Session).unwrap(); /// // Take the previously set callback /// let mut old_cb = c.replace_message_callback(None).unwrap(); /// // Set our callback /// c.replace_message_callback(Some(Box::new(move |conn, msg| { /// // Handle all signals on the spot /// if msg.msg_type() == MessageType::Signal { /// println!("Got signal: {:?}", msg.get_items()); /// // Stop all further processing of the message /// return true; /// } /// // Delegate the rest of the messages to the previous callback /// // in chain, e.g. to have them yielded by `iter().next()` /// old_cb(conn, msg) /// }))); /// /// # if false { /// for _ in c.iter(1000) { /// // `ConnectionItem::Signal` would never be yielded here. /// } /// # } /// ``` pub fn replace_message_callback(&self, f: Option) -> Option { mem::replace(&mut *self.i.filter_cb.borrow_mut(), f) } /// Sets a callback to be called if a file descriptor status changes. /// /// For async I/O. In rare cases, the number of fds to poll for read/write can change. /// If this ever happens, you'll get a callback. The watch changed is provided as a parameter. /// /// In rare cases this might not even happen in the thread calling anything on the connection, /// so the callback needs to be `Send`. /// A mutex is held during the callback. If you try to call set_watch_callback from a callback, /// you will deadlock. /// /// (Previously, this was instead put in a ConnectionItem queue, but this was not working correctly. /// see https://github.com/diwic/dbus-rs/issues/99 for additional info.) pub fn set_watch_callback(&self, f: Box) { self.i.watches.as_ref().unwrap().set_on_update(f); } fn check_panic(&self) { let p = mem::replace(&mut *self.i.filter_cb_panic.borrow_mut(), Ok(())); if let Err(perr) = p { panic::resume_unwind(perr); } } fn next_msg(&self) -> Option { while let Some(msg) = self.i.pending_items.borrow_mut().pop_front() { let mut v: MsgHandlerList = mem::replace(&mut *self.i.handlers.borrow_mut(), vec!()); let b = msghandler_process(&mut v, &msg, self); let mut v2 = self.i.handlers.borrow_mut(); v.append(&mut *v2); *v2 = v; if !b { return Some(msg) }; }; None } } impl Drop for Connection { fn drop(&mut self) { unsafe { ffi::dbus_connection_close(self.conn()); ffi::dbus_connection_unref(self.conn()); } } } impl fmt::Debug for Connection { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "D-Bus Connection({})", self.unique_name()) } } #[derive(Clone, Debug)] /// Type of messages to be handled by a MsgHandler. /// /// Note: More variants can be added in the future; but unless you're writing your own D-Bus engine /// you should not have to match on these anyway. pub enum MsgHandlerType { /// Handle all messages All, /// Handle only messages of a specific type MsgType(MessageType), /// Handle only method replies with this serial number Reply(u32), } impl MsgHandlerType { fn matches_msg(&self, m: &Message) -> bool { match *self { MsgHandlerType::All => true, MsgHandlerType::MsgType(t) => m.msg_type() == t, MsgHandlerType::Reply(serial) => { let t = m.msg_type(); ((t == MessageType::MethodReturn) || (t == MessageType::Error)) && (m.get_reply_serial() == Some(serial)) } } } } /// A trait for handling incoming messages. pub trait MsgHandler { /// Type of messages for which the handler will be called /// /// Note: The return value of this function might be cached, so it must return the same value all the time. fn handler_type(&self) -> MsgHandlerType; /// Function to be called if the message matches the MsgHandlerType fn handle_msg(&mut self, _msg: &Message) -> Option { None } } /// The result from MsgHandler::handle. #[derive(Debug, Default)] pub struct MsgHandlerResult { /// Indicates that the message has been dealt with and should not be processed further. pub handled: bool, /// Indicates that this MsgHandler no longer wants to receive messages and should be removed. pub done: bool, /// Messages to send (e g, a reply to a method call) pub reply: Vec, } type MsgHandlerList = Vec>; fn msghandler_process(v: &mut MsgHandlerList, m: &Message, c: &Connection) -> bool { let mut ii: isize = -1; loop { ii += 1; let i = ii as usize; if i >= v.len() { return false }; if !v[i].handler_type().matches_msg(m) { continue; } if let Some(r) = v[i].handle_msg(m) { for msg in r.reply.into_iter() { c.send(msg).unwrap(); } if r.done { v.remove(i); ii -= 1; } if r.handled { return true; } } } } /// The struct returned from `Connection::send_and_reply`. /// /// It implements the `MsgHandler` trait so you can use `Connection::add_handler`. pub struct MessageReply(Option, u32); impl<'a, F: FnOnce(Result<&Message, Error>) + 'a> MsgHandler for MessageReply { fn handler_type(&self) -> MsgHandlerType { MsgHandlerType::Reply(self.1) } fn handle_msg(&mut self, msg: &Message) -> Option { let e = match msg.msg_type() { MessageType::MethodReturn => Ok(msg), MessageType::Error => Err(msg.set_error_from_msg().unwrap_err()), _ => unreachable!(), }; debug_assert_eq!(msg.get_reply_serial(), Some(self.1)); self.0.take().unwrap()(e); return Some(MsgHandlerResult { handled: true, done: true, reply: Vec::new() }) } } #[test] fn message_reply() { use std::{cell, rc}; let c = Connection::get_private(BusType::Session).unwrap(); assert!(c.is_connected()); let m = Message::new_method_call("org.freedesktop.DBus", "/", "org.freedesktop.DBus", "ListNames").unwrap(); let quit = rc::Rc::new(cell::Cell::new(false)); let quit2 = quit.clone(); let reply = c.send_with_reply(m, move |result| { let r = result.unwrap(); let _: ::arg::Array<&str, _> = r.get1().unwrap(); quit2.set(true); }).unwrap(); for _ in c.iter(1000).with(reply) { if quit.get() { return; } } assert!(false); } dbus-0.6.5/src/connection2.rs010064400017500001750000000166121351376776700143120ustar0000000000000000use crate::{BusType, Error, Message, to_c_str, Watch}; use std::{ptr, str}; use std::ffi::CStr; use std::os::raw::{c_void}; #[derive(Debug)] pub struct ConnHandle(*mut ffi::DBusConnection); unsafe impl Send for ConnHandle {} unsafe impl Sync for ConnHandle {} impl Drop for ConnHandle { fn drop(&mut self) { unsafe { ffi::dbus_connection_close(self.0); ffi::dbus_connection_unref(self.0); } } } /// Experimental rewrite of Connection [unstable / experimental] /// /// Slightly lower level, with better support for async operations. /// Also, this struct is Send + Sync. /// /// Blocking operations should be clearly marked as such, although if you /// try to access the connection from several threads at the same time, /// blocking might occur due to an internal mutex inside the dbus library. /// /// This version avoids dbus_connection_dispatch, and thus avoids /// callbacks from that function. Instead the same functionality needs to be /// implemented by these bindings somehow - this is not done yet. #[derive(Debug)] pub struct TxRx { handle: ConnHandle, } impl TxRx { #[inline(always)] pub (crate) fn conn(&self) -> *mut ffi::DBusConnection { self.handle.0 } fn conn_from_ptr(ptr: *mut ffi::DBusConnection) -> Result { let handle = ConnHandle(ptr); /* No, we don't want our app to suddenly quit if dbus goes down */ unsafe { ffi::dbus_connection_set_exit_on_disconnect(ptr, 0) }; let c = TxRx { handle }; Ok(c) } /// Creates a new D-Bus connection. /// /// Blocking: until the connection is up and running. 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) } Self::conn_from_ptr(conn) } /// Creates a new D-Bus connection to a remote address. /// /// Note: for all common cases (System / Session bus) you probably want "get_private" instead. /// /// Blocking: until the connection is established. pub fn open_private(address: &str) -> Result { let mut e = Error::empty(); let conn = unsafe { ffi::dbus_connection_open_private(to_c_str(address).as_ptr(), e.get_mut()) }; if conn == ptr::null_mut() { return Err(e) } Self::conn_from_ptr(conn) } /// Registers a new D-Bus connection with the bus. /// /// Note: `get_private` does this automatically, useful with `open_private` /// /// Blocking: until a "Hello" response is received from the server. pub fn register(&mut self) -> Result<(), Error> { // This function needs to take &mut self, because it changes unique_name and unique_name takes a &self let mut e = Error::empty(); if unsafe { ffi::dbus_bus_register(self.conn(), e.get_mut()) == 0 } { Err(e) } else { Ok(()) } } /// Gets whether the connection is currently open. pub fn is_connected(&self) -> bool { unsafe { ffi::dbus_connection_get_is_connected(self.conn()) != 0 } } /// Get the connection's unique name. /// /// It's usually something like ":1.54" pub fn unique_name(&self) -> Option<&str> { let c = unsafe { ffi::dbus_bus_get_unique_name(self.conn()) }; if c == ptr::null_mut() { return None; } let s = unsafe { CStr::from_ptr(c) }; str::from_utf8(s.to_bytes()).ok() } /// Puts a message into libdbus out queue. Use "flush" or "read_write" to make sure it is sent over the wire. /// /// Returns a serial number than can be used to match against a reply. pub fn send(&self, msg: Message) -> Result { let mut serial = 0u32; let r = unsafe { ffi::dbus_connection_send(self.conn(), msg.ptr(), &mut serial) }; if r == 0 { return Err(()); } Ok(serial) } /// Flush the queue of outgoing messages. /// /// Blocking: until the outgoing queue is empty. pub fn flush(&self) { unsafe { ffi::dbus_connection_flush(self.conn()) } } /// Read and write to the connection. /// /// Incoming messages are put in the internal queue, outgoing messages are written. /// /// Blocking: If there are no messages, for up to timeout_ms milliseconds, or forever if timeout_ms is None. /// For non-blocking behaviour, set timeout_ms to Some(0). pub fn read_write(&self, timeout_ms: Option) -> Result<(), ()> { let t = timeout_ms.unwrap_or(-1); if unsafe { ffi::dbus_connection_read_write(self.conn(), t) == 0 } { Err(()) } else { Ok(()) } } /// Removes a message from the incoming queue, or returns None if the queue is empty. /// /// Use "read_write" first, so that messages are put into the incoming queue. /// For unhandled messages, please call MessageDispatcher::default_dispatch to return /// default replies for method calls. pub fn pop_message(&self) -> Option { let mptr = unsafe { ffi::dbus_connection_pop_message(self.conn()) }; if mptr == ptr::null_mut() { None } else { Some(Message::from_ptr(mptr, false)) } } /// Get an up-to-date list of file descriptors to watch. /// /// Might be changed into something that allows for callbacks when the watch list is changed. pub fn watch_fds(&mut self) -> Result, ()> { extern "C" fn add_watch_cb(watch: *mut ffi::DBusWatch, data: *mut c_void) -> u32 { unsafe { let wlist: &mut Vec = &mut *(data as *mut _); wlist.push(Watch::from_raw(watch)); } 1 } let mut r = vec!(); if unsafe { ffi::dbus_connection_set_watch_functions(self.conn(), Some(add_watch_cb), None, None, &mut r as *mut _ as *mut _, None) } == 0 { return Err(()) } assert!(unsafe { ffi::dbus_connection_set_watch_functions(self.conn(), None, None, None, ptr::null_mut(), None) } != 0); Ok(r) } } #[test] fn test_txrx_send_sync() { fn is_send(_: &T) {} fn is_sync(_: &T) {} let c = TxRx::get_private(BusType::Session).unwrap(); is_send(&c); is_sync(&c); } #[test] fn txrx_simple_test() { let mut c = TxRx::get_private(BusType::Session).unwrap(); assert!(c.is_connected()); let fds = c.watch_fds().unwrap(); println!("{:?}", fds); assert!(fds.len() > 0); let m = Message::new_method_call("org.freedesktop.DBus", "/", "org.freedesktop.DBus", "ListNames").unwrap(); let reply = c.send(m).unwrap(); let my_name = c.unique_name().unwrap(); loop { while let Some(mut msg) = c.pop_message() { println!("{:?}", msg); if msg.get_reply_serial() == Some(reply) { let r = msg.as_result().unwrap(); let z: ::arg::Array<&str, _> = r.get1().unwrap(); for n in z { println!("{}", n); if n == my_name { return; } // Hooray, we found ourselves! } assert!(false); } else if let Some(r) = crate::MessageDispatcher::<()>::default_dispatch(&msg) { c.send(r).unwrap(); } } c.read_write(Some(100)).unwrap(); } } dbus-0.6.5/src/crossroads/crossroads.rs010064400017500001750000000205301346705304100164040ustar0000000000000000use std::collections::BTreeMap; use std::any::{TypeId, Any}; use std::ffi::{CString, CStr}; use std::fmt; use crate::{Path as PathName, Interface as IfaceName, Member as MemberName, Signature, Message, MessageType}; use super::info::{IfaceInfo, MethodInfo, PropInfo, IfaceInfoBuilder}; use super::handlers::{Handlers, Par, ParInfo, Mut, MutCtx, MutMethods}; use super::stdimpl::DBusProperties; // The key is an IfaceName, but if we have that we bump into https://github.com/rust-lang/rust/issues/59732 // so we use CString as a workaround. #[derive(Default, Debug)] struct IfaceReg(BTreeMap)>); #[derive(Default)] pub struct PathData(Vec<(TypeId, H::Iface)>); impl PathData { pub fn insert_par(&mut self, i: I) { let id = TypeId::of::(); let t = Box::new(i); self.0.push((id, t)); } } impl PathData { pub fn insert_mut(&mut self, i: I) { let id = TypeId::of::(); let t = Box::new(i); self.0.push((id, t)); } } impl fmt::Debug for PathData { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "...") } } impl PathData { fn new() -> Self { PathData(vec!()) } } #[derive(Debug)] struct IfacePaths(BTreeMap>); impl Default for IfacePaths { fn default() -> Self { IfacePaths(BTreeMap::new()) } } struct MsgHeaders<'a> { m: MemberName<'a>, i: IfaceName<'a>, p: PathName<'a>, } fn msg_headers(msg: &Message) -> Option { if msg.msg_type() != MessageType::MethodCall { return None }; let p = msg.path()?; let i = msg.interface()?; let m = msg.member()?; Some(MsgHeaders { m, i, p }) } #[derive(Debug)] pub (super) struct MLookup<'a, H: Handlers> { pub (super) cr: &'a Crossroads, pub (super) data: &'a PathData, pub (super) iface: &'a H::Iface, pub (super) iinfo: &'a IfaceInfo<'static, H>, // pub (super) minfo: Option<&'a MethodInfo<'static, H>>, // pub (super) pinfo: Option<&'a PropInfo<'static, H>>, } #[derive(Debug)] pub struct Crossroads { reg: IfaceReg, paths: IfacePaths, } impl Crossroads { pub fn register_custom(&mut self, info: IfaceInfo<'static, H>) -> Option> { self.reg.0.insert(info.name.clone().into_cstring(), (TypeId::of::(), info)).map(|x| x.1) } pub fn insert>>(&mut self, name: N, data: PathData) { self.paths.0.insert(name.into().into_cstring(), data); } pub fn get_data>>(&self, name: N) -> Option<&PathData> { self.paths.0.get(name.into().as_cstr()) } pub fn register<'a, I: 'static, N: Into>>(&'a mut self, name: N) -> IfaceInfoBuilder<'a, I, H> { IfaceInfoBuilder::new(Some(self), name.into()) } fn reg_lookup(&self, headers: &MsgHeaders) -> Option<(MLookup, &MethodInfo<'static, H>)> { let (typeid, iinfo) = self.reg.0.get(headers.i.as_cstr())?; let minfo = iinfo.methods.iter().find(|x| x.name() == &headers.m)?; let data = self.paths.0.get(headers.p.as_cstr())?; let (_, iface) = data.0.iter().find(|x| x.0 == *typeid)?; Some((MLookup { cr: self, data, iface, iinfo }, minfo)) } pub (super) fn reg_prop_lookup<'a>(&'a self, data: &'a PathData, iname: &CStr, propname: &CStr) -> Option<(MLookup<'a, H>, &PropInfo<'static, H>)> { let (typeid, iinfo) = self.reg.0.get(iname)?; let pinfo = iinfo.props.iter().find(|x| x.name.as_cstr() == propname)?; let (_, iface) = data.0.iter().find(|x| x.0 == *typeid)?; Some((MLookup { cr: self, data, iface, iinfo}, pinfo)) } } impl Crossroads { pub fn dispatch_par(&self, msg: &Message) -> Option> { let headers = msg_headers(msg)?; let (lookup, minfo) = self.reg_lookup(&headers)?; let handler = minfo.handler(); let iface = &**lookup.iface; let mut info = ParInfo::new(msg, lookup); let r = (handler)(iface, &mut info); Some(r.into_iter().collect()) } pub fn new_par() -> Self { let mut cr = Crossroads { reg: IfaceReg(BTreeMap::new()), paths: IfacePaths(BTreeMap::new()), }; DBusProperties::register(&mut cr); cr } } impl Crossroads { pub fn dispatch_mut(&mut self, msg: &Message) -> Option> { let headers = msg_headers(msg)?; let (typeid, iinfo) = self.reg.0.get_mut(headers.i.as_cstr())?; let minfo = iinfo.methods.iter_mut().find(|x| x.name() == &headers.m)?; let ctx = MutCtx::new(msg); let r = match minfo.handler_mut().0 { MutMethods::MutIface(ref mut f) => { let data = self.paths.0.get_mut(headers.p.as_cstr())?; let (_, iface) = data.0.iter_mut().find(|x| x.0 == *typeid)?; let iface = &mut **iface; f(iface, &ctx) } }; Some(r.into_iter().collect()) } pub fn new_mut() -> Self { let cr = Crossroads { reg: IfaceReg(BTreeMap::new()), paths: IfacePaths(BTreeMap::new()), }; // DBusProperties::register(&mut cr); cr } } #[cfg(test)] mod test { use super::*; #[test] fn test_send_sync() { fn is_send(_: &T) {} fn is_sync(_: &T) {} let c = Crossroads::new_par(); dbg!(&c); is_send(&c); is_sync(&c); } #[test] fn cr_mut() { let mut cr = Crossroads::new_mut(); struct Score(u16); let mut call_times = 0u32; cr.register::("com.example.dbusrs.crossroads.score") .method_iface("UpdateScore", ("change",), ("new_score", "call_times"), move |score, _, (change,): (u16,)| { score.0 += change; call_times += 1; Ok((score.0, call_times)) }); let mut pdata = PathData::new(); pdata.insert_mut(Score(7u16)); cr.insert("/", pdata); let msg = Message::new_method_call("com.example.dbusrs.crossroads.score", "/", "com.example.dbusrs.crossroads.score", "UpdateScore").unwrap(); let mut msg = msg.append1(5u16); crate::message::message_set_serial(&mut msg, 57); let mut r = cr.dispatch_mut(&msg).unwrap(); assert_eq!(r.len(), 1); r[0].as_result().unwrap(); let (new_score, call_times): (u16, u32) = r[0].read2().unwrap(); assert_eq!(new_score, 12); assert_eq!(call_times, 1); } #[test] fn cr_par() { let mut cr = Crossroads::new_par(); struct Score(u16); cr.register::("com.example.dbusrs.crossroads.score") .method("Hello", ("sender",), ("reply",), |score, _, (sender,): (String,)| { assert_eq!(score.0, 7u16); Ok((format!("Hello {}, my score is {}!", sender, score.0),)) }) .prop_ro("Score", |score, _| { assert_eq!(score.0, 7u16); Ok(score.0) }) .signal::<(u16,),_>("ScoreChanged", ("NewScore",)); let mut pdata = PathData::new(); pdata.insert_par(Score(7u16)); pdata.insert_par(DBusProperties); cr.insert("/", pdata); let msg = Message::new_method_call("com.example.dbusrs.crossroads.score", "/", "com.example.dbusrs.crossroads.score", "Hello").unwrap(); let mut msg = msg.append1("example"); crate::message::message_set_serial(&mut msg, 57); let mut r = cr.dispatch_par(&msg).unwrap(); assert_eq!(r.len(), 1); r[0].as_result().unwrap(); let rr: String = r[0].read1().unwrap(); assert_eq!(&rr, "Hello example, my score is 7!"); let msg = Message::new_method_call("com.example.dbusrs.crossroads.score", "/", "org.freedesktop.DBus.Properties", "Get").unwrap(); let mut msg = msg.append2("com.example.dbusrs.crossroads.score", "Score"); crate::message::message_set_serial(&mut msg, 57); let mut r = cr.dispatch_par(&msg).unwrap(); assert_eq!(r.len(), 1); r[0].as_result().unwrap(); let z: u16 = r[0].read1().unwrap(); assert_eq!(z, 7u16); } } dbus-0.6.5/src/crossroads/handlers.rs010064400017500001750000000114341346733221400160270ustar0000000000000000use std::{fmt, cell}; use std::any::Any; use crate::arg::ArgBuilder; use crate::{Path as PathName, Interface as IfaceName, Member as MemberName, Signature, Message, arg}; use super::crossroads::{Crossroads, PathData, MLookup}; use super::info::{MethodInfo, PropInfo}; use super::MethodErr; pub struct DebugMethod(pub H::Method); impl fmt::Debug for DebugMethod { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "...") } } pub struct DebugProp(pub Option, pub Option); impl fmt::Debug for DebugProp { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "...") } } pub trait Handlers { type Method; type GetProp; type SetProp; type Iface; } /// Parallel tree - Par #[derive(Debug, Clone, Copy, Default)] pub struct Par; impl Par { pub fn typed_getprop(getf: G) -> ::GetProp where G: Fn(&I, &ParInfo) -> Result + Send + Sync + 'static { Box::new(move |data, ia, info| { let iface: &I = data.downcast_ref().unwrap(); let t = getf(iface, info)?; ia.append(t); Ok(()) }) } pub fn typed_setprop arg::Get<'z>, S>(setf: S) -> ::SetProp where S: Fn(&I, &ParInfo, T) -> Result<(), MethodErr> + Send + Sync + 'static { Box::new(move |data, ii, info| { let iface: &I = data.downcast_ref().unwrap(); let t: T = ii.read()?; setf(iface, info, t) }) } } #[derive(Debug)] pub struct ParInfo<'a> { lookup: MLookup<'a, Par>, message: &'a Message, } impl<'a> ParInfo<'a> { pub fn msg(&self) -> &Message { self.message } pub (super) fn new(msg: &'a Message, lookup: MLookup<'a, Par>) -> Self { ParInfo { lookup, message: msg } } pub fn path_data(&self) -> &PathData { self.lookup.data } pub fn crossroads(&self) -> &Crossroads { self.lookup.cr } } impl Handlers for Par { type Method = Box Option + Send + Sync + 'static>; type GetProp = Box Result<(), MethodErr> + Send + Sync + 'static>; type SetProp = Box Result<(), MethodErr> + Send + Sync + 'static>; type Iface = Box; } impl MethodInfo<'_, Par> { pub fn new_par(name: N, f: F) -> Self where F: Fn(&T, &ParInfo) -> Result, MethodErr> + Send + Sync + 'static, N: Into>, T: Any + Send + Sync + 'static, { Self::new(name.into(), Box::new(move |data, info| { let x = data.downcast_ref().unwrap(); f(x, info).unwrap_or_else(|e| { Some(e.to_message(info.message)) }) })) } } /// Mutable, non-Send tree #[derive(Debug, Clone, Copy, Default)] pub struct Mut; #[derive(Debug)] pub struct MutCtx<'a> { message: &'a Message, send_extra: cell::RefCell>, } impl<'a> MutCtx<'a> { pub fn msg(&self) -> &Message { self.message } pub fn send(&self, msg: Message) { self.send_extra.borrow_mut().push(msg); } pub (super) fn new(msg: &'a Message) -> Self { MutCtx { message: msg, send_extra: Default::default() } } } impl Handlers for Mut { type Method = MutMethod; type GetProp = Box Result<(), MethodErr> + 'static>; type SetProp = Box Result<(), MethodErr> + 'static>; type Iface = Box; } pub struct MutMethod(pub (super) MutMethods); pub (super) enum MutMethods { MutIface(Box Option + 'static>), // Info(Box Option + 'static>), // MutCr(fn(&mut Crossroads, &Message) -> Vec), } impl Mut { pub fn typed_method_iface(mut f: F) -> ::Method where F: FnMut(&mut I, &MutCtx, IA) -> Result + 'static { MutMethod(MutMethods::MutIface(Box::new(move |data, info| { let iface: &mut I = data.downcast_mut().unwrap(); let ia = match IA::read(info.msg()) { Err(e) => return Some(MethodErr::from(e).to_message(info.msg())), Ok(ia) => ia, }; match f(iface, info, ia) { Err(e) => Some(e.to_message(info.msg())), Ok(r) => { let mut m = info.msg().method_return(); OA::append(r, &mut m); Some(m) }, } }))) } } dbus-0.6.5/src/crossroads/info.rs010064400017500001750000000171411346733225100151640ustar0000000000000000use crate::{Path as PathName, Interface as IfaceName, Member as MemberName, Signature, Message}; use std::borrow::Cow; use std::collections::BTreeMap; use std::any::Any; use std::mem; use crate::arg::{Arg, Append, Get, ArgBuilder, TypeMismatchError, IterAppend}; use std::marker::PhantomData; use super::MethodErr; use super::handlers::{Handlers, DebugMethod, DebugProp, Par, ParInfo, Mut, MutCtx}; use super::crossroads::{Crossroads, PathData}; fn build_argvec(a: A::strs) -> Vec> { let mut v = vec!(); A::strs_sig(a, |name, sig| { v.push(Argument { name: name.into(), sig }) }); v } #[derive(Default, Debug, Clone)] struct Annotations(Option>); #[derive(Debug, Clone)] pub struct Argument<'a> { name: Cow<'a, str>, sig: Signature<'a>, } #[derive(Debug)] pub struct IfaceInfo<'a, H: Handlers> { pub (crate) name: IfaceName<'a>, pub (crate) methods: Vec>, pub (crate) props: Vec>, pub (crate) signals: Vec>, } #[derive(Debug)] pub struct MethodInfo<'a, H: Handlers> { name: MemberName<'a>, handler: DebugMethod, i_args: Vec>, o_args: Vec>, anns: Annotations, } impl<'a, H: Handlers> MethodInfo<'a, H> { pub fn name(&self) -> &MemberName<'a> { &self.name } pub fn handler(&self) -> &H::Method { &self.handler.0 } pub fn handler_mut(&mut self) -> &mut H::Method { &mut self.handler.0 } } #[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, } #[derive(Debug)] pub struct PropInfo<'a, H: Handlers> { pub (crate) name: MemberName<'a>, pub (crate) handlers: DebugProp, anns: Annotations, sig: Signature<'a>, emits: EmitsChangedSignal, auto_emit: bool, rw: Access, } #[derive(Debug)] pub struct SignalInfo<'a> { name: MemberName<'a>, args: Vec>, anns: Annotations, } #[derive(Debug)] pub struct IfaceInfoBuilder<'a, I: 'static, H: Handlers> { cr: Option<&'a mut Crossroads>, info: IfaceInfo<'static, H>, _dummy: PhantomData<*const I>, } impl<'a, I, H: Handlers> IfaceInfoBuilder<'a, I, H> { pub fn new(cr: Option<&'a mut Crossroads>, name: IfaceName<'static>) -> Self { IfaceInfoBuilder { cr, _dummy: PhantomData, info: IfaceInfo::new_empty(name) } } pub fn signal>>(mut self, name: N, args: A::strs) -> Self { let s = SignalInfo { name: name.into(), args: build_argvec::(args), anns: Default::default() }; self.info.signals.push(s); self } } impl<'a, I: 'static, H: Handlers> Drop for IfaceInfoBuilder<'a, I, H> { fn drop(&mut self) { if let Some(ref mut cr) = self.cr { let info = IfaceInfo::new_empty(self.info.name.clone()); // workaround for not being able to consume self.info cr.register_custom::(mem::replace(&mut self.info, info)); } } } impl<'a, I: 'static> IfaceInfoBuilder<'a, I, Par> { pub fn method(mut self, name: N, in_args: IA::strs, out_args: OA::strs, f: F) -> Self where N: Into>, F: Fn(&I, &ParInfo, IA) -> Result + Send + Sync + 'static { let f: ::Method = Box::new(move |data, info| { let iface: &I = data.downcast_ref().unwrap(); let r = IA::read(info.msg()).map_err(From::from); let r = r.and_then(|ia| f(iface, info, ia)); match r { Err(e) => Some(e.to_message(info.msg())), Ok(r) => { let mut m = info.msg().method_return(); OA::append(r, &mut m); Some(m) }, } }); let m = MethodInfo { name: name.into(), handler: DebugMethod(f), i_args: build_argvec::(in_args), o_args: build_argvec::(out_args), anns: Default::default() }; self.info.methods.push(m); self } pub fn prop_rw(mut self, name: N, getf: G, setf: S) -> Self where T: Arg + Append + for<'z> Get<'z> + Send + Sync + 'static, N: Into>, G: Fn(&I, &ParInfo) -> Result + Send + Sync + 'static, S: Fn(&I, &ParInfo, T) -> Result<(), MethodErr> + Send + Sync + 'static { let p = PropInfo::new(name.into(), T::signature(), Some(Par::typed_getprop(getf)), Some(Par::typed_setprop(setf))); self.info.props.push(p); self } pub fn prop_ro(mut self, name: N, getf: G) -> Self where T: Arg + Append + Send + Sync + 'static, N: Into>, G: Fn(&I, &ParInfo) -> Result + Send + Sync + 'static, { let p = PropInfo::new(name.into(), T::signature(), Some(Par::typed_getprop(getf)), None); self.info.props.push(p); self } } impl<'a, I: 'static> IfaceInfoBuilder<'a, I, Mut> { pub fn method_iface(mut self, name: N, in_args: IA::strs, out_args: OA::strs, f: F) -> Self where N: Into>, F: FnMut(&mut I, &MutCtx, IA) -> Result + Send + Sync + 'static { let m = MethodInfo { name: name.into(), handler: DebugMethod(Mut::typed_method_iface(f)), i_args: build_argvec::(in_args), o_args: build_argvec::(out_args), anns: Default::default() }; self.info.methods.push(m); self } } impl MethodInfo<'_, H> { pub fn new(name: MemberName<'static>, f: H::Method) -> Self { MethodInfo { name: name, handler: DebugMethod(f), i_args: Default::default(), o_args: Default::default(), anns: Default::default() } } } impl PropInfo<'_, H> { pub fn new(name: MemberName<'static>, sig: Signature<'static>, get: Option, set: Option) -> Self { let a = match (&get, &set) { (Some(_), Some(_)) => Access::ReadWrite, (Some(_), None) => Access::Read, (None, Some(_)) => Access::Write, _ => unimplemented!(), }; PropInfo { name, handlers: DebugProp(get, set), sig, auto_emit: true, rw: a, emits: EmitsChangedSignal::True, anns: Default::default() } } } impl<'a, H: Handlers> IfaceInfo<'a, H> { pub fn new_empty(name: IfaceName<'static>) -> Self { IfaceInfo { name, methods: vec!(), props: vec!(), signals: vec!() } } pub fn new(name: N, methods: M, properties: P, signals: S) -> Self where N: Into>, M: IntoIterator>, P: IntoIterator>, S: IntoIterator> { IfaceInfo { name: name.into(), methods: methods.into_iter().collect(), props: properties.into_iter().collect(), signals: signals.into_iter().collect() } } } dbus-0.6.5/src/crossroads/mod.rs010064400017500001750000000006161345471176300150140ustar0000000000000000//! Will eventually superseed the "tree" module. It's unstable and experimental for now. #![allow(unused_imports, dead_code, missing_docs)] mod info; mod handlers; mod crossroads; mod stdimpl; pub use crate::tree::MethodErr as MethodErr; pub use self::info::{IfaceInfo, MethodInfo, PropInfo}; pub use self::crossroads::{Crossroads, PathData}; pub use self::handlers::{Handlers, Par, ParInfo}; dbus-0.6.5/src/crossroads/stdimpl.rs010064400017500001750000000035411345664334700157150ustar0000000000000000use super::crossroads::Crossroads; use super::handlers::{ParInfo, Par}; use super::info::{IfaceInfo, MethodInfo, PropInfo}; use crate::arg; use super::MethodErr; pub struct DBusProperties; impl DBusProperties { pub fn register(cr: &mut Crossroads) { cr.register_custom::(IfaceInfo::new("org.freedesktop.DBus.Properties", vec!(MethodInfo::new_par("Get", |_: &DBusProperties, info| { let (iname, propname) = info.msg().read2()?; let (lookup, pinfo) = info.crossroads().reg_prop_lookup(info.path_data(), iname, propname) .ok_or_else(|| { MethodErr::no_property(&"Could not find property") })?; let handler = &pinfo.handlers.0.as_ref() .ok_or_else(|| { MethodErr::no_property(&"Property can not be read") })?; let iface = &**lookup.iface; let mut pinfo = ParInfo::new(info.msg(), lookup); let mut mret = info.msg().method_return(); { let mut ia = arg::IterAppend::new(&mut mret); (handler)(iface, &mut ia, &mut pinfo)?; } Ok(Some(mret)) })), vec!(), vec!() )); } } pub struct DBusIntrospectable; use crate::crossroads as cr; pub trait Introspectable { fn introspect(&self, info: &cr::ParInfo) -> Result; } pub fn introspectable_ifaceinfo() -> cr::IfaceInfo<'static, cr::Par> where I: Introspectable + Send + Sync + 'static { cr::IfaceInfo::new("org.freedesktop.DBus.Introspectable", vec!( MethodInfo::new_par("Introspect", |intf: &I, info| { let xml_data = intf.introspect(info)?; let rm = info.msg().method_return(); let rm = rm.append1(xml_data); Ok(Some(rm)) }), ), vec!(), vec!()) } dbus-0.6.5/src/dispatcher.rs010064400017500001750000000116121351376776700142120ustar0000000000000000use crate::{Message, MessageType, Error, to_c_str, c_str_to_slice}; use std::ptr; use std::collections::HashMap; /// [Unstable and Experimental] pub trait MessageDispatcherConfig: Sized { /// The type of method reply stored inside the dispatcher type Reply; /// Called when a method call has received a reply. fn on_reply(reply: Self::Reply, msg: Message, dispatcher: &mut MessageDispatcher); /// Called when a signal is received. /// /// Defaults to doing nothing. #[allow(unused_variables)] fn on_signal(msg: Message, dispatcher: &mut MessageDispatcher) {} /// Called when a method call is received. /// /// Defaults to calling default_dispatch. fn on_method_call(msg: Message, dispatcher: &mut MessageDispatcher) { if let Some(reply) = MessageDispatcher::::default_dispatch(&msg) { Self::on_send(reply, dispatcher); } } /// Called in the other direction, i e, when a message should be sent over the connection. fn on_send(msg: Message, dispatcher: &mut MessageDispatcher); } /// Dummy implementation impl MessageDispatcherConfig for () { type Reply = (); fn on_reply(_: Self::Reply, _: Message, _: &mut MessageDispatcher) { unreachable!() } fn on_send(_: Message, _: &mut MessageDispatcher) { unreachable!() } } /// [Unstable and Experimental] Meant for usage with RxTx. pub struct MessageDispatcher { waiting_replies: HashMap, inner: C, } impl MessageDispatcher { /// Creates a new message dispatcher. pub fn new(inner: C) -> Self { MessageDispatcher { waiting_replies: HashMap::new(), inner: inner, } } /// "Inner" accessor pub fn inner(&self) -> &C { &self.inner } /// "Inner" mutable accessor pub fn inner_mut(&mut self) -> &mut C { &mut self.inner } /// Adds a waiting reply to a method call. func will be called when a method reply is dispatched. pub fn add_reply(&mut self, serial: u32, func: C::Reply) { if let Some(_) = self.waiting_replies.insert(serial, func) { // panic because we're overwriting something else, or just ignore? } } /// Cancels a waiting reply. pub fn cancel_reply(&mut self, serial: u32) -> Option { self.waiting_replies.remove(&serial) } /// Dispatch an incoming message. pub fn dispatch(&mut self, msg: Message) { if let Some(serial) = msg.get_reply_serial() { if let Some(sender) = self.waiting_replies.remove(&serial) { C::on_reply(sender, msg, self); return; } } match msg.msg_type() { MessageType::Signal => C::on_signal(msg, self), MessageType::MethodCall => C::on_method_call(msg, self), MessageType::Error | MessageType::MethodReturn => {}, MessageType::Invalid => unreachable!(), } } /// Handles what we need to be a good D-Bus citizen. /// /// Call this if you have not handled the message yourself: /// * It handles calls to org.freedesktop.DBus.Peer. /// * For other method calls, it sends an error reply back that the method was unknown. pub fn default_dispatch(m: &Message) -> Option { Self::peer(&m) .or_else(|| Self::unknown_method(&m)) } /// Replies if this is a call to org.freedesktop.DBus.Peer, otherwise returns None. pub fn peer(m: &Message) -> Option { if let Some(intf) = m.interface() { if &*intf != "org.freedesktop.DBus.Peer" { return None; } if let Some(method) = m.member() { if &*method == "Ping" { return Some(m.method_return()) } if &*method == "GetMachineId" { let mut r = m.method_return(); let mut e = Error::empty(); unsafe { let id = ffi::dbus_try_get_local_machine_id(e.get_mut()); if id != ptr::null_mut() { r = r.append1(c_str_to_slice(&(id as *const _)).unwrap()); ffi::dbus_free(id as *mut _); return Some(r) } } } } Some(m.error(&"org.freedesktop.DBus.Error.UnknownMethod".into(), &to_c_str("Method does not exist"))) } else { None } } /// For method calls, it replies that the method was unknown, otherwise returns None. pub fn unknown_method(m: &Message) -> Option { if m.msg_type() != MessageType::MethodCall { return None; } // if m.get_no_reply() { return None; } // The reference implementation does not do this? Some(m.error(&"org.freedesktop.DBus.Error.UnknownMethod".into(), &to_c_str("Path, Interface, or Method does not exist"))) } } dbus-0.6.5/src/lib.rs010064400017500001750000000223151351377057500126230ustar0000000000000000//! 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. //! //! In addition to the API documentation, which you're currently reading, you might want to //! look in the examples directory, which contains many examples and an argument guide. //! README.md also contain a few quick "getting started" examples. //! //! In addition to this crate, there are two companion crates, dbus-codegen for generating Rust //! code from D-Bus introspection data, and dbus-tokio for integrating D-Bus with [Tokio](http://tokio.rs). //! However, at the time of this writing, these are far less mature than this crate. #![warn(missing_docs)] extern crate libc; pub use ffi::DBusBusType as BusType; pub use connection::DBusNameFlag as NameFlag; pub use ffi::DBusRequestNameReply as RequestNameReply; pub use ffi::DBusReleaseNameReply as ReleaseNameReply; pub use ffi::DBusMessageType as MessageType; pub use message::{Message, MessageItem, MessageItemArray, FromMessageItem, OwnedFd, ArrayError, ConnPath}; pub use connection::{Connection, ConnectionItems, ConnectionItem, ConnMsgs, MsgHandler, MsgHandlerResult, MsgHandlerType, MessageCallback}; pub use prop::PropHandler; pub use prop::Props; pub use watch::{Watch, WatchEvent}; pub use signalargs::SignalArgs; /// A TypeSig describes the type of a MessageItem. #[deprecated(note="Use Signature instead")] pub type TypeSig<'a> = std::borrow::Cow<'a, str>; use std::ffi::{CString, CStr}; use std::ptr; use std::os::raw::c_char; #[allow(missing_docs)] extern crate libdbus_sys as ffi; mod message; mod prop; mod watch; mod connection; mod signalargs; mod matchrule; pub use matchrule::MatchRule; mod strings; pub use strings::{Signature, Path, Interface, Member, ErrorName, BusName}; pub mod arg; pub mod stdintf; pub mod tree; 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 {} // Note! For this Sync impl to be safe, it requires that no functions that take &self, // actually calls into FFI. All functions that call into FFI with a ffi::DBusError // must take &mut self. unsafe impl Sync for Error {} fn c_str_to_slice(c: & *const 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(()) } } } impl From for Error { fn from(t: arg::TypeMismatchError) -> Error { Error::new_custom("org.freedesktop.DBus.Error.Failed", &format!("{}", t)) } } impl From for Error { fn from(t: tree::MethodErr) -> Error { Error::new_custom(t.errorname(), t.description()) } } #[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 d = c.watch_fds(); assert!(d.len() > 0); println!("Fds to watch: {:?}", d); } } dbus-0.6.5/src/matchrule.rs010064400017500001750000000057721351376776700140620ustar0000000000000000use crate::{Message, MessageType, BusName, Path, Interface, Member}; #[derive(Clone, Debug, Default)] /// A "match rule", that can match Messages on its headers. /// /// A field set to "None" means no filter for that header, /// a field set to "Some(_)" must match exactly. pub struct MatchRule<'a> { /// Match on message type (you typically want to do this) pub msg_type: Option, /// Match on message sender pub sender: Option>, /// Match on message object path pub path: Option>, /// Match on message interface pub interface: Option>, /// Match on message member (signal or method name) pub member: Option>, _more_fields_may_come: (), } fn msg_type_str(m: MessageType) -> &'static str { use MessageType::*; match m { Signal => "signal", MethodCall => "method_call", MethodReturn => "method_return", Error => "error", Invalid => unreachable!(), } } impl<'a> MatchRule<'a> { /// Make a string which you can use in the call to "add_match". /// /// Panics: if msg_type is set to Some(MessageType::Invalid) pub fn match_str(&self) -> String { let mut v = vec!(); if let Some(x) = self.msg_type { v.push(("type", msg_type_str(x))) }; if let Some(ref x) = self.sender { v.push(("sender", &x)) }; if let Some(ref x) = self.path { v.push(("path", &x)) }; if let Some(ref x) = self.interface { v.push(("interface", &x)) }; if let Some(ref x) = self.member { v.push(("member", &x)) }; // For now we don't need to worry about internal quotes in strings as those are not valid names. // If we start matching against arguments, we need to worry. let v: Vec<_> = v.into_iter().map(|(k, v)| format!("{}='{}'", k, v)).collect(); v.join(",") } /// Returns whether or not the message matches the rule. pub fn matches(&self, msg: &Message) -> bool { if let Some(x) = self.msg_type { if x != msg.msg_type() { return false; }}; if self.sender.is_some() && msg.sender() != self.sender { return false }; if self.path.is_some() && msg.path() != self.path { return false }; if self.interface.is_some() && msg.interface() != self.interface { return false }; if self.member.is_some() && msg.member() != self.member { return false }; true } /// Create a new struct which matches every message. pub fn new() -> Self { Default::default() } /// Returns a clone with no static references pub fn into_static(&self) -> MatchRule<'static> { MatchRule { msg_type: self.msg_type, sender: self.sender.as_ref().map(|x| x.clone().into_static()), path: self.path.as_ref().map(|x| x.clone().into_static()), interface: self.interface.as_ref().map(|x| x.clone().into_static()), member: self.member.as_ref().map(|x| x.clone().into_static()), _more_fields_may_come: (), } } } dbus-0.6.5/src/message.rs010064400017500001750000001344221351376776700135150ustar0000000000000000use std::borrow::Cow; use std::{fmt, mem, ptr, ops}; use super::{ffi, Error, MessageType, Signature, libc, to_c_str, c_str_to_slice, init_dbus}; use super::{BusName, Path, Interface, Member, ErrorName, Connection, SignalArgs}; use std::os::unix::io::{RawFd, AsRawFd}; use std::ffi::CStr; use std::os::raw::{c_void, c_char, c_int}; use super::arg::{Append, IterAppend, Get, Iter, Arg, RefArg, TypeMismatchError}; #[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, /// The supplied signature is not a valid array signature InvalidSignature, } fn new_dbus_message_iter() -> ffi::DBusMessageIter { unsafe { mem::zeroed() }} /// 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 } } #[derive(Debug, Clone, PartialEq, PartialOrd)] /// An array of MessageItem where every MessageItem is of the same type. pub struct MessageItemArray { v: Vec, // signature includes the "a"! sig: Signature<'static>, } impl MessageItemArray { /// Creates a new array where every element has the supplied signature. /// /// Signature is the full array signature, not the signature of the element. pub fn new(v: Vec, sig: Signature<'static>) -> Result { let a = MessageItemArray {v: v, sig: sig }; if a.sig.as_bytes()[0] != ffi::DBUS_TYPE_ARRAY as u8 { return Err(ArrayError::InvalidSignature) } { let esig = a.element_signature(); for i in &a.v { let b = if let MessageItem::DictEntry(ref k, ref v) = *i { let s = format!("{{{}{}}}", k.signature(), v.signature()); s.as_bytes() == esig.to_bytes() } else { i.signature().as_cstr() == esig }; if !b { return Err(ArrayError::DifferentElementTypes) } } } Ok(a) } fn element_signature(&self) -> &CStr { let z = &self.sig.as_cstr().to_bytes_with_nul()[1..]; unsafe { CStr::from_bytes_with_nul_unchecked(z) } } fn make_sig(m: &MessageItem) -> Signature<'static> { if let MessageItem::DictEntry(ref k, ref v) = *m { Signature::new(format!("a{{{}{}}}", k.signature(), v.signature())).unwrap() } else { Signature::new(format!("a{}", m.signature())).unwrap() } } /// Signature of array (full array signature) pub fn signature(&self) -> &Signature<'static> { &self.sig } /// Consumes the MessageItemArray in order to allow you to modify the individual items of the array. pub fn into_vec(self) -> Vec { self.v } } impl ops::Deref for MessageItemArray { type Target = [MessageItem]; fn deref(&self) -> &Self::Target { &self.v } } /// MessageItem - used as parameters and return values from /// method calls, or as data added to a signal (old, enum version). /// /// Note that the newer generic design (see `arg` module) is both faster /// and less error prone than MessageItem, and should be your first hand choice /// whenever applicable. #[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 Signature. Array(MessageItemArray), /// 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 entry. These are only allowed inside an array. DictEntry(Box, Box), /// A D-Bus objectpath requires its content to be a valid objectpath, /// so this cannot be any string. ObjectPath(Path<'static>), /// 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) -> T { unsafe { let mut c: T = mem::zeroed(); let p = &mut c as *mut _ as *mut c_void; ffi::dbus_message_iter_get_basic(i, p); c } } fn iter_append_array(i: &mut ffi::DBusMessageIter, a: &[MessageItem], t: &CStr) { let mut subiter = new_dbus_message_iter(); assert!(unsafe { ffi::dbus_message_iter_open_container(i, ffi::DBUS_TYPE_ARRAY, t.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 asig = a.signature(); let atype = asig.as_cstr(); 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 Signature for this MessageItem. /// /// Note: Since dictionary entries have no valid signature, calling this function for a dict entry will cause a panic. pub fn signature(&self) -> Signature<'static> { use arg::Variant; match *self { MessageItem::Str(_) => ::signature(), MessageItem::Bool(_) => ::signature(), MessageItem::Byte(_) => ::signature(), MessageItem::Int16(_) => ::signature(), MessageItem::Int32(_) => ::signature(), MessageItem::Int64(_) => ::signature(), MessageItem::UInt16(_) => ::signature(), MessageItem::UInt32(_) => ::signature(), MessageItem::UInt64(_) => ::signature(), MessageItem::Double(_) => ::signature(), MessageItem::Array(ref a) => a.sig.clone(), MessageItem::Struct(ref s) => Signature::new(format!("({})", s.iter().fold(String::new(), |s, i| s + &*i.signature()))).unwrap(), MessageItem::Variant(_) => as Arg>::signature(), MessageItem::DictEntry(_, _) => { panic!("Dict entries are only valid inside arrays, and therefore has no signature on their own") }, MessageItem::ObjectPath(_) => ::signature(), MessageItem::UnixFd(_) => ::signature(), } } /// Get the D-Bus ASCII type-code for this MessageItem. #[deprecated(note="superseded by signature")] #[allow(deprecated)] pub fn type_sig(&self) -> super::TypeSig<'static> { Cow::Owned(format!("{}", self.signature())) } /// 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(MessageItemArray::new(v, Signature::new("a{sv}").unwrap()).unwrap())) } /// 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 s = MessageItemArray::make_sig(&v[0]); Ok(MessageItem::Array(MessageItemArray::new(v, s)?)) } fn new_array2(i: I) -> MessageItem where D: Into, D: Default, I: Iterator { let v: Vec = i.map(|ii| ii.into()).collect(); let s = { let d; let t = if v.len() == 0 { d = D::default().into(); &d } else { &v[0] }; MessageItemArray::make_sig(t) }; MessageItem::Array(MessageItemArray::new(v, s).unwrap()) } 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_single(i: &mut ffi::DBusMessageIter) -> Option { let t = unsafe { ffi::dbus_message_iter_get_arg_type(i) }; match t { ffi::DBUS_TYPE_INVALID => { None }, 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()); Some(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"); } Some(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 c = unsafe { ffi::dbus_message_iter_get_signature(&mut subiter) }; let s = format!("a{}", c_str_to_slice(&(c as *const c_char)).unwrap()); unsafe { ffi::dbus_free(c as *mut c_void) }; let t = Signature::new(s).unwrap(); let a = MessageItem::from_iter(&mut subiter); Some(MessageItem::Array(MessageItemArray { v: a, sig: t })) }, ffi::DBUS_TYPE_STRUCT => { let mut subiter = new_dbus_message_iter(); unsafe { ffi::dbus_message_iter_recurse(i, &mut subiter) }; Some(MessageItem::Struct(MessageItem::from_iter(&mut subiter))) }, ffi::DBUS_TYPE_STRING => { let mut c: *const c_char = ptr::null(); unsafe { let p: *mut c_void = mem::transmute(&mut c); ffi::dbus_message_iter_get_basic(i, p); }; Some(MessageItem::Str(c_str_to_slice(&c).expect("D-Bus string error").to_string())) }, ffi::DBUS_TYPE_OBJECT_PATH => { let mut c: *const c_char = ptr::null(); unsafe { let p: *mut 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"); Some(MessageItem::ObjectPath(o)) }, ffi::DBUS_TYPE_UNIX_FD => Some(MessageItem::UnixFd(OwnedFd::new(iter_get_basic(i)))), ffi::DBUS_TYPE_BOOLEAN => Some(MessageItem::Bool(iter_get_basic::(i) != 0)), ffi::DBUS_TYPE_BYTE => Some(MessageItem::Byte(iter_get_basic(i))), ffi::DBUS_TYPE_INT16 => Some(MessageItem::Int16(iter_get_basic(i))), ffi::DBUS_TYPE_INT32 => Some(MessageItem::Int32(iter_get_basic(i))), ffi::DBUS_TYPE_INT64 => Some(MessageItem::Int64(iter_get_basic(i))), ffi::DBUS_TYPE_UINT16 => Some(MessageItem::UInt16(iter_get_basic(i))), ffi::DBUS_TYPE_UINT32 => Some(MessageItem::UInt32(iter_get_basic(i))), ffi::DBUS_TYPE_UINT64 => Some(MessageItem::UInt64(iter_get_basic(i))), ffi::DBUS_TYPE_DOUBLE => Some(MessageItem::Double(iter_get_basic(i))), _ => { None /* Only the new msgarg module supports signatures */ } } } fn from_iter(i: &mut ffi::DBusMessageIter) -> Vec { let mut v = Vec::new(); while let Some(m) = Self::from_iter_single(i) { v.push(m); unsafe { ffi::dbus_message_iter_next(i) }; } v } fn iter_append_basic(&self, i: &mut ffi::DBusMessageIter, v: T) { let t = self.array_type() as c_int; let p = &v as *const _ as *const c_void; unsafe { ffi::dbus_message_iter_append_basic(i, t, 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, if b { 1u32 } else { 0u32 }), &MessageItem::Byte(b) => self.iter_append_basic(i, b), &MessageItem::Int16(b) => self.iter_append_basic(i, b), &MessageItem::Int32(b) => self.iter_append_basic(i, b), &MessageItem::Int64(b) => self.iter_append_basic(i, b), &MessageItem::UInt16(b) => self.iter_append_basic(i, b), &MessageItem::UInt32(b) => self.iter_append_basic(i, b), &MessageItem::UInt64(b) => self.iter_append_basic(i, b), &MessageItem::UnixFd(ref b) => self.iter_append_basic(i, b.as_raw_fd()), &MessageItem::Double(b) => self.iter_append_basic(i, b), &MessageItem::Array(ref a) => iter_append_array(i, &a.v, a.element_signature()), &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) } } // For use by the msgarg module pub fn append_messageitem(i: &mut ffi::DBusMessageIter, m: &MessageItem) { m.iter_append(i) } // For use by the msgarg module pub fn get_messageitem(i: &mut ffi::DBusMessageIter) -> Option { MessageItem::from_iter_single(i) } 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<'static>) -> 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<'static> { fn from(i: &'a MessageItem) -> Result<&'a Path<'static>,()> { 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.v), &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<'d, 'p, 'i, 'm, D, P, I, M>(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. /// /// Note: use `iter_init` or `get1`/`get2`/etc instead for faster access to the arguments. /// This method is provided for backwards compatibility. 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) } } /// Returns true if the message does not expect a reply. pub fn get_no_reply(&self) -> bool { unsafe { ffi::dbus_message_get_no_reply(self.msg) != 0 } } /// Set whether or not the message expects a reply. /// /// Set to true if you send a method call and do not want a reply. pub fn set_no_reply(&self, v: bool) { unsafe { ffi::dbus_message_set_no_reply(self.msg, if v { 1 } else { 0 }) } } /// Returns true if the message can cause a service to be auto-started. pub fn get_auto_start(&self) -> bool { unsafe { ffi::dbus_message_get_auto_start(self.msg) != 0 } } /// Sets whether or not the message can cause a service to be auto-started. /// /// Defaults to true. pub fn set_auto_start(&self, v: bool) { unsafe { ffi::dbus_message_set_auto_start(self.msg, if v { 1 } else { 0 }) } } /// Add one or more MessageItems to this Message. /// /// Note: using `append1`, `append2` or `append3` might be faster, especially for large arrays. /// This method is provided for backwards compatibility. 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)` /// /// Note: using `append1`, `append2` or `append3` might be faster, especially for large arrays. /// This method is provided for backwards compatibility. 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 } /// Appends one argument to this message. /// Use in builder style: e g `m.method_return().append1(7i32)` pub fn append1(mut self, a: A) -> Self { { let mut m = IterAppend::new(&mut self); m.append(a); } self } /// Appends two arguments to this message. /// Use in builder style: e g `m.method_return().append2(7i32, 6u8)` pub fn append2(mut self, a1: A1, a2: A2) -> Self { { let mut m = IterAppend::new(&mut self); m.append(a1); m.append(a2); } self } /// Appends three arguments to this message. /// Use in builder style: e g `m.method_return().append3(7i32, 6u8, true)` pub fn append3(mut self, a1: A1, a2: A2, a3: A3) -> Self { { let mut m = IterAppend::new(&mut self); m.append(a1); m.append(a2); m.append(a3); } self } /// Appends RefArgs to this message. /// Use in builder style: e g `m.method_return().append_ref(&[7i32, 6u8, true])` pub fn append_ref(mut self, r: &[A]) -> Self { { let mut m = IterAppend::new(&mut self); for rr in r { rr.append(&mut m); } } self } /// Gets the first argument from the message, if that argument is of type G1. /// Returns None if there are not enough arguments, or if types don't match. pub fn get1<'a, G1: Get<'a>>(&'a self) -> Option { let mut i = Iter::new(&self); i.get() } /// Gets the first two arguments from the message, if those arguments are of type G1 and G2. /// Returns None if there are not enough arguments, or if types don't match. pub fn get2<'a, G1: Get<'a>, G2: Get<'a>>(&'a self) -> (Option, Option) { let mut i = Iter::new(&self); let g1 = i.get(); if !i.next() { return (g1, None); } (g1, i.get()) } /// Gets the first three arguments from the message, if those arguments are of type G1, G2 and G3. /// Returns None if there are not enough arguments, or if types don't match. pub fn get3<'a, G1: Get<'a>, G2: Get<'a>, G3: Get<'a>>(&'a self) -> (Option, Option, Option) { let mut i = Iter::new(&self); let g1 = i.get(); if !i.next() { return (g1, None, None) } let g2 = i.get(); if !i.next() { return (g1, g2, None) } (g1, g2, i.get()) } /// Gets the first four arguments from the message, if those arguments are of type G1, G2, G3 and G4. /// Returns None if there are not enough arguments, or if types don't match. pub fn get4<'a, G1: Get<'a>, G2: Get<'a>, G3: Get<'a>, G4: Get<'a>>(&'a self) -> (Option, Option, Option, Option) { let mut i = Iter::new(&self); let g1 = i.get(); if !i.next() { return (g1, None, None, None) } let g2 = i.get(); if !i.next() { return (g1, g2, None, None) } let g3 = i.get(); if !i.next() { return (g1, g2, g3, None) } (g1, g2, g3, i.get()) } /// Gets the first five arguments from the message, if those arguments are of type G1, G2, G3 and G4. /// Returns None if there are not enough arguments, or if types don't match. /// Note: If you need more than five arguments, use `iter_init` instead. pub fn get5<'a, G1: Get<'a>, G2: Get<'a>, G3: Get<'a>, G4: Get<'a>, G5: Get<'a>>(&'a self) -> (Option, Option, Option, Option, Option) { let mut i = Iter::new(&self); let g1 = i.get(); if !i.next() { return (g1, None, None, None, None) } let g2 = i.get(); if !i.next() { return (g1, g2, None, None, None) } let g3 = i.get(); if !i.next() { return (g1, g2, g3, None, None) } let g4 = i.get(); if !i.next() { return (g1, g2, g3, g4, None) } (g1, g2, g3, g4, i.get()) } /// Gets the first argument from the message, if that argument is of type G1. /// /// Returns a TypeMismatchError if there are not enough arguments, or if types don't match. pub fn read1<'a, G1: Arg + Get<'a>>(&'a self) -> Result { let mut i = Iter::new(&self); i.read() } /// Gets the first two arguments from the message, if those arguments are of type G1 and G2. /// /// Returns a TypeMismatchError if there are not enough arguments, or if types don't match. pub fn read2<'a, G1: Arg + Get<'a>, G2: Arg + Get<'a>>(&'a self) -> Result<(G1, G2), TypeMismatchError> { let mut i = Iter::new(&self); Ok((try!(i.read()), try!(i.read()))) } /// Gets the first three arguments from the message, if those arguments are of type G1, G2 and G3. /// /// Returns a TypeMismatchError if there are not enough arguments, or if types don't match. pub fn read3<'a, G1: Arg + Get<'a>, G2: Arg + Get<'a>, G3: Arg + Get<'a>>(&'a self) -> Result<(G1, G2, G3), TypeMismatchError> { let mut i = Iter::new(&self); Ok((try!(i.read()), try!(i.read()), try!(i.read()))) } /// Gets the first four arguments from the message, if those arguments are of type G1, G2, G3 and G4. /// /// Returns a TypeMismatchError if there are not enough arguments, or if types don't match. pub fn read4<'a, G1: Arg + Get<'a>, G2: Arg + Get<'a>, G3: Arg + Get<'a>, G4: Arg + Get<'a>>(&'a self) -> Result<(G1, G2, G3, G4), TypeMismatchError> { let mut i = Iter::new(&self); Ok((try!(i.read()), try!(i.read()), try!(i.read()), try!(i.read()))) } /// Gets the first five arguments from the message, if those arguments are of type G1, G2, G3, G4 and G5. /// /// Returns a TypeMismatchError if there are not enough arguments, or if types don't match. /// Note: If you need more than five arguments, use `iter_init` instead. pub fn read5<'a, G1: Arg + Get<'a>, G2: Arg + Get<'a>, G3: Arg + Get<'a>, G4: Arg + Get<'a>, G5: Arg + Get<'a>>(&'a self) -> Result<(G1, G2, G3, G4, G5), TypeMismatchError> { let mut i = Iter::new(&self); Ok((try!(i.read()), try!(i.read()), try!(i.read()), try!(i.read()), try!(i.read()))) } /// Returns a struct for retreiving the arguments from a message. Supersedes get_items(). pub fn iter_init<'a>(&'a self) -> Iter<'a> { Iter::new(&self) } /// Gets the MessageType of the Message. pub fn msg_type(&self) -> MessageType { unsafe { mem::transmute(ffi::dbus_message_get_type(self.msg)) } } fn msg_internal_str<'a>(&'a self, c: *const libc::c_char) -> Option<&'a [u8]> { if c == ptr::null() { None } else { Some( unsafe { CStr::from_ptr(c) }.to_bytes_with_nul()) } } /// Gets the name of the connection that originated this message. pub fn sender<'a>(&'a self) -> Option> { self.msg_internal_str(unsafe { ffi::dbus_message_get_sender(self.msg) }) .map(|s| unsafe { BusName::from_slice_unchecked(s) }) } /// 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<'a>(&'a self) -> Option> { self.msg_internal_str(unsafe { ffi::dbus_message_get_path(self.msg) }) .map(|s| unsafe { Path::from_slice_unchecked(s) }) } /// Gets the destination this Message is being sent to. pub fn destination<'a>(&'a self) -> Option> { self.msg_internal_str(unsafe { ffi::dbus_message_get_destination(self.msg) }) .map(|s| unsafe { BusName::from_slice_unchecked(s) }) } /// Sets the destination of this Message /// /// If dest is none, that means broadcast to all relevant destinations. pub fn set_destination(&mut self, dest: Option) { let c_dest = dest.as_ref().map(|d| d.as_cstr().as_ptr()).unwrap_or(ptr::null()); assert!(unsafe { ffi::dbus_message_set_destination(self.msg, c_dest) } != 0); } /// Gets the interface this Message is being sent to. pub fn interface<'a>(&'a self) -> Option> { self.msg_internal_str(unsafe { ffi::dbus_message_get_interface(self.msg) }) .map(|s| unsafe { Interface::from_slice_unchecked(s) }) } /// Gets the interface member being called. pub fn member<'a>(&'a self) -> Option> { self.msg_internal_str(unsafe { ffi::dbus_message_get_member(self.msg) }) .map(|s| unsafe { Member::from_slice_unchecked(s) }) } /// 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> { self.set_error_from_msg().map(|_| self) } pub (super) fn set_error_from_msg(&self) -> Result<(), Error> { let mut e = Error::empty(); if unsafe { ffi::dbus_set_error_from_message(e.get_mut(), self.msg) } != 0 { Err(e) } else { Ok(()) } } pub (crate) fn ptr(&self) -> *mut ffi::DBusMessage { self.msg } pub (crate) fn from_ptr(ptr: *mut ffi::DBusMessage, add_ref: bool) -> Message { if add_ref { unsafe { ffi::dbus_message_ref(ptr) }; } Message { msg: ptr } } } 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()) } } /// A convenience struct that wraps connection, destination and path. /// /// Useful if you want to make many method calls to the same destination path. #[derive(Clone, Debug)] pub struct ConnPath<'a, C> { /// Some way to access the connection, e g a &Connection or Rc pub conn: C, /// Destination, i e what D-Bus service you're communicating with pub dest: BusName<'a>, /// Object path on the destination pub path: Path<'a>, /// Timeout in milliseconds for blocking method calls pub timeout: i32, } impl<'a, C: ::std::ops::Deref> ConnPath<'a, C> { /// Make a D-Bus method call, where you can append arguments inside the closure. pub fn method_call_with_args(&self, i: &Interface, m: &Member, f: F) -> Result { let mut msg = Message::method_call(&self.dest, &self.path, i, m); f(&mut msg); self.conn.send_with_reply_and_block(msg, self.timeout) } /// Emit a D-Bus signal, where you can append arguments inside the closure. pub fn signal_with_args(&self, i: &Interface, m: &Member, f: F) -> Result { let mut msg = Message::signal(&self.path, i, m); f(&mut msg); self.conn.send(msg).map_err(|_| Error::new_custom("org.freedesktop.DBus.Error.Failed", "Sending signal failed")) } /// Emit a D-Bus signal, where the arguments are in a struct. pub fn emit(&self, signal: &S) -> Result { let msg = signal.to_emit_message(&self.path); self.conn.send(msg).map_err(|_| Error::new_custom("org.freedesktop.DBus.Error.Failed", "Sending signal failed")) } } // For purpose of testing the library only. #[cfg(test)] pub (crate) 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, Message, MessageType, BusType, MessageItem, OwnedFd, libc, Path, BusName}; #[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(); loop { for n in c.incoming(1000) { if n.msg_type() == MessageType::MethodCall { let z: OwnedFd = n.read1().unwrap(); println!("Got {:?}", z); let mut q: libc::c_char = 100; assert_eq!(1, unsafe { libc::read(z.as_raw_fd(), &mut q as *mut _ as *mut libc::c_void, 1) }); assert_eq!(q, 'z' as libc::c_char); return; } else { 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(); loop { for n in c.incoming(1000) { if n.msg_type() == MessageType::MethodCall { let receiving = format!("{:?}", n.get_items()); println!("Receiving {}", receiving); assert_eq!(sending, receiving); return; } else { 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.signature(), "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(); loop { for n in c.incoming(1000) { if n.msg_type() == MessageType::MethodCall { let receiving = format!("{:?}", n.get_items()); println!("Receiving {}", receiving); assert_eq!(sending, receiving); return; } else { 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(); } #[test] fn set_valid_destination() { let mut m = Message::new_method_call("org.test.rust", "/", "org.test.rust", "Test").unwrap(); let d = Some(BusName::new(":1.14").unwrap()); m.set_destination(d); assert!(!m.get_no_reply()); m.set_no_reply(true); assert!(m.get_no_reply()); } } dbus-0.6.5/src/methoddisp.rs010066400017500001750000001264521312351165400142120ustar0000000000000000/// NOTE: No longer used - replaced with files in the "tree" directory. #![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::{CStr, CString}; use std::fmt; use super::arg; type ArcMap = BTreeMap, Arc>; #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] /// A D-Bus Argument. pub struct Argument(Option, Signature<'static>); impl Argument { /// Create a new Argument. pub fn new(name: Option, sig: Signature<'static>) -> 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))) } } // Small helper struct to reduce memory somewhat for objects without annotations #[derive(Clone, Debug, Default)] struct Annotations(Option>); impl Annotations { fn new() -> Annotations { Annotations(None) } fn insert, V: Into>(&mut self, n: N, v: V) { if self.0.is_none() { self.0 = Some(BTreeMap::new()) } self.0.as_mut().unwrap().insert(n.into(), v.into()); } fn introspect(&self, indent: &str) -> String { self.0.as_ref().map(|s| s.iter().fold("".into(), |aa, (ak, av)| { format!("{}{}\n", aa, indent, ak, av) })).unwrap_or(String::new()) } } // Doesn't work, conflicting impls // impl> From for Argument impl From> for Argument { fn from(t: Signature<'static>) -> Argument { Argument(None, t) } } impl<'a> From<&'a str> for Argument { fn from(t: &'a str) -> Argument { Argument(None, String::from(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<'static>, 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: Annotations, } 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 an "in" Argument to this Method. pub fn inarg>(mut self, s: S) -> Self { self.i_args.push((s.into(), A::signature()).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 an "out" Argument to this Method. pub fn outarg>(mut self, s: S) -> Self { self.o_args.push((s.into(), A::signature()).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, value); 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<'static>, cb: M) -> Self { Method { name: Arc::new(n), i_args: vec!(), o_args: vec!(), anns: Annotations::new(), cb: cb } } } #[derive(Debug)] /// Represents a D-Bus interface. pub struct Interface { name: Arc>, methods: ArcMap, Method>, signals: ArcMap, Signal>, properties: ArcMap>, anns: Annotations, } 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. Lets you keep another clone of the signal /// (which you can use to emit the signal, once it belongs to an object path). /// /// Note: You are not allowed to add a signal to more than one interface. pub fn add_s_arc(mut self, s: Arc) -> Self { self.signals.insert(s.name.clone(), 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. Lets you keep another clone of the property /// (which you can use to get and set the current value of the property). /// /// Note: You are not allowed to add a property to more than one interface. Later function calls might panic if you do so. pub fn add_p_arc(mut self, p: Arc>) -> Self { self.properties.insert(p.name.clone(), 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, value); 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<'static>) -> Interface { Interface { name: Arc::new(t), methods: BTreeMap::new(), signals: BTreeMap::new(), properties: BTreeMap::new(), anns: Annotations::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: Annotations, } 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(|s| s.append2(arg::Dict::new(vec!((&**self.name, arg::Variant(m.clone())))), arg::Array::<&str, _>::new(vec!())) ), EmitsChangedSignal::Invalidates => self.get_signal().map(|s| { s.append2(arg::Dict::<&str, arg::Variant, _>::new(vec!()), arg::Array::new(vec!(&**self.name))) }), }; *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, value); 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: Annotations::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: Annotations, } 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 } /// Returns a message which emits the signal when sent. /// Panics if the signal is not inserted in an object path. /// /// Same as "emit" but does not take a "MessageItem" argument. pub fn msg(&self) -> Message { self.emit(&[]) } /// 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 an Argument to the Signal. pub fn sarg>(mut self, s: S) -> Self { self.arguments.push((s.into(), A::signature()).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, value); self } /// Add an annotation that this entity is deprecated. pub fn deprecated(self) -> Self { self.annotate("org.freedesktop.DBus.Deprecated", "true") } } 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, Interface>, } impl ObjectPath { fn new(p: Path<'static>) -> ObjectPath { ObjectPath { name: Arc::new(p), ifaces: BTreeMap::new() } } fn get_iface<'a>(&'a self, i: Option<&'a CStr>) -> Result<&Arc>, MethodErr> { let iface_name = try!(i.ok_or_else(|| MethodErr::invalid_arg(&0))); let j = try!(IfaceName::from_slice(iface_name.to_bytes_with_nul()).map_err(|e| MethodErr::invalid_arg(&e))); self.ifaces.get(&j).ok_or_else(|| MethodErr::no_interface(&j)) } fn prop_set(&self, m: &Message, o: &ObjectPath, t: &Tree) -> MethodResult { let (iname, p) = m.get2(); let iface = try!(self.get_iface(iname)); let prop_name: &str = try!(p.ok_or_else(|| MethodErr::invalid_arg(&1))); let prop: &Property = try!(iface.properties.get(&String::from(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 (iname, p) = m.get2(); let iface = try!(self.get_iface(iname)); let prop_name: &str = try!(p.ok_or_else(|| MethodErr::invalid_arg(&1))); let prop: &Property = try!(iface.properties.get(&String::from(prop_name)) .ok_or_else(|| MethodErr::no_property(&prop_name))); let r = try!(prop.remote_get(m)); Ok(vec!(m.method_return().append1(arg::Variant(r)))) } fn prop_get_all(&self, m: &Message) -> MethodResult { let iface = try!(self.get_iface(m.get1())); let mut q = vec!(); for v in iface.properties.values() { q.push((&**v.name, arg::Variant(try!(v.remote_get(m))))); } Ok(vec!(m.method_return().append1(arg::Dict::new(q)))) } 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) ) .inarg::<&str,_>("interface_name") .inarg::<&str,_>("property_name") .outarg::,_>("value")) .add_m(f.method_sync("GetAll", |m,o,_| o.prop_get_all(m)) .inarg::<&str,_>("interface_name") .outarg::, ()>,_>("props")) .add_m(f.method_sync("Set", |m,o,t| o.prop_set(m, o, t)) .inarg::<&str,_>("interface_name") .inarg::<&str,_>("property_name") .inarg::,_>("value")); self.ifaces.insert(i.name.clone(), Arc::new(i)); } /// Add an Interface to this Object Path. pub fn add(mut self, p: Interface) -> Self { use std::mem; for s in p.signals.values() { let n = Some((self.name.clone(), p.name.clone())); let o = mem::replace(&mut *s.owner.lock().unwrap(), n); assert!(o.is_none(), "Signal {} already added to object path", s.name); }; for s in p.properties.values() { let n = Some((self.name.clone(), p.name.clone())); let o = mem::replace(&mut *s.owner.lock().unwrap(), n); assert!(o.is_none(), "Property {} already added to object path", s.name); }; 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\""), m.anns.introspect(" ") ))), introspect_map(&iv.properties, "property", " ", |p| ( format!(" type=\"{}\" access=\"{}\"", p.get_value().type_sig(), p.rw.introspect()), p.anns.introspect(" ") )), introspect_map(&iv.signals, "signal", " ", |s| (format!(""), format!("{}{}", Argument::introspect_all(&s.arguments, " ", ""), s.anns.introspect(" ") ))), iv.anns.introspect(" ") )) ); 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, ObjectPath> } 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 an Object Path to this Tree. /// /// Note: This does not unregister a path with the connection, so if the tree is currently registered, /// you might want to call Connection::register_object_path to add the path manually. pub fn add(mut self, p: ObjectPath) -> Self { self.paths.insert(p.name.clone(), Arc::new(p)); self } /// Adds an ObjectPath to this Tree. Returns a reference to the ObjectPath. /// The note for add() also applies here. pub fn add_o_ref(&mut self, p: ObjectPath) -> Arc> { let name = p.name.clone(); let o = Arc::new(p); self.paths.insert(name, o.clone()); o } /// Remove a object path from the Tree. Returns the object path removed, or None if not found. /// /// Note: This does not unregister a path with the connection, so if the tree is currently registered, /// you might want to call Connection::unregister_object_path to remove the path manually. pub fn remove(&mut self, p: &Path<'static>) -> Option>> { // There is no real reason p needs to have a static lifetime; but // the borrow checker doesn't agree. :-( self.paths.remove(p) } /// 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: Annotations::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(); println!("{:?}", r1.get_items()); let vv: super::arg::Variant = r1.get1().unwrap(); assert!(vv.0 == 7 || vv.0 == 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 count; let f = Factory::new_fnmut(); count = Arc::new(f.property("changes", 0i32)); let mut i = f.interface("com.example.dbus.rs").add_p_arc(count.clone()); 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!()) })); } */ #[test] fn prop_server() { let setme: Arc>>>>; // Yikes! setme = Arc::new(RefCell::new(None)); let f = Factory::new_fnmut(); let mut i = f.interface("com.example.dbus.rs"); let count = i.add_p_ref(f.property("changes", 0i32)); let count2 = count.clone(); let setme2 = setme.clone(); let setme3 = Arc::new(f.property("setme", 0u8).access(Access::ReadWrite).on_set(move |m,_,_| { let ss2 = setme2.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 = count2.get_value().inner().unwrap(); count2.set_value((v + 1).into()).unwrap(); Ok(r) })); *setme.borrow_mut() = Some(setme3.clone()); let i = i.add_p_arc(setme3); 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")).deprecated()) ); 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.6.5/src/objpath.rs010066400017500001750000000505351312351165400134770ustar0000000000000000#![allow(deprecated)] use 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. #[deprecated(note="please use `tree` module instead")] 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. #[deprecated(note="please use `tree` module instead")] 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. #[deprecated(note="please use `tree` module instead")] 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.6.5/src/prop.rs010064400017500001750000000122171351376776700130460ustar0000000000000000use 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<'a>, path: Path<'a>, interface: Interface<'a>, 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.6.5/src/signalargs.rs010064400017500001750000000076231351376776700142250ustar0000000000000000use arg; use {Message, MessageType, BusName, Path, Interface, Member, MatchRule}; /// Helper methods for structs representing a Signal /// /// # Example /// /// Listen to InterfacesRemoved signal from org.bluez.obex. /// /// ```rust,no_run /// use dbus::{Connection, BusType, SignalArgs}; /// use dbus::stdintf::org_freedesktop_dbus::ObjectManagerInterfacesRemoved as IR; /// /// let c = Connection::get_private(BusType::Session).unwrap(); /// // Add a match for this signal /// let mstr = IR::match_str(Some(&"org.bluez.obex".into()), None); /// c.add_match(&mstr).unwrap(); /// /// // Wait for the signal to arrive. /// for msg in c.incoming(1000) { /// if let Some(ir) = IR::from_message(&msg) { /// println!("Interfaces {:?} have been removed from bluez on path {}.", ir.interfaces, ir.object); /// } /// } /// ``` pub trait SignalArgs: Default { /// D-Bus name of signal const NAME: &'static str; /// D-Bus name of interface this signal belongs to const INTERFACE: &'static str; /// Low-level method for appending this struct to a message. /// /// You're more likely to use one of the more high level functions. fn append(&self, i: &mut arg::IterAppend); /// Low-level method for getting arguments from a message. /// /// You're more likely to use one of the more high level functions. fn get(&mut self, i: &mut arg::Iter) -> Result<(), arg::TypeMismatchError>; /// Returns a message that emits the signal. fn to_emit_message(&self, path: &Path) -> Message { let mut m = Message::signal(path, &Interface::from(Self::INTERFACE), &Member::from(Self::NAME)); self.append(&mut arg::IterAppend::new(&mut m)); m } /// If the message is a signal of the correct type, return its arguments, otherwise return None. /// /// This does not check sender and path of the message, which is likely relevant to you as well. fn from_message(m: &Message) -> Option { if m.msg_type() != MessageType::Signal { None } else if m.interface().as_ref().map(|x| &**x) != Some(Self::INTERFACE) { None } else if m.member().as_ref().map(|x| &**x) != Some(Self::NAME) { None } else { let mut z: Self = Default::default(); z.get(&mut m.iter_init()).ok().map(|_| z) } } /// Returns a match rule matching this signal. /// /// If sender and/or path is None, matches all senders and/or paths. fn match_rule<'a>(sender: Option<&'a BusName>, path: Option<&'a Path>) -> MatchRule<'a> { let mut m: MatchRule = Default::default(); m.sender = sender.map(|x| x.clone()); m.path = path.map(|x| x.clone()); m.msg_type = Some(MessageType::Signal); m.interface = Some(Self::INTERFACE.into()); m.member = Some(Self::NAME.into()); m } /// Returns a string that can be sent to `Connection::add_match`. /// /// If sender and/or path is None, matches all senders and/or paths. fn match_str(sender: Option<&BusName>, path: Option<&Path>) -> String { Self::match_rule(sender, path).match_str() } } #[test] fn intf_removed() { use {Connection, BusType}; use stdintf::org_freedesktop_dbus::ObjectManagerInterfacesRemoved as IR; let c = Connection::get_private(BusType::Session).unwrap(); let mstr = IR::match_str(Some(&c.unique_name().into()), Some(&"/hello".into())); println!("Match str: {}", mstr); c.add_match(&mstr).unwrap(); let ir = IR { object: "/hello".into(), interfaces: vec!("ABC.DEF".into(), "GHI.JKL".into()) }; let cp = c.with_path("dbus.dummy", "/hello", 2000); cp.emit(&ir).unwrap(); for msg in c.incoming(1000) { if &*msg.sender().unwrap() != &*c.unique_name() { continue; } if let Some(ir2) = IR::from_message(&msg) { assert_eq!(ir2.object, ir.object); assert_eq!(ir2.interfaces, ir.interfaces); break; } } } dbus-0.6.5/src/stdintf.rs010064400017500001750000000215001351376776700135340ustar0000000000000000//! This module contains some standard interfaces and an easy way to call them. //! //! See the [D-Bus specification](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces) for more information about these standard interfaces. //! //! The code here was originally created by dbus-codegen. //! //! # Example //! ``` //! use dbus::{Connection, BusType}; //! use dbus::stdintf::org_freedesktop_dbus::Introspectable; //! let c = Connection::get_private(BusType::Session).unwrap(); //! let p = c.with_path("org.freedesktop.DBus", "/", 10000); //! println!("Introspection XML: {}", p.introspect().unwrap()); //! ``` //! #![allow(missing_docs)] pub use self::org_freedesktop_dbus::Peer as OrgFreedesktopDBusPeer; pub use self::org_freedesktop_dbus::Introspectable as OrgFreedesktopDBusIntrospectable; pub use self::org_freedesktop_dbus::Properties as OrgFreedesktopDBusProperties; pub use self::org_freedesktop_dbus::ObjectManager as OrgFreedesktopDBusObjectManager; pub mod org_freedesktop_dbus { use arg; /// Method of the [org.freedesktop.DBus.Introspectable](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-introspectable) interface. pub trait Introspectable { type Err; fn introspect(&self) -> Result; } impl<'a, C: ::std::ops::Deref> Introspectable for ::ConnPath<'a, C> { type Err = ::Error; fn introspect(&self) -> Result { let mut m = try!(self.method_call_with_args(&"org.freedesktop.DBus.Introspectable".into(), &"Introspect".into(), |_| { })); try!(m.as_result()); let mut i = m.iter_init(); let xml: String = try!(i.read()); Ok(xml) } } /// Methods of the [org.freedesktop.DBus.Properties](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties) interface. pub trait Properties { type Err; fn get arg::Get<'b>>(&self, interface_name: &str, property_name: &str) -> Result; fn get_all(&self, interface_name: &str) -> Result<::std::collections::HashMap>>, Self::Err>; fn set(&self, interface_name: &str, property_name: &str, value: I2) -> Result<(), Self::Err>; } impl<'a, C: ::std::ops::Deref> Properties for ::ConnPath<'a, C> { type Err = ::Error; fn get arg::Get<'b>>(&self, interface_name: &str, property_name: &str) -> Result { let mut m = try!(self.method_call_with_args(&"org.freedesktop.DBus.Properties".into(), &"Get".into(), |msg| { let mut i = arg::IterAppend::new(msg); i.append(interface_name); i.append(property_name); })); try!(m.as_result()); let mut i = m.iter_init(); let value: arg::Variant = try!(i.read()); Ok(value.0) } fn get_all(&self, interface_name: &str) -> Result<::std::collections::HashMap>>, Self::Err> { let mut m = try!(self.method_call_with_args(&"org.freedesktop.DBus.Properties".into(), &"GetAll".into(), |msg| { let mut i = arg::IterAppend::new(msg); i.append(interface_name); })); try!(m.as_result()); let mut i = m.iter_init(); let properties: ::std::collections::HashMap>> = try!(i.read()); Ok(properties) } fn set(&self, interface_name: &str, property_name: &str, value: I2) -> Result<(), Self::Err> { let mut m = try!(self.method_call_with_args(&"org.freedesktop.DBus.Properties".into(), &"Set".into(), |msg| { let mut i = arg::IterAppend::new(msg); i.append(interface_name); i.append(property_name); i.append(arg::Variant(value)); })); try!(m.as_result()); Ok(()) } } #[derive(Debug, Default)] /// Struct to send/receive the PropertiesChanged signal of the /// [org.freedesktop.DBus.Properties](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties) interface. pub struct PropertiesPropertiesChanged { pub interface_name: String, pub changed_properties: ::std::collections::HashMap>>, pub invalidated_properties: Vec, } impl ::SignalArgs for PropertiesPropertiesChanged { const NAME: &'static str = "PropertiesChanged"; const INTERFACE: &'static str = "org.freedesktop.DBus.Properties"; fn append(&self, i: &mut arg::IterAppend) { (&self.interface_name as &arg::RefArg).append(i); (&self.changed_properties as &arg::RefArg).append(i); (&self.invalidated_properties as &arg::RefArg).append(i); } fn get(&mut self, i: &mut arg::Iter) -> Result<(), arg::TypeMismatchError> { self.interface_name = try!(i.read()); self.changed_properties = try!(i.read()); self.invalidated_properties = try!(i.read()); Ok(()) } } /// Method of the [org.freedesktop.DBus.ObjectManager](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager) interface. pub trait ObjectManager { type Err; fn get_managed_objects(&self) -> Result<::std::collections::HashMap<::Path<'static>, ::std::collections::HashMap>>>>, Self::Err>; } impl<'a, C: ::std::ops::Deref> ObjectManager for ::ConnPath<'a, C> { type Err = ::Error; fn get_managed_objects(&self) -> Result<::std::collections::HashMap<::Path<'static>, ::std::collections::HashMap>>>>, Self::Err> { let mut m = try!(self.method_call_with_args(&"org.freedesktop.DBus.ObjectManager".into(), &"GetManagedObjects".into(), |_| { })); try!(m.as_result()); let mut i = m.iter_init(); let objects: ::std::collections::HashMap<::Path<'static>, ::std::collections::HashMap>>>> = try!(i.read()); Ok(objects) } } #[derive(Debug, Default)] /// Struct to send/receive the InterfacesAdded signal of the /// [org.freedesktop.DBus.ObjectManager](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager) interface. pub struct ObjectManagerInterfacesAdded { pub object: ::Path<'static>, pub interfaces: ::std::collections::HashMap>>>, } impl ::SignalArgs for ObjectManagerInterfacesAdded { const NAME: &'static str = "InterfacesAdded"; const INTERFACE: &'static str = "org.freedesktop.DBus.ObjectManager"; fn append(&self, i: &mut arg::IterAppend) { (&self.object as &arg::RefArg).append(i); (&self.interfaces as &arg::RefArg).append(i); } fn get(&mut self, i: &mut arg::Iter) -> Result<(), arg::TypeMismatchError> { self.object = try!(i.read()); self.interfaces = try!(i.read()); Ok(()) } } #[derive(Debug, Default)] /// Struct to send/receive the InterfacesRemoved signal of the /// [org.freedesktop.DBus.ObjectManager](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager) interface. pub struct ObjectManagerInterfacesRemoved { pub object: ::Path<'static>, pub interfaces: Vec, } impl ::SignalArgs for ObjectManagerInterfacesRemoved { const NAME: &'static str = "InterfacesRemoved"; const INTERFACE: &'static str = "org.freedesktop.DBus.ObjectManager"; fn append(&self, i: &mut arg::IterAppend) { (&self.object as &arg::RefArg).append(i); (&self.interfaces as &arg::RefArg).append(i); } fn get(&mut self, i: &mut arg::Iter) -> Result<(), arg::TypeMismatchError> { self.object = try!(i.read()); self.interfaces = try!(i.read()); Ok(()) } } /// Methods of the [org.freedesktop.DBus.Peer](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-peer) interface. pub trait Peer { type Err; fn ping(&self) -> Result<(), Self::Err>; fn get_machine_id(&self) -> Result; } impl<'a, C: ::std::ops::Deref> Peer for ::ConnPath<'a, C> { type Err = ::Error; fn ping(&self) -> Result<(), Self::Err> { let mut m = try!(self.method_call_with_args(&"org.freedesktop.DBus.Peer".into(), &"Ping".into(), |_| { })); try!(m.as_result()); Ok(()) } fn get_machine_id(&self) -> Result { let mut m = try!(self.method_call_with_args(&"org.freedesktop.DBus.Peer".into(), &"GetMachineId".into(), |_| { })); try!(m.as_result()); let mut i = m.iter_init(); let machine_uuid: String = try!(i.read()); Ok(machine_uuid) } } } dbus-0.6.5/src/strings.rs010064400017500001750000000152771351376776700135700ustar0000000000000000// CString wrappers. use std::{str, fmt, ops, default, hash}; use std::ffi::{CStr, CString}; use std::borrow::{Borrow, Cow}; use std::os::raw::c_char; #[cfg(not(feature = "no-string-validation"))] use Error; #[cfg(not(feature = "no-string-validation"))] use ffi; macro_rules! cstring_wrapper { ($t: ident, $s: ident) => { impl<'m> $t<'m> { #[cfg(feature = "no-string-validation")] fn check_valid(_: *const c_char) -> Result<(), String> { Ok(()) } #[cfg(not(feature = "no-string-validation"))] fn check_valid(c: *const c_char) -> Result<(), String> { let mut e = Error::empty(); let b = unsafe { ffi::$s(c, e.get_mut()) }; if b != 0 { Ok(()) } else { Err(e.message().unwrap().into()) } } /// Creates a new instance of this struct. /// /// Note: If the no-string-validation feature is activated, this string /// will not be checked for conformance with the D-Bus specification. pub fn new>>(s: S) -> Result<$t<'m>, String> { let c = try!(CString::new(s).map_err(|e| e.to_string())); $t::check_valid(c.as_ptr()).map(|_| $t(Cow::Owned(c))) } /// Creates a new instance of this struct. If you end it with \0, /// it can borrow the slice without extra allocation. /// /// Note: If the no-string-validation feature is activated, this string /// will not be checked for conformance with the D-Bus specification. pub fn from_slice(s: &'m [u8]) -> Result<$t<'m>, String> { if s.len() == 0 || s[s.len()-1] != 0 { return $t::new(s) }; $t::check_valid(s.as_ptr() as *const c_char).map(|_| { let c = unsafe { CStr::from_ptr(s.as_ptr() as *const c_char) }; $t(Cow::Borrowed(c)) }) } /// This function creates a new instance of this struct, without checking. /// It's up to you to guarantee that s ends with a \0 and is valid. pub unsafe fn from_slice_unchecked(s: &'m [u8]) -> $t<'m> { debug_assert!(s[s.len()-1] == 0); $t(Cow::Borrowed(CStr::from_ptr(s.as_ptr() as *const c_char))) } /// View this struct as a CStr. pub fn as_cstr(&self) -> &CStr { &self.0 } /// Makes sure this string does not contain borrows. pub fn into_static(self) -> $t<'static> { $t(Cow::Owned(self.0.into_owned())) } /// Converts this struct to a CString. pub fn into_cstring(self) -> CString { self.0.into_owned() } } /* /// #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<'m> From for $t<'m> { fn from(s: String) -> $t<'m> { $t::new(s).unwrap() } } /// #Panics /// /// If given string is not valid. impl<'m> From<&'m String> for $t<'m> { fn from(s: &'m String) -> $t<'m> { $t::from_slice(s.as_bytes()).unwrap() } } /// #Panics /// /// If given string is not valid. impl<'m> From<&'m str> for $t<'m> { fn from(s: &'m str) -> $t<'m> { $t::from_slice(s.as_bytes()).unwrap() } } impl<'m> From<$t<'m>> for CString { fn from(s: $t<'m>) -> CString { s.0.into_owned() } } /// #Panics /// /// If given string is not valid. impl<'m> From> for $t<'m> { fn from(s: Cow<'m, str>) -> $t<'m> { match s { Cow::Borrowed(z) => z.into(), Cow::Owned(z) => z.into(), } } } impl<'inner, 'm: 'inner> From<&'m $t<'inner>> for $t<'m> { fn from(borrow: &'m $t<'inner>) -> $t<'m> { $t(Cow::Borrowed(borrow.0.borrow())) } } impl<'m> ops::Deref for $t<'m> { type Target = str; fn deref(&self) -> &str { str::from_utf8(self.0.to_bytes()).unwrap() } } impl<'m> fmt::Display for $t<'m> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let s: &str = &self; (&s as &fmt::Display).fmt(f) } } impl<'m> AsRef for $t<'m> { fn as_ref(&self) -> &CStr { &self.0 } } impl<'m> hash::Hash for $t<'m> { fn hash(&self, state: &mut H) { self.0.hash(state); } } }} /// 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<'a>(Cow<'a, CStr>); cstring_wrapper!(Signature, dbus_signature_validate_single); impl Signature<'static> { /// Makes a D-Bus signature that corresponds to A. pub fn make() -> Signature<'static> { A::signature() } } /// 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<'a>(Cow<'a, CStr>); cstring_wrapper!(Path, dbus_validate_path); // This is needed so one can make arrays of paths easily impl<'a> default::Default for Path<'a> { fn default() -> Path<'a> { Path(Cow::Borrowed(unsafe { CStr::from_ptr(b"/\0".as_ptr() as *const c_char)})) } } /// 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<'a>(Cow<'a, CStr>); 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<'a>(Cow<'a, CStr>); 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<'a>(Cow<'a, CStr>); 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<'a>(Cow<'a, CStr>); cstring_wrapper!(ErrorName, dbus_validate_error_name); #[test] fn some_path() { use std::os::raw::c_char; let p1: Path = "/valid".into(); let p2 = Path::new("##invalid##"); assert_eq!(p1, Path(Cow::Borrowed(unsafe { CStr::from_ptr(b"/valid\0".as_ptr() as *const c_char) }))); #[cfg(not(feature = "no-string-validation"))] assert_eq!(p2, Err("Object path was not valid: '##invalid##'".into())); #[cfg(feature = "no-string-validation")] assert_eq!(p2, Ok(Path(Cow::Borrowed(unsafe { CStr::from_ptr(b"##invalid##\0".as_ptr() as *const c_char) })))); } #[test] fn reborrow_path() { let p1 = Path::from("/valid"); let p2 = p1.clone(); { let p2_borrow: &Path = &p2; let p3 = Path::from(p2_borrow); // Check path created from borrow assert_eq!(p2, p3); } // Check path that was previously borrowed assert_eq!(p1, p2); } #[test] fn make_sig() { assert_eq!(&*Signature::make::<(&str, u8)>(), "(sy)"); } dbus-0.6.5/src/tree/factory.rs010064400017500001750000000116511351376776700144750ustar0000000000000000use super::{MethodType, DataType, MTFn, MTFnMut, MTSync, MethodResult, MethodInfo}; use super::{Tree, ObjectPath, Interface, Property, Signal, Method}; use super::objectpath::IfaceCache; use std::sync::Arc; use Interface as IfaceName; use {Member, Path, arg}; use std::cell::RefCell; /// The factory is used to create object paths, interfaces, methods etc. /// /// There are three factories: /// /// **MTFn** - all methods are `Fn()`. /// /// **MTFnMut** - 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. /// /// **MTSync** - all methods are `Fn() + Send + Sync + 'static`. This means that the methods /// can be called from different threads in parallel. /// #[derive(Debug, Clone)] pub struct Factory, D: DataType=()>(Arc>); impl, D: DataType> From>> for Factory { fn from(f: Arc>) -> Self { Factory(f) } } impl Factory, ()> { /// Creates a new factory for single-thread use. pub fn new_fn() -> Factory, D> { Factory(IfaceCache::new()) } /// Creates a new factory for single-thread use, where callbacks can mutate their environment. pub fn new_fnmut() -> Factory, D> { Factory(IfaceCache::new()) } /// Creates a new factory for multi-thread use. pub fn new_sync() -> Factory, D> { Factory(IfaceCache::new()) } } impl Factory, D> { /// Creates a new method for single-thread use. pub fn method(&self, t: T, data: D::Method, handler: H) -> Method, D> where H: 'static + Fn(&MethodInfo, D>) -> MethodResult, T: Into> { super::leaves::new_method(t.into(), data, Box::new(handler) as Box<_>) } } impl Factory, D> { /// Creates a new method for single-thread use. pub fn method(&self, t: T, data: D::Method, handler: H) -> Method, D> where H: 'static + FnMut(&MethodInfo, D>) -> MethodResult, T: Into> { super::leaves::new_method(t.into(), data, Box::new(RefCell::new(handler)) as Box<_>) } } impl Factory, D> { /// Creates a new method for multi-thread use. pub fn method(&self, t: T, data: D::Method, handler: H) -> Method, D> where H: Fn(&MethodInfo, D>) -> MethodResult + Send + Sync + 'static, T: Into> { super::leaves::new_method(t.into(), data, Box::new(handler) as Box<_>) } } impl, D: DataType> Factory { /// Creates a new property. /// /// `A` is used to calculate the type signature of the property. pub fn property>(&self, name: T, data: D::Property) -> Property { let sig = A::signature(); super::leaves::new_property(name.into(), sig, data) } /// Creates a new signal. pub fn signal>>(&self, name: T, data: D::Signal) -> Signal { super::leaves::new_signal(name.into(), data) } /// Creates a new interface. pub fn interface>>(&self, name: T, data: D::Interface) -> Interface { super::objectpath::new_interface(name.into(), data) } /// Creates a new object path. pub fn object_path>>(&self, name: T, data: D::ObjectPath) -> ObjectPath { super::objectpath::new_objectpath(name.into(), data, self.0.clone()) } /// Creates a new tree. pub fn tree(&self, data: D::Tree) -> Tree { super::objectpath::new_tree(data) } /// Creates a new method - usually you'll use "method" instead. /// /// This is useful for being able to create methods in code which is generic over methodtype. pub fn method_sync(&self, t: T, data: D::Method, handler: H) -> Method where H: Fn(&MethodInfo) -> MethodResult + Send + Sync + 'static, T: Into> { super::leaves::new_method(t.into(), data, M::make_method(handler)) } } #[test] fn create_fnmut() { let f = Factory::new_fnmut::<()>(); let mut move_me = 5u32; let m = f.method("test", (), move |m| { move_me += 1; Ok(vec!(m.msg.method_return().append1(&move_me))) }); assert_eq!(&**m.get_name(), "test"); } #[test] fn fn_customdata() { #[derive(Default)] struct Custom; impl DataType for Custom { type Tree = (); type ObjectPath = Arc; type Interface = (); type Property = (); type Method = i32; type Signal = (); } let f = Factory::new_fn::(); let m = f.method("test", 789, |_| unimplemented!()); assert_eq!(*m.get_data(), 789); let o = f.object_path("/test/test", Arc::new(7)); assert_eq!(**o.get_data(), 7); } dbus-0.6.5/src/tree/leaves.rs010064400017500001750000000636261351376776700143160ustar0000000000000000// Methods, signals, properties, and interfaces. use super::utils::{Argument, Annotations, Introspect, introspect_args}; use super::{MethodType, MethodInfo, MethodResult, MethodErr, DataType, PropInfo, MTFn, MTFnMut, MTSync}; use {Member, Signature, Message, Path, MessageItem}; use Interface as IfaceName; use arg; use std::fmt; use std::cell::RefCell; use stdintf::org_freedesktop_dbus::PropertiesPropertiesChanged; // Workaround for https://github.com/rust-lang/rust/issues/31518 struct DebugMethod, D: DataType>(Box); impl, D: DataType> fmt::Debug for DebugMethod { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "") } } struct DebugGetProp, D: DataType>(Box); impl, D: DataType> fmt::Debug for DebugGetProp { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "") } } struct DebugSetProp, D: DataType>(Box); impl, D: DataType> fmt::Debug for DebugSetProp { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "") } } #[derive(Debug)] /// A D-Bus Method. pub struct Method, D: DataType> { cb: DebugMethod, data: D::Method, name: Member<'static>, i_args: Vec, o_args: Vec, anns: Annotations, } impl, D: DataType> 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 an "in" Argument to this Method. pub fn inarg>(mut self, s: S) -> Self { self.i_args.push((s.into(), A::signature()).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 an "out" Argument to this Method. pub fn outarg>(mut self, s: S) -> Self { self.o_args.push((s.into(), A::signature()).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 } /// Builder method that adds an annotation to the method. pub fn annotate, V: Into>(mut self, name: N, value: V) -> Self { self.anns.insert(name, value); self } /// Builder method that adds an annotation that this entity is deprecated. pub fn deprecated(self) -> Self { self.annotate("org.freedesktop.DBus.Deprecated", "true") } /// Call the Method pub fn call(&self, minfo: &MethodInfo) -> MethodResult { M::call_method(&self.cb.0, minfo) } /// Get method name pub fn get_name(&self) -> &Member<'static> { &self.name } /// Get associated data pub fn get_data(&self) -> &D::Method { &self.data } } impl, D: DataType> Introspect for Method { fn xml_name(&self) -> &'static str { "method" } fn xml_params(&self) -> String { String::new() } fn xml_contents(&self) -> String { format!("{}{}{}", introspect_args(&self.i_args, " ", " direction=\"in\""), introspect_args(&self.o_args, " ", " direction=\"out\""), self.anns.introspect(" ")) } } pub fn new_method, D: DataType>(n: Member<'static>, data: D::Method, cb: Box) -> Method { Method { name: n, i_args: vec!(), o_args: vec!(), anns: Annotations::new(), cb: DebugMethod(cb), data: data } } #[derive(Debug)] /// A D-Bus Signal. pub struct Signal { name: Member<'static>, data: D::Signal, arguments: Vec, anns: Annotations, } impl Signal { /// 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 an Argument to the Signal. pub fn sarg>(mut self, s: S) -> Self { self.arguments.push((s.into(), A::signature()).into()); self } /// Builder method that adds multiple Arguments to the Signal. 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, value); self } /// Add an annotation that this entity is deprecated. pub fn deprecated(self) -> Self { self.annotate("org.freedesktop.DBus.Deprecated", "true") } /// Get signal name pub fn get_name(&self) -> &Member<'static> { &self.name } /// Get associated data pub fn get_data(&self) -> &D::Signal { &self.data } /// Returns a message which emits the signal when sent. /// /// Same as "msg" but also takes a "MessageItem" argument. pub fn emit(&self, p: &Path<'static>, i: &IfaceName<'static>, items: &[MessageItem]) -> Message { let mut m = self.msg(p, i); m.append_items(items); m } /// Returns a message which emits the signal when sent. /// /// Same as "emit" but does not take a "MessageItem" argument. pub fn msg(&self, p: &Path<'static>, i: &IfaceName<'static>) -> Message { Message::signal(p, i, &self.name) } } impl Introspect for Signal { fn xml_name(&self) -> &'static str { "signal" } fn xml_params(&self) -> String { String::new() } fn xml_contents(&self) -> String { format!("{}{}", introspect_args(&self.arguments, " ", ""), self.anns.introspect(" ")) } } pub fn new_signal(n: Member<'static>, data: D::Signal) -> Signal { Signal { name: n, arguments: vec!(), anns: Annotations::new(), data: data } } #[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", } } } pub fn prop_append_dict<'v, M: MethodType + 'v, D: DataType + 'v, I: Iterator>> (iter: &mut arg::IterAppend, mut props: I, minfo: &MethodInfo) -> Result<(), MethodErr> { let mut result = Ok(()); iter.append_dict(&Signature::make::<&str>(), &Signature::make::>(), |subiter| loop { let p = if let Some(p) = props.next() { p } else { return }; if p.can_get().is_err() { continue; } let pinfo = minfo.to_prop_info(minfo.iface, p); subiter.append_dict_entry(|mut entryiter| { entryiter.append(&*p.get_name()); result = p.get_as_variant(&mut entryiter, &pinfo); }); if result.is_err() { return }; }); result } #[derive(Debug)] /// A D-Bus Property. pub struct Property, D: DataType> { name: String, data: D::Property, sig: Signature<'static>, emits: EmitsChangedSignal, auto_emit: bool, rw: Access, get_cb: Option>, set_cb: Option>, anns: Annotations, } impl, D: DataType> Property { /// Builder method that allows setting the Property's signal /// behavior when changed. /// /// Note: if e is set to const, the property will be read only. pub fn emits_changed(mut self, e: EmitsChangedSignal) -> Self { self.emits = e; if self.emits == EmitsChangedSignal::Const { self.rw = Access::Read }; self } /// Builder method that determines whether or not setting this property /// will result in an PropertiesChanged signal. Defaults to true. /// /// When set to true (the default), the behaviour is determined by "emits_changed". /// When set to false, no PropertiesChanged signal will be emitted (but the signal /// still shows up in introspection data). /// You can still emit the signal manually by, e g, calling `add_propertieschanged` /// and send the resulting message(s). pub fn auto_emit_on_set(mut self, b: bool) -> Self { self.auto_emit = b; self } /// Builder method that allows setting the Property as readable, /// writable, or both. /// /// Note: might modify emits_changed as well, if property is changed to non-readonly and emit is set to "Const". pub fn access(mut self, e: Access) -> Self { self.rw = e; if self.rw != Access::Read && self.emits == EmitsChangedSignal::Const { self.emits = EmitsChangedSignal::False }; self } /// Builder method that adds an annotation to the method. pub fn annotate, V: Into>(mut self, name: N, value: V) -> Self { self.anns.insert(name, value); self } /// Builder method that adds an annotation that this entity is deprecated. pub fn deprecated(self) -> Self { self.annotate("org.freedesktop.DBus.Deprecated", "true") } /// Get property name pub fn get_name(&self) -> &str { &self.name } /// Get associated data pub fn get_data(&self) -> &D::Property { &self.data } /// Returns Ok if the property is gettable pub fn can_get(&self) -> Result<(), MethodErr> { if self.rw == Access::Write || self.get_cb.is_none() { Err(MethodErr::failed(&format!("Property {} is write only", &self.name))) } else { Ok(()) } } /// Calls the on_get function and appends the result as a variant. /// /// Note: Will panic if get_cb is not set. pub fn get_as_variant(&self, i: &mut arg::IterAppend, pinfo: &PropInfo) -> Result<(), MethodErr> { let mut r = Ok(()); i.append_variant(&self.sig, |subi| { r = M::call_getprop(&*self.get_cb.as_ref().unwrap().0, subi, pinfo); }); r } /// Returns Ok if the property is settable. /// /// Will verify signature in case iter is not None; iter is supposed to point at the Variant with the item inside. pub fn can_set(&self, i: Option) -> Result<(), MethodErr> { use arg::Arg; if self.rw == Access::Read || self.set_cb.is_none() || self.emits == EmitsChangedSignal::Const { return Err(MethodErr::ro_property(&self.name)) } if let Some(mut i) = i { let mut subiter = try!(i.recurse(arg::Variant::::ARG_TYPE).ok_or_else(|| MethodErr::invalid_arg(&2))); if &*subiter.signature() != &*self.sig { return Err(MethodErr::failed(&format!("Property {} cannot change type", &self.name))) } } Ok(()) } /// Calls the on_set function, which reads from i. /// /// The return value might contain an extra message containing the EmitsChanged signal. /// Note: Will panic if set_cb is not set. pub fn set_as_variant(&self, i: &mut arg::Iter, pinfo: &PropInfo) -> Result, MethodErr> { use arg::Arg; let mut subiter = try!(i.recurse(arg::Variant::::ARG_TYPE).ok_or_else(|| MethodErr::invalid_arg(&2))); try!(M::call_setprop(&*self.set_cb.as_ref().unwrap().0, &mut subiter, pinfo)); self.get_emits_changed_signal(pinfo) } /// Gets the signal (if any) associated with the Property. fn get_signal(&self, p: &PropInfo) -> Message { Message::signal(p.path.get_name(), &"org.freedesktop.DBus.Properties".into(), &"PropertiesChanged".into()) .append1(&**p.iface.get_name()) } /// Adds this property to a list of PropertiesChanged signals. /// /// "v" is updated with the signal for this property. "new_value" is only called if self.emits is "true", /// it should return the value of the property. /// If no PropertiesChanged signal should be emitted for this property, "v" is left unchanged. pub fn add_propertieschanged Box>(&self, v: &mut Vec, iface: &IfaceName, new_value: F) { // Impl note: It is a bit silly that this function cannot be used from e g get_emits_changed_signal below, // but it is due to the fact that we cannot create a RefArg out of an IterAppend; which is what the 'on_get' // handler currently receives. if self.emits == EmitsChangedSignal::Const || self.emits == EmitsChangedSignal::False { return; } let vpos = v.iter().position(|vv| &*vv.interface_name == &**iface); let vpos = vpos.unwrap_or_else(|| { let mut z: PropertiesPropertiesChanged = Default::default(); z.interface_name = (&**iface).into(); v.push(z); v.len()-1 }); let vv = &mut v[vpos]; if self.emits == EmitsChangedSignal::Invalidates { vv.invalidated_properties.push(self.name.clone()); } else { vv.changed_properties.insert(self.name.clone(), arg::Variant(new_value())); } } fn get_emits_changed_signal(&self, m: &PropInfo) -> Result, MethodErr> { if !self.auto_emit { return Ok(None) } match self.emits { EmitsChangedSignal::False => Ok(None), EmitsChangedSignal::Const => Err(MethodErr::ro_property(&self.name)), EmitsChangedSignal::True => Ok(Some({ let mut s = self.get_signal(m); { let mut iter = arg::IterAppend::new(&mut s); try!(prop_append_dict(&mut iter, Some(self).into_iter(), &m.to_method_info())); iter.append(arg::Array::<&str, _>::new(vec!())); } s })), EmitsChangedSignal::Invalidates => Ok(Some(self.get_signal(m).append2( arg::Dict::<&str, arg::Variant, _>::new(vec!()), arg::Array::new(Some(&*self.name).into_iter()) ))), } } } impl<'a, D: DataType> Property, D> { /// Sets the callback for getting a property. /// /// For single-thread use. pub fn on_get(mut self, handler: H) -> Property, D> where H: 'static + Fn(&mut arg::IterAppend, &PropInfo, D>) -> Result<(), MethodErr> { self.get_cb = Some(DebugGetProp(Box::new(handler) as Box<_>)); self } /// Sets the callback for setting a property. /// /// For single-thread use. pub fn on_set(mut self, handler: H) -> Property, D> where H: 'static + Fn(&mut arg::Iter, &PropInfo, D>) -> Result<(), MethodErr> { self.set_cb = Some(DebugSetProp(Box::new(handler) as Box<_>)); self } } impl<'a, D: DataType> Property, D> { /// Sets the callback for getting a property. /// /// For single-thread use. pub fn on_get(mut self, handler: H) -> Property, D> where H: 'static + Fn(&mut arg::IterAppend, &PropInfo, D>) -> Result<(), MethodErr> { self.get_cb = Some(DebugGetProp(Box::new(RefCell::new(handler)) as Box<_>)); self } /// Sets the callback for setting a property. /// /// For single-thread use. pub fn on_set(mut self, handler: H) -> Property, D> where H: 'static + Fn(&mut arg::Iter, &PropInfo, D>) -> Result<(), MethodErr> { self.set_cb = Some(DebugSetProp(Box::new(RefCell::new(handler)) as Box<_>)); self } } impl Property, D> { /// Sets the callback for getting a property. /// /// For multi-thread use. pub fn on_get(mut self, handler: H) -> Property, D> where H: Fn(&mut arg::IterAppend, &PropInfo, D>) -> Result<(), MethodErr> + Send + Sync + 'static { self.get_cb = Some(DebugGetProp(Box::new(handler) as Box<_>)); self } /// Sets the callback for setting a property. /// /// For single-thread use. pub fn on_set(mut self, handler: H) -> Property, D> where H: Fn(&mut arg::Iter, &PropInfo, D>) -> Result<(), MethodErr> + Send + Sync + 'static { self.set_cb = Some(DebugSetProp(Box::new(handler) as Box<_>)); self } } impl, D: DataType> Property where D::Property: arg::Append + Clone { /// Adds a "standard" get handler. pub fn default_get(mut self) -> Self { let g = |i: &mut arg::IterAppend, p: &PropInfo| { i.append(p.prop.get_data()); Ok(()) }; self.get_cb = Some(DebugGetProp(M::make_getprop(g))); self } } impl, D: DataType> Property where D::Property: arg::RefArg { /// Adds a "standard" get handler (for RefArgs). pub fn default_get_refarg(mut self) -> Self { let g = |i: &mut arg::IterAppend, p: &PropInfo| { (p.prop.get_data() as &arg::RefArg).append(i); Ok(()) }; self.get_cb = Some(DebugGetProp(M::make_getprop(g))); self } } impl, D: DataType> Introspect for Property { fn xml_name(&self) -> &'static str { "property" } fn xml_params(&self) -> String { format!(" type=\"{}\" access=\"{}\"", self.sig, self.rw.introspect()) } fn xml_contents(&self) -> String { let s = match self.emits { EmitsChangedSignal::True => return self.anns.introspect(" "), EmitsChangedSignal::False => "false", EmitsChangedSignal::Const => "const", EmitsChangedSignal::Invalidates => "invalidates", }; let mut tempanns = self.anns.clone(); tempanns.insert("org.freedesktop.DBus.Property.EmitsChangedSignal", s); tempanns.introspect(" ") } } pub fn new_property, D: DataType> (n: String, sig: Signature<'static>, data: D::Property) -> Property { Property { name: n, emits: EmitsChangedSignal::True, auto_emit: true, rw: Access::Read, sig: sig, anns: Annotations::new(), set_cb: None, get_cb: None, data: data } } #[test] fn test_prop_handlers() { use tree::Factory; use std::collections::BTreeMap; use arg::{Dict, Variant}; #[derive(Default, Debug)] struct Custom; impl DataType for Custom { type Tree = (); type ObjectPath = (); type Interface = (); type Property = i32; type Method = (); type Signal = (); } let f = Factory::new_fn::(); let tree = f.tree(()).add(f.object_path("/test", ()).introspectable().object_manager() .add(f.interface("com.example.test", ()) .add_p(f.property::("Value1", 5i32).default_get()) .add_p(f.property::("Value2", 9i32).default_get()) ) ); let mut msg = Message::new_method_call("com.example.test", "/test", "org.freedesktop.DBus.Properties", "Get").unwrap() .append2("com.example.test", "Value1"); ::message::message_set_serial(&mut msg, 4); let res = tree.handle(&msg).unwrap(); assert_eq!(res[0].get1(), Some(arg::Variant(5i32))); let mut msg = Message::new_method_call("com.example.test", "/test", "org.freedesktop.DBus.Properties", "Set").unwrap() .append3("com.example.test", "Value1", arg::Variant(3i32)); ::message::message_set_serial(&mut msg, 4); let mut res = tree.handle(&msg).unwrap(); assert!(res[0].as_result().is_err()); let mut msg = Message::new_method_call("com.example.test", "/test", "org.freedesktop.DBus.Properties", "GetAll").unwrap() .append1("com.example.test"); ::message::message_set_serial(&mut msg, 4); let res = tree.handle(&msg).unwrap(); let d: Dict<&str, Variant, _> = res[0].get1().unwrap(); let z2: BTreeMap<_, _> = d.collect(); assert_eq!(z2.get("Value1"), Some(&arg::Variant(5i32))); assert_eq!(z2.get("Value2"), Some(&arg::Variant(9i32))); assert_eq!(z2.get("Mooh"), None); let mut msg = Message::new_method_call("com.example.test", "/test", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects").unwrap(); ::message::message_set_serial(&mut msg, 4); let res = tree.handle(&msg).unwrap(); let pdict: arg::Dict, _>, _>, _> = res[0].get1().unwrap(); let pmap: BTreeMap<_, _> = pdict.collect(); let idict = pmap.get(&Path::from("/test")).unwrap(); let imap: BTreeMap<_, _> = idict.collect(); let propdict = imap.get("com.example.test").unwrap(); let propmap: BTreeMap<_, _> = propdict.collect(); assert_eq!(propmap.get("Value1"), Some(&arg::Variant(5i32))); assert_eq!(propmap.get("Value2"), Some(&arg::Variant(9i32))); assert_eq!(propmap.get("Mooh"), None); } #[test] fn test_set_prop() { use tree::{Factory, Access}; use std::cell::{Cell, RefCell}; use std::collections::BTreeMap; use std::rc::Rc; let changes = Rc::new(Cell::new(0i32)); let (changes1, changes2) = (changes.clone(), changes.clone()); let setme = Rc::new(RefCell::new("I have not been set yet!".to_owned())); let (setme1, setme2) = (setme.clone(), setme.clone()); let f = Factory::new_fn::<()>(); let tree = f.tree(()).add(f.object_path("/example", ()).introspectable() .add(f.interface("com.example.dbus.rs", ()) .add_p(f.property::("changes", ()) .on_get(move |i, _| { i.append(changes1.get()); Ok(()) })) .add_p(f.property::("setme", ()) .access(Access::ReadWrite) .on_get(move |i, _| { i.append(&*setme1.borrow()); Ok(()) }) .on_set(move |i, _| { *setme2.borrow_mut() = i.get().unwrap(); changes2.set(changes2.get() + 1); Ok(()) })) ) ); // Read-only let mut msg = Message::new_method_call("com.example.dbus.rs", "/example", "org.freedesktop.DBus.Properties", "Set").unwrap() .append3("com.example.dbus.rs", "changes", arg::Variant(5i32)); ::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() .append3("com.example.dbus.rs", "setme", arg::Variant(8i32)); ::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() .append3("com.example.dbus.rs", "setme", arg::Variant("Correct")); ::message::message_set_serial(&mut msg, 30); let r = tree.handle(&msg).unwrap(); assert_eq!(changes.get(), 1); assert_eq!(&**setme.borrow(), "Correct"); println!("{:?}", r); assert_eq!(r.len(), 2); assert_eq!(&*r[0].member().unwrap(), "PropertiesChanged"); let (s, d): (Option<&str>, Option, _>>) = r[0].get2(); assert_eq!(s, Some("com.example.dbus.rs")); let z2: BTreeMap<_, _> = d.unwrap().collect(); assert_eq!(z2.get("setme"), Some(&arg::Variant("Correct"))); } #[test] fn test_sync_prop() { use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use tree::{Factory, Access, EmitsChangedSignal}; let f = Factory::new_sync::<()>(); let count = Arc::new(AtomicUsize::new(3)); let (cget, cset) = (count.clone(), count.clone()); let tree1 = Arc::new(f.tree(()).add(f.object_path("/syncprop", ()).introspectable() .add(f.interface("com.example.syncprop", ()) .add_p(f.property::("syncprop", ()) .access(Access::ReadWrite) .emits_changed(EmitsChangedSignal::False) .on_get(move |i,_| { i.append(cget.load(Ordering::SeqCst) as u32); Ok(()) }) .on_set(move |i,_| { cset.store(i.get::().unwrap() as usize, Ordering::SeqCst); Ok(()) }) ) ) )); let tree2 = tree1.clone(); println!("{:#?}", tree2); ::std::thread::spawn(move || { let mut msg = Message::new_method_call("com.example.syncprop", "/syncprop", "org.freedesktop.DBus.Properties", "Set").unwrap() .append3("com.example.syncprop", "syncprop", arg::Variant(5u32)); ::message::message_set_serial(&mut msg, 30); let mut r = tree2.handle(&msg).unwrap(); assert!(r[0].as_result().is_ok()); }); loop { let mut msg = Message::new_method_call("com.example.echoserver", "/syncprop", "org.freedesktop.DBus.Properties", "Get").unwrap() .append("com.example.syncprop").append1("syncprop"); ::message::message_set_serial(&mut msg, 4); let mut r = tree1.handle(&msg).unwrap(); let r = r[0].as_result().unwrap(); let z: arg::Variant = r.get1().unwrap(); if z.0 == 5 { break; } assert_eq!(z.0, 3); } assert_eq!(count.load(Ordering::SeqCst), 5); } dbus-0.6.5/src/tree/methodtype.rs010064400017500001750000000261051351376776700152100ustar0000000000000000// Methods and method types. Glue to make stuff generic over MFn, MFnMut and MSync use std::fmt; use {ErrorName, Message, stdintf}; use arg::{Iter, IterAppend, TypeMismatchError}; use std::marker::PhantomData; use super::{Method, Interface, Property, ObjectPath, Tree}; use std::cell::RefCell; use std::ffi::CString; use super::super::Error as dbusError; #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] /// A D-Bus Method Error, containing an error name and a description. pub struct MethodErr(ErrorName<'static>, 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 Method was unknown. pub fn no_method(a: &T) -> MethodErr { ("org.freedesktop.DBus.Error.UnknownMethod", format!("Unknown method {}", 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() } /// Error name accessor pub fn errorname(&self) -> &ErrorName<'static> { &self.0 } /// Description accessor pub fn description(&self) -> &str { &self.1 } /// Creates an error reply from a method call message. /// /// Note: You normally don't need to use this function, /// as it is called internally from Tree::handle. pub fn to_message(&self, msg: &Message) -> Message { msg.error(&self.0, &CString::new(&*self.1).unwrap()) } } impl From for MethodErr { fn from(t: TypeMismatchError) -> MethodErr { ("org.freedesktop.DBus.Error.Failed", format!("{}", t)).into() } } impl>, M: Into> From<(T, M)> for MethodErr { fn from((t, m): (T, M)) -> MethodErr { MethodErr(t.into(), m.into()) } } impl From for MethodErr { fn from(t: dbusError) -> MethodErr { let n = t.name().unwrap_or("org.freedesktop.DBus.Error.Failed"); let m = t.message().unwrap_or("Unknown error"); MethodErr(String::from(n).into(), m.into()) } } /// Result containing the Messages returned from the Method, or a MethodErr. pub type MethodResult = Result, MethodErr>; /// Associated data for different objects in a tree. /// /// These currently require a debug bound, due to https://github.com/rust-lang/rust/issues/31518 pub trait DataType: Sized + Default { /// Type of associated data on the Tree. type Tree: fmt::Debug; /// Type of associated data on every ObjectPath. type ObjectPath: fmt::Debug; /// Type of associated data on every Property. type Property: fmt::Debug; /// Type of associated data on every Interface. type Interface: fmt::Debug; /// Type of associated data on every Method. type Method: fmt::Debug; /// Type of associated data on every Signal. type Signal: fmt::Debug; } /// No associated data for the tree. impl DataType for () { type Tree = (); type ObjectPath = (); type Interface = (); type Property = (); type Method = (); type Signal = (); } /// A helper trait used internally to make the tree generic over MTFn, MTFnMut and MTSync. /// /// You should not need to call these methods directly, it's primarily for internal use. pub trait MethodType: Sized + Default { /// For internal use. type Method: ?Sized; /// For internal use. type GetProp: ?Sized; /// For internal use. type SetProp: ?Sized; /// For internal use. fn call_getprop(&Self::GetProp, &mut IterAppend, &PropInfo) -> Result<(), MethodErr>; /// For internal use. fn call_setprop(&Self::SetProp, &mut Iter, &PropInfo) -> Result<(), MethodErr>; /// For internal use. fn call_method(&Self::Method, &MethodInfo) -> MethodResult; /// For internal use. fn make_getprop(h: H) -> Box where H: Fn(&mut IterAppend, &PropInfo) -> Result<(), MethodErr> + Send + Sync + 'static; /// For internal use. fn make_method(h: H) -> Box where H: Fn(&MethodInfo) -> MethodResult + Send + Sync + 'static; } /// An abstract type to represent Fn functions. #[derive(Default, Debug, Copy, Clone)] pub struct MTFn(PhantomData<*const D>); impl MethodType for MTFn { type GetProp = Fn(&mut IterAppend, &PropInfo) -> Result<(), MethodErr>; type SetProp = Fn(&mut Iter, &PropInfo) -> Result<(), MethodErr>; type Method = Fn(&MethodInfo) -> MethodResult; fn call_getprop(p: &Self::GetProp, i: &mut IterAppend, pinfo: &PropInfo) -> Result<(), MethodErr> { p(i, pinfo) } fn call_setprop(p: &Self::SetProp, i: &mut Iter, pinfo: &PropInfo) -> Result<(), MethodErr> { p(i, pinfo) } fn call_method(p: &Self::Method, minfo: &MethodInfo) -> MethodResult { p(minfo) } fn make_getprop(h: H) -> Box where H: Fn(&mut IterAppend, &PropInfo) -> Result<(), MethodErr> + Send + Sync + 'static { Box::new(h) } fn make_method(h: H) -> Box where H: Fn(&MethodInfo) -> MethodResult + Send + Sync + 'static { Box::new(h) } } /// An abstract type to represent FnMut functions. #[derive(Default, Debug, Copy, Clone)] pub struct MTFnMut(PhantomData<*const D>); impl MethodType for MTFnMut { type GetProp = RefCell) -> Result<(), MethodErr>>; type SetProp = RefCell) -> Result<(), MethodErr>>; type Method = RefCell) -> MethodResult>; fn call_getprop(p: &Self::GetProp, i: &mut IterAppend, pinfo: &PropInfo) -> Result<(), MethodErr> { (&mut *p.borrow_mut())(i, pinfo) } fn call_setprop(p: &Self::SetProp, i: &mut Iter, pinfo: &PropInfo) -> Result<(), MethodErr> { (&mut *p.borrow_mut())(i, pinfo) } fn call_method(p: &Self::Method, minfo: &MethodInfo) -> MethodResult { (&mut *p.borrow_mut())(minfo) } fn make_getprop(h: H) -> Box where H: Fn(&mut IterAppend, &PropInfo) -> Result<(), MethodErr> + Send + Sync + 'static { Box::new(RefCell::new(h)) } fn make_method(h: H) -> Box where H: Fn(&MethodInfo) -> MethodResult + Send + Sync + 'static { Box::new(RefCell::new(h)) } } /// An abstract type to represent Fn + Send + Sync functions (that can be called from several threads in parallel). #[derive(Default, Debug, Copy, Clone)] pub struct MTSync(PhantomData<*const D>); impl MethodType for MTSync { type GetProp = Fn(&mut IterAppend, &PropInfo) -> Result<(), MethodErr> + Send + Sync + 'static; type SetProp = Fn(&mut Iter, &PropInfo) -> Result<(), MethodErr> + Send + Sync + 'static; type Method = Fn(&MethodInfo) -> MethodResult + Send + Sync + 'static; fn call_getprop(p: &Self::GetProp, i: &mut IterAppend, pinfo: &PropInfo) -> Result<(), MethodErr> { p(i, pinfo) } fn call_setprop(p: &Self::SetProp, i: &mut Iter, pinfo: &PropInfo) -> Result<(), MethodErr> { p(i, pinfo) } fn call_method(p: &Self::Method, minfo: &MethodInfo) -> MethodResult { p(minfo) } fn make_getprop(h: H) -> Box where H: Fn(&mut IterAppend, &PropInfo) -> Result<(), MethodErr> + Send + Sync + 'static { Box::new(h) } fn make_method(h: H) -> Box where H: Fn(&MethodInfo) -> MethodResult + Send + Sync + 'static { Box::new(h) } } #[derive(Debug, Copy, Clone)] /// Contains information about the incoming method call. pub struct MethodInfo<'a, M: 'a + MethodType, D: 'a + DataType> { /// Message pub msg: &'a Message, /// The method to be called pub method: &'a Method, /// Interface pub iface: &'a Interface, /// Object path pub path: &'a ObjectPath, /// Tree pub tree: &'a Tree, } impl<'a, M: 'a + MethodType, D: 'a + DataType> MethodInfo<'a, M, D> { /// MethodInfo to PropInfo conversion pub fn to_prop_info(&self, iface: &'a Interface, prop: &'a Property) -> PropInfo<'a, M, D> { PropInfo { msg: self.msg, method: self.method, iface: iface, prop: prop, path: self.path, tree: self.tree } } } impl<'a, M: 'a + MethodType, D: 'a + DataType> stdintf::OrgFreedesktopDBusIntrospectable for MethodInfo<'a, M, D> { type Err = MethodErr; fn introspect(&self) -> Result { Ok(self.path.introspect(self.tree)) } } // Mostly autogenerated by dbus-codegen pub fn org_freedesktop_dbus_introspectable_server(factory: &super::Factory, data: D::Interface) -> super::Interface where D: super::DataType, D::Method: Default, M: MethodType, { let i = factory.interface("org.freedesktop.DBus.Introspectable", data); let h = move |minfo: &super::MethodInfo| { let d: &stdintf::OrgFreedesktopDBusIntrospectable = minfo; let arg0 = try!(d.introspect()); let rm = minfo.msg.method_return(); let rm = rm.append1(arg0); Ok(vec!(rm)) }; let m = factory.method_sync("Introspect", Default::default(), h); let m = m.out_arg(("xml_data", "s")); let i = i.add_m(m); i } #[derive(Debug, Copy, Clone)] /// Contains information about the incoming property get/set request. pub struct PropInfo<'a, M: 'a + MethodType, D: 'a + DataType> { /// Message pub msg: &'a Message, /// Get, Set or GetAll pub method: &'a Method, /// The property to be set/get pub prop: &'a Property, /// The interface the property belongs to pub iface: &'a Interface, /// Object path pub path: &'a ObjectPath, /// Tree pub tree: &'a Tree, } impl<'a, M: 'a + MethodType, D: 'a + DataType> PropInfo<'a, M, D> { /// PropInfo to MethodInfo conversion. pub fn to_method_info(&self) -> MethodInfo<'a, M, D> { MethodInfo { msg: self.msg, method: self.method, iface: self.iface, path: self.path, tree: self.tree } } } dbus-0.6.5/src/tree/mod.rs010064400017500001750000000024021351376776700135770ustar0000000000000000//! Contains functionality for dispatching methods on a D-Bus "server". //! //! # Example //! ```rust,no_run //! 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.msg.method_return().append("Thanks!"))) } //! ).out_arg("s")) //! )); //! //! let c = Connection::get_private(BusType::Session).unwrap(); //! t.set_registered(&c, true).unwrap(); //! c.add_handler(t); //! /* Run forever */ //! loop { c.incoming(1000).next(); } //! ``` //! //! See `examples/server.rs` and `examples/adv_server.rs` for more thorough examples. mod utils; mod methodtype; mod leaves; mod objectpath; mod factory; pub use self::utils::{Argument, Iter}; pub use self::methodtype::{MethodErr, MethodInfo, PropInfo, MethodResult, MethodType, DataType, MTFn, MTFnMut, MTSync}; pub use self::leaves::{Method, Signal, Property, Access, EmitsChangedSignal}; pub use self::objectpath::{Interface, ObjectPath, Tree, TreeServer}; pub use self::factory::Factory; dbus-0.6.5/src/tree/objectpath.rs010064400017500001750000000536261351377016100151370ustar0000000000000000use super::utils::{ArcMap, Iter, IterE, Annotations, Introspect}; use super::{Factory, MethodType, MethodInfo, MethodResult, MethodErr, DataType, Property, Method, Signal, methodtype}; use std::sync::{Arc, Mutex}; use {Member, Message, Path, Signature, MessageType, Connection, ConnectionItem, Error, arg, MsgHandler, MsgHandlerType, MsgHandlerResult}; use Interface as IfaceName; use std::fmt; use std::ffi::CStr; use super::leaves::prop_append_dict; fn introspect_map (h: &ArcMap, indent: &str) -> String { h.iter().fold("".into(), |a, (k, v)| { let (name, params, contents) = (v.xml_name(), v.xml_params(), v.xml_contents()); format!("{}{}<{} name=\"{}\"{}{}>\n", a, indent, name, &*k, params, if contents.len() > 0 { format!(">\n{}{}, D: DataType> { name: Arc>, methods: ArcMap, Method>, signals: ArcMap, Signal>, properties: ArcMap>, anns: Annotations, data: D::Interface, } impl, D: DataType> Interface { /// Builder function that adds a method to the interface. pub fn add_m>>>(mut self, m: I) -> Self { let m = m.into(); self.methods.insert(m.get_name().clone(), m); self } /// Builder function that adds a signal to the interface. pub fn add_s>>>(mut self, s: I) -> Self { let m = s.into(); self.signals.insert(m.get_name().clone(), m); self } /// Builder function that adds a property to the interface. pub fn add_p>>>(mut self, p: I) -> Self { let m = p.into(); self.properties.insert(m.get_name().to_owned(), m); self } /// Builder function that adds an annotation to this interface. pub fn annotate, V: Into>(mut self, name: N, value: V) -> Self { self.anns.insert(name, value); self } /// Builder function that adds an annotation that this entity is deprecated. pub fn deprecated(self) -> Self { self.annotate("org.freedesktop.DBus.Deprecated", "true") } /// Get interface name pub fn get_name(&self) -> &IfaceName<'static> { &self.name } /// Get associated data pub fn get_data(&self) -> &D::Interface { &self.data } /// Iterates over methods implemented by this interface. pub fn iter_m<'a>(&'a self) -> Iter<'a, Method> { IterE::Member(self.methods.values()).into() } /// Iterates over signals implemented by this interface. pub fn iter_s<'a>(&'a self) -> Iter<'a, Signal> { IterE::Member(self.signals.values()).into() } /// Iterates over properties implemented by this interface. pub fn iter_p<'a>(&'a self) -> Iter<'a, Property> { IterE::String(self.properties.values()).into() } } impl, D: DataType> Introspect for Interface { fn xml_name(&self) -> &'static str { "interface" } fn xml_params(&self) -> String { String::new() } fn xml_contents(&self) -> String { format!("{}{}{}{}", introspect_map(&self.methods, " "), introspect_map(&self.properties, " "), introspect_map(&self.signals, " "), self.anns.introspect(" ")) } } pub fn new_interface, D: DataType>(t: IfaceName<'static>, d: D::Interface) -> Interface { Interface { name: Arc::new(t), methods: ArcMap::new(), signals: ArcMap::new(), properties: ArcMap::new(), anns: Annotations::new(), data: d } } #[derive(Debug)] /// Cache of built-in interfaces, in order to save memory when many object paths implement the same interface(s). pub struct IfaceCache, D: DataType>(Mutex, Interface>>); impl, D: DataType> IfaceCache where D::Interface: Default { pub fn get> + Clone, F>(&self, s: S, f: F) -> Arc> where F: FnOnce(Interface) -> Interface { let s2 = s.clone().into(); let mut m = self.0.lock().unwrap(); m.entry(s2).or_insert_with(|| { let i = new_interface(s.into(), Default::default()); Arc::new(f(i)) }).clone() } } impl, D: DataType> IfaceCache { pub fn get_factory> + Clone, F>(&self, s: S, f: F) -> Arc> where F: FnOnce() -> Interface { let s2 = s.clone().into(); let mut m = self.0.lock().unwrap(); m.entry(s2).or_insert_with(|| { Arc::new(f()) }).clone() } pub fn new() -> Arc { Arc::new(IfaceCache(Mutex::new(ArcMap::new()))) } } #[derive(Debug)] /// A D-Bus Object Path. pub struct ObjectPath, D: DataType> { name: Arc>, default_iface: Option>, ifaces: ArcMap>, Interface>, ifacecache: Arc>, data: D::ObjectPath, } impl, D: DataType> ObjectPath { /// Get property name pub fn get_name(&self) -> &Path<'static> { &self.name } /// Get associated data pub fn get_data(&self) -> &D::ObjectPath { &self.data } /// Iterates over interfaces implemented by this object path. pub fn iter<'a>(&'a self) -> Iter<'a, Interface> { IterE::Iface(self.ifaces.values()).into() } pub(super) fn introspect(&self, tree: &Tree) -> String { let ifacestr = introspect_map(&self.ifaces, " "); 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_iface<'a>(&'a self, iface_name: &'a CStr) -> Result<&Arc>, MethodErr> { let j = try!(IfaceName::from_slice(iface_name.to_bytes_with_nul()).map_err(|e| MethodErr::invalid_arg(&e))); self.ifaces.get(&j).ok_or_else(|| MethodErr::no_interface(&j)) } fn prop_get(&self, m: &MethodInfo) -> MethodResult { let (iname, prop_name): (&CStr, &str) = try!(m.msg.read2()); let iface = try!(self.get_iface(iname)); let prop: &Property = try!(iface.properties.get(&String::from(prop_name)) .ok_or_else(|| MethodErr::no_property(&prop_name))); try!(prop.can_get()); let mut mret = m.msg.method_return(); { let mut iter = arg::IterAppend::new(&mut mret); let pinfo = m.to_prop_info(iface, prop); try!(prop.get_as_variant(&mut iter, &pinfo)); } Ok(vec!(mret)) } fn prop_get_all(&self, m: &MethodInfo) -> MethodResult { let iface = try!(self.get_iface(try!(m.msg.read1()))); let mut mret = m.msg.method_return(); try!(prop_append_dict(&mut arg::IterAppend::new(&mut mret), iface.properties.values().map(|v| &**v), m)); Ok(vec!(mret)) } fn prop_set(&self, m: &MethodInfo) -> MethodResult { let (iname, prop_name): (&CStr, &str) = try!(m.msg.read2()); let iface = try!(self.get_iface(iname)); let prop: &Property = try!(iface.properties.get(&String::from(prop_name)) .ok_or_else(|| MethodErr::no_property(&prop_name))); let mut iter = arg::Iter::new(m.msg); iter.next(); iter.next(); let mut iter2 = iter; try!(prop.can_set(Some(iter))); let pinfo = m.to_prop_info(iface, prop); let mut r: Vec = try!(prop.set_as_variant(&mut iter2, &pinfo)).into_iter().collect(); r.push(m.msg.method_return()); Ok(r) } fn get_managed_objects(&self, m: &MethodInfo) -> MethodResult { use arg::{Dict, Variant}; let mut paths = m.tree.children(&self, false); paths.push(&self); let mut result = Ok(()); let mut r = m.msg.method_return(); { let mut i = arg::IterAppend::new(&mut r); i.append_dict(&Signature::make::(), &Signature::make::,()>,()>>(), |ii| { for p in paths { ii.append_dict_entry(|pi| { pi.append(&*p.name); pi.append_dict(&Signature::make::<&str>(), &Signature::make::,()>>(), |pii| { for ifaces in p.ifaces.values() { let m2 = MethodInfo { msg: m.msg, path: p, iface: ifaces, tree: m.tree, method: m.method }; pii.append_dict_entry(|ppii| { ppii.append(&**ifaces.name); result = prop_append_dict(ppii, ifaces.properties.values().map(|v| &**v), &m2); }); if result.is_err() { break; } } }); }); if result.is_err() { break; } } }); } try!(result); Ok(vec!(r)) } fn handle(&self, m: &Message, t: &Tree) -> MethodResult { let iname = m.interface().or_else(|| { self.default_iface.clone() }); let i = try!(iname.and_then(|i| self.ifaces.get(&i)).ok_or_else(|| MethodErr::no_interface(&""))); let me = try!(m.member().and_then(|me| i.methods.get(&me)).ok_or_else(|| MethodErr::no_method(&""))); let minfo = MethodInfo { msg: m, tree: t, path: self, iface: i, method: me }; me.call(&minfo) } } impl, D: DataType> ObjectPath where ::Interface: Default, ::Method: Default { /// Adds introspection support for this object path. pub fn introspectable(self) -> Self { let z = self.ifacecache.get_factory("org.freedesktop.DBus.Introspectable", || { let f = Factory::from(self.ifacecache.clone()); methodtype::org_freedesktop_dbus_introspectable_server(&f, Default::default()) }); self.add(z) } /// Builder function that adds a interface to the object path. pub fn add>>>(mut self, s: I) -> Self { let m = s.into(); if !m.properties.is_empty() { self.add_property_handler(); } self.ifaces.insert(m.name.clone(), m); self } /// Builder function that sets what interface should be dispatched on an incoming /// method call without interface. pub fn default_interface(mut self, i: IfaceName<'static>) -> Self { self.default_iface = Some(i); self } /// 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(mut self) -> Self { use arg::{Variant, Dict}; let ifname = IfaceName::from("org.freedesktop.DBus.ObjectManager"); if self.ifaces.contains_key(&ifname) { return self }; let z = self.ifacecache.get(ifname, |i| { i.add_m(super::leaves::new_method("GetManagedObjects".into(), Default::default(), M::make_method(|m| m.path.get_managed_objects(m))) .outarg::,()>,()>,()>,_>("objpath_interfaces_and_properties")) }); self.ifaces.insert(z.name.clone(), z); self } fn add_property_handler(&mut self) { use arg::{Variant, Dict}; let ifname = IfaceName::from("org.freedesktop.DBus.Properties"); if self.ifaces.contains_key(&ifname) { return }; let z = self.ifacecache.get(ifname, |i| { i.add_m(super::leaves::new_method("Get".into(), Default::default(), M::make_method(|m| m.path.prop_get(m))) .inarg::<&str,_>("interface_name") .inarg::<&str,_>("property_name") .outarg::,_>("value")) .add_m(super::leaves::new_method("GetAll".into(), Default::default(), M::make_method(|m| m.path.prop_get_all(m))) .inarg::<&str,_>("interface_name") .outarg::, ()>,_>("props")) .add_m(super::leaves::new_method("Set".into(), Default::default(), M::make_method(|m| m.path.prop_set(m))) .inarg::<&str,_>("interface_name") .inarg::<&str,_>("property_name") .inarg::,_>("value")) }); self.ifaces.insert(z.name.clone(), z); } } pub fn new_objectpath, D: DataType>(n: Path<'static>, d: D::ObjectPath, cache: Arc>) -> ObjectPath { ObjectPath { name: Arc::new(n), data: d, ifaces: ArcMap::new(), ifacecache: cache, default_iface: None } } /// A collection of object paths. #[derive(Debug, Default)] pub struct Tree, D: DataType> { paths: ArcMap>, ObjectPath>, data: D::Tree, } impl, D: DataType> Tree { /// Builder function that adds an object path to this tree. /// /// Note: This does not register a path with the connection, so if the tree is currently registered, /// you might want to call Connection::register_object_path to add the path manually. pub fn add>>>(mut self, s: I) -> Self { self.insert(s); self } /// Get a reference to an object path from the tree. pub fn get(&self, p: &Path<'static>) -> Option<&Arc>> { self.paths.get(p) } /// Iterates over object paths in this tree. pub fn iter<'a>(&'a self) -> Iter<'a, ObjectPath> { IterE::Path(self.paths.values()).into() } /// Non-builder function that adds an object path to this tree. /// /// Note: This does not register a path with the connection, so if the tree is currently registered, /// you might want to call Connection::register_object_path to add the path manually. pub fn insert>>>(&mut self, s: I) { let m = s.into(); self.paths.insert(m.name.clone(), m); } /// Remove a object path from the Tree. Returns the object path removed, or None if not found. /// /// Note: This does not unregister a path with the connection, so if the tree is currently registered, /// you might want to call Connection::unregister_object_path to remove the path manually. pub fn remove(&mut self, p: &Path<'static>) -> Option>> { // There is no real reason p needs to have a static lifetime; but // the borrow checker doesn't agree. :-( self.paths.remove(p) } /// 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(()) } /// 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, D> { TreeServer { iter: i, tree: &self, conn: c } } /// Handles a message. /// /// Will return None in case the object path was not /// found in this tree, 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!(e.to_message(m))))) } } 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() } /// Get associated data pub fn get_data(&self) -> &D::Tree { &self.data } } pub fn new_tree, D: DataType>(d: D::Tree) -> Tree { Tree { paths: ArcMap::new(), data: d } } impl, D: DataType> MsgHandler for Tree { fn handle_msg(&mut self, msg: &Message) -> Option { self.handle(msg).map(|v| MsgHandlerResult { handled: true, done: false, reply: v }) } fn handler_type(&self) -> MsgHandlerType { MsgHandlerType::MsgType(MessageType::MethodCall) } } impl, D: DataType> MsgHandler for Arc> { fn handle_msg(&mut self, msg: &Message) -> Option { self.handle(msg).map(|v| MsgHandlerResult { handled: true, done: false, reply: v }) } fn handler_type(&self) -> MsgHandlerType { MsgHandlerType::MsgType(MessageType::MethodCall) } } /// 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: MethodType + 'a, D: DataType + 'a> { iter: I, conn: &'a Connection, tree: &'a Tree, } impl<'a, I: Iterator, M: 'a + MethodType, D: DataType + 'a> Iterator for TreeServer<'a, I, M, D> { 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; } } } #[test] fn test_iter() { let f = super::Factory::new_fn::<()>(); let t = f.tree(()) .add(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", ())) .add_s(f.signal("Echoed", ()).arg(("data", "s")).deprecated() ) )).add(f.object_path("/echo/subpath", ())); let paths: Vec<_> = t.iter().collect(); assert_eq!(paths.len(), 2); } #[test] fn test_set_default_interface() { let iface_name: IfaceName<'_> = "com.example.echo".into(); let f = super::Factory::new_fn::<()>(); let t = f.object_path("/echo", ()).default_interface(iface_name.clone()); assert_eq!(t.default_iface, Some(iface_name)); } #[test] fn test_introspection() { let f = super::Factory::new_fn::<()>(); 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", ())) .add_s(f.signal("Echoed", ()).arg(("data", "s")).deprecated()) ); 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.6.5/src/tree/utils.rs010064400017500001750000000066111351376776700141660ustar0000000000000000// Small structs that don't have their own unit. use {Signature, Member, Path, Interface as IfaceName}; use std::collections::{BTreeMap, btree_map}; use std::sync::Arc; pub type ArcMap = BTreeMap>; #[derive(Clone, Debug)] pub enum IterE<'a, V: 'a> { Path(btree_map::Values<'a, Arc>, Arc>), Iface(btree_map::Values<'a, Arc>, Arc>), Member(btree_map::Values<'a, Member<'static>, Arc>), String(btree_map::Values<'a, String, Arc>), } #[derive(Clone, Debug)] /// Iterator struct, returned from iterator methods on Tree, Objectpath and Interface. pub struct Iter<'a, V: 'a>(IterE<'a, V>); impl<'a, V: 'a> From> for Iter<'a, V> { fn from(x: IterE<'a, V>) -> Iter<'a, V> { Iter(x) }} impl<'a, V: 'a> Iterator for Iter<'a, V> { type Item = &'a Arc; fn next(&mut self) -> Option { match self.0 { IterE::Path(ref mut x) => x.next(), IterE::Iface(ref mut x) => x.next(), IterE::Member(ref mut x) => x.next(), IterE::String(ref mut x) => x.next(), } } } #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] /// A D-Bus Argument. pub struct Argument(Option, Signature<'static>); impl Argument { /// Create a new Argument. pub fn new(name: Option, sig: Signature<'static>) -> Argument { Argument(name, sig) } /// Descriptive name (if any). pub fn name(&self) -> Option<&str> { self.0.as_ref().map(|s| &**s) } /// Type signature of argument. pub fn signature(&self) -> &Signature<'static> { &self.1 } 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) } } pub fn introspect_args(args: &[Argument], indent: &str, dir: &str) -> String { args.iter().fold("".to_string(), |aa, az| format!("{}{}", aa, az.introspect(indent, dir))) } // Small helper struct to reduce memory somewhat for objects without annotations #[derive(Clone, Debug, Default)] pub struct Annotations(Option>); impl Annotations { pub fn new() -> Annotations { Annotations(None) } pub fn insert, V: Into>(&mut self, n: N, v: V) { if self.0.is_none() { self.0 = Some(BTreeMap::new()) } self.0.as_mut().unwrap().insert(n.into(), v.into()); } pub fn introspect(&self, indent: &str) -> String { self.0.as_ref().map(|s| s.iter().fold("".into(), |aa, (ak, av)| { format!("{}{}\n", aa, indent, ak, av) })).unwrap_or(String::new()) } } // Doesn't work, conflicting impls // impl> From for Argument impl From> for Argument { fn from(t: Signature<'static>) -> Argument { Argument(None, t) } } impl<'a> From<&'a str> for Argument { fn from(t: &'a str) -> Argument { Argument(None, String::from(t).into()) } } impl, S: Into>> From<(N, S)> for Argument { fn from((n, s): (N, S)) -> Argument { Argument(Some(n.into()), s.into()) } } pub trait Introspect { // At some point we might want to switch to fmt::Write / fmt::Formatter for performance... fn xml_name(&self) -> &'static str; fn xml_params(&self) -> String; fn xml_contents(&self) -> String; } dbus-0.6.5/src/watch.rs010064400017500001750000000227611351377234100131610ustar0000000000000000use ffi; use libc; use super::Connection; use std::mem; use std::sync::{Mutex, RwLock}; use std::os::unix::io::{RawFd, AsRawFd}; use std::os::raw::{c_void, c_uint}; /// A file descriptor to watch for incoming events (for async I/O). /// /// # Example /// ``` /// extern crate libc; /// extern crate dbus; /// fn main() { /// use dbus::{Connection, BusType, WatchEvent}; /// let c = Connection::get_private(BusType::Session).unwrap(); /// /// // Get a list of fds to poll for /// let mut fds: Vec<_> = c.watch_fds().iter().map(|w| w.to_pollfd()).collect(); /// /// // Poll them with a 1 s timeout /// let r = unsafe { libc::poll(fds.as_mut_ptr(), fds.len() as libc::c_ulong, 1000) }; /// assert!(r >= 0); /// /// // And handle incoming events /// for pfd in fds.iter().filter(|pfd| pfd.revents != 0) { /// for item in c.watch_handle(pfd.fd, WatchEvent::from_revents(pfd.revents)) { /// // Handle item /// println!("Received ConnectionItem: {:?}", item); /// } /// } /// } /// ``` #[repr(C)] #[derive(Debug, PartialEq, Copy, Clone)] /// The enum is here for backwards compatibility mostly. /// /// It should really be bitflags instead. pub enum WatchEvent { /// The fd is readable Readable = ffi::DBUS_WATCH_READABLE as isize, /// The fd is writable Writable = ffi::DBUS_WATCH_WRITABLE as isize, /// An error occured on the fd Error = ffi::DBUS_WATCH_ERROR as isize, /// The fd received a hangup. Hangup = ffi::DBUS_WATCH_HANGUP as isize, } impl WatchEvent { /// After running poll, this transforms the revents into a parameter you can send into `Connection::watch_handle` pub fn from_revents(revents: libc::c_short) -> c_uint { 0 + if (revents & libc::POLLIN) != 0 { WatchEvent::Readable as c_uint } else { 0 } + if (revents & libc::POLLOUT) != 0 { WatchEvent::Writable as c_uint } else { 0 } + if (revents & libc::POLLERR) != 0 { WatchEvent::Error as c_uint } else { 0 } + if (revents & libc::POLLHUP) != 0 { WatchEvent::Hangup as c_uint } else { 0 } } } #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] /// A file descriptor, and an indication whether it should be read from, written to, or both. 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 } /// Returns the current watch as a libc::pollfd, to use with libc::poll pub fn to_pollfd(&self) -> libc::pollfd { libc::pollfd { fd: self.fd, revents: 0, events: libc::POLLERR + libc::POLLHUP + if self.readable() { libc::POLLIN } else { 0 } + if self.writable() { libc::POLLOUT } else { 0 }, } } /* pub (crate) unsafe fn from_raw(watch: *mut ffi::DBusWatch) -> Self { let mut w = Watch { fd: ffi::dbus_watch_get_unix_fd(watch), read: false, write: false}; let enabled = ffi::dbus_watch_get_enabled(watch) != 0; if enabled { let flags = ffi::dbus_watch_get_flags(watch); w.read = (flags & WatchEvent::Readable as c_uint) != 0; w.write = (flags & WatchEvent::Writable as c_uint) != 0; } w } */ } 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: RwLock>, enabled_fds: Mutex>, on_update: Mutex>, } impl WatchList { pub fn new(c: &Connection, on_update: Box) -> Box { let w = Box::new(WatchList { on_update: Mutex::new(on_update), watches: RwLock::new(vec!()), enabled_fds: Mutex::new(vec!()) }); if unsafe { ffi::dbus_connection_set_watch_functions(super::connection::conn_handle(c), 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 set_on_update(&self, on_update: Box) { *self.on_update.lock().unwrap() = on_update; } pub fn watch_handle(&self, fd: RawFd, flags: c_uint) { // println!("watch_handle {} flags {}", fd, flags); for &q in self.watches.read().unwrap().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.lock().unwrap().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.read().unwrap().contains(&watch) && unsafe { ffi::dbus_watch_get_enabled(watch) != 0 }; let flags = unsafe { ffi::dbus_watch_get_flags(watch) }; if enabled { w.read = (flags & WatchEvent::Readable as c_uint) != 0; w.write = (flags & WatchEvent::Writable as 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.read().unwrap().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.lock().unwrap(); 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) }; } let func = self.on_update.lock().unwrap(); (*func)(w); } } extern "C" fn add_watch_cb(watch: *mut ffi::DBusWatch, data: *mut c_void) -> u32 { let wlist: &WatchList = unsafe { mem::transmute(data) }; // println!("Add watch {:?}", watch); wlist.watches.write().unwrap().push(watch); wlist.update(watch); 1 } extern "C" fn remove_watch_cb(watch: *mut ffi::DBusWatch, data: *mut c_void) { let wlist: &WatchList = unsafe { mem::transmute(data) }; // println!("Removed watch {:?}", watch); wlist.watches.write().unwrap().retain(|w| *w != watch); wlist.update(watch); } extern "C" fn toggled_watch_cb(watch: *mut ffi::DBusWatch, data: *mut 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}; #[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<_> = c.watch_fds().iter().map(|w| w.to_pollfd()).collect(); 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 { libc::poll(fds.as_mut_ptr(), fds.len() as libc::nfds_t, 1000) } > 0); for f in fds.iter().filter(|pfd| pfd.revents != 0) { let m = WatchEvent::from_revents(f.revents); println!("Async: fd {}, revents {} -> {}", f.fd, f.revents, m); assert!(f.revents & libc::POLLIN != 0 || f.revents & libc::POLLOUT != 0); for e in c.watch_handle(f.fd, m) { println!("Async: got {:?}", e); match e { 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!() }; } } } } dbus-0.6.5/.cargo_vcs_info.json0000644000000001120000000000000120320ustar00{ "git": { "sha1": "d8bd9c5640725aa82582ab87e672ce3bb7fc6a3e" } }