dbus-0.9.0/.cargo_vcs_info.json0000644000000001121373112151200120230ustar { "git": { "sha1": "28cd465a470307f2a9a05da4398bb0d15528259f" } } dbus-0.9.0/Cargo.lock0000644000000170301373112151200100050ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "dbus" version = "0.9.0" dependencies = [ "futures-channel", "futures-util", "libc", "libdbus-sys", "tempfile", ] [[package]] name = "futures-channel" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" dependencies = [ "futures-core", ] [[package]] name = "futures-core" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" [[package]] name = "futures-macro" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" dependencies = [ "proc-macro-hack", "proc-macro2", "quote", "syn", ] [[package]] name = "futures-task" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" dependencies = [ "once_cell", ] [[package]] name = "futures-util" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" dependencies = [ "futures-core", "futures-macro", "futures-task", "pin-project", "pin-utils", "proc-macro-hack", "proc-macro-nested", "slab", ] [[package]] name = "getrandom" version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "libc" version = "0.2.77" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" [[package]] name = "libdbus-sys" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc12a3bc971424edbbf7edaf6e5740483444db63aa8e23d3751ff12a30f306f0" dependencies = [ "pkg-config", ] [[package]] name = "once_cell" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" [[package]] name = "pin-project" version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca4433fff2ae79342e497d9f8ee990d174071408f28f726d6d83af93e58e48aa" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c0e815c3ee9a031fdf5af21c10aa17c573c9c6a566328d99e3936c34e36461f" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33" [[package]] name = "ppv-lite86" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" [[package]] name = "proc-macro-hack" version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598" [[package]] name = "proc-macro-nested" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" [[package]] name = "proc-macro2" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c" dependencies = [ "unicode-xid", ] [[package]] name = "quote" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ "getrandom", "libc", "rand_chacha", "rand_core", "rand_hc", ] [[package]] name = "rand_chacha" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ "getrandom", ] [[package]] name = "rand_hc" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ "rand_core", ] [[package]] name = "redox_syscall" version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "remove_dir_all" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" dependencies = [ "winapi", ] [[package]] name = "slab" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" [[package]] name = "syn" version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6690e3e9f692504b941dc6c3b188fd28df054f7fb8469ab40680df52fdcc842b" dependencies = [ "proc-macro2", "quote", "unicode-xid", ] [[package]] name = "tempfile" version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" dependencies = [ "cfg-if", "libc", "rand", "redox_syscall", "remove_dir_all", "winapi", ] [[package]] name = "unicode-xid" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" dbus-0.9.0/Cargo.toml0000644000000030621373112151200100300ustar # 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] edition = "2018" name = "dbus" version = "0.9.0" 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" [package.metadata.docs.rs] features = ["futures"] [dependencies.futures-channel] version = "0.3" optional = true [dependencies.futures-util] version = "0.3" optional = true [dependencies.libc] version = "0.2.66" [dependencies.libdbus-sys] version = "0.2" [dev-dependencies.tempfile] version = "3" [features] futures = ["futures-util", "futures-channel"] no-string-validation = [] [badges.is-it-maintained-issue-resolution] repository = "diwic/dbus-rs" [badges.is-it-maintained-open-issues] repository = "diwic/dbus-rs" [badges.maintenance] status = "actively-developed" [badges.travis-ci] repository = "diwic/dbus-rs" dbus-0.9.0/Cargo.toml.orig010066400017500001750000000020241373112112500135210ustar 00000000000000[package] name = "dbus" version = "0.9.0" 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" edition = "2018" [dependencies] libc = "0.2.66" libdbus-sys = { path = "../libdbus-sys", version = "0.2" } futures-util = { version = "0.3", optional = true } futures-channel = { version = "0.3", optional = true } [dev-dependencies] tempfile = "3" [features] no-string-validation = [] futures = ["futures-util", "futures-channel"] [badges] is-it-maintained-open-issues = { repository = "diwic/dbus-rs" } is-it-maintained-issue-resolution = { repository = "diwic/dbus-rs" } travis-ci = { repository = "diwic/dbus-rs" } maintenance = { status = "actively-developed" } [package.metadata.docs.rs] features = [ "futures" ] dbus-0.9.0/LICENSE-APACHE010066400017500001750000000261421364707574700126150ustar 00000000000000Apache 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.9.0/LICENSE-MIT010066400017500001750000000021231364707574700123160ustar 00000000000000Copyright (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.9.0/changes-in-0.7.md010066400017500001750000000065151364707574700135330ustar 00000000000000D-Bus crate 0.7 overview ======================== In 0.7 the `Connection` struct has been rewritten (but the old one is still there). The new struct(s) have the following advantages: * It makes it possible to make a connection `Send`/`Sync`, see `blocking::SyncConnection`. * The call to `dbus_connection_dispatch` has been removed and what it did has been rewritten in Rust. This means better performance and panic safety. * The two-layer design (with `Channel` as a low-level connection) makes it easier to write a custom `Connection`, should that be necessary. * Preparation for a first class async/non-blocking `Connection`. A `nonblock` module is in the works. * Some preparations for interfacing the `dbus` crate with a native backend instead of relying on `libdbus`, should someone want to write such a backend. There is a lot more to do before that becomes a reality though. Things that have moved ====================== If you want the quickest upgrade experience, then you can just update your imports: * The old `Connection` is now at `ffidisp::Connection`, likewise for `ConnPath`, `BusType` and many others. Have a look at the `ffidisp` module to see if your struct has moved there. * The `stdintf` module is at `ffidisp::stdintf`. * The old `MessageItem`, should you still need it, is now under `arg::messageitem`. But do use the generic functions instead of `MessageItem` whenever possible. `MessageItem::DictEntry` has changed to `MessageItem::Dict` (which contains the entire dict, not just one entry) to make it less error prone. Migrating / upgrading ===================== On a long term, consider migrating / upgrading to `blocking::Connection` or `blocking::SyncConnection`. You would need to make the following adjustments: * Create and connect your connection easily with just `Connection::new_session()` or `Connection::new_system()`. * Instead of `ConnPath`, use a `Proxy`. It works approximately the same way. * `blocking::stdintf` can be helpful to make standard method calls, such as getting and setting properties on a remote peer. * `Connection::register_name` has been renamed to `request_name`. (This was just a misnaming.) * Instead of `incoming()` to process incoming messages, use `process()` (which takes a std `Duration`). This will not hand out any messages though, instead register callbacks using (in order from most convenient to most generic): `Proxy::match_signal`, `Proxy::match_start` or `channel::MatchingReceiver::start_receive`. * For `tree`s, you must now make sure the root path (`/`) is always part of your tree. * For `tree`s, call `Tree::start_receive()` to attach the tree and the connection, then call `Connection::process()` to process incoming messages. Have a look at the `client`, `server` and `match_signal` examples to get started. ReadAll / AppendAll =================== The `ReadAll` and `AppendAll` traits were present in later 0.6.x versions as well, but are worthy a mention because they can make code more ergonomic in many cases. They are implemented for tuples: the empty tuple `()`, the single-tuple `(TYPE,)`, and usual tuples `(TYPE1, TYPE2)` up to 11. So if you have a method call that takes `STRING arg1, INT32 arg2` and returns a `BOOLEAN`, you can call it like this: ``` let (r,): (bool,) = myProxy.method_call(interface, name, (arg1, arg2))?; ``` ...where `arg1` is a `&str` and `arg2` is a `i32`. dbus-0.9.0/examples/argument_guide.md010066400017500001750000000207451364707574700160330ustar 00000000000000Preamble -------- 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::new_system()?; // Make a "ConnPath" struct that just contains a Connection, a destination and a path. let p = c.with_proxy("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager", Duration::new(5, 0)); // 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 = p.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::new_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", Duration::new(5, 0)); // 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.9.0/examples/client.rs010066400017500001750000000015761364707574700143370ustar 00000000000000use dbus::blocking::Connection; use std::time::Duration; fn main() -> Result<(), Box> { // First open up a connection to the session bus. let conn = Connection::new_session()?; // Second, create a wrapper struct around the connection that makes it easy // to send method calls to a specific destination and path. let proxy = conn.with_proxy("org.freedesktop.DBus", "/", Duration::from_millis(5000)); // Now make the method call. The ListNames method call takes zero input parameters and // one output parameter which is an array of strings. // Therefore the input is a zero tuple "()", and the output is a single tuple "(names,)". let (names,): (Vec,) = proxy.method_call("org.freedesktop.DBus", "ListNames", ())?; // Let's print all the names to stdout. for name in names { println!("{}", name); } Ok(()) } dbus-0.9.0/examples/match_signal.rs010066400017500001750000000036261367310650700154740ustar 00000000000000/// Connects to the "server" example. Have the server example running when you're running this example. use dbus::arg; // First, let's autogenerate some code using the dbus-codegen-rust tool. // With the server example running, the below was (part of) the output when running: // `dbus-codegen-rust -d com.example.dbustest -p /hello -m None` #[derive(Debug)] pub struct ComExampleDbustestHelloHappened { pub sender: String, } impl arg::AppendAll for ComExampleDbustestHelloHappened { fn append(&self, i: &mut arg::IterAppend) { arg::RefArg::append(&self.sender, i); } } impl arg::ReadAll for ComExampleDbustestHelloHappened { fn read(i: &mut arg::Iter) -> Result { Ok(ComExampleDbustestHelloHappened { sender: i.read()?, }) } } impl dbus::message::SignalArgs for ComExampleDbustestHelloHappened { const NAME: &'static str = "HelloHappened"; const INTERFACE: &'static str = "com.example.dbustest"; } // Autogenerated code ends here. use dbus::blocking::Connection; use dbus::Message; use std::error::Error; use std::time::Duration; fn main() -> Result<(), Box> { // Let's start by starting up a connection to the session bus. let c = Connection::new_session()?; { let proxy = c.with_proxy("com.example.dbustest", "/hello", Duration::from_millis(5000)); // Let's start listening to signals. let _id = proxy.match_signal(|h: ComExampleDbustestHelloHappened, _: &Connection, _: &Message| { println!("Hello happened from sender: {}", h.sender); true }); // Say hello to the server example, so we get a signal. let (s,): (String,) = proxy.method_call("com.example.dbustest", "Hello", ("match_signal example",))?; println!("{}", s); } // Listen to incoming signals forever. loop { c.process(Duration::from_millis(1000))?; } } dbus-0.9.0/examples/monitor.rs010066400017500001750000000016471367310627000145300ustar 00000000000000use dbus::blocking::Connection; use dbus::message::MatchRule; use std::time::Duration; // This programs implements the equivalent of running the "dbus-monitor" tool fn main() { // First open up a connection to the session bus. let conn = Connection::new_session().expect("D-Bus connection failed"); // Second create a rule to match messages we want to receive; in this example we add no // further requirements, so all messages will match let mut rule = MatchRule::new(); rule.eavesdrop = true; // this lets us eavesdrop on *all* session messages, not just ours // Start matching conn.add_match(rule, |_: (), _, msg| { println!("{:?}", msg); true }).expect("add_match failed"); // Loop and print out all messages received as they come. // Some can be quite large, e.g. if they contain embedded images.. loop { conn.process(Duration::from_millis(1000)).unwrap(); }; } dbus-0.9.0/examples/properties.rs010066400017500001750000000042751372522615700152420ustar 00000000000000use dbus::{blocking::Connection, arg}; use std::collections::HashMap; use std::time::Duration; fn print_refarg(value: &dyn 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() -> Result<(), Box> { // Connect to server and create a proxy object. A proxy implements several interfaces, // in this case we'll use OrgFreedesktopDBusProperties, which allows us to call "get". let c = Connection::new_session()?; let p = c.with_proxy("org.mpris.MediaPlayer2.rhythmbox", "/org/mpris/MediaPlayer2", Duration::from_millis(5000)); use dbus::blocking::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")?; println!("Option 1:"); // We now iterate over the hashmap. for (key, value) in metadata.iter() { print!(" {}: ", key); print_refarg(&value); } // As an alternative, if we just want a specific property and know the type of it, we can use // prop_cast: let title: Option<&String> = arg::prop_cast(&metadata, "xesam:title"); if let Some(title) = title { println!("The title is: {}", title); } // 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")?; // 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); } Ok(()) } dbus-0.9.0/examples/properties_msgitem.rs010066400017500001750000000005411364707574700167710ustar 00000000000000use dbus::ffidisp::Connection; use dbus::arg::messageitem::Props; fn main() { let c = Connection::new_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.9.0/examples/rtkit.rs010066400017500001750000000034521364707574700142110ustar 00000000000000/* 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. */ use std::cmp; use std::time::Duration; fn make_realtime(prio: u32) -> Result> { let c = dbus::blocking::Connection::new_system()?; let proxy = c.with_proxy("org.freedesktop.RealtimeKit1", "/org/freedesktop/RealtimeKit1", Duration::from_millis(10000)); use dbus::blocking::stdintf::org_freedesktop_dbus::Properties; // Make sure we don't fail by wanting too much let max_prio: i32 = proxy.get("org.freedesktop.RealtimeKit1", "MaxRealtimePriority")?; let prio = cmp::min(prio, max_prio as u32); // Enforce RLIMIT_RTPRIO, also a must before asking rtkit for rtprio let max_rttime: i64 = proxy.get("org.freedesktop.RealtimeKit1", "RTTimeUSecMax")?; let new_limit = libc::rlimit64 { rlim_cur: max_rttime as u64, rlim_max: max_rttime as u64 }; 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 = proxy.method_call("org.freedesktop.RealtimeKit1", "MakeThreadRealtime", (thread_id as u64, prio)); if r.is_err() { unsafe { libc::setrlimit64(libc::RLIMIT_RTTIME, &old_limit) }; } r?; Ok(prio) } fn main() { match make_realtime(5) { Ok(n) => println!("Got rtprio, level {}", n), Err(e) => println!("No rtprio: {}", e), } } dbus-0.9.0/examples/unity_focused_window.rs010066400017500001750000000016741364707574700173270ustar 00000000000000extern 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::{ffidisp::Connection, Message, MessageType}; fn focus_msg(msg: &Message) -> Option<&str> { if msg.msg_type() != MessageType::Signal { return None }; if &*msg.interface().unwrap() != "com.canonical.Unity.WindowStack" { return None }; if &*msg.member().unwrap() != "FocusedWindowChanged" { return None }; let (_, app) = msg.get2::(); app } fn main() { let c = Connection::new_session().unwrap(); c.add_match("interface='com.canonical.Unity.WindowStack',member='FocusedWindowChanged'").unwrap(); loop { if let Some(msg) = c.incoming(1000).next() { if let Some(app) = focus_msg(&msg) { println!("{} has now focus.", app); } } } } dbus-0.9.0/src/arg/array_impl.rs010066400017500001750000000503321372737405100147210ustar 00000000000000use super::*; use crate::{Message, ffi}; use crate::strings::{Signature, Path}; 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, BuildHasher}; // 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_by_ref(&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| RefArg::append(arg,s)); } #[inline] fn as_any(&self) -> &dyn any::Any where Self: 'static { self } #[inline] fn as_any_mut(&mut self) -> &mut dyn any::Any where Self: 'static { self } #[inline] fn as_static_inner(&self, index: usize) -> Option<&(dyn RefArg + 'static)> where Self: 'static { self.get(index).map(|x| x as &dyn RefArg) } 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| RefArg::append(arg,s)); } #[inline] fn as_any(&self) -> &dyn any::Any where Self: 'static { self } #[inline] fn as_any_mut(&mut self) -> &mut dyn any::Any where Self: 'static { self } fn as_iter<'a>(&'a self) -> Option + 'a>> { Some(Box::new(self.iter().map(|b| b as &dyn RefArg))) } #[inline] fn as_static_inner(&self, index: usize) -> Option<&(dyn RefArg + 'static)> where Self: 'static { self.get(index).map(|x| x as &dyn 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: *mut T = 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.is_null() { 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. 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 + Clone> Append for Dict<'a, K, V, I> { fn append_by_ref(&self, i: &mut IterAppend) { let z = self.0.clone(); 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_by_ref(ss); v.append_by_ref(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_by_ref(&self, i: &mut IterAppend) { Dict::new(self.iter()).append_by_ref(i); } } impl<'a, K: DictKey + Get<'a> + Eq + Hash, V: Arg + Get<'a>, S: BuildHasher + Default> Get<'a> for HashMap { fn get(i: &mut Iter<'a>) -> Option { // TODO: Full element signature is not verified. Dict::get(i).map(|d| d.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) -> &dyn any::Any where Self: 'static { self } #[inline] fn as_any_mut(&mut self) -> &mut dyn 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 &dyn RefArg, v as &dyn 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_by_ref(&self, i: &mut IterAppend) { Array::new(self).append_by_ref(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<(fn() -> T, &'a ())>); impl<'a, T: 'a, I: Iterator> Array<'a, T, I> { /// Creates a new Array from an iterator. 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 + Clone> Append for Array<'a, T, I> { fn append_by_ref(&self, i: &mut IterAppend) { let z = self.0.clone(); i.append_container(ArgType::Array, Some(T::signature().as_cstr()), |s| for arg in z { arg.append_by_ref(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 box_clone. impl<'a, T, I> RefArg for Array<'static, T, I> where T: 'a + Arg + RefArg, I: fmt::Debug + Clone + Send + Sync + Iterator { 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 { RefArg::append(arg, s); } ); } #[inline] fn as_any(&self) -> &dyn any::Any where Self: 'static { self } #[inline] fn as_any_mut(&mut self) -> &mut dyn 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(i: &mut Iter) -> 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) -> &dyn any::Any where Self: 'static { self } #[inline] fn as_any_mut(&mut self) -> &mut dyn 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 &dyn RefArg, v as &dyn RefArg].into_iter()))) } fn as_static_inner(&self, index: usize) -> Option<&(dyn RefArg + 'static)> where Self: 'static { let (k, v) = self.data.get(index / 2)?; Some(if index & 1 != 0 { v } else { k }) } #[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) -> &dyn any::Any where Self: 'static { self } #[inline] fn as_any_mut(&mut self) -> &mut dyn 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 &dyn RefArg, v as &dyn RefArg].into_iter()))) } fn as_static_inner(&self, index: usize) -> Option<&(dyn RefArg + 'static)> where Self: 'static { let (k, v) = self.data.get(index / 2)?; Some(if index & 1 != 0 { v } else { k }) } #[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(i: &mut Iter) -> 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 { RefArg::append(arg,s) } ); } #[inline] fn as_any(&self) -> &dyn any::Any where Self: 'static { self } #[inline] fn as_any_mut(&mut self) -> &mut dyn any::Any where Self: 'static { self } fn as_iter<'a>(&'a self) -> Option + 'a>> { Some(Box::new(self.data.iter().map(|b| b as &dyn RefArg))) } fn as_static_inner(&self, index: usize) -> Option<&(dyn RefArg + 'static)> where Self: 'static { self.data.get(index).map(|x| x as &dyn 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(i: &mut Iter) -> 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.9.0/src/arg/basic_impl.rs010066400017500001750000000235501373106336600146650ustar 00000000000000use crate::ffi; use super::*; use super::check; use crate::strings::{Signature, Path, Member, ErrorName, Interface}; 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 { if ffi::dbus_message_iter_get_arg_type(i) != arg_type as c_int { return None }; let mut c = mem::MaybeUninit::uninit(); ffi::dbus_message_iter_get_basic(i, &mut c as *mut _ as *mut c_void); Some(c.assume_init()) } } 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_by_ref(&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) -> &dyn any::Any { self } #[inline] fn as_any_mut(&mut self) -> &mut dyn 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, "y\0", i, Some(i as i64), u, Some(u as u64), f, Some(f as f64)); integer_impl!(i16, Int16, "n\0", i, Some(i as i64), _u, None, f, Some(f as f64)); integer_impl!(u16, UInt16, "q\0", i, Some(i as i64), u, Some(u as u64), f, Some(f as f64)); integer_impl!(i32, Int32, "i\0", i, Some(i as i64), _u, None, f, Some(f as f64)); integer_impl!(u32, UInt32, "u\0", i, Some(i as i64), u, Some(u as u64), f, Some(f as f64)); integer_impl!(i64, Int64, "x\0", i, Some(i), _u, None, _f, None); integer_impl!(u64, UInt64, "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) -> &dyn any::Any { self } #[inline] fn as_any_mut(&mut self) -> &mut dyn 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\0") } } } impl Append for bool { fn append_by_ref(&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("d\0") } } } impl Append for f64 { fn append_by_ref(&self, i: &mut IterAppend) { arg_append_basic(&mut i.0, ArgType::Double, *self) } } impl DictKey for f64 {} impl<'a> Get<'a> for f64 { fn get(i: &mut Iter) -> Option { arg_get_basic(&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("s\0") } } } impl<'a> Append for &'a str { fn append_by_ref(&self, i: &mut IterAppend) { use std::borrow::Cow; let b: &[u8] = self.as_bytes(); let v: Cow<[u8]> = if !b.is_empty() && 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("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) } fn append_by_ref(&self, i: &mut IterAppend) { (&**self).append_by_ref(i) } } impl<'a> DictKey for String {} impl<'a> Get<'a> for String { fn get(i: &mut Iter<'a>) -> Option { <&str>::get(i).map(String::from) } } 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("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("h\0") } } } impl Append for OwnedFd { fn append_by_ref(&self, i: &mut IterAppend) { 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(|fd| unsafe { OwnedFd::new(fd) }) } } 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) -> &dyn any::Any { self } #[inline] fn as_any_mut(&mut self) -> &mut dyn 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_by_ref(&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 { let c = arg_get_str(&mut i.0, ArgType::$s)?; let s = std::str::from_utf8(c.to_bytes_with_nul()).ok()?; Some($t::from_slice_unchecked(s).into_static()) }} } } } string_impl!(Interface, String, "s\0"); string_impl!(ErrorName, String, "s\0"); string_impl!(Member, String, "s\0"); string_impl!(Path, ObjectPath, "o\0"); string_impl!(Signature, Signature, "g\0"); dbus-0.9.0/src/arg/messageitem.rs010066400017500001750000001124451364707574700151040ustar 00000000000000//! MessageItem - old, enum design that is used as parameters and return values from //! method calls, or as data added to a signal. //! //! Note that the newer generic design (see `arg` module) is, in general, both faster //! and smaller than MessageItem, and should be your first hand choice //! whenever applicable. There is also a trait object design called `RefArg` in //! case the generic design is too inflexible. use crate::strings::{Signature, Path, Interface, BusName}; use crate::arg; use crate::arg::{Iter, IterAppend, Arg, ArgType, OwnedFd}; use std::ffi::CStr; use std::{ops, any}; use crate::{ffidisp::Connection, Message, Error}; use std::collections::BTreeMap; use std::convert::TryFrom; #[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, } #[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 { if i.signature().as_cstr() != esig { 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> { 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 } } impl arg::Append for MessageItemArray { fn append_by_ref(&self, i: &mut IterAppend) { i.append_container(ArgType::Array, Some(self.element_signature()), |s| { for a in &self.v { a.append_by_ref(s) } }); } } #[derive(Debug, Clone, PartialEq, PartialOrd)] /// An array of MessageItem where every MessageItem is of the same type. pub struct MessageItemDict { v: Vec<(MessageItem, MessageItem)>, // signature includes the "a"! sig: Signature<'static>, } impl MessageItemDict { /// Creates a new dict where every key and value elements have the supplied signature. pub fn new(v: Vec<(MessageItem, MessageItem)>, keysig: Signature<'static>, valuesig: Signature<'static>) -> Result { let sig = Signature::from(format!("a{{{}{}}}", keysig, valuesig)); let a = MessageItemDict {v: v, sig: sig }; for (k, v) in &a.v { if keysig != k.signature() || valuesig != v.signature() { 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) } } /// Signature of array (full array signature) pub fn signature(&self) -> &Signature<'static> { &self.sig } /// Consumes the MessageItemDict in order to allow you to modify the individual items of the dict. pub fn into_vec(self) -> Vec<(MessageItem, MessageItem)> { self.v } } impl ops::Deref for MessageItemDict { type Target = [(MessageItem, MessageItem)]; fn deref(&self) -> &Self::Target { &self.v } } impl arg::Append for MessageItemDict { fn append_by_ref(&self, i: &mut IterAppend) { i.append_container(ArgType::Array, Some(self.element_signature()), |s| { for (k, v) in &self.v { s.append_container(ArgType::DictEntry, None, |ss| { k.append_by_ref(ss); v.append_by_ref(ss); }); } }); } } /// 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. All keys and values are required to be of the same type. /// Not all types can be dictionary keys, but all can be dictionary values. Dict(MessageItemDict), /// A D-Bus objectpath requires its content to be a valid objectpath, /// so this cannot be any string. ObjectPath(Path<'static>), /// A D-Bus signature requires its content to be a valid type signature, /// so this cannot be any string. Signature(Signature<'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), } impl MessageItem { /// Get the D-Bus Signature for this MessageItem. pub fn signature(&self) -> Signature<'static> { use crate::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::Dict(ref a) => a.sig.clone(), MessageItem::ObjectPath(_) => ::signature(), MessageItem::Signature(_) => ::signature(), MessageItem::UnixFd(_) => ::signature(), } } /// Get the arg type of this MessageItem. pub fn arg_type(&self) -> arg::ArgType { match self { MessageItem::Str(_) => ArgType::String, MessageItem::Bool(_) => ArgType::Boolean, MessageItem::Byte(_) => ArgType::Byte, MessageItem::Int16(_) => ArgType::Int16, MessageItem::Int32(_) => ArgType::Int32, MessageItem::Int64(_) => ArgType::Int64, MessageItem::UInt16(_) => ArgType::UInt16, MessageItem::UInt32(_) => ArgType::UInt32, MessageItem::UInt64(_) => ArgType::UInt64, MessageItem::Double(_) => ArgType::Double, MessageItem::Array(_) => ArgType::Array, MessageItem::Struct(_) => ArgType::Struct, MessageItem::Variant(_) => ArgType::Variant, MessageItem::Dict(_) => ArgType::Array, MessageItem::ObjectPath(_) => ArgType::ObjectPath, MessageItem::Signature(_) => ArgType::Signature, MessageItem::UnixFd(_) => ArgType::UnixFd, } } /// 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) = r?; v.push((s.into(), Box::new(vv).into())); } Ok(MessageItem::Dict(MessageItemDict::new(v, Signature::new("s").unwrap(), Signature::new("v").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.is_empty() { return Err(ArrayError::EmptyArray); } let s = MessageItemArray::make_sig(&v[0]); Ok(MessageItem::Array(MessageItemArray::new(v, s)?)) } /// Creates an MessageItem::Dict from a list of MessageItem pairs. /// /// Note: This requires `v` to be non-empty. See also /// `MessageItem::from(&[(T1, T2)])`, which can handle empty arrays as well. pub fn new_dict(v: Vec<(MessageItem, MessageItem)>) -> Result { if v.is_empty() { return Err(ArrayError::EmptyArray); } let (s1, s2) = (v[0].0.signature(), v[0].1.signature()); Ok(MessageItem::Dict(MessageItemDict::new(v, s1, s2)?)) } /// Get the inner value of a `MessageItem` /// /// # Example /// ``` /// use dbus::arg::messageitem::MessageItem; /// let m: MessageItem = 5i64.into(); /// let s: i64 = m.inner().unwrap(); /// assert_eq!(s, 5i64); /// ``` pub fn inner<'a, T: TryFrom<&'a MessageItem>>(&'a self) -> Result { T::try_from(self) } /// Get the underlying `MessageItem` of a `MessageItem::Variant` /// /// Nested `MessageItem::Variant`s are unwrapped recursively until a /// non-`Variant` is found. /// /// # Example /// ``` /// use dbus::arg::messageitem::MessageItem; /// let nested = MessageItem::Variant(Box::new(6i64.into())); /// let flat: MessageItem = 6i64.into(); /// assert_ne!(&nested, &flat); /// assert_eq!(nested.peel(), &flat); /// ``` pub fn peel(&self) -> &Self { let mut current = self; while let MessageItem::Variant(b) = current { current = &*b; } current } 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.is_empty() { d = D::default().into(); &d } else { &v[0] }; MessageItemArray::make_sig(t) }; MessageItem::Array(MessageItemArray::new(v, s).unwrap()) } fn new_dict2(i: I) -> MessageItem where K: Into + Default, V: Into + Default, I: Iterator { let v: Vec<(MessageItem, MessageItem)> = i.map(|(k, v)| (k.into(), v.into())).collect(); let (kt, vt) = if v.is_empty() { let kd = K::default().into(); let vd = V::default().into(); (kd.signature(), vd.signature()) } else { (v[0].0.signature(), v[0].1.signature()) }; MessageItem::Dict(MessageItemDict::new(v, kt, vt).unwrap()) } } macro_rules! msgitem_convert { ($t: ty, $s: ident) => { impl From<$t> for MessageItem { fn from(i: $t) -> MessageItem { MessageItem::$s(i) } } impl<'a> TryFrom<&'a MessageItem> for $t { type Error = (); fn try_from(i: &'a MessageItem) -> Result<$t,()> { if let MessageItem::$s(b) = i.peel() { 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_array2(i.iter().cloned()) } } /// Create a `MessageItem::Dict`. impl<'a, T1, T2> From<&'a [(T1, T2)]> for MessageItem where T1: Into + Clone + Default, T2: Into + Clone + Default { fn from(i: &'a [(T1, T2)]) -> MessageItem { MessageItem::new_dict2(i.iter().cloned()) } } 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: Signature<'static>) -> MessageItem { MessageItem::Signature(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) } } impl<'a> TryFrom<&'a MessageItem> for &'a str { type Error = (); fn try_from(i: &'a MessageItem) -> Result<&'a str, Self::Error> { match i.peel() { MessageItem::Str(ref b) => Ok(b), MessageItem::ObjectPath(ref b) => Ok(b), MessageItem::Signature(ref b) => Ok(b), _ => Err(()), } } } impl<'a> TryFrom<&'a MessageItem> for &'a String { type Error = (); fn try_from(i: &'a MessageItem) -> Result<&'a String,()> { if let MessageItem::Str(b) = i.peel() { Ok(b) } else { Err(()) } } } impl<'a> TryFrom<&'a MessageItem> for &'a Path<'static> { type Error = (); fn try_from(i: &'a MessageItem) -> Result<&'a Path<'static>,()> { if let MessageItem::ObjectPath(b) = i.peel() { Ok(b) } else { Err(()) } } } impl<'a> TryFrom<&'a MessageItem> for &'a Signature<'static> { type Error = (); fn try_from(i: &'a MessageItem) -> Result<&'a Signature<'static>,()> { if let MessageItem::Signature(b) = i.peel() { Ok(b) } else { Err(()) } } } impl<'a> TryFrom<&'a MessageItem> for &'a Box { type Error = (); fn try_from(i: &'a MessageItem) -> Result<&'a Box,()> { if let MessageItem::Variant(b) = i { Ok(b) } else { Err(()) } } } impl<'a> TryFrom<&'a MessageItem> for &'a Vec { type Error = (); fn try_from(i: &'a MessageItem) -> Result<&'a Vec,()> { match i.peel() { MessageItem::Array(b) => Ok(&b.v), MessageItem::Struct(b) => Ok(b), _ => Err(()), } } } impl<'a> TryFrom<&'a MessageItem> for &'a [MessageItem] { type Error = (); fn try_from(i: &'a MessageItem) -> Result<&'a [MessageItem],()> { i.inner::<&Vec>().map(|s| &**s) } } impl<'a> TryFrom<&'a MessageItem> for &'a OwnedFd { type Error = (); fn try_from(i: &'a MessageItem) -> Result<&'a OwnedFd,()> { if let MessageItem::UnixFd(ref b) = i { Ok(b) } else { Err(()) } } } impl<'a> TryFrom<&'a MessageItem> for &'a [(MessageItem, MessageItem)] { type Error = (); fn try_from(i: &'a MessageItem) -> Result<&'a [(MessageItem, MessageItem)],()> { if let MessageItem::Dict(ref d) = i { Ok(&*d.v) } else { Err(()) } } } impl arg::Append for MessageItem { fn append_by_ref(&self, i: &mut IterAppend) { match self { MessageItem::Str(a) => a.append_by_ref(i), MessageItem::Bool(a) => a.append_by_ref(i), MessageItem::Byte(a) => a.append_by_ref(i), MessageItem::Int16(a) => a.append_by_ref(i), MessageItem::Int32(a) => a.append_by_ref(i), MessageItem::Int64(a) => a.append_by_ref(i), MessageItem::UInt16(a) => a.append_by_ref(i), MessageItem::UInt32(a) => a.append_by_ref(i), MessageItem::UInt64(a) => a.append_by_ref(i), MessageItem::Double(a) => a.append_by_ref(i), MessageItem::Array(a) => a.append_by_ref(i), MessageItem::Struct(a) => i.append_container(ArgType::Struct, None, |s| { for v in a { v.append_by_ref(s); } }), MessageItem::Variant(a) => { i.append_container(ArgType::Variant, Some(a.signature().as_cstr()), |s| a.append_by_ref(s)) }, MessageItem::Dict(a) => a.append_by_ref(i), MessageItem::ObjectPath(a) => a.append_by_ref(i), MessageItem::Signature(a) => a.append_by_ref(i), MessageItem::UnixFd(a) => a.append_by_ref(i), } } } impl<'a> arg::Get<'a> for MessageItem { fn get(i: &mut Iter<'a>) -> Option { Some(match i.arg_type() { ArgType::Array => { let mut s = i.recurse(ArgType::Array).unwrap(); if i.signature().as_bytes()[1] == b'{' { // Dict let mut v = vec!(); while s.arg_type() == ArgType::DictEntry { let mut ss = s.recurse(ArgType::DictEntry).unwrap(); let kk = MessageItem::get(&mut ss).unwrap(); ss.next(); let vv = MessageItem::get(&mut ss).unwrap(); v.push((kk, vv)); s.next(); }; MessageItem::Dict(MessageItemDict { v: v, sig: i.signature() }) } else { let mut v = vec!(); while let Some(mi) = MessageItem::get(&mut s) { v.push(mi); s.next(); }; MessageItem::Array(MessageItemArray { v: v, sig: i.signature() }) } }, ArgType::Variant => MessageItem::Variant({ let mut s = i.recurse(ArgType::Variant).unwrap(); Box::new(MessageItem::get(&mut s).unwrap()) }), ArgType::Boolean => MessageItem::Bool(i.get::().unwrap()), ArgType::Invalid => return None, ArgType::String => MessageItem::Str(i.get::().unwrap()), ArgType::DictEntry => return None, ArgType::Byte => MessageItem::Byte(i.get::().unwrap()), ArgType::Int16 => MessageItem::Int16(i.get::().unwrap()), ArgType::UInt16 => MessageItem::UInt16(i.get::().unwrap()), ArgType::Int32 => MessageItem::Int32(i.get::().unwrap()), ArgType::UInt32 => MessageItem::UInt32(i.get::().unwrap()), ArgType::Int64 => MessageItem::Int64(i.get::().unwrap()), ArgType::UInt64 => MessageItem::UInt64(i.get::().unwrap()), ArgType::Double => MessageItem::Double(i.get::().unwrap()), ArgType::UnixFd => MessageItem::UnixFd(i.get::().unwrap()), ArgType::Struct => MessageItem::Struct({ let mut s = i.recurse(ArgType::Struct).unwrap(); let mut v = vec!(); while let Some(mi) = MessageItem::get(&mut s) { v.push(mi); s.next(); }; v }), ArgType::ObjectPath => MessageItem::ObjectPath(i.get::().unwrap().into_static()), ArgType::Signature => MessageItem::Signature(i.get::().unwrap().into_static()), }) } } impl arg::RefArg for MessageItem { fn arg_type(&self) -> ArgType { MessageItem::arg_type(&self) } fn signature(&self) -> Signature<'static> { MessageItem::signature(&self) } fn append(&self, i: &mut IterAppend) { arg::Append::append_by_ref(self, i) } #[inline] fn as_any(&self) -> &dyn any::Any where Self: 'static { self } #[inline] fn as_any_mut(&mut self) -> &mut dyn any::Any where Self: 'static { self } #[inline] fn box_clone(&self) -> Box { Box::new(self.clone()) } } impl arg::Append for arg::Variant { fn append_by_ref(&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_by_ref(s)); } } /// 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 = self.conn.send_with_reply_and_block(m, self.timeout_ms)?; let reply = 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); 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 = self.conn.send_with_reply_and_block(m, self.timeout_ms)?; 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 = self.conn.send_with_reply_and_block(m, self.timeout_ms)?; let reply = r.as_result()?.get_items(); (|| { if reply.len() != 1 { return Err(()) }; let mut tree = BTreeMap::new(); let a: &[(MessageItem, MessageItem)] = reply[0].inner()?; for (k, v) in a.iter() { let (k, v): (&String, &Box) = (k.inner()?, v.inner()?); tree.insert(k.clone(), *v.clone()); } Ok(tree) })().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 = 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 = 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> { self.p.set(propname, value.clone())?; self.map.insert(propname.to_string(), value); Ok(()) } } #[cfg(test)] mod test { extern crate tempfile; use crate::{Message, MessageType, Path, Signature}; use libc; use crate::arg::messageitem::MessageItem; use crate::arg::OwnedFd; use crate::ffidisp::{Connection, BusType}; #[test] fn unix_fd() { use std::io::prelude::*; use std::io::SeekFrom; use std::fs::OpenOptions; use std::os::unix::io::{IntoRawFd, 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 = tempfile::Builder::new().prefix("dbus-rs-test").tempdir().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 = unsafe { OwnedFd::new(file.into_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_dict(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_dict(paths.iter().map( |(path, ifaces)| (MessageItem::ObjectPath(Path::new(*path).unwrap()), MessageItem::new_dict(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(); } /* Unfortunately org.freedesktop.DBus has no properties we can use for testing, but hostname1 should be around on most distros. */ #[test] fn test_get_hostname1_prop() { use super::Props; let c = Connection::new_system().unwrap(); let p = Props::new(&c, "org.freedesktop.hostname1", "/org/freedesktop/hostname1", "org.freedesktop.hostname1", 10000); /* Let's use both the get and getall methods and see if we get the same result */ let v = p.get("StaticHostname").unwrap(); let vall = p.get_all().unwrap(); let v2 = vall.get("StaticHostname").unwrap(); assert_eq!(&v, &*v2); match v { MessageItem::Str(ref s) => { println!("StaticHostname is {}", s); } _ => { panic!("Invalid Get: {:?}", v); } }; } #[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 message_inner_str() { let ob = MessageItem::ObjectPath("/path".into()); assert_eq!("/path", ob.inner::<&str>().unwrap()); let ob = MessageItem::ObjectPath("/path".into()); assert_ne!("/path/another", ob.inner::<&str>().unwrap()); let ob = MessageItem::Str("String".into()); assert_eq!("String", ob.inner::<&str>().unwrap()); let ob = MessageItem::Str("String".into()); assert_ne!("StringDiff", ob.inner::<&str>().unwrap()); let ob = MessageItem::Signature(Signature::make::()); assert_eq!("i", ob.inner::<&str>().unwrap()); let ob = MessageItem::Signature(Signature::make::()); assert_ne!("i", ob.inner::<&str>().unwrap()); } #[test] fn message_peel() { let flat_str = MessageItem::Str("foobar".into()); assert_eq!(flat_str.peel(), &flat_str); let flat_path = MessageItem::ObjectPath("/path".into()); assert_eq!(flat_path.peel(), &flat_path); let flat_sig = MessageItem::Signature(Signature::make::()); assert_eq!(flat_sig.peel(), &flat_sig); let flat_int = MessageItem::Int32(1234); assert_eq!(flat_int.peel(), &flat_int); let layered_str = MessageItem::Variant(Box::new(flat_str)); assert_eq!(layered_str.peel(), &MessageItem::Str("foobar".into())); let layered_path = MessageItem::Variant(Box::new(flat_path)); assert_eq!(layered_path.peel(), &MessageItem::ObjectPath("/path".into())); let layered_sig = MessageItem::Variant(Box::new(flat_sig)); assert_eq!(layered_sig.peel(), &MessageItem::Signature(Signature::make::())); let layered_int = MessageItem::Variant(Box::new(flat_int)); assert_eq!(layered_int.peel(), &MessageItem::Int32(1234)); let very_deep = MessageItem::Variant(Box::new( MessageItem::Variant(Box::new( MessageItem::Variant(Box::new( MessageItem::Variant(Box::new( MessageItem::Variant(Box::new( MessageItem::Variant(Box::new( MessageItem::Variant(Box::new( MessageItem::Variant(Box::new( MessageItem::Variant(Box::new( MessageItem::Variant(Box::new( MessageItem::Int32(1234) )))))))))))))))))))); assert_eq!(very_deep.peel(), &MessageItem::Int32(1234)); } #[test] fn inner_from_variant() { let msg_u8 = MessageItem::Variant(Box::new(3u8.into())); assert_eq!(msg_u8.inner::().unwrap(), 3u8); let msg_u16 = MessageItem::Variant(Box::new(4u16.into())); assert_eq!(msg_u16.inner::().unwrap(), 4u16); let msg_u32 = MessageItem::Variant(Box::new(5u32.into())); assert_eq!(msg_u32.inner::().unwrap(), 5u32); let msg_u64 = MessageItem::Variant(Box::new(6u64.into())); assert_eq!(msg_u64.inner::().unwrap(), 6u64); let msg_i16 = MessageItem::Variant(Box::new(4i16.into())); assert_eq!(msg_i16.inner::().unwrap(), 4i16); let msg_i32 = MessageItem::Variant(Box::new(5i32.into())); assert_eq!(msg_i32.inner::().unwrap(), 5i32); let msg_i64 = MessageItem::Variant(Box::new(6i64.into())); assert_eq!(msg_i64.inner::().unwrap(), 6i64); let msg_f64 = MessageItem::Variant(Box::new(6.5f64.into())); assert_eq!(msg_f64.inner::().unwrap(), 6.5f64); let msg_bool = MessageItem::Variant(Box::new(false.into())); assert_eq!(msg_bool.inner::().unwrap(), false); let msg_string = MessageItem::Variant(Box::new("asdf".to_string().into())); assert_eq!(msg_string.inner::<&String>().unwrap(), "asdf"); let path: Path = "/path".into(); let msg_path = MessageItem::Variant(Box::new(MessageItem::ObjectPath(path.clone()))); assert_eq!(msg_path.inner::<&Path>().unwrap(), &path); let sig: Signature = "a{si}".into(); let msg_sig = MessageItem::Variant(Box::new(MessageItem::Signature(sig.clone()))); assert_eq!(msg_sig.inner::<&Signature>().unwrap(), &sig); assert_eq!(msg_string.inner::<&str>().unwrap(), "asdf"); assert_eq!(msg_path.inner::<&str>().unwrap(), "/path"); assert_eq!(msg_sig.inner::<&str>().unwrap(), "a{si}"); } } dbus-0.9.0/src/arg/mod.rs010066400017500001750000000452171373106375300133460ustar 00000000000000//! 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 mod messageitem; pub use self::msgarg::{Arg, FixedArray, Get, DictKey, Append, RefArg, AppendAll, ReadAll, ArgAll, cast, cast_mut, prop_cast}; pub use self::array_impl::{Array, Dict}; pub use self::variantstruct_impl::Variant; use std::{fmt, mem, ptr, error}; use crate::{ffi, Message, Signature, Path}; use std::ffi::{CStr, CString}; use std::os::raw::{c_void, c_int}; use std::os::unix::io::{RawFd, AsRawFd, FromRawFd, IntoRawFd}; use std::collections::VecDeque; fn check(f: &str, i: u32) { if i == 0 { panic!("D-Bus error: '{}' failed", f) }} fn ffi_iter() -> ffi::DBusMessageIter { // Safe because DBusMessageIter contains only fields that are allowed to be zeroed (i e no references or similar) 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. /// /// This function is unsafe, because you could potentially send in an invalid file descriptor, /// or close it during the lifetime of this struct. This could potentially be unsound. pub unsafe 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 { let x = unsafe { libc::dup(self.fd) }; if x == -1 { panic!("Duplicating file descriptor failed") } unsafe { OwnedFd::new(x) } } } impl AsRawFd for OwnedFd { fn as_raw_fd(&self) -> RawFd { self.fd } } impl IntoRawFd for OwnedFd { fn into_raw_fd(self) -> RawFd { self.into_fd() } } impl FromRawFd for OwnedFd { unsafe fn from_raw_fd(fd: RawFd) -> Self { OwnedFd::new(fd) } } #[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. /// /// 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(std::str::from_utf8(cc.to_bytes()).unwrap()); 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 = 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, &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 } /// Returns a Vec of all possible argtypes. pub fn all() -> Vec { ALL_ARG_TYPES.iter().map(|x| x.0).collect() } /// 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<&dyn error::Error> { None } } impl fmt::Display for TypeMismatchError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "D-Bus argument type mismatch at position {}: expected {}, found {}", 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 msg = Message::new_signal("/", "a.b", "C").unwrap(); let mut q = IterAppend::new(&mut msg); q.append(5u8); q.append(Array::new(&[5u8, 6, 7])); q.append((8u8, &[9u8, 6, 7][..])); q.append(Variant((6u8, 7u8))); } dbus-0.9.0/src/arg/msgarg.rs010066400017500001750000000470701372737100100140370ustar 00000000000000#![allow(dead_code)] use crate::{Signature, arg::TypeMismatchError, arg::Variant}; use std::{fmt, any}; use std::sync::Arc; // use std::rc::Rc; use std::collections::HashMap; 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 type signature for this type. fn signature() -> Signature<'static>; } /// Helper trait to introspect many arguments. pub trait ArgAll { /// A tuple of &static str. Used for introspection. #[allow(non_camel_case_types)] // Note: This should be changed for 0.9 - but for now, don't break backwards compatibility type strs; /// Enumerates all arguments with their signatures (introspection helper method). fn strs_sig)>(a: Self::strs, f: F); } /// Types that can be appended to a message as arguments implement this trait. pub trait Append { /// Performs the append operation by consuming self. fn append(self, ia: &mut IterAppend) where Self: Sized { self.append_by_ref(ia) } /// Performs the append operation by borrowing self. fn append_by_ref(&self, _: &mut IterAppend); } /// Helper trait to append many arguments to a message. pub trait AppendAll { /// Performs the append operation by borrowing self. 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 + Send + Sync { /// 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). /// /// If the type is an array, and the element type is byte, numeric, boolean, or string, then the /// representation is guaranteed to be a Vec. Arrays of structs, dicts, and so on are not /// safe to use with this. /// /// Note: The internal representation of complex types (Dict, Struct) is unstable /// and as_any should not be relied upon for these types. Use as_iter instead. fn as_any(&self) -> &dyn any::Any where Self: 'static; /// Transforms this argument to Any (which can be downcasted to read the current value). /// /// If the type is an array, and the element type is byte, numeric, boolean, or string, then the /// representation is guaranteed to be a Vec. Arrays of structs, dicts, and so on are not /// safe to use with this. /// /// Note: The internal representation of complex types (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 dyn 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. /// For Dicts, keys and values are interleaved. #[inline] fn as_iter<'a>(&'a self) -> Option + 'a>> { None } /// Try to read the inner of an argument, as another argument, specifying an index. /// /// Works for: Variant, Array, Struct, Dict. /// For Dicts, even indices gets a key, odd indices gets a value. #[inline] fn as_static_inner(&self, _index: usize) -> Option<&(dyn RefArg + 'static)> where Self: 'static { 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; } 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 (dyn 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 (dyn RefArg + 'static)) -> Option<&'a mut T> { a.as_any_mut().downcast_mut() } /// Descend into a hashmap returned by e g "Properties::get_all" to retrieve the value of a property. /// /// Shortcut for get + cast. Returns None both if the property does not exist, or if it was of a different type. pub fn prop_cast<'a, T: 'static>(map: &'a HashMap>>, key: &str) -> Option<&'a T> { map.get(key).and_then(|v| cast(&v.0)) } /// 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> Append for &'a T { fn append_by_ref(&self, i: &mut IterAppend) { (&**self).append_by_ref(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) -> &dyn any::Any where T: 'static { (&**self).as_any() } #[inline] fn as_any_mut(&mut self) -> &mut dyn 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 as_static_inner(&self, index: usize) -> Option<&(dyn RefArg + 'static)> where Self: 'static { (&**self).as_static_inner(index) } #[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) -> &dyn any::Any where T: 'static { (&**self).as_any() } #[inline] fn as_any_mut(&mut $ss) -> &mut dyn 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 as_static_inner(&self, index: usize) -> Option<&(dyn RefArg + 'static)> where Self: 'static { (&**self).as_static_inner(index) } #[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($t::new) } } } } impl Append for Box { fn append_by_ref(&self, i: &mut IterAppend) { (&**self).append_by_ref(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()); macro_rules! argall_impl { ($($n: ident $t: ident $s: ty,)+) => { impl<$($t: Arg),*> ArgAll for ($($t,)*) { type strs = ($(&'static $s,)*); fn strs_sig)>(z: Self::strs, mut q: Q) { let ( $($n,)*) = z; $( q($n, $t::signature()); )* } } 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, )* )) } } } } impl ArgAll for () { type strs = (); fn strs_sig)>(_: Self::strs, _: F) {} } impl AppendAll for () { fn append(&self, _: &mut IterAppend) {} } impl ReadAll for () { fn read(_: &mut Iter) -> Result { Ok(()) } } argall_impl!(a A str,); argall_impl!(a A str, b B str,); argall_impl!(a A str, b B str, c C str,); argall_impl!(a A str, b B str, c C str, d D str,); argall_impl!(a A str, b B str, c C str, d D str, e E str,); argall_impl!(a A str, b B str, c C str, d D str, e E str, f F str,); argall_impl!(a A str, b B str, c C str, d D str, e E str, f F str, g G str,); argall_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,); argall_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,); argall_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 { use crate::{channel::{Channel, BusType}, Message, Path, Signature}; use crate::message::MessageType; use crate::arg::{Array, Variant, Dict, Iter, ArgType, TypeMismatchError, RefArg, cast}; use std::collections::HashMap; #[test] fn refarg() { let c = Channel::get_private(BusType::Session).unwrap(); let m = Message::new_method_call(c.unique_name().unwrap(), "/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<&dyn 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 &dyn RefArg, &vstr as &dyn 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 &dyn RefArg, &1.5f64 as &dyn RefArg]); c.send(m).unwrap(); loop { if let Some(m) = c.blocking_pop_message(std::time::Duration::from_millis(1000)).unwrap() { if m.msg_type() != MessageType::MethodCall { continue; } 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 = Channel::get_private(BusType::Session).unwrap(); let m = Message::new_method_call(c.unique_name().unwrap(), "/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(); loop { if let Some(m) = c.blocking_pop_message(std::time::Duration::from_millis(1000)).unwrap() { if m.msg_type() != MessageType::MethodCall { continue; } 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; } } } #[test] fn cast_vecs() { let c = Channel::get_private(BusType::Session).unwrap(); let m = Message::new_method_call(c.unique_name().unwrap(), "/hello", "com.example.hello", "Hello").unwrap(); macro_rules! append_array { ($m:expr, $t:ty) => { $m.append1(Variant(&Array::<&$t, _>::new(&vec![Default::default()]))); }; } let m = append_array!(m, bool); let m = append_array!(m, u8); let m = append_array!(m, u16); let m = append_array!(m, i16); let m = append_array!(m, u32); let m = append_array!(m, i32); let m = append_array!(m, f64); let m = append_array!(m, String); c.send(m).unwrap(); loop { if let Some(m) = c.blocking_pop_message(std::time::Duration::from_millis(1000)).unwrap() { if m.msg_type() != MessageType::MethodCall { continue; } let mut i = m.iter_init(); let mut i2 = m.iter_init(); macro_rules! check_array { ($t:ty) => { let array: Variant> = i.read().unwrap(); assert_eq!( cast::>(&(array.0)), Some(&vec![Default::default()]), "a variant containing an array of of {0} should be castable to a Vec<{0}>", std::any::type_name::<$t>() ); let refarg = i2.get_refarg().unwrap(); println!("refarg {:?}", refarg); let st_inner = refarg.as_static_inner(0).unwrap(); println!("st_inner {:?}", st_inner); i2.next(); assert_eq!(cast::>(st_inner), Some(&vec![Default::default()])); }; } check_array!(bool); check_array!(u8); check_array!(u16); check_array!(i16); check_array!(u32); check_array!(i32); check_array!(f64); check_array!(String); break; } } } } dbus-0.9.0/src/arg/variantstruct_impl.rs010066400017500001750000000162301373106244300165050ustar 00000000000000use super::*; use crate::Signature; use std::any; use std::collections::VecDeque; #[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(Variant) } } 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("v\0") } } } impl Append for Variant { fn append_by_ref(&self, i: &mut IterAppend) { let z = &self.0; i.append_container(ArgType::Variant, Some(T::signature().as_cstr()), |s| z.append_by_ref(s)); } } impl Append for Variant> { fn append_by_ref(&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(Variant)) } } impl<'a> Get<'a> for Variant> { fn get(i: &mut Iter<'a>) -> Option>> { i.recurse(ArgType::Variant).map(Variant) } } /* 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("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) -> &dyn any::Any where T: 'static { self } #[inline] fn as_any_mut(&mut self) -> &mut dyn 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: &dyn RefArg = &self.0; Some(Box::new(iter::once(z))) } #[inline] fn box_clone(&self) -> Box { Box::new(Variant(self.0.box_clone())) } #[inline] fn as_static_inner(&self, index: usize) -> Option<&(dyn RefArg + 'static)> where Self: 'static { if index == 0 { Some(&self.0) } else { None } } } 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_by_ref(&self, i: &mut IterAppend) { let ( $($n,)*) = self; i.append_container(ArgType::Struct, None, |s| { $( $n.append_by_ref(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) -> &dyn any::Any where Self: 'static { self } fn as_any_mut(&mut self) -> &mut dyn any::Any where Self: 'static { self } fn as_iter<'a>(&'a self) -> Option + 'a>> { let &( $(ref $n,)*) = self; let v = vec!( $( $n as &dyn RefArg, )* ); Some(Box::new(v.into_iter())) } fn as_static_inner(&self, index: usize) -> Option<&(dyn RefArg + 'static)> where Self: 'static { let &( $(ref $n,)*) = self; let arr = [ $($n as &dyn RefArg,)*]; arr.get(index).map(|x| *x) } #[inline] fn box_clone(&self) -> Box { let &( $(ref $n,)*) = self; let mut z = VecDeque::new(); $( z.push_back($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 VecDeque> { 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) -> &dyn any::Any where Self: 'static { self } #[inline] fn as_any_mut(&mut self) -> &mut dyn any::Any where Self: 'static { self } fn as_iter<'a>(&'a self) -> Option + 'a>> { Some(Box::new(self.iter().map(|b| &**b))) } #[inline] fn as_static_inner(&self, index: usize) -> Option<&(dyn RefArg + 'static)> where Self: 'static { self.get(index).map(|x| x as &dyn RefArg) } #[inline] fn box_clone(&self) -> Box { let t: VecDeque> = self.iter().map(|x| x.box_clone()).collect(); Box::new(t) } } dbus-0.9.0/src/blocking/generated_org_freedesktop_dbus.rs010066400017500001750000000202701372736331200220120ustar 00000000000000// This code was autogenerated with `dbus-codegen-rust -d org.freedesktop.DBus -p /org/freedesktop/DBus -m none -c blocking -g -i org.freedesktop. --dbuscrate crate --interfaces DBus -o dbus/src/blocking/generated_org_freedesktop_dbus.rs`, see https://github.com/diwic/dbus-rs use crate as dbus; #[allow(unused_imports)] use crate::arg; use crate::blocking; pub trait DBus { fn hello(&self) -> Result; fn request_name(&self, arg0: &str, arg1: u32) -> Result; fn release_name(&self, arg0: &str) -> Result; fn start_service_by_name(&self, arg0: &str, arg1: u32) -> Result; fn update_activation_environment(&self, arg0: ::std::collections::HashMap<&str, &str>) -> Result<(), dbus::Error>; fn name_has_owner(&self, arg0: &str) -> Result; fn list_names(&self) -> Result, dbus::Error>; fn list_activatable_names(&self) -> Result, dbus::Error>; fn add_match(&self, arg0: &str) -> Result<(), dbus::Error>; fn remove_match(&self, arg0: &str) -> Result<(), dbus::Error>; fn get_name_owner(&self, arg0: &str) -> Result; fn list_queued_owners(&self, arg0: &str) -> Result, dbus::Error>; fn get_connection_unix_user(&self, arg0: &str) -> Result; fn get_connection_unix_process_id(&self, arg0: &str) -> Result; fn get_adt_audit_session_data(&self, arg0: &str) -> Result, dbus::Error>; fn get_connection_selinux_security_context(&self, arg0: &str) -> Result, dbus::Error>; fn reload_config(&self) -> Result<(), dbus::Error>; fn get_id(&self) -> Result; fn get_connection_credentials(&self, arg0: &str) -> Result<::std::collections::HashMap>>, dbus::Error>; fn features(&self) -> Result, dbus::Error>; fn interfaces(&self) -> Result, dbus::Error>; } impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref> DBus for blocking::Proxy<'a, C> { fn hello(&self) -> Result { self.method_call("org.freedesktop.DBus", "Hello", ()) .and_then(|r: (String, )| Ok(r.0, )) } fn request_name(&self, arg0: &str, arg1: u32) -> Result { self.method_call("org.freedesktop.DBus", "RequestName", (arg0, arg1, )) .and_then(|r: (u32, )| Ok(r.0, )) } fn release_name(&self, arg0: &str) -> Result { self.method_call("org.freedesktop.DBus", "ReleaseName", (arg0, )) .and_then(|r: (u32, )| Ok(r.0, )) } fn start_service_by_name(&self, arg0: &str, arg1: u32) -> Result { self.method_call("org.freedesktop.DBus", "StartServiceByName", (arg0, arg1, )) .and_then(|r: (u32, )| Ok(r.0, )) } fn update_activation_environment(&self, arg0: ::std::collections::HashMap<&str, &str>) -> Result<(), dbus::Error> { self.method_call("org.freedesktop.DBus", "UpdateActivationEnvironment", (arg0, )) } fn name_has_owner(&self, arg0: &str) -> Result { self.method_call("org.freedesktop.DBus", "NameHasOwner", (arg0, )) .and_then(|r: (bool, )| Ok(r.0, )) } fn list_names(&self) -> Result, dbus::Error> { self.method_call("org.freedesktop.DBus", "ListNames", ()) .and_then(|r: (Vec, )| Ok(r.0, )) } fn list_activatable_names(&self) -> Result, dbus::Error> { self.method_call("org.freedesktop.DBus", "ListActivatableNames", ()) .and_then(|r: (Vec, )| Ok(r.0, )) } fn add_match(&self, arg0: &str) -> Result<(), dbus::Error> { self.method_call("org.freedesktop.DBus", "AddMatch", (arg0, )) } fn remove_match(&self, arg0: &str) -> Result<(), dbus::Error> { self.method_call("org.freedesktop.DBus", "RemoveMatch", (arg0, )) } fn get_name_owner(&self, arg0: &str) -> Result { self.method_call("org.freedesktop.DBus", "GetNameOwner", (arg0, )) .and_then(|r: (String, )| Ok(r.0, )) } fn list_queued_owners(&self, arg0: &str) -> Result, dbus::Error> { self.method_call("org.freedesktop.DBus", "ListQueuedOwners", (arg0, )) .and_then(|r: (Vec, )| Ok(r.0, )) } fn get_connection_unix_user(&self, arg0: &str) -> Result { self.method_call("org.freedesktop.DBus", "GetConnectionUnixUser", (arg0, )) .and_then(|r: (u32, )| Ok(r.0, )) } fn get_connection_unix_process_id(&self, arg0: &str) -> Result { self.method_call("org.freedesktop.DBus", "GetConnectionUnixProcessID", (arg0, )) .and_then(|r: (u32, )| Ok(r.0, )) } fn get_adt_audit_session_data(&self, arg0: &str) -> Result, dbus::Error> { self.method_call("org.freedesktop.DBus", "GetAdtAuditSessionData", (arg0, )) .and_then(|r: (Vec, )| Ok(r.0, )) } fn get_connection_selinux_security_context(&self, arg0: &str) -> Result, dbus::Error> { self.method_call("org.freedesktop.DBus", "GetConnectionSELinuxSecurityContext", (arg0, )) .and_then(|r: (Vec, )| Ok(r.0, )) } fn reload_config(&self) -> Result<(), dbus::Error> { self.method_call("org.freedesktop.DBus", "ReloadConfig", ()) } fn get_id(&self) -> Result { self.method_call("org.freedesktop.DBus", "GetId", ()) .and_then(|r: (String, )| Ok(r.0, )) } fn get_connection_credentials(&self, arg0: &str) -> Result<::std::collections::HashMap>>, dbus::Error> { self.method_call("org.freedesktop.DBus", "GetConnectionCredentials", (arg0, )) .and_then(|r: (::std::collections::HashMap>>, )| Ok(r.0, )) } fn features(&self) -> Result, dbus::Error> { ::get(&self, "org.freedesktop.DBus", "Features") } fn interfaces(&self) -> Result, dbus::Error> { ::get(&self, "org.freedesktop.DBus", "Interfaces") } } #[derive(Debug)] pub struct DBusNameOwnerChanged { pub arg0: String, pub arg1: String, pub arg2: String, } impl arg::AppendAll for DBusNameOwnerChanged { fn append(&self, i: &mut arg::IterAppend) { arg::RefArg::append(&self.arg0, i); arg::RefArg::append(&self.arg1, i); arg::RefArg::append(&self.arg2, i); } } impl arg::ReadAll for DBusNameOwnerChanged { fn read(i: &mut arg::Iter) -> Result { Ok(DBusNameOwnerChanged { arg0: i.read()?, arg1: i.read()?, arg2: i.read()?, }) } } impl dbus::message::SignalArgs for DBusNameOwnerChanged { const NAME: &'static str = "NameOwnerChanged"; const INTERFACE: &'static str = "org.freedesktop.DBus"; } #[derive(Debug)] pub struct DBusNameLost { pub arg0: String, } impl arg::AppendAll for DBusNameLost { fn append(&self, i: &mut arg::IterAppend) { arg::RefArg::append(&self.arg0, i); } } impl arg::ReadAll for DBusNameLost { fn read(i: &mut arg::Iter) -> Result { Ok(DBusNameLost { arg0: i.read()?, }) } } impl dbus::message::SignalArgs for DBusNameLost { const NAME: &'static str = "NameLost"; const INTERFACE: &'static str = "org.freedesktop.DBus"; } #[derive(Debug)] pub struct DBusNameAcquired { pub arg0: String, } impl arg::AppendAll for DBusNameAcquired { fn append(&self, i: &mut arg::IterAppend) { arg::RefArg::append(&self.arg0, i); } } impl arg::ReadAll for DBusNameAcquired { fn read(i: &mut arg::Iter) -> Result { Ok(DBusNameAcquired { arg0: i.read()?, }) } } impl dbus::message::SignalArgs for DBusNameAcquired { const NAME: &'static str = "NameAcquired"; const INTERFACE: &'static str = "org.freedesktop.DBus"; } dbus-0.9.0/src/blocking/generated_org_freedesktop_standard_interfaces.rs010066400017500001750000000144251372736331200250650ustar 00000000000000// This code was autogenerated with `dbus-codegen-rust -m none -c blocking -g -i org.freedesktop.DBus --dbuscrate crate --file dbus-codegen/data/standard_interfaces.xml -o dbus/src/blocking/generated_org_freedesktop_standard_interfaces.rs`, see https://github.com/diwic/dbus-rs use crate as dbus; #[allow(unused_imports)] use crate::arg; use crate::blocking; pub trait Properties { fn get arg::Get<'b> + 'static>(&self, interface_name: &str, property_name: &str) -> Result; fn get_all(&self, interface_name: &str) -> Result<::std::collections::HashMap>>, dbus::Error>; fn set(&self, interface_name: &str, property_name: &str, value: I2) -> Result<(), dbus::Error>; } impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref> Properties for blocking::Proxy<'a, C> { fn get arg::Get<'b> + 'static>(&self, interface_name: &str, property_name: &str) -> Result { self.method_call("org.freedesktop.DBus.Properties", "Get", (interface_name, property_name, )) .and_then(|r: (arg::Variant, )| Ok((r.0).0, )) } fn get_all(&self, interface_name: &str) -> Result<::std::collections::HashMap>>, dbus::Error> { self.method_call("org.freedesktop.DBus.Properties", "GetAll", (interface_name, )) .and_then(|r: (::std::collections::HashMap>>, )| Ok(r.0, )) } fn set(&self, interface_name: &str, property_name: &str, value: I2) -> Result<(), dbus::Error> { self.method_call("org.freedesktop.DBus.Properties", "Set", (interface_name, property_name, arg::Variant(value), )) } } #[derive(Debug)] pub struct PropertiesPropertiesChanged { pub interface_name: String, pub changed_properties: ::std::collections::HashMap>>, pub invalidated_properties: Vec, } impl arg::AppendAll for PropertiesPropertiesChanged { fn append(&self, i: &mut arg::IterAppend) { arg::RefArg::append(&self.interface_name, i); arg::RefArg::append(&self.changed_properties, i); arg::RefArg::append(&self.invalidated_properties, i); } } impl arg::ReadAll for PropertiesPropertiesChanged { fn read(i: &mut arg::Iter) -> Result { Ok(PropertiesPropertiesChanged { interface_name: i.read()?, changed_properties: i.read()?, invalidated_properties: i.read()?, }) } } impl dbus::message::SignalArgs for PropertiesPropertiesChanged { const NAME: &'static str = "PropertiesChanged"; const INTERFACE: &'static str = "org.freedesktop.DBus.Properties"; } pub trait Introspectable { fn introspect(&self) -> Result; } impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref> Introspectable for blocking::Proxy<'a, C> { fn introspect(&self) -> Result { self.method_call("org.freedesktop.DBus.Introspectable", "Introspect", ()) .and_then(|r: (String, )| Ok(r.0, )) } } pub trait Peer { fn ping(&self) -> Result<(), dbus::Error>; fn get_machine_id(&self) -> Result; } impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref> Peer for blocking::Proxy<'a, C> { fn ping(&self) -> Result<(), dbus::Error> { self.method_call("org.freedesktop.DBus.Peer", "Ping", ()) } fn get_machine_id(&self) -> Result { self.method_call("org.freedesktop.DBus.Peer", "GetMachineId", ()) .and_then(|r: (String, )| Ok(r.0, )) } } pub trait ObjectManager { fn get_managed_objects(&self) -> Result<::std::collections::HashMap, ::std::collections::HashMap>>>>, dbus::Error>; } impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref> ObjectManager for blocking::Proxy<'a, C> { fn get_managed_objects(&self) -> Result<::std::collections::HashMap, ::std::collections::HashMap>>>>, dbus::Error> { self.method_call("org.freedesktop.DBus.ObjectManager", "GetManagedObjects", ()) .and_then(|r: (::std::collections::HashMap, ::std::collections::HashMap>>>>, )| Ok(r.0, )) } } #[derive(Debug)] pub struct ObjectManagerInterfacesAdded { pub object: dbus::Path<'static>, pub interfaces: ::std::collections::HashMap>>>, } impl arg::AppendAll for ObjectManagerInterfacesAdded { fn append(&self, i: &mut arg::IterAppend) { arg::RefArg::append(&self.object, i); arg::RefArg::append(&self.interfaces, i); } } impl arg::ReadAll for ObjectManagerInterfacesAdded { fn read(i: &mut arg::Iter) -> Result { Ok(ObjectManagerInterfacesAdded { object: i.read()?, interfaces: i.read()?, }) } } impl dbus::message::SignalArgs for ObjectManagerInterfacesAdded { const NAME: &'static str = "InterfacesAdded"; const INTERFACE: &'static str = "org.freedesktop.DBus.ObjectManager"; } #[derive(Debug)] pub struct ObjectManagerInterfacesRemoved { pub object: dbus::Path<'static>, pub interfaces: Vec, } impl arg::AppendAll for ObjectManagerInterfacesRemoved { fn append(&self, i: &mut arg::IterAppend) { arg::RefArg::append(&self.object, i); arg::RefArg::append(&self.interfaces, i); } } impl arg::ReadAll for ObjectManagerInterfacesRemoved { fn read(i: &mut arg::Iter) -> Result { Ok(ObjectManagerInterfacesRemoved { object: i.read()?, interfaces: i.read()?, }) } } impl dbus::message::SignalArgs for ObjectManagerInterfacesRemoved { const NAME: &'static str = "InterfacesRemoved"; const INTERFACE: &'static str = "org.freedesktop.DBus.ObjectManager"; } dbus-0.9.0/src/blocking.rs010066400017500001750000000400571372736425000136040ustar 00000000000000//! Connections and proxies that make blocking method calls. use crate::strings::{BusName, Path, Interface, Member}; use crate::arg::{AppendAll, ReadAll, IterAppend}; use crate::{channel, Error, Message}; use crate::message::{MatchRule, SignalArgs}; use crate::channel::{Channel, BusType, Token}; use std::{cell::RefCell, time::Duration, sync::Mutex}; use crate::filters::Filters; #[allow(missing_docs)] mod generated_org_freedesktop_standard_interfaces; mod generated_org_freedesktop_dbus; /// 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 was created by dbus-codegen. pub mod stdintf { #[allow(missing_docs)] pub mod org_freedesktop_dbus { pub use super::super::generated_org_freedesktop_standard_interfaces::*; #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum RequestNameReply { PrimaryOwner = 1, InQueue = 2, Exists = 3, AlreadyOwner = 4, } #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum ReleaseNameReply { Released = 1, NonExistent = 2, NotOwner = 3, } pub (crate) fn request_name(s: &S, name: &str, allow_replacement: bool, replace_existing: bool, do_not_queue: bool) -> Result { let flags: u32 = if allow_replacement { 1 } else { 0 } + if replace_existing { 2 } else { 0 } + if do_not_queue { 4 } else { 0 }; let proxy = super::proxy(s); use super::org_freedesktop::DBus; let r = proxy.request_name(name, flags)?; use RequestNameReply::*; let all = [PrimaryOwner, InQueue, Exists, AlreadyOwner]; all.iter().find(|x| **x as u32 == r).copied().ok_or_else(|| crate::Error::new_failed("Invalid reply from DBus server") ) } pub (crate) fn release_name(s: &S, name: &str) -> Result { let proxy = super::proxy(s); use super::org_freedesktop::DBus; let r = proxy.release_name(name)?; use ReleaseNameReply::*; let all = [Released, NonExistent, NotOwner]; all.iter().find(|x| **x as u32 == r).copied().ok_or_else(|| crate::Error::new_failed("Invalid reply from DBus server") ) } } // Not public yet, because of lack of named arguments pub (super) mod org_freedesktop { pub(crate) use super::super::generated_org_freedesktop_dbus::*; } pub (crate) fn proxy(c: C) -> crate::blocking::Proxy<'static, C> { super::Proxy::new("org.freedesktop.DBus", "/org/freedesktop/DBus", std::time::Duration::from_millis(5000), c) } } /// A connection to D-Bus, thread local + non-async version pub struct LocalConnection { channel: Channel, filters: RefCell>, } /// A connection to D-Bus, non-async version where callbacks are Send but not Sync. pub struct Connection { channel: Channel, filters: RefCell>, } /// A connection to D-Bus, Send + Sync + non-async version pub struct SyncConnection { channel: Channel, filters: Mutex> } use crate::blocking::stdintf::org_freedesktop_dbus; macro_rules! connimpl { ($c: ident, $cb: ident $(, $ss:tt)*) => { type $cb = Box bool $(+ $ss)* + 'static>; impl $c { /// Create a new connection to the session bus. pub fn new_session() -> Result { Channel::get_private(BusType::Session).map(From::from) } /// Create a new connection to the system-wide bus. pub fn new_system() -> Result { Channel::get_private(BusType::System).map(From::from) } /// Get the connection's unique name. /// /// It's usually something like ":1.54" pub fn unique_name(&self) -> BusName { self.channel.unique_name().unwrap().into() } /// Create a convenience struct for easier calling of many methods on the same destination and path. pub fn with_proxy<'a, 'b, D: Into>, P: Into>>(&'b self, dest: D, path: P, timeout: Duration) -> Proxy<'a, &'b Self> { Proxy { connection: self, destination: dest.into(), path: path.into(), timeout } } /// Request a name on the D-Bus. /// /// For detailed information on the flags and return values, see the libdbus documentation. pub fn request_name<'a, N: Into>>(&self, name: N, allow_replacement: bool, replace_existing: bool, do_not_queue: bool) -> Result { org_freedesktop_dbus::request_name(&self.channel, &name.into(), allow_replacement, replace_existing, do_not_queue) } /// Release a previously requested name on the D-Bus. pub fn release_name<'a, N: Into>>(&self, name: N) -> Result { org_freedesktop_dbus::release_name(&self.channel, &name.into()) } /// Adds a new match to the connection, and sets up a callback when this message arrives. /// /// The returned value can be used to remove the match. The match is also removed if the callback /// returns "false". pub fn add_match(&self, match_rule: MatchRule<'static>, f: F) -> Result where F: FnMut(S, &Self, &Message) -> bool $(+ $ss)* + 'static { let m = match_rule.match_str(); self.add_match_no_cb(&m)?; use channel::MatchingReceiver; Ok(self.start_receive(match_rule, MakeSignal::make(f, m))) } /// Adds a new match to the connection, without setting up a callback when this message arrives. pub fn add_match_no_cb(&self, match_str: &str) -> Result<(), Error> { use crate::blocking::stdintf::org_freedesktop::DBus; let proxy = stdintf::proxy(self); proxy.add_match(match_str) } /// Removes a match from the connection, without removing any callbacks. pub fn remove_match_no_cb(&self, match_str: &str) -> Result<(), Error> { use crate::blocking::stdintf::org_freedesktop::DBus; let proxy = stdintf::proxy(self); proxy.remove_match(match_str) } /// Removes a previously added match and callback from the connection. pub fn remove_match(&self, id: Token) -> Result<(), Error> { use channel::MatchingReceiver; let (mr, _) = self.stop_receive(id).ok_or_else(|| Error::new_failed("No match with that id found"))?; self.remove_match_no_cb(&mr.match_str()) } /// Tries to handle an incoming message if there is one. If there isn't one, /// it will wait up to timeout /// /// This method only takes "&self" instead of "&mut self", but it is a logic error to call /// it recursively and might lead to panics or deadlocks. pub fn process(&self, timeout: Duration) -> Result { if let Some(msg) = self.channel.blocking_pop_message(timeout)? { let ff = self.filters_mut().remove_matching(&msg); if let Some(mut ff) = ff { if ff.2(msg, self) { self.filters_mut().insert(ff); } } else if let Some(reply) = crate::channel::default_reply(&msg) { let _ = self.channel.send(reply); } Ok(true) } else { Ok(false) } } /// The channel for this connection pub fn channel(&self) -> &Channel { &self.channel } } impl BlockingSender for $c { fn send_with_reply_and_block(&self, msg: Message, timeout: Duration) -> Result { self.channel.send_with_reply_and_block(msg, timeout) } } impl From for $c { fn from(channel: Channel) -> $c { $c { channel, filters: Default::default(), } } } impl channel::Sender for $c { fn send(&self, msg: Message) -> Result { self.channel.send(msg) } } impl bool $(+ $ss)* + 'static> MakeSignal<$cb, S, $c> for F { fn make(mut self, mstr: String) -> $cb { Box::new(move |msg: Message, conn: &$c| { if let Ok(s) = S::read(&mut msg.iter_init()) { if self(s, conn, &msg) { return true }; let proxy = stdintf::proxy(conn); use crate::blocking::stdintf::org_freedesktop::DBus; let _ = proxy.remove_match(&mstr); false } else { true } }) } } impl channel::MatchingReceiver for $c { type F = $cb; fn start_receive(&self, m: MatchRule<'static>, f: Self::F) -> Token { self.filters_mut().add(m, f) } fn stop_receive(&self, id: Token) -> Option<(MatchRule<'static>, Self::F)> { self.filters_mut().remove(id) } } } } connimpl!(Connection, FilterCb, Send); connimpl!(LocalConnection, LocalFilterCb); connimpl!(SyncConnection, SyncFilterCb, Send, Sync); impl Connection { fn filters_mut(&self) -> std::cell::RefMut> { self.filters.borrow_mut() } } impl LocalConnection { fn filters_mut(&self) -> std::cell::RefMut> { self.filters.borrow_mut() } } impl SyncConnection { fn filters_mut(&self) -> std::sync::MutexGuard> { self.filters.lock().unwrap() } } /// Abstraction over different connections pub trait BlockingSender { /// Sends a message over the D-Bus and blocks, waiting for a reply or a timeout. This is used for method calls. /// /// Note: In case of an error reply, this is returned as an Err(), not as a Ok(Message) with the error type. fn send_with_reply_and_block(&self, msg: Message, timeout: Duration) -> Result; } impl BlockingSender for Channel { fn send_with_reply_and_block(&self, msg: Message, timeout: Duration) -> Result { Channel::send_with_reply_and_block(self, msg, timeout) } } /// A struct that wraps a connection, destination and path. /// /// A D-Bus "Proxy" is a client-side object that corresponds to a remote object on the server side. /// Calling methods on the proxy object calls methods on the remote object. /// Read more in the [D-Bus tutorial](https://dbus.freedesktop.org/doc/dbus-tutorial.html#proxies) #[derive(Clone, Debug)] pub struct Proxy<'a, C> { /// Destination, i e what D-Bus service you're communicating with pub destination: BusName<'a>, /// Object path on the destination pub path: Path<'a>, /// Timeout for method calls pub timeout: Duration, /// Some way to send and/or receive messages, either blocking or non-blocking. pub connection: C, } impl<'a, C> Proxy<'a, C> { /// Creates a new proxy struct. pub fn new>, P: Into>>(dest: D, path: P, timeout: Duration, connection: C) -> Self { Proxy { destination: dest.into(), path: path.into(), timeout, connection } } } impl<'a, T: BlockingSender, C: std::ops::Deref> Proxy<'a, C> { // impl<'a, S: std::convert::AsRef> Proxy<'a, S> { /// Make a method call using typed input and output arguments, then block waiting for a reply. /// /// # Example /// /// ``` /// use dbus::blocking::{Connection, Proxy}; /// /// let conn = Connection::new_session()?; /// let proxy = Proxy::new("org.freedesktop.DBus", "/", std::time::Duration::from_millis(5000), &conn); /// let (has_owner,): (bool,) = proxy.method_call("org.freedesktop.DBus", "NameHasOwner", ("dummy.name.without.owner",))?; /// assert_eq!(has_owner, false); /// # Ok::<(), Box>(()) /// ``` pub fn method_call<'i, 'm, R: ReadAll, A: AppendAll, I: Into>, M: Into>>(&self, i: I, m: M, args: A) -> Result { let mut msg = Message::method_call(&self.destination, &self.path, &i.into(), &m.into()); args.append(&mut IterAppend::new(&mut msg)); let r = self.connection.send_with_reply_and_block(msg, self.timeout)?; Ok(R::read(&mut r.iter_init())?) } /// Starts matching incoming messages on this destination and path. /// /// For matching signals, match_signal might be more convenient. /// /// The match rule will be modified to include this path and destination only. /// /// If call_add_match is true, will notify the D-Bus server that matching should start. pub fn match_start(&self, mut mr: MatchRule<'static>, call_add_match: bool, f: ::F) -> Result where T: channel::MatchingReceiver { mr.path = Some(self.path.clone().into_static()); mr.sender = Some(self.destination.clone().into_static()); if call_add_match { use crate::blocking::stdintf::org_freedesktop::DBus; let proxy = stdintf::proxy(&*self.connection); proxy.add_match(&mr.match_str())?; } Ok(self.connection.start_receive(mr, f)) } /// Stops matching a signal added with match_start or match_signal. /// /// If call_remove_match is true, will notify the D-Bus server that matching should stop, /// this should be true in case match_signal was used. pub fn match_stop(&self, id: Token, call_remove_match: bool) -> Result<(), Error> where T: channel::MatchingReceiver { if let Some((mr, _)) = self.connection.stop_receive(id) { if call_remove_match { use crate::blocking::stdintf::org_freedesktop::DBus; let proxy = stdintf::proxy(&*self.connection); proxy.remove_match(&mr.match_str())?; } } Ok(()) } /// Sets up an incoming signal match, that calls the supplied callback every time the signal is received. /// /// The returned value can be used to remove the match. The match is also removed if the callback /// returns "false". pub fn match_signal(&self, f: F) -> Result where T: channel::MatchingReceiver, F: MakeSignal<::F, S, T> { let mr = S::match_rule(Some(&self.destination), Some(&self.path)).static_clone(); let ff = f.make(mr.match_str()); self.match_start(mr, true, ff) } } /// Internal helper trait pub trait MakeSignal { /// Internal helper trait fn make(self, mstr: String) -> G; } #[test] fn test_add_match() { use self::stdintf::org_freedesktop_dbus::PropertiesPropertiesChanged as Ppc; let c = Connection::new_session().unwrap(); let x = c.add_match(Ppc::match_rule(None, None), |_: Ppc, _, _| { true }).unwrap(); c.remove_match(x).unwrap(); } #[test] fn test_conn_send_sync() { fn is_send(_: &T) {} fn is_sync(_: &T) {} let c = SyncConnection::new_session().unwrap(); is_send(&c); is_sync(&c); let c = Connection::new_session().unwrap(); is_send(&c); } #[test] fn test_peer() { let c = Connection::new_session().unwrap(); let c_name = c.unique_name().into_static(); use std::sync::Arc; let done = Arc::new(false); let d2 = done.clone(); let j = std::thread::spawn(move || { let c2 = Connection::new_session().unwrap(); let proxy = c2.with_proxy(c_name, "/", Duration::from_secs(5)); let (s2,): (String,) = proxy.method_call("org.freedesktop.DBus.Peer", "GetMachineId", ()).unwrap(); println!("{}", s2); assert_eq!(Arc::strong_count(&d2), 2); s2 }); assert_eq!(Arc::strong_count(&done), 2); for _ in 0..30 { c.process(Duration::from_millis(100)).unwrap(); if Arc::strong_count(&done) < 2 { break; } } let s2 = j.join().unwrap(); let proxy = c.with_proxy("org.a11y.Bus", "/org/a11y/bus", Duration::from_secs(5)); let (s1,): (String,) = proxy.method_call("org.freedesktop.DBus.Peer", "GetMachineId", ()).unwrap(); assert_eq!(s1, s2); } dbus-0.9.0/src/channel/dispatcher.rs010066400017500001750000000115261364707574700155640ustar 00000000000000use 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 => {}, } } /// 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.9.0/src/channel.rs010066400017500001750000000461731367705423200134310ustar 00000000000000//! Connection base / building block. //! //! Contains some helper structs and traits common to all Connection types.- use crate::{Error, Message, to_c_str, c_str_to_slice, MessageType}; use std::{str, time::Duration, collections::HashMap}; use std::sync::{Mutex, atomic::AtomicU8, atomic::Ordering}; use std::ffi::CStr; use std::os::raw::{c_void, c_int}; use crate::message::MatchRule; use std::os::unix::io::RawFd; #[derive(Debug)] struct ConnHandle(*mut ffi::DBusConnection, bool); unsafe impl Send for ConnHandle {} unsafe impl Sync for ConnHandle {} impl Drop for ConnHandle { fn drop(&mut self) { if self.1 { unsafe { ffi::dbus_connection_close(self.0); ffi::dbus_connection_unref(self.0); }} } } #[derive(Debug, Eq, PartialEq, Hash)] struct WatchHandle(*mut ffi::DBusWatch); unsafe impl Send for WatchHandle {} unsafe impl Sync for WatchHandle {} /// Which bus to connect to #[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)] pub enum BusType { /// The Session bus - local to every logged in session Session = ffi::DBusBusType::Session as isize, /// The system wide bus System = ffi::DBusBusType::System as isize, /// The bus that started us, if any Starter = ffi::DBusBusType::Starter as isize, } #[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 { /// File descriptor pub fd: RawFd, /// True if wakeup should happen when the file descriptor is ready for reading pub read: bool, /// True if wakeup should happen when the file descriptor is ready for writing pub write: bool, } impl Watch { unsafe fn from_raw_enabled(watch: *mut ffi::DBusWatch) -> (Self, bool) { 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; let flags = ffi::dbus_watch_get_flags(watch); use std::os::raw::c_uint; w.read = (flags & ffi::DBUS_WATCH_READABLE as c_uint) != 0; w.write = (flags & ffi::DBUS_WATCH_WRITABLE as c_uint) != 0; (w, enabled) } } /// This struct must be boxed as it is called from D-Bus callbacks! #[derive(Debug)] struct WatchMap { conn: ConnHandle, list: Mutex>, current_rw: AtomicU8, current_fd: Option, } fn calc_rw(list: &HashMap) -> u8 { let mut r = 0; for (w, b) in list.values() { if *b && w.read { r |= 1; } if *b && w.write { r |= 2; } } r } impl WatchMap { fn new(conn: ConnHandle) -> Box { extern "C" fn add_watch_cb(watch: *mut ffi::DBusWatch, data: *mut c_void) -> u32 { unsafe { let wm: &WatchMap = &*(data as *mut _); wm.list.lock().unwrap().insert(WatchHandle(watch), Watch::from_raw_enabled(watch)); 1 }} extern "C" fn remove_watch_cb(watch: *mut ffi::DBusWatch, data: *mut c_void) { unsafe { let wm: &WatchMap = &*(data as *mut _); wm.list.lock().unwrap().remove(&WatchHandle(watch)); }} extern "C" fn toggled_watch_cb(watch: *mut ffi::DBusWatch, data: *mut c_void) { unsafe { let wm: &WatchMap = &*(data as *mut _); let mut list = wm.list.lock().unwrap(); let (_, ref mut b) = list.get_mut(&WatchHandle(watch)).unwrap(); *b = ffi::dbus_watch_get_enabled(watch) != 0; wm.current_rw.store(calc_rw(&list), Ordering::Release); }} let mut wm = Box::new(WatchMap { conn, list: Default::default(), current_rw: Default::default(), current_fd: None }); let wptr: &WatchMap = &wm; if unsafe { ffi::dbus_connection_set_watch_functions(wm.conn.0, Some(add_watch_cb), Some(remove_watch_cb), Some(toggled_watch_cb), wptr as *const _ as *mut _, None) } == 0 { panic!("Cannot enable watch tracking (OOM?)") } { let list = wm.list.lock().unwrap(); wm.current_rw.store(calc_rw(&list), Ordering::Release); // This will never panic in practice, see https://lists.freedesktop.org/archives/dbus/2019-July/017786.html for (w, _) in list.values() { if let Some(ref fd) = &wm.current_fd { assert_eq!(*fd, w.fd); } else { wm.current_fd = Some(w.fd); } } } wm } } impl Drop for WatchMap { fn drop(&mut self) { let wptr: &WatchMap = &self; if unsafe { ffi::dbus_connection_set_watch_functions(self.conn.0, None, None, None, wptr as *const _ as *mut _, None) } == 0 { panic!("Cannot disable watch tracking (OOM?)") } } } /// Low-level connection - handles read/write to the socket /// /// You probably do not need to worry about this as you would typically /// use the various blocking and non-blocking "Connection" structs instead. /// /// This version avoids dbus_connection_dispatch, and thus avoids /// callbacks from that function. Instead the same functionality /// is implemented in the various blocking and non-blocking "Connection" components. /// /// Blocking operations are 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. #[derive(Debug)] pub struct Channel { handle: ConnHandle, watchmap: Option>, } impl Drop for Channel { fn drop(&mut self) { self.set_watch_enabled(false); // Make sure "watchmap" is destroyed before "handle" is } } impl Channel { #[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, true); /* 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 = Channel { handle, watchmap: None }; 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 b = match bus { BusType::Session => ffi::DBusBusType::Session, BusType::System => ffi::DBusBusType::System, BusType::Starter => ffi::DBusBusType::Starter, }; let conn = unsafe { ffi::dbus_bus_get_private(b, e.get_mut()) }; if conn.is_null() { 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.is_null() { 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.is_null() { return None; } let s = unsafe { CStr::from_ptr(c) }; str::from_utf8(s.to_bytes()).ok() } /// Puts a message into libdbus out queue, and tries to send it. /// /// Returns a serial number than can be used to match against a reply. /// /// Note: usually the message is sent when this call happens, but in /// case internal D-Bus buffers are full, it will be left in the out queue. /// Call "flush" or "read_write" to retry flushing the out queue. 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) } /// Sends a message over the D-Bus and waits for a reply. This is used for method calls. /// /// Blocking: until a reply is received or the timeout expires. /// /// Note: In case of an error reply, this is returned as an Err(), not as a Ok(Message) with the error type. /// /// Note: In case pop_message and send_with_reply_and_block is called in parallel from different threads, /// they might race to retreive the reply message from the internal queue. pub fn send_with_reply_and_block(&self, msg: Message, timeout: Duration) -> Result { let mut e = Error::empty(); let response = unsafe { ffi::dbus_connection_send_with_reply_and_block(self.conn(), msg.ptr(), timeout.as_millis() as c_int, e.get_mut()) }; if response.is_null() { return Err(e); } Ok(Message::from_ptr(response, false)) } /// 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, or forever if timeout is None. /// For non-blocking behaviour, set timeout to Some(0). pub fn read_write(&self, timeout: Option) -> Result<(), ()> { let t = timeout.map_or(-1, |t| t.as_millis() as c_int); if unsafe { ffi::dbus_connection_read_write(self.conn(), t) == 0 } { Err(()) } else { Ok(()) } } /// Gets whether the output message buffer is non-empty pub fn has_messages_to_send(&self) -> bool { unsafe { ffi::dbus_connection_has_messages_to_send(self.conn()) == 1 } } /// 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.is_null() { None } else { let msg = Message::from_ptr(mptr, false); // println!("Incoming: {:?}", msg); Some(msg) } } /// Removes a message from the incoming queue, or waits until timeout if the queue is empty. /// pub fn blocking_pop_message(&self, timeout: Duration) -> Result, Error> { if let Some(msg) = self.pop_message() { return Ok(Some(msg)) } self.read_write(Some(timeout)).map_err(|_| Error::new_failed("Failed to read/write data, disconnected from D-Bus?") )?; Ok(self.pop_message()) } /// Enables watch tracking, a prequisite for calling watch. /// /// (In theory, this could panic in case libdbus ever changes to listen to /// something else than one file descriptor, /// but this should be extremely unlikely to ever happen.) pub fn set_watch_enabled(&mut self, enable: bool) { if enable == self.watchmap.is_some() { return } if enable { self.watchmap = Some(WatchMap::new(ConnHandle(self.conn(), false))); } else { self.watchmap = None; } } /// Gets the file descriptor to listen for read/write. /// /// Panics: if set_watch_enabled is false. /// /// (In theory, this could panic in case libdbus ever changes to listen to /// something else than one file descriptor, /// but this should be extremely unlikely to ever happen.) pub fn watch(&self) -> Watch { let wm = self.watchmap.as_ref().unwrap(); let rw = wm.current_rw.load(Ordering::Acquire); Watch { fd: wm.current_fd.unwrap(), read: (rw & 1) != 0, write: (rw & 2) != 0, } } /// Get an up-to-date list of file descriptors to watch. /// /// Obsolete - in practice, you can use watch and set_watch_enabled instead. pub fn watch_fds(&mut self) -> Result, ()> { let en = self.watchmap.is_some(); self.set_watch_enabled(true); let mut wlist: Vec = self.watchmap.as_ref().unwrap().list.lock().unwrap().values() .map(|&(w, b)| Watch { fd: w.fd, read: b && w.read, write: b && w.write }) .collect(); self.set_watch_enabled(en); if wlist.len() == 2 && wlist[0].fd == wlist[1].fd { // This is always true in practice, see https://lists.freedesktop.org/archives/dbus/2019-July/017786.html wlist = vec!(Watch { fd: wlist[0].fd, read: wlist[0].read || wlist[1].read, write: wlist[0].write || wlist[1].write }); } Ok(wlist) } } /// Abstraction over different connections that send data pub trait Sender { /// Schedules a message for sending. /// /// Returns a serial number than can be used to match against a reply. fn send(&self, msg: Message) -> Result; } /// Use in case you don't want the send the message, but just collect it instead. impl Sender for std::cell::RefCell> { fn send(&self, msg: Message) -> Result { self.borrow_mut().push(msg); Ok(0) } } /// Use in case you don't want the send the message, but just collect it instead. impl Sender for std::sync::Mutex> { fn send(&self, msg: Message) -> Result { self.lock().unwrap().push(msg); Ok(0) } } /// Token used to identify a callback in the MatchingReceiver trait #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct Token(pub usize); /// Abstraction over different connections that receive data pub trait MatchingReceiver { /// Type of callback type F; /// Add a callback to be called in case a message matches. /// /// Returns an id that can be used to remove the callback. fn start_receive(&self, m: MatchRule<'static>, f: Self::F) -> Token; /// Remove a previously added callback. fn stop_receive(&self, id: Token) -> Option<(MatchRule<'static>, Self::F)>; } impl Sender for Channel { fn send(&self, msg: Message) -> Result { Channel::send(self, msg) } } /// 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_reply(m: &Message) -> Option { peer(&m).or_else(|| unknown_method(&m)) } /// Replies if this is a call to org.freedesktop.DBus.Peer, otherwise returns None. 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(); unsafe { let id = ffi::dbus_get_local_machine_id(); if !id.is_null() { r = r.append1(c_str_to_slice(&(id as *const _)).unwrap()); ffi::dbus_free(id as *mut _); return Some(r) } } return Some(m.error(&"org.freedesktop.DBus.Error.Failed".into(), &to_c_str("Failed to retreive UUID"))) } } 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. 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"))) } #[test] fn test_channel_send_sync() { fn is_send(_: &T) {} fn is_sync(_: &T) {} let c = Channel::get_private(BusType::Session).unwrap(); is_send(&c); is_sync(&c); } #[test] fn channel_simple_test() { let mut c = Channel::get_private(BusType::Session).unwrap(); assert!(c.is_connected()); let fds = c.watch_fds().unwrap(); println!("{:?}", fds); assert!(fds.len() == 1); 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: crate::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) = default_reply(&msg) { c.send(r).unwrap(); } } c.read_write(Some(std::time::Duration::from_millis(100))).unwrap(); } } #[test] fn test_bus_type_is_compatible_with_set() { use std::collections::HashSet; let mut set: HashSet = HashSet::new(); set.insert(BusType::Starter); set.insert(BusType::Starter); assert_eq!(set.len(), 1); assert!(!set.contains(&BusType::Session)); assert!(!set.contains(&BusType::System)); assert!(set.contains(&BusType::Starter)); } #[test] fn watchmap() { let mut c = Channel::get_private(BusType::Session).unwrap(); c.set_watch_enabled(true); let w = c.watch(); assert_eq!(w.write, false); assert_eq!(w.read, true); c.set_watch_enabled(false); println!("{:?}", w); c.set_watch_enabled(true); } dbus-0.9.0/src/error.rs010066400017500001750000000137571372736331200131520ustar 00000000000000use crate::arg::TypeMismatchError; use std::ffi::CString; use std::{ptr, fmt}; use crate::{arg, to_c_str, c_str_to_slice, init_dbus, Message}; use crate::strings::ErrorName; use std::error::Error as stdError; /// D-Bus Error wrapper. /// /// This is a wrapper around the libc dbus error object. 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 {} impl Error { /// Create a new custom D-Bus Error. pub fn new_custom<'a, N: Into>>(name: N, message: &str) -> Error { let n = to_c_str(&name.into()); 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 } /// Create a new generic D-Bus Error with "org.freedesktop.DBus.Error.Failed" as the Error name. pub fn new_failed(message: &str) -> Error { Error::new_custom("org.freedesktop.DBus.Error.Failed", message) } pub (crate) 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) } pub (crate) 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 fmt::Debug for Error { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "D-Bus error: {} ({})", self.message().unwrap_or(""), self.name().unwrap_or("")) } } impl stdError for Error { fn description(&self) -> &str { "D-Bus error" } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), 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: MethodErr) -> Error { Error::new_custom(t.errorname(), t.description()) } } #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] /// A D-Bus Method Error, containing an error name and a description. /// /// Unlike the "Error" struct, this is a Rust native struct. 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 Object path was unknown. pub fn no_path(a: &T) -> MethodErr { ("org.freedesktop.DBus.Error.UnknownObject", format!("Unknown object path {}", a)).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 fmt::Display for MethodErr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.description()) } } impl stdError for MethodErr {} 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: Error) -> 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()) } } dbus-0.9.0/src/ffidisp/connection.rs010066400017500001750000000613601364707574700156120ustar 00000000000000//! Contains structs and traits relevant to the connection itself, and dispatching incoming messages. use crate::{Error, ffi, to_c_str, c_str_to_slice, Message, MessageType}; use crate::ffidisp::ConnPath; use std::{fmt, mem, ptr, thread, panic, ops}; use std::{collections::VecDeque, time::Duration}; use std::cell::{Cell, RefCell}; use std::os::unix::io::RawFd; use std::os::raw::{c_void, c_char, c_int, c_uint}; use crate::strings::{BusName, Path}; use super::{Watch, WatchList, MessageCallback, ConnectionItem, MsgHandler, MsgHandlerList, MessageReply, BusType}; /* 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 (crate) 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 connection to the session bus. /// /// Just a shortcut for `get_private(BusType::Session)`. pub fn new_session() -> Result { Self::get_private(BusType::Session) } /// Creates a new connection to the system bus. /// /// Just a shortcut for `get_private(BusType::System)`. pub fn new_system() -> Result { Self::get_private(BusType::System) } /// 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.is_null() { 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.is_null() { 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.is_null() { 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::{ffidisp::Connection, Message}; /// /// let c = Connection::new_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::ffidisp::Connection; /// let c = Connection::new_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::{ffidisp::Connection, MessageType}; /// let c = Connection::new_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()) } } impl crate::channel::Sender for Connection { fn send(&self, msg: Message) -> Result { Connection::send(self, msg) } } impl crate::blocking::BlockingSender for Connection { fn send_with_reply_and_block(&self, msg: Message, timeout: Duration) -> Result { Connection::send_with_reply_and_block(self, msg, timeout.as_millis() as i32) } } 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; } } } } /// 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"); } } } #[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 _: crate::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.9.0/src/ffidisp/stdintf.rs010066400017500001750000000224161364707574700151250ustar 00000000000000//! 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::ffidisp::{Connection, BusType}; //! use dbus::ffidisp::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 crate::{arg, message, ffidisp}; /// 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 ffidisp::ConnPath<'a, C> { type Err = crate::Error; fn introspect(&self) -> Result { let mut m = self.method_call_with_args(&"org.freedesktop.DBus.Introspectable".into(), &"Introspect".into(), |_| { })?; m.as_result()?; let mut i = m.iter_init(); let xml: String = 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 ffidisp::ConnPath<'a, C> { type Err = crate::Error; fn get arg::Get<'b>>(&self, interface_name: &str, property_name: &str) -> Result { let mut m = 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); })?; m.as_result()?; let mut i = m.iter_init(); let value: arg::Variant = i.read()?; Ok(value.0) } fn get_all(&self, interface_name: &str) -> Result<::std::collections::HashMap>>, Self::Err> { let mut m = self.method_call_with_args(&"org.freedesktop.DBus.Properties".into(), &"GetAll".into(), |msg| { let mut i = arg::IterAppend::new(msg); i.append(interface_name); })?; m.as_result()?; let mut i = m.iter_init(); let properties: ::std::collections::HashMap>> = i.read()?; Ok(properties) } fn set(&self, interface_name: &str, property_name: &str, value: I2) -> Result<(), Self::Err> { let mut m = 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)); })?; 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 arg::AppendAll for PropertiesPropertiesChanged { fn append(&self, i: &mut arg::IterAppend) { arg::RefArg::append(&self.interface_name, i); arg::RefArg::append(&self.changed_properties, i); arg::RefArg::append(&self.invalidated_properties ,i); } } impl arg::ReadAll for PropertiesPropertiesChanged { fn read(i: &mut arg::Iter) -> Result { Ok(PropertiesPropertiesChanged { interface_name: i.read()?, changed_properties: i.read()?, invalidated_properties: i.read()?, }) } } impl message::SignalArgs for PropertiesPropertiesChanged { const NAME: &'static str = "PropertiesChanged"; const INTERFACE: &'static str = "org.freedesktop.DBus.Properties"; } /// 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, ::std::collections::HashMap>>>>, Self::Err>; } impl<'a, C: ::std::ops::Deref> ObjectManager for ffidisp::ConnPath<'a, C> { type Err = crate::Error; fn get_managed_objects(&self) -> Result<::std::collections::HashMap, ::std::collections::HashMap>>>>, Self::Err> { let mut m = self.method_call_with_args(&"org.freedesktop.DBus.ObjectManager".into(), &"GetManagedObjects".into(), |_| { })?; m.as_result()?; let mut i = m.iter_init(); let objects: ::std::collections::HashMap, ::std::collections::HashMap>>>> = 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: crate::Path<'static>, pub interfaces: ::std::collections::HashMap>>>, } impl arg::AppendAll for ObjectManagerInterfacesAdded { fn append(&self, i: &mut arg::IterAppend) { arg::RefArg::append(&self.object, i); arg::RefArg::append(&self.interfaces, i); } } impl arg::ReadAll for ObjectManagerInterfacesAdded { fn read(i: &mut arg::Iter) -> Result { Ok(ObjectManagerInterfacesAdded { object: i.read()?, interfaces: i.read()?, }) } } impl message::SignalArgs for ObjectManagerInterfacesAdded { const NAME: &'static str = "InterfacesAdded"; const INTERFACE: &'static str = "org.freedesktop.DBus.ObjectManager"; } #[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: crate::Path<'static>, pub interfaces: Vec, } impl arg::AppendAll for ObjectManagerInterfacesRemoved { fn append(&self, i: &mut arg::IterAppend) { arg::RefArg::append(&self.object, i); arg::RefArg::append(&self.interfaces, i); } } impl arg::ReadAll for ObjectManagerInterfacesRemoved { fn read(i: &mut arg::Iter) -> Result { Ok(ObjectManagerInterfacesRemoved { object: i.read()?, interfaces: i.read()?, }) } } impl message::SignalArgs for ObjectManagerInterfacesRemoved { const NAME: &'static str = "InterfacesRemoved"; const INTERFACE: &'static str = "org.freedesktop.DBus.ObjectManager"; } /// 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 ffidisp::ConnPath<'a, C> { type Err = crate::Error; fn ping(&self) -> Result<(), Self::Err> { let mut m = self.method_call_with_args(&"org.freedesktop.DBus.Peer".into(), &"Ping".into(), |_| { })?; m.as_result()?; Ok(()) } fn get_machine_id(&self) -> Result { let mut m = self.method_call_with_args(&"org.freedesktop.DBus.Peer".into(), &"GetMachineId".into(), |_| { })?; m.as_result()?; let mut i = m.iter_init(); let machine_uuid: String = i.read()?; Ok(machine_uuid) } } } dbus-0.9.0/src/ffidisp/watch.rs010066400017500001750000000222561364707574700145620ustar 00000000000000use crate::ffi; use libc; use crate::ffidisp::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::ffidisp::{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 }, } } } 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(crate::ffidisp::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 test_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.msg_type(), MessageType::MethodCall); assert_eq!(&*m.path().unwrap(), "/test"); assert_eq!(&*m.interface().unwrap(), "com.example.asynctest"); assert_eq!(&*m.member().unwrap(), "AsyncTest"); let mut mr = Message::new_method_return(&m).unwrap(); mr.append_items(&["Goodies".into()]); c.send(mr).unwrap(); } ConnectionItem::MethodReturn(m) => { assert_eq!(m.msg_type(), 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.9.0/src/ffidisp.rs010066400017500001750000000261411364707574700134510ustar 00000000000000//! A connection that uses FFI callbacks to dispatch messages. //! //! This is the legacy design used up to 0.6.x. It is not recommended for new development. use super::{Error, ffi, Message, MessageType}; use std::panic; use crate::strings::{BusName, Path, Member, Interface}; use crate::arg::{AppendAll, ReadAll, IterAppend}; use crate::message::SignalArgs; pub mod stdintf; mod connection; pub use connection::{Connection, ConnMsgs}; /// 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_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_failed("Sending signal failed")) } /// Make a method call using typed input and output arguments. /// /// # Example /// /// ``` /// use dbus::ffidisp::{Connection, BusType}; /// /// let conn = Connection::get_private(BusType::Session)?; /// let dest = conn.with_path("org.freedesktop.DBus", "/", 5000); /// let (has_owner,): (bool,) = dest.method_call("org.freedesktop.DBus", "NameHasOwner", ("dummy.name.without.owner",))?; /// assert_eq!(has_owner, false); /// # Ok::<(), Box>(()) /// ``` pub fn method_call<'i, 'm, R: ReadAll, A: AppendAll, I: Into>, M: Into>>(&self, i: I, m: M, args: A) -> Result { let mut r = self.method_call_with_args(&i.into(), &m.into(), |mut msg| { args.append(&mut IterAppend::new(&mut msg)); })?; r.as_result()?; Ok(R::read(&mut r.iter_init())?) } } /// 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>; pub use crate::ffi::DBusRequestNameReply as RequestNameReply; pub use crate::ffi::DBusReleaseNameReply as ReleaseNameReply; pub use crate::ffi::DBusBusType as BusType; mod watch; pub use self::watch::{Watch, WatchEvent}; use watch::WatchList; #[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 NameFlag { /// 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 NameFlag { /// 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), } } } #[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>; /// 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() }) } } #[cfg(test)] mod test { use super::{Connection, BusType, ConnectionItem, NameFlag, RequestNameReply, ReleaseNameReply}; use crate::Message; #[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 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.path(), s.interface(), s.member()); match (&*p.unwrap(), &*i.unwrap(), &*m.unwrap()) { ("/mysignal", "com.example.signaltest", "ThisIsASignal") => { assert_eq!(&*s.sender().unwrap(), &*uname); break; }, (_, _, _) => println!("Other signal: {:?}", s), } } _ => {}, } } 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.9.0/src/filters.rs010066400017500001750000000021031364707574700134650ustar 00000000000000use std::collections::BTreeMap; use crate::message::MatchRule; use crate::Message; use crate::channel::Token; pub struct Filters { list: BTreeMap, F)>, nextid: Token, } impl Default for Filters { fn default() -> Self { Filters { list: BTreeMap::new(), nextid: Token(1), }} } impl Filters { pub fn add(&mut self, m: MatchRule<'static>, f: F) -> Token { let id = self.nextid; self.nextid.0 += 1; self.list.insert(id, (m, f)); id } pub fn insert(&mut self, (t, m, f): (Token, MatchRule<'static>, F)) { self.list.insert(t, (m, f)); } pub fn remove(&mut self, id: Token) -> Option<(MatchRule<'static>, F)> { self.list.remove(&id) } pub fn remove_matching(&mut self, msg: &Message) -> Option<(Token, MatchRule<'static>, F)> { if let Some(k) = self.list.iter_mut().find(|(_, v)| v.0.matches(&msg)).map(|(k, _)| *k) { let v = self.list.remove(&k).unwrap(); Some((k, v.0, v.1)) } else { None } } } dbus-0.9.0/src/lib.rs010066400017500001750000000035771372736331200125660ustar 00000000000000//! 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 some additional documents. //! README.md also contains a few quick "getting started" examples (as well as information about //! the `futures` and `no-string-validation` features). //! //! In addition to this crate, there are some companion crates: //! * dbus-tokio for integrating D-Bus with [Tokio](http://tokio.rs) //! * dbus-codegen for generating code from D-Bus introspection data //! * libdbus-sys contains the raw bindings to the C libdbus library. #![warn(missing_docs)] extern crate libc; #[allow(missing_docs)] extern crate libdbus_sys as ffi; pub use crate::message::{Message, MessageType}; pub mod message; pub mod ffidisp; mod error; pub use error::{Error, MethodErr}; pub mod channel; mod filters; pub mod blocking; #[cfg(feature = "futures")] pub mod nonblock; pub mod strings; pub use crate::strings::{Signature, Path}; pub mod arg; // pub mod tree; static INITDBUS: std::sync::Once = std::sync::Once::new(); use std::ffi::{CString, CStr}; use std::os::raw::c_char; 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!"); } }); } fn c_str_to_slice(c: & *const c_char) -> Option<&str> { if c.is_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() } dbus-0.9.0/src/message/matchrule.rs010066400017500001750000000113601364707574700154320ustar 00000000000000use crate::{Message, MessageType}; use crate::strings::{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>, /// If false (the default), match if sender could possibly match, due to mismatch between unique names and taken bus names pub strict_sender: bool, /// Match on message object path pub path: Option>, /// If true, will match all subpaths to the path as well as the path itself. Defaults to false. pub path_is_namespace: bool, /// Match on message interface pub interface: Option>, /// Match on message member (signal or method name) pub member: Option>, /// If true, also receive messages not intended for us. Defaults to false. pub eavesdrop: bool, _more_fields_may_come: (), } fn msg_type_str(m: MessageType) -> &'static str { use crate::MessageType::*; match m { Signal => "signal", MethodCall => "method_call", MethodReturn => "method_return", Error => "error", } } impl<'a> MatchRule<'a> { /// Make a string which you can use in the call to "add_match". 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)) }; let pn = if self.path_is_namespace { "path_namespace" } else { "path" }; if let Some(ref x) = self.path { v.push((pn, &x)) }; if let Some(ref x) = self.interface { v.push(("interface", &x)) }; if let Some(ref x) = self.member { v.push(("member", &x)) }; if self.eavesdrop { v.push(("eavesdrop", "true")) }; // 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(",") } fn path_match(&self, msg: &Message) -> bool { if let Some(ref x) = self.path { if let Some(ref p) = msg.path() { if x != p { if self.path_is_namespace { p.starts_with(&**x) && &p[x.len()..x.len()+1] == "/" } else { false } } else { true } } else { false } } else { true } } /// 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 let Some(ref x) = self.sender { if let Some(s) = msg.sender() { let check = self.strict_sender || (s.starts_with(":") == x.starts_with(":")); if check && s != *x { return false } } else if self.strict_sender { return false } }; if !self.path_match(msg) { 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() } /// Create a new struct which matches every incoming method call message. pub fn new_method_call() -> Self { let mut m = Self::new(); m.msg_type = Some(MessageType::MethodCall); m } /// Create a new struct which matches signals on the interface and member name. pub fn new_signal>, N: Into>>(intf: I, name: N) -> Self { let mut m = Self::new(); m.msg_type = Some(MessageType::Signal); m.interface = Some(intf.into()); m.member = Some(name.into()); m } /// Returns a clone with no borrowed references pub fn static_clone(&self) -> MatchRule<'static> { MatchRule { msg_type: self.msg_type, sender: self.sender.as_ref().map(|x| x.clone().into_static()), strict_sender: self.strict_sender, 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()), path_is_namespace: self.path_is_namespace, eavesdrop: self.eavesdrop, _more_fields_may_come: (), } } } dbus-0.9.0/src/message/signalargs.rs010066400017500001750000000074231367310637000155670ustar 00000000000000use crate::arg; use crate::{Message, MessageType}; use crate::message::MatchRule; use crate::strings::{BusName, Path, Interface, Member}; /// Helper methods for structs representing a Signal /// /// # Example /// /// Listen to InterfacesRemoved signal from org.bluez.obex. /// /// ```rust,no_run /// use dbus::blocking::Connection; /// use dbus::message::SignalArgs; /// use dbus::blocking::stdintf::org_freedesktop_dbus::ObjectManagerInterfacesRemoved as IR; /// use std::time::Duration; /// /// let c = Connection::new_session().unwrap(); /// // Add a match for this signal /// let mr = IR::match_rule(Some(&"org.bluez.obex".into()), None).static_clone(); /// c.add_match(mr, |ir: IR, _, _| { /// println!("Interfaces {:?} have been removed from bluez on path {}.", ir.interfaces, ir.object); /// true /// }); /// /// // Wait for the signal to arrive. /// loop { c.process(Duration::from_millis(1000)).unwrap(); } /// ``` pub trait SignalArgs { /// D-Bus name of signal const NAME: &'static str; /// D-Bus name of interface this signal belongs to const INTERFACE: &'static str; /// Returns a message that emits the signal. fn to_emit_message(&self, path: &Path) -> Message where Self: arg::AppendAll { let mut m = Message::signal(path, &Interface::from(Self::INTERFACE), &Member::from(Self::NAME)); arg::AppendAll::append(self, &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. #[allow(clippy::if_same_then_else)] fn from_message(m: &Message) -> Option where Self: Sized + arg::ReadAll { 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 { arg::ReadAll::read(&mut m.iter_init()).ok() } } /// 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.cloned(); m.path = path.cloned(); 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 crate::blocking::LocalConnection; use crate::blocking::stdintf::org_freedesktop_dbus::ObjectManagerInterfacesRemoved as IR; use std::{time::Duration, cell::Cell, rc::Rc}; let c = LocalConnection::new_session().unwrap(); let mr = IR::match_rule(Some(&c.unique_name().into()), Some(&"/hello".into())).static_clone(); println!("Match: {:?}", mr); let ir = IR { object: "/hello".into(), interfaces: vec!("ABC.DEF".into(), "GHI.JKL".into()) }; let ir_msg = ir.to_emit_message(&"/hello".into()); let done = Rc::new(Cell::new(false)); let done2 = done.clone(); c.add_match(mr, move |ir2: IR, _, _| { assert_eq!(ir2.object, ir.object); assert_eq!(ir2.interfaces, ir.interfaces); done2.set(true); false }).unwrap(); use crate::channel::Sender; c.send(ir_msg).expect("Failed to send message"); while !done.get() { c.process(Duration::from_millis(1000)).unwrap(); } } dbus-0.9.0/src/message.rs010066400017500001750000000454311373106362100134320ustar 00000000000000//! Contains structs and traits closely related to D-Bus messages. use std::{fmt, ptr}; use super::{ffi, Error, libc, init_dbus}; use crate::strings::{BusName, Path, Interface, Member, ErrorName}; use std::ffi::CStr; use super::arg::{Append, AppendAll, IterAppend, ReadAll, Get, Iter, Arg, RefArg, TypeMismatchError}; #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)] /// One of the four different message types. pub enum MessageType { /// This is a method call D-Bus message MethodCall = 1, /// This is a method return Ok D-Bus message, used when the method call message was successfully processed MethodReturn = 2, /// This is a method return with error D-Bus message, used when the method call message could not be handled Error = 3, /// This is a signal, usually sent to whoever wants to listen Signal = 4, } mod signalargs; pub use self::signalargs::SignalArgs; mod matchrule; pub use self::matchrule::MatchRule; /// A D-Bus message. A message contains headers - usually destination address, path, interface and member, /// and a list of arguments. 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_ptr(), p.as_ptr(), i.as_ptr(), m.as_ptr()) }; if ptr.is_null() { 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_ptr(), path.as_ptr(), iface.as_ptr(), name.as_ptr()) }; if ptr.is_null() { panic!("D-Bus error: dbus_message_new_method_call failed") } Message { msg: ptr} } /// Creates a new method call message. pub fn call_with_args<'d, 'p, 'i, 'm, A, D, P, I, M>(destination: D, path: P, iface: I, method: M, args: A) -> Message where D: Into>, P: Into>, I: Into>, M: Into>, A: AppendAll { let mut msg = Message::method_call(&destination.into(), &path.into(), &iface.into(), &method.into()); msg.append_all(args); msg } /// 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 = Path::new(path)?; let i = Interface::new(iface)?; let m = Member::new(name)?; let ptr = unsafe { ffi::dbus_message_new_signal(p.as_ptr(), i.as_ptr(), m.as_ptr()) }; if ptr.is_null() { 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_ptr(), iface.as_ptr(), name.as_ptr()) }; if ptr.is_null() { 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.is_null() { 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.is_null() { panic!("D-Bus error: dbus_message_new_method_return failed") } Message {msg: ptr} } /// Creates a reply for a method call message. /// /// Panics if called for a message which is not a method call. pub fn return_with_args(&self, args: A) -> Message { let mut m = self.method_return(); m.append_all(args); m } /// 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_ptr(), error_message.as_ptr()) }; if ptr.is_null() { 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 = self.iter_init(); let mut v = vec!(); while let Some(z) = crate::arg::messageitem::MessageItem::get(&mut i) { v.push(z); i.next(); } v } /// Get the D-Bus serial of a message, if one was specified. pub fn get_serial(&self) -> Option { let x = unsafe { ffi::dbus_message_get_serial(self.msg) }; if x == 0 { None } else { Some(x) } } /// 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(&mut 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(&mut 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: &[crate::arg::messageitem::MessageItem]) { let mut ia = IterAppend::new(self); for a in v { a.append_by_ref(&mut ia); } } /// 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 } /// Appends arguments to a message. pub fn append_all(&mut self, a: A) { let mut m = IterAppend::new(self); a.append(&mut m); } /// 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((i.read()?, 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((i.read()?, i.read()?, 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((i.read()?, i.read()?, i.read()?, 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((i.read()?, i.read()?, i.read()?, i.read()?, i.read()?)) } /// Gets arguments from a message. /// /// If this was an error reply or if types mismatch, an error is returned. pub fn read_all(&self) -> Result { self.set_error_from_msg()?; Ok(R::read(&mut self.iter_init())?) } /// Returns a struct for retreiving the arguments from a message. Supersedes get_items(). pub fn iter_init(&self) -> Iter { Iter::new(&self) } /// Gets the MessageType of the Message. pub fn msg_type(&self) -> MessageType { match unsafe { ffi::dbus_message_get_type(self.msg) } { 1 => MessageType::MethodCall, 2 => MessageType::MethodReturn, 3 => MessageType::Error, 4 => MessageType::Signal, x => panic!("Invalid message type {}", x), } } fn msg_internal_str<'a>(&'a self, c: *const libc::c_char) -> Option<&'a str> { if c.is_null() { return None }; let cc = unsafe { CStr::from_ptr(c) }; std::str::from_utf8(cc.to_bytes_with_nul()).ok() } /// Gets the name of the connection that originated this message. pub fn sender(&self) -> Option { self.msg_internal_str(unsafe { ffi::dbus_message_get_sender(self.msg) }) .map(|s| unsafe { BusName::from_slice_unchecked(s) }) } /// Gets the object path this Message is being sent to. pub fn path(&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(&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(&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(&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 (crate) 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 } } /// Sets serial number manually - should not be used in production code /// /// However, this can be very useful in test code that is supposed to handle a method call. /// This way, you can create a method call and handle it without sending it to a real D-Bus instance. pub fn set_serial(&mut self, val: u32) { unsafe { ffi::dbus_message_set_serial(self.msg, val) }; } } 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> { let mut x = f.debug_struct("Message"); x.field("Type", &self.msg_type()); // The &&** derefs to a &&str, which implements &dyn Debug if let Some(ref path) = self.path() { x.field("Path", &&**path); } if let Some(ref iface) = self.interface() { x.field("Interface", &&**iface); } if let Some(ref member) = self.member() { x.field("Member", &&**member); } if let Some(ref sender) = self.sender() { x.field("Sender", &&**sender); } if let Some(ref dest) = self.destination() { x.field("Destination", &&**dest); } if let Some(ref serial) = self.get_serial() { x.field("Serial", serial); } if let Some(ref rs) = self.get_reply_serial() { x.field("ReplySerial", rs); } let mut args = vec!(); let mut iter = self.iter_init(); while let Some(a) = iter.get_refarg() { args.push(a); iter.next(); } let args2: &[_] = &args; x.field("Args", &args2); x.finish() } } #[cfg(test)] mod test { use crate::{Message}; use crate::strings::BusName; #[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.9.0/src/methoddisp.rs010066400017500001750000001264621364707574700141740ustar 00000000000000/// 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.9.0/src/nonblock/generated_org_freedesktop_dbus.rs010066400017500001750000000204061364707574700220450ustar 00000000000000// This code was autogenerated with `dbus-codegen-rust -d org.freedesktop.DBus -p /org/freedesktop/DBus -m none -c nonblock -g -i org.freedesktop. --dbuscrate crate --filter DBus -o dbus/src/nonblock/generated_org_freedesktop_dbus.rs`, see https://github.com/diwic/dbus-rs #![allow(missing_docs)] use crate as dbus; use crate::arg; use crate::nonblock; pub trait DBus { fn hello(&self) -> nonblock::MethodReply; fn request_name(&self, arg0: &str, arg1: u32) -> nonblock::MethodReply; fn release_name(&self, arg0: &str) -> nonblock::MethodReply; fn start_service_by_name(&self, arg0: &str, arg1: u32) -> nonblock::MethodReply; fn update_activation_environment(&self, arg0: ::std::collections::HashMap<&str, &str>) -> nonblock::MethodReply<()>; fn name_has_owner(&self, arg0: &str) -> nonblock::MethodReply; fn list_names(&self) -> nonblock::MethodReply>; fn list_activatable_names(&self) -> nonblock::MethodReply>; fn add_match(&self, arg0: &str) -> nonblock::MethodReply<()>; fn remove_match(&self, arg0: &str) -> nonblock::MethodReply<()>; fn get_name_owner(&self, arg0: &str) -> nonblock::MethodReply; fn list_queued_owners(&self, arg0: &str) -> nonblock::MethodReply>; fn get_connection_unix_user(&self, arg0: &str) -> nonblock::MethodReply; fn get_connection_unix_process_id(&self, arg0: &str) -> nonblock::MethodReply; fn get_adt_audit_session_data(&self, arg0: &str) -> nonblock::MethodReply>; fn get_connection_selinux_security_context(&self, arg0: &str) -> nonblock::MethodReply>; fn reload_config(&self) -> nonblock::MethodReply<()>; fn get_id(&self) -> nonblock::MethodReply; fn get_connection_credentials(&self, arg0: &str) -> nonblock::MethodReply<::std::collections::HashMap>>>; fn features(&self) -> nonblock::MethodReply>; fn interfaces(&self) -> nonblock::MethodReply>; } impl<'a, T: nonblock::NonblockReply, C: ::std::ops::Deref> DBus for nonblock::Proxy<'a, C> { fn hello(&self) -> nonblock::MethodReply { self.method_call("org.freedesktop.DBus", "Hello", ()) .and_then(|r: (String, )| Ok(r.0, )) } fn request_name(&self, arg0: &str, arg1: u32) -> nonblock::MethodReply { self.method_call("org.freedesktop.DBus", "RequestName", (arg0, arg1, )) .and_then(|r: (u32, )| Ok(r.0, )) } fn release_name(&self, arg0: &str) -> nonblock::MethodReply { self.method_call("org.freedesktop.DBus", "ReleaseName", (arg0, )) .and_then(|r: (u32, )| Ok(r.0, )) } fn start_service_by_name(&self, arg0: &str, arg1: u32) -> nonblock::MethodReply { self.method_call("org.freedesktop.DBus", "StartServiceByName", (arg0, arg1, )) .and_then(|r: (u32, )| Ok(r.0, )) } fn update_activation_environment(&self, arg0: ::std::collections::HashMap<&str, &str>) -> nonblock::MethodReply<()> { self.method_call("org.freedesktop.DBus", "UpdateActivationEnvironment", (arg0, )) } fn name_has_owner(&self, arg0: &str) -> nonblock::MethodReply { self.method_call("org.freedesktop.DBus", "NameHasOwner", (arg0, )) .and_then(|r: (bool, )| Ok(r.0, )) } fn list_names(&self) -> nonblock::MethodReply> { self.method_call("org.freedesktop.DBus", "ListNames", ()) .and_then(|r: (Vec, )| Ok(r.0, )) } fn list_activatable_names(&self) -> nonblock::MethodReply> { self.method_call("org.freedesktop.DBus", "ListActivatableNames", ()) .and_then(|r: (Vec, )| Ok(r.0, )) } fn add_match(&self, arg0: &str) -> nonblock::MethodReply<()> { self.method_call("org.freedesktop.DBus", "AddMatch", (arg0, )) } fn remove_match(&self, arg0: &str) -> nonblock::MethodReply<()> { self.method_call("org.freedesktop.DBus", "RemoveMatch", (arg0, )) } fn get_name_owner(&self, arg0: &str) -> nonblock::MethodReply { self.method_call("org.freedesktop.DBus", "GetNameOwner", (arg0, )) .and_then(|r: (String, )| Ok(r.0, )) } fn list_queued_owners(&self, arg0: &str) -> nonblock::MethodReply> { self.method_call("org.freedesktop.DBus", "ListQueuedOwners", (arg0, )) .and_then(|r: (Vec, )| Ok(r.0, )) } fn get_connection_unix_user(&self, arg0: &str) -> nonblock::MethodReply { self.method_call("org.freedesktop.DBus", "GetConnectionUnixUser", (arg0, )) .and_then(|r: (u32, )| Ok(r.0, )) } fn get_connection_unix_process_id(&self, arg0: &str) -> nonblock::MethodReply { self.method_call("org.freedesktop.DBus", "GetConnectionUnixProcessID", (arg0, )) .and_then(|r: (u32, )| Ok(r.0, )) } fn get_adt_audit_session_data(&self, arg0: &str) -> nonblock::MethodReply> { self.method_call("org.freedesktop.DBus", "GetAdtAuditSessionData", (arg0, )) .and_then(|r: (Vec, )| Ok(r.0, )) } fn get_connection_selinux_security_context(&self, arg0: &str) -> nonblock::MethodReply> { self.method_call("org.freedesktop.DBus", "GetConnectionSELinuxSecurityContext", (arg0, )) .and_then(|r: (Vec, )| Ok(r.0, )) } fn reload_config(&self) -> nonblock::MethodReply<()> { self.method_call("org.freedesktop.DBus", "ReloadConfig", ()) } fn get_id(&self) -> nonblock::MethodReply { self.method_call("org.freedesktop.DBus", "GetId", ()) .and_then(|r: (String, )| Ok(r.0, )) } fn get_connection_credentials(&self, arg0: &str) -> nonblock::MethodReply<::std::collections::HashMap>>> { self.method_call("org.freedesktop.DBus", "GetConnectionCredentials", (arg0, )) .and_then(|r: (::std::collections::HashMap>>, )| Ok(r.0, )) } fn features(&self) -> nonblock::MethodReply> { ::get(&self, "org.freedesktop.DBus", "Features") } fn interfaces(&self) -> nonblock::MethodReply> { ::get(&self, "org.freedesktop.DBus", "Interfaces") } } #[derive(Debug)] pub struct DBusNameOwnerChanged { pub arg0: String, pub arg1: String, pub arg2: String, } impl arg::AppendAll for DBusNameOwnerChanged { fn append(&self, i: &mut arg::IterAppend) { arg::RefArg::append(&self.arg0, i); arg::RefArg::append(&self.arg1, i); arg::RefArg::append(&self.arg2, i); } } impl arg::ReadAll for DBusNameOwnerChanged { fn read(i: &mut arg::Iter) -> Result { Ok(DBusNameOwnerChanged { arg0: i.read()?, arg1: i.read()?, arg2: i.read()?, }) } } impl dbus::message::SignalArgs for DBusNameOwnerChanged { const NAME: &'static str = "NameOwnerChanged"; const INTERFACE: &'static str = "org.freedesktop.DBus"; } #[derive(Debug)] pub struct DBusNameLost { pub arg0: String, } impl arg::AppendAll for DBusNameLost { fn append(&self, i: &mut arg::IterAppend) { arg::RefArg::append(&self.arg0, i); } } impl arg::ReadAll for DBusNameLost { fn read(i: &mut arg::Iter) -> Result { Ok(DBusNameLost { arg0: i.read()?, }) } } impl dbus::message::SignalArgs for DBusNameLost { const NAME: &'static str = "NameLost"; const INTERFACE: &'static str = "org.freedesktop.DBus"; } #[derive(Debug)] pub struct DBusNameAcquired { pub arg0: String, } impl arg::AppendAll for DBusNameAcquired { fn append(&self, i: &mut arg::IterAppend) { arg::RefArg::append(&self.arg0, i); } } impl arg::ReadAll for DBusNameAcquired { fn read(i: &mut arg::Iter) -> Result { Ok(DBusNameAcquired { arg0: i.read()?, }) } } impl dbus::message::SignalArgs for DBusNameAcquired { const NAME: &'static str = "NameAcquired"; const INTERFACE: &'static str = "org.freedesktop.DBus"; } dbus-0.9.0/src/nonblock/generated_org_freedesktop_standard_interfaces.rs010066400017500001750000000142351364707574700251160ustar 00000000000000// This code was autogenerated with `dbus-codegen-rust -m none -c nonblock -g -i org.freedesktop.DBus --dbuscrate crate`, see https://github.com/diwic/dbus-rs use crate as dbus; use crate::arg; use crate::nonblock; pub trait Properties { fn get arg::Get<'b> + 'static>(&self, interface_name: &str, property_name: &str) -> nonblock::MethodReply; fn get_all(&self, interface_name: &str) -> nonblock::MethodReply<::std::collections::HashMap>>>; fn set(&self, interface_name: &str, property_name: &str, value: I2) -> nonblock::MethodReply<()>; } impl<'a, T: nonblock::NonblockReply, C: ::std::ops::Deref> Properties for nonblock::Proxy<'a, C> { fn get arg::Get<'b> + 'static>(&self, interface_name: &str, property_name: &str) -> nonblock::MethodReply { self.method_call("org.freedesktop.DBus.Properties", "Get", (interface_name, property_name, )) .and_then(|r: (arg::Variant, )| Ok((r.0).0, )) } fn get_all(&self, interface_name: &str) -> nonblock::MethodReply<::std::collections::HashMap>>> { self.method_call("org.freedesktop.DBus.Properties", "GetAll", (interface_name, )) .and_then(|r: (::std::collections::HashMap>>, )| Ok(r.0, )) } fn set(&self, interface_name: &str, property_name: &str, value: I2) -> nonblock::MethodReply<()> { self.method_call("org.freedesktop.DBus.Properties", "Set", (interface_name, property_name, arg::Variant(value), )) } } #[derive(Debug)] pub struct PropertiesPropertiesChanged { pub interface_name: String, pub changed_properties: ::std::collections::HashMap>>, pub invalidated_properties: Vec, } impl arg::AppendAll for PropertiesPropertiesChanged { fn append(&self, i: &mut arg::IterAppend) { arg::RefArg::append(&self.interface_name, i); arg::RefArg::append(&self.changed_properties, i); arg::RefArg::append(&self.invalidated_properties, i); } } impl arg::ReadAll for PropertiesPropertiesChanged { fn read(i: &mut arg::Iter) -> Result { Ok(PropertiesPropertiesChanged { interface_name: i.read()?, changed_properties: i.read()?, invalidated_properties: i.read()?, }) } } impl dbus::message::SignalArgs for PropertiesPropertiesChanged { const NAME: &'static str = "PropertiesChanged"; const INTERFACE: &'static str = "org.freedesktop.DBus.Properties"; } pub trait Introspectable { fn introspect(&self) -> nonblock::MethodReply; } impl<'a, T: nonblock::NonblockReply, C: ::std::ops::Deref> Introspectable for nonblock::Proxy<'a, C> { fn introspect(&self) -> nonblock::MethodReply { self.method_call("org.freedesktop.DBus.Introspectable", "Introspect", ()) .and_then(|r: (String, )| Ok(r.0, )) } } pub trait Peer { fn ping(&self) -> nonblock::MethodReply<()>; fn get_machine_id(&self) -> nonblock::MethodReply; } impl<'a, T: nonblock::NonblockReply, C: ::std::ops::Deref> Peer for nonblock::Proxy<'a, C> { fn ping(&self) -> nonblock::MethodReply<()> { self.method_call("org.freedesktop.DBus.Peer", "Ping", ()) } fn get_machine_id(&self) -> nonblock::MethodReply { self.method_call("org.freedesktop.DBus.Peer", "GetMachineId", ()) .and_then(|r: (String, )| Ok(r.0, )) } } pub trait ObjectManager { fn get_managed_objects(&self) -> nonblock::MethodReply<::std::collections::HashMap, ::std::collections::HashMap>>>>>; } impl<'a, T: nonblock::NonblockReply, C: ::std::ops::Deref> ObjectManager for nonblock::Proxy<'a, C> { fn get_managed_objects(&self) -> nonblock::MethodReply<::std::collections::HashMap, ::std::collections::HashMap>>>>> { self.method_call("org.freedesktop.DBus.ObjectManager", "GetManagedObjects", ()) .and_then(|r: (::std::collections::HashMap, ::std::collections::HashMap>>>>, )| Ok(r.0, )) } } #[derive(Debug)] pub struct ObjectManagerInterfacesAdded { pub object: dbus::Path<'static>, pub interfaces: ::std::collections::HashMap>>>, } impl arg::AppendAll for ObjectManagerInterfacesAdded { fn append(&self, i: &mut arg::IterAppend) { arg::RefArg::append(&self.object, i); arg::RefArg::append(&self.interfaces, i); } } impl arg::ReadAll for ObjectManagerInterfacesAdded { fn read(i: &mut arg::Iter) -> Result { Ok(ObjectManagerInterfacesAdded { object: i.read()?, interfaces: i.read()?, }) } } impl dbus::message::SignalArgs for ObjectManagerInterfacesAdded { const NAME: &'static str = "InterfacesAdded"; const INTERFACE: &'static str = "org.freedesktop.DBus.ObjectManager"; } #[derive(Debug)] pub struct ObjectManagerInterfacesRemoved { pub object: dbus::Path<'static>, pub interfaces: Vec, } impl arg::AppendAll for ObjectManagerInterfacesRemoved { fn append(&self, i: &mut arg::IterAppend) { arg::RefArg::append(&self.object, i); arg::RefArg::append(&self.interfaces, i); } } impl arg::ReadAll for ObjectManagerInterfacesRemoved { fn read(i: &mut arg::Iter) -> Result { Ok(ObjectManagerInterfacesRemoved { object: i.read()?, interfaces: i.read()?, }) } } impl dbus::message::SignalArgs for ObjectManagerInterfacesRemoved { const NAME: &'static str = "InterfacesRemoved"; const INTERFACE: &'static str = "org.freedesktop.DBus.ObjectManager"; } dbus-0.9.0/src/nonblock.rs010066400017500001750000000473351370461266500136300ustar 00000000000000//! Async version of connection. //! //! This module requires the `futures` feature to be enabled. //! //! Current status: //! * Basic client functionality is up and running, i e, you can make method calls and //! receive incoming messages (e g signals). //! * As for server side code, you can use the `tree` module with this connection, but it does not //! support async method handlers. //! //! You're probably going to need a companion crate - dbus-tokio - for this connection to make sense. //! (Although you can also just call read_write and process_all at regular intervals, and possibly //! set a timeout handler.) use crate::{Error, Message}; use crate::channel::{MatchingReceiver, Channel, Sender, Token}; use crate::strings::{BusName, Path, Interface, Member}; use crate::arg::{AppendAll, ReadAll, IterAppend}; use crate::message::MatchRule; use std::sync::{Arc, Mutex}; use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use std::{task, pin, mem}; use std::cell::RefCell; use std::time::Duration; use crate::filters::Filters; use std::future::Future; use std::time::Instant; use std::collections::HashMap; #[allow(missing_docs)] mod generated_org_freedesktop_standard_interfaces; mod generated_org_freedesktop_dbus; /// 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 was created by dbus-codegen. pub mod stdintf { #[allow(missing_docs)] pub mod org_freedesktop_dbus { pub use super::super::generated_org_freedesktop_standard_interfaces::*; #[allow(unused_imports)] pub(crate) use super::super::generated_org_freedesktop_dbus::*; #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum RequestNameReply { PrimaryOwner = 1, InQueue = 2, Exists = 3, AlreadyOwner = 4, } #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum ReleaseNameReply { Released = 1, NonExistent = 2, NotOwner = 3, } } } type Replies = HashMap; /// A connection to D-Bus, thread local + async version pub struct LocalConnection { channel: Channel, filters: RefCell>, replies: RefCell>, timeout_maker: Option, waker: Option, } /// A connection to D-Bus, async version, which is Send but not Sync. pub struct Connection { channel: Channel, filters: RefCell>, replies: RefCell>, timeout_maker: Option, waker: Option, } /// A connection to D-Bus, Send + Sync + async version pub struct SyncConnection { channel: Channel, filters: Mutex>, replies: Mutex>, timeout_maker: Option, waker: Option, } use stdintf::org_freedesktop_dbus::DBus; macro_rules! connimpl { ($c: ident, $cb: ident, $rcb: ident $(, $ss:tt)*) => { type $cb = Box bool $(+ $ss)* + 'static>; type $rcb = Box; impl From for $c { fn from(x: Channel) -> Self { $c { channel: x, replies: Default::default(), filters: Default::default(), timeout_maker: None, waker: None, } } } impl AsRef for $c { fn as_ref(&self) -> &Channel { &self.channel } } impl Sender for $c { fn send(&self, msg: Message) -> Result { self.channel.send(msg) } } impl MatchingReceiver for $c { type F = $cb; fn start_receive(&self, m: MatchRule<'static>, f: Self::F) -> Token { self.filters_mut().add(m, f) } fn stop_receive(&self, id: Token) -> Option<(MatchRule<'static>, Self::F)> { self.filters_mut().remove(id) } } impl NonblockReply for $c { type F = $rcb; fn send_with_reply(&self, msg: Message, f: Self::F) -> Result { let token = { // We must hold the mutex from moment we send the message // To moment we set a handler for the reply // So reply can't arrive before we set handler let mut replies = self.replies_mut(); self.channel.send(msg).map(|x| { let t = Token(x as usize); replies.insert(t, f); t }) }; if self.channel.has_messages_to_send() { // Non-blocking send failed // Wake up task that will send the message if self.waker.as_ref().map(|wake| wake().is_err() ).unwrap_or(false) { return Err(()); } } token } fn cancel_reply(&self, id: Token) -> Option { self.replies_mut().remove(&id) } fn make_f(g: G) -> Self::F { Box::new(g) } fn timeout_maker(&self) -> Option { self.timeout_maker } fn set_timeout_maker(&mut self, f: Option) -> Option { mem::replace(&mut self.timeout_maker, f) } fn set_waker(&mut self, f: Option) -> Option { mem::replace(&mut self.waker, f) } } impl Process for $c { fn process_one(&self, msg: Message) { if let Some(serial) = msg.get_reply_serial() { if let Some(f) = self.replies_mut().remove(&Token(serial as usize)) { f(msg, self); return; } } let ff = self.filters_mut().remove_matching(&msg); if let Some(mut ff) = ff { if ff.2(msg, self) { self.filters_mut().insert(ff); } } else if let Some(reply) = crate::channel::default_reply(&msg) { let _ = self.send(reply); } } } impl $c { fn dbus_proxy(&self) -> Proxy<&Self> { Proxy::new("org.freedesktop.DBus", "/org/freedesktop/DBus", Duration::from_secs(10), self) } /// Get the connection's unique name. /// /// It's usually something like ":1.54" pub fn unique_name(&self) -> BusName { self.channel.unique_name().unwrap().into() } /// Request a name on the D-Bus. /// /// For detailed information on the flags and return values, see the libdbus documentation. pub async fn request_name<'a, N: Into>>(&self, name: N, allow_replacement: bool, replace_existing: bool, do_not_queue: bool) -> Result { let flags: u32 = if allow_replacement { 1 } else { 0 } + if replace_existing { 2 } else { 0 } + if do_not_queue { 4 } else { 0 }; let r = self.dbus_proxy().request_name(&name.into(), flags).await?; use stdintf::org_freedesktop_dbus::RequestNameReply::*; let all = [PrimaryOwner, InQueue, Exists, AlreadyOwner]; all.iter().find(|x| **x as u32 == r).copied().ok_or_else(|| crate::Error::new_failed("Invalid reply from DBus server") ) } /// Release a previously requested name on the D-Bus. pub async fn release_name<'a, N: Into>>(&self, name: N) -> Result { let r = self.dbus_proxy().release_name(&name.into()).await?; use stdintf::org_freedesktop_dbus::ReleaseNameReply::*; let all = [Released, NonExistent, NotOwner]; all.iter().find(|x| **x as u32 == r).copied().ok_or_else(|| crate::Error::new_failed("Invalid reply from DBus server") ) } /// Adds a new match to the connection, and sets up a callback when this message arrives. /// /// The returned value can be used to remove the match. pub async fn add_match(&self, match_rule: MatchRule<'static>) -> Result { let m = match_rule.match_str(); self.add_match_no_cb(&m).await?; let mi = Arc::new(MatchInner { token: Default::default(), cb: Default::default(), }); let mi_weak = Arc::downgrade(&mi); let token = self.start_receive(match_rule, Box::new(move |msg, _| { mi_weak.upgrade().map(|mi| mi.incoming(msg)).unwrap_or(false) })); mi.token.store(token.0, SeqCst); Ok(MsgMatch(mi)) } /// Adds a new match to the connection, without setting up a callback when this message arrives. pub async fn add_match_no_cb(&self, match_str: &str) -> Result<(), Error> { self.dbus_proxy().add_match(match_str).await } /// Removes a match from the connection, without removing any callbacks. pub async fn remove_match_no_cb(&self, match_str: &str) -> Result<(), Error> { self.dbus_proxy().remove_match(match_str).await } /// Removes a previously added match and callback from the connection. pub async fn remove_match(&self, id: Token) -> Result<(), Error> { let (mr, _) = self.stop_receive(id).ok_or_else(|| Error::new_failed("No match with that id found"))?; self.remove_match_no_cb(&mr.match_str()).await } } } } connimpl!(Connection, FilterCb, RepliesCb, Send); connimpl!(LocalConnection, LocalFilterCb, LocalRepliesCb); connimpl!(SyncConnection, SyncFilterCb, SyncRepliesCb, Send); impl Connection { fn filters_mut(&self) -> std::cell::RefMut> { self.filters.borrow_mut() } fn replies_mut(&self) -> std::cell::RefMut> { self.replies.borrow_mut() } } impl LocalConnection { fn filters_mut(&self) -> std::cell::RefMut> { self.filters.borrow_mut() } fn replies_mut(&self) -> std::cell::RefMut> { self.replies.borrow_mut() } } impl SyncConnection { fn filters_mut(&self) -> std::sync::MutexGuard> { self.filters.lock().unwrap() } fn replies_mut(&self) -> std::sync::MutexGuard> { self.replies.lock().unwrap() } } /// Internal callback for the executor when a timeout needs to be made. pub type TimeoutMakerCb = fn(timeout: Instant) -> pin::Pin + Send + Sync + 'static>>; /// Internal callback for the executor when we need wakeup a task pub type WakerCb = Box Result<(), ()> + Send + Sync +'static>; /// Internal helper trait for async method replies. pub trait NonblockReply { /// Callback type type F; /// Sends a message and calls the callback when a reply is received. fn send_with_reply(&self, msg: Message, f: Self::F) -> Result; /// Cancels a pending reply. fn cancel_reply(&self, id: Token) -> Option; /// Internal helper function that creates a callback. fn make_f(g: G) -> Self::F where Self: Sized; /// Set the internal timeout maker fn set_timeout_maker(&mut self, f: Option) -> Option; /// Get the internal timeout maker fn timeout_maker(&self) -> Option; /// Set the wakeup call fn set_waker(&mut self, f: Option) -> Option; } /// Internal helper trait, implemented for connections that process incoming messages. pub trait Process: Sender + AsRef { /// Dispatches all pending messages, without blocking. /// /// This is usually called from the reactor only, after read_write. /// Despite this taking &self and not "&mut self", it is a logic error to call this /// recursively or from more than one thread at a time. fn process_all(&self) { let c: &Channel = self.as_ref(); while let Some(msg) = c.pop_message() { self.process_one(msg); } } /// Dispatches a message. fn process_one(&self, msg: Message); } /// A struct used to handle incoming matches /// /// Note: Due to the lack of async destructors, please call Connection.remove_match() /// in order to properly stop matching (instead of just dropping this struct). pub struct MsgMatch(Arc); struct MatchInner { token: AtomicUsize, cb: Mutex bool + Send>>>, } impl MatchInner { fn incoming(&self, msg: Message) -> bool { if let Some(ref mut cb) = self.cb.lock().unwrap().as_mut() { cb(msg) } else { true } } } impl MsgMatch { /// Configures the match to receive a synchronous callback with only a message parameter. pub fn msg_cb bool + Send + 'static>(self, f: F) -> Self { { let mut cb = self.0.cb.lock().unwrap(); *cb = Some(Box::new(f)); } self } /// Configures the match to receive a synchronous callback with a message parameter and typed /// message arguments. /// /// # Example /// /// ```ignore /// let mr = MatchRule::new_signal("com.example.dbustest", "HelloHappened"); /// let incoming_signal = connection.add_match(mr).await?.cb(|_, (source,): (String,)| { /// println!("Hello from {} happened on the bus!", source); /// true /// }); /// ``` pub fn cb bool + Send + 'static>(self, mut f: F) -> Self { self.msg_cb(move |msg| { if let Ok(r) = R::read(&mut msg.iter_init()) { f(msg, r) } else { true } }) } /// Configures the match to receive a stream of messages. /// /// Note: If the receiving end is disconnected and a message is received, /// the message matching will end but not in a clean fashion. Call remove_match() to /// stop matching cleanly. pub fn msg_stream(self) -> (Self, futures_channel::mpsc::UnboundedReceiver) { let (sender, receiver) = futures_channel::mpsc::unbounded(); (self.msg_cb(move |msg| { sender.unbounded_send(msg).is_ok() }), receiver) } /// Configures the match to receive a stream of messages, parsed and ready. /// /// Note: If the receiving end is disconnected and a message is received, /// the message matching will end but not in a clean fashion. Call remove_match() to /// stop matching cleanly. /// /// # Example /// /// ```ignore /// let mr = MatchRule::new_signal("com.example.dbustest", "HelloHappened"); /// let (incoming_signal, stream) = conn.add_match(mr).await?.stream(); /// let stream = stream.for_each(|(_, (source,)): (_, (String,))| { /// println!("Hello from {} happened on the bus!", source); /// async {} /// }); /// ``` pub fn stream(self) -> (Self, futures_channel::mpsc::UnboundedReceiver<(Message, R)>) { let (sender, receiver) = futures_channel::mpsc::unbounded(); (self.cb(move |msg, r| { sender.unbounded_send((msg, r)).is_ok() }), receiver) } /// The token retreived can be used in a call to remove_match to stop matching on the data. pub fn token(&self) -> Token { Token(self.0.token.load(SeqCst)) } } /// A struct that wraps a connection, destination and path. /// /// A D-Bus "Proxy" is a client-side object that corresponds to a remote object on the server side. /// Calling methods on the proxy object calls methods on the remote object. /// Read more in the [D-Bus tutorial](https://dbus.freedesktop.org/doc/dbus-tutorial.html#proxies) #[derive(Clone, Debug)] pub struct Proxy<'a, C> { /// Destination, i e what D-Bus service you're communicating with pub destination: BusName<'a>, /// Object path on the destination pub path: Path<'a>, /// Some way to send and/or receive messages, non-blocking. pub connection: C, /// Timeout for method calls pub timeout: Duration, } impl<'a, C> Proxy<'a, C> { /// Creates a new proxy struct. pub fn new>, P: Into>>(dest: D, path: P, timeout: Duration, connection: C) -> Self { Proxy { destination: dest.into(), path: path.into(), timeout, connection } } } struct MRAwait { mrouter: MROuter, token: Result, timeout: Instant, timeoutfn: Option } async fn method_call_await(mra: MRAwait) -> Result { use futures_util::future; let MRAwait { mrouter, token, timeout, timeoutfn } = mra; if token.is_err() { return Err(Error::new_failed("Failed to send message")) }; let timeout = if let Some(tfn) = timeoutfn { tfn(timeout) } else { Box::pin(future::pending()) }; match future::select(mrouter, timeout).await { future::Either::Left((r, _)) => r, future::Either::Right(_) => Err(Error::new_custom("org.freedesktop.DBus.Error.Timeout", "Timeout waiting for reply")), } } impl<'a, T, C> Proxy<'a, C> where T: NonblockReply, C: std::ops::Deref { fn method_call_setup(&self, msg: Message) -> MRAwait { let mr = Arc::new(Mutex::new(MRInner::Neither)); let mrouter = MROuter(mr.clone()); let f = T::make_f(move |msg: Message, _: &T| { let mut inner = mr.lock().unwrap(); let old = mem::replace(&mut *inner, MRInner::Ready(Ok(msg))); if let MRInner::Pending(waker) = old { waker.wake() } }); let timeout = Instant::now() + self.timeout; let token = self.connection.send_with_reply(msg, f); let timeoutfn = self.connection.timeout_maker(); MRAwait { mrouter, token, timeout, timeoutfn } } /// Make a method call using typed input argument, returns a future that resolves to the typed output arguments. pub fn method_call<'i, 'm, R: ReadAll + 'static, A: AppendAll, I: Into>, M: Into>>(&self, i: I, m: M, args: A) -> MethodReply { let mut msg = Message::method_call(&self.destination, &self.path, &i.into(), &m.into()); args.append(&mut IterAppend::new(&mut msg)); let mra = self.method_call_setup(msg); let r = method_call_await(mra); let r = futures_util::FutureExt::map(r, |r| -> Result { r.and_then(|rmsg| rmsg.read_all()) } ); MethodReply::new(r) } } enum MRInner { Ready(Result), Pending(task::Waker), Neither, } struct MROuter(Arc>); impl Future for MROuter { type Output = Result; fn poll(self: pin::Pin<&mut Self>, ctx: &mut task::Context) -> task::Poll { let mut inner = self.0.lock().unwrap(); let r = mem::replace(&mut *inner, MRInner::Neither); if let MRInner::Ready(r) = r { task::Poll::Ready(r) } else { *inner = MRInner::Pending(ctx.waker().clone()); return task::Poll::Pending } } } /// Future method reply, used while waiting for a method call reply from the server. pub struct MethodReply(pin::Pin> + Send + 'static>>); impl MethodReply { /// Creates a new method reply from a future. fn new> + Send + 'static>(fut: Fut) -> Self { MethodReply(Box::pin(fut)) } } impl Future for MethodReply { type Output = Result; fn poll(mut self: pin::Pin<&mut Self>, ctx: &mut task::Context) -> task::Poll> { self.0.as_mut().poll(ctx) } } impl MethodReply { /// Convenience combinator in case you want to post-process the result after reading it pub fn and_then(self, f: impl FnOnce(T) -> Result + Send + Sync + 'static) -> MethodReply { MethodReply(Box::pin(async move { let x = self.0.await?; f(x) })) } } #[test] fn test_conn_send_sync() { fn is_send() {} fn is_sync() {} is_send::(); is_send::(); is_sync::(); is_send::(); } dbus-0.9.0/src/prop.rs010066400017500001750000000121161364707574700130020ustar 00000000000000use 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 = self.conn.send_with_reply_and_block(m, self.timeout_ms)?; let reply = 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 = self.conn.send_with_reply_and_block(m, self.timeout_ms)?; 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 = self.conn.send_with_reply_and_block(m, self.timeout_ms)?; let reply = r.as_result()?.get_items(); (|| { if reply.len() != 1 { return Err(()) }; let mut t = BTreeMap::new(); let a: &[MessageItem] = reply[0].inner()?; for p in a.iter() { let (k, v) = p.inner()?; let (k, v): (&String, &MessageItem) = (k.inner()?, 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 = 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 = 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> { 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.9.0/src/strings.rs010066400017500001750000000172671373110443500135040ustar 00000000000000//! This module contains strings with a specific format, such as a valid //! Interface name, a valid Error name, etc. //! //! (The internal representation of these strings are `Cow`, but with a \0 byte //! at the end to use it libdbus calls without extra allocations. This is usually nothing //! you have to worry about.) 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 crate::Error; #[cfg(not(feature = "no-string-validation"))] use crate::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 mut s = s.into(); s.push_str("\0"); unsafe { $t::check_valid(CStr::from_bytes_with_nul_unchecked(s.as_bytes()).as_ptr() as *const c_char)?; } Ok(Self(Cow::Owned(s))) } /// 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 str) -> Result<$t<'m>, String> { let ss = s.as_bytes(); if ss.len() == 0 || ss[ss.len()-1] != 0 { return $t::new(s) }; $t::check_valid(s.as_ptr() as *const c_char).map(|_| { unsafe { Self::from_slice_unchecked(s) } }) } /// 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 str) -> $t<'m> { let ss = s.as_bytes(); debug_assert!(ss[ss.len()-1] == 0); $t(Cow::Borrowed(s)) } /// View this struct as a CStr. /// /// Note: As of dbus 0.9, this is made private to be able to make it easier for a potential /// native implementation using "str" instead of "cstr". pub (crate) fn as_cstr(&self) -> &CStr { unsafe { CStr::from_bytes_with_nul_unchecked(self.0.as_bytes()) } } #[allow(dead_code)] pub (crate) fn as_ptr(&self) -> *const c_char { self.as_cstr().as_ptr() } /// 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 { // Change this when https://github.com/rust-lang/rust/issues/73179 is on stable. let mut x: Vec = self.0.into_owned().into(); x.pop(); CString::new(x).unwrap() } } /* /// #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).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).unwrap() } } /// #Panics /// /// If given string is not valid. impl<'m> From<&'m CStr> for $t<'m> { fn from(s: &'m CStr) -> $t<'m> { let x = str::from_utf8(s.to_bytes_with_nul()).unwrap(); $t::from_slice(x).unwrap() } } impl<'m> From<$t<'m>> for CString { fn from(s: $t<'m>) -> CString { s.into_cstring() } } /// #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 { self.0.split_at(self.0.len()-1).0 } } impl<'m> fmt::Display for $t<'m> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { ::fmt(&*self, f) } } /* As of dbus 0.9, this has been removed to prepare for a potential native implementation. 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. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] pub struct Signature<'a>(Cow<'a, str>); 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, str>); 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("/\0")) } } /// 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, str>); 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, str>); 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, str>); cstring_wrapper!(BusName, dbus_validate_bus_name); /// A wrapper around a string that is guaranteed to be /// a valid D-Bus error name. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] pub struct ErrorName<'a>(Cow<'a, str>); cstring_wrapper!(ErrorName, dbus_validate_error_name); #[test] fn some_path() { let p1: Path = "/valid".into(); let p2 = Path::new("##invalid##"); assert_eq!(p1, Path(Cow::Borrowed("/valid\0"))); #[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)"); }