udev-0.9.0/.cargo_vcs_info.json0000644000000001360000000000100120050ustar { "git": { "sha1": "78fecd94a795c76681d80cda6ac9ad10268f962d" }, "path_in_vcs": "" }udev-0.9.0/.github/workflows/ci.yml000064400000000000000000000132621046102023000153140ustar 00000000000000name: Build on: push: branches: - master tags: - 'v[0-9]+.[0-9]+.[0-9]+' pull_request: jobs: format: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 with: submodules: true - name: Setup Rust uses: actions-rs/toolchain@v1 with: toolchain: stable profile: minimal components: rustfmt default: true override: true - name: Cargo cache uses: actions/cache@v2 with: path: | ~/.cargo/registry ~/.cargo/git key: ${{ runner.os }}-cargo-rust_stable-${{ hashFiles('**/Cargo.toml') }} - name: Format uses: actions-rs/cargo@v1 with: command: fmt args: --all -- --check doc: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 with: submodules: true - name: Setup Rust uses: actions-rs/toolchain@v1 with: toolchain: stable profile: minimal components: rust-docs default: true override: true - name: Install libudev run: sudo apt-get install -y libudev-dev - name: Cargo cache uses: actions/cache@v2 with: path: | ~/.cargo/registry ~/.cargo/git key: ${{ runner.os }}-cargo-rust_stable-${{ hashFiles('**/Cargo.toml') }} - name: Documentation uses: actions-rs/cargo@v1 env: DOCS_RS: 1 with: command: doc args: --features hwdb check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 with: submodules: true - uses: actions-rs/toolchain@v1 with: toolchain: stable profile: minimal components: clippy default: true override: true - name: Install libudev run: sudo apt-get install -y libudev-dev - name: Cargo cache uses: actions/cache@v2 with: path: | ~/.cargo/registry ~/.cargo/git key: ${{ runner.os }}-cargo-rust_stable-${{ hashFiles('**/Cargo.toml') }} - name: Build cache uses: actions/cache@v2 with: path: target key: ${{ runner.os }}-build-rust_stable-check-${{ hashFiles('**/Cargo.toml') }} - uses: actions-rs/clippy-check@v1 with: token: ${{ secrets.GITHUB_TOKEN }} args: --all --features hwdb check-minimal: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 with: submodules: true - uses: actions-rs/toolchain@v1 with: toolchain: nightly profile: minimal default: true override: true - name: Install libudev run: sudo apt-get install -y libudev-dev - name: Cargo cache uses: actions/cache@v2 with: path: | ~/.cargo/registry ~/.cargo/git key: ${{ runner.os }}-cargo-rust_nightly-${{ hashFiles('**/Cargo.toml') }} - name: Build cache uses: actions/cache@v2 with: path: target key: ${{ runner.os }}-build-rust_nightly-check-minimal-${{ hashFiles('**/Cargo.toml') }} - uses: actions-rs/cargo@v1 with: command: check args: --all --features hwdb -Z minimal-versions test: needs: - format - doc - check - check-minimal strategy: fail-fast: ${{ startsWith(github.ref, 'refs/tags/') }} matrix: rust: [stable, beta, nightly] features: - --no-default-features - --no-default-features --features "mio06" - --no-default-features --features "mio07" - --no-default-features --features "mio08" - --no-default-features --features "mio10" - --no-default-features --features "hwdb" - '' # default include: - rust: stable - rust: beta - rust: nightly runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 with: submodules: true - uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.rust }} profile: minimal default: true override: true - name: Install libudev run: sudo apt-get install -y libudev-dev - name: Cargo cache uses: actions/cache@v2 with: path: | ~/.cargo/registry ~/.cargo/git key: ${{ runner.os }}-cargo-rust_stable-${{ hashFiles('**/Cargo.toml') }} - name: Build cache uses: actions/cache@v2 with: path: target key: ${{ runner.os }}-build-rust_stable-check-${{ hashFiles('**/Cargo.toml') }} - name: Update deps uses: actions-rs/cargo@v1 with: command: update - name: Test uses: actions-rs/cargo@v1 env: RUST_BACKTRACE: full with: command: test args: --all ${{ matrix.features }} publish: if: github.repository == 'Smithay/udev-rs' && startsWith(github.ref, 'refs/tags/') needs: - format - doc - check - check-minimal - test runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 with: submodules: true - name: Setup Rust uses: actions-rs/toolchain@v1 with: toolchain: stable override: true - name: Install libudev run: sudo apt-get install -y libudev-dev - name: Publish crates uses: katyo/publish-crates@v1 with: registry-token: ${{ secrets.CRATES_TOKEN }} udev-0.9.0/.gitignore000064400000000000000000000000221046102023000125570ustar 00000000000000target Cargo.lock udev-0.9.0/Cargo.lock0000644000000261400000000000100077630ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "fuchsia-zircon" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" dependencies = [ "bitflags", "fuchsia-zircon-sys", ] [[package]] name = "fuchsia-zircon-sys" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "io-lifetimes" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi", "libc", "windows-sys 0.48.0", ] [[package]] name = "iovec" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" dependencies = [ "libc", ] [[package]] name = "kernel32-sys" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" dependencies = [ "winapi 0.2.8", "winapi-build", ] [[package]] name = "libc" version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libudev-sys" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" dependencies = [ "libc", "pkg-config", ] [[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "mio" version = "0.6.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" dependencies = [ "cfg-if", "fuchsia-zircon", "fuchsia-zircon-sys", "iovec", "kernel32-sys", "libc", "log", "miow 0.2.2", "net2", "slab", "winapi 0.2.8", ] [[package]] name = "mio" version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" dependencies = [ "libc", "log", "miow 0.3.7", "ntapi", "winapi 0.3.9", ] [[package]] name = "mio" version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", "wasi", "windows-sys 0.48.0", ] [[package]] name = "mio" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4929e1f84c5e54c3ec6141cd5d8b5a5c055f031f80cf78f2072920173cb4d880" dependencies = [ "hermit-abi", "libc", "log", "wasi", "windows-sys 0.52.0", ] [[package]] name = "miow" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" dependencies = [ "kernel32-sys", "net2", "winapi 0.2.8", "ws2_32-sys", ] [[package]] name = "miow" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" dependencies = [ "winapi 0.3.9", ] [[package]] name = "net2" version = "0.2.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac" dependencies = [ "cfg-if", "libc", "winapi 0.3.9", ] [[package]] name = "ntapi" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" dependencies = [ "winapi 0.3.9", ] [[package]] name = "pkg-config" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "udev" version = "0.9.0" dependencies = [ "io-lifetimes", "libc", "libudev-sys", "mio 0.6.23", "mio 0.7.14", "mio 0.8.11", "mio 1.0.0", "pkg-config", ] [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winapi" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" [[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-build" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" [[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" [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets 0.48.5", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm 0.48.5", "windows_aarch64_msvc 0.48.5", "windows_i686_gnu 0.48.5", "windows_i686_msvc 0.48.5", "windows_x86_64_gnu 0.48.5", "windows_x86_64_gnullvm 0.48.5", "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "ws2_32-sys" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" dependencies = [ "winapi 0.2.8", "winapi-build", ] udev-0.9.0/Cargo.toml0000644000000027070000000000100100110ustar # 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 are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] name = "udev" version = "0.9.0" authors = [ "David Cuddeback ", "Victoria Brekenfeld ", ] description = "libudev bindings for Rust" homepage = "https://github.com/Smithay/udev-rs" documentation = "http://docs.rs/udev/" readme = "README.md" keywords = [ "udev", "hardware", "bindings", "sysfs", ] license = "MIT" repository = "https://github.com/Smithay/udev-rs" [dependencies.io-lifetimes] version = "1.0.3" [dependencies.libc] version = "0.2" [dependencies.libudev-sys] version = "0.1.4" [dependencies.mio06] version = "^0.6.21" optional = true package = "mio" [dependencies.mio07] version = "0.7" features = ["os-ext"] optional = true package = "mio" [dependencies.mio08] version = "0.8" features = ["os-ext"] optional = true package = "mio" [dependencies.mio10] version = "1.0" features = ["os-ext"] optional = true package = "mio" [build-dependencies.pkg-config] version = "0.3.3" [features] hwdb = [] mio = ["mio10"] udev-0.9.0/Cargo.toml.orig000064400000000000000000000017511046102023000134700ustar 00000000000000[package] name = "udev" version = "0.9.0" authors = ["David Cuddeback ", "Victoria Brekenfeld "] description = "libudev bindings for Rust" license = "MIT" homepage = "https://github.com/Smithay/udev-rs" repository = "https://github.com/Smithay/udev-rs" documentation = "http://docs.rs/udev/" keywords = ["udev", "hardware", "bindings", "sysfs"] readme = "README.md" [dependencies] io-lifetimes = "1.0.3" libudev-sys = "0.1.4" libc = "0.2" mio06 = { package = "mio", version = "^0.6.21", optional = true } mio07 = { package = "mio", version = "0.7", features = ["os-ext"], optional = true } mio08 = { package = "mio", version = "0.8", features = ["os-ext"], optional = true } mio10 = { package = "mio", version = "1.0", features = ["os-ext"], optional = true } [build-dependencies] pkg-config = "0.3.3" #force a newer version for libudev-sys to fix minimal versions [features] mio = ["mio10"] # mio feature defaults to the newest mio version hwdb = [] udev-0.9.0/LICENSE000064400000000000000000000020431046102023000116010ustar 00000000000000Copyright (c) 2015 David Cuddeback 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. udev-0.9.0/README.md000064400000000000000000000047161046102023000120640ustar 00000000000000# udev This crate provides a safe wrapper around the native `libudev` library. It applies the RAII pattern and Rust lifetimes to ensure safe usage of all `libudev` functionality. The RAII pattern ensures that all acquired resources are released when they're no longer needed, and Rust lifetimes ensure that resources are released in a proper order. * [Documentation](http://docs.rs/udev/) ## Dependencies In order to use the `libudev` crate, you must have a Linux system with the `libudev` library installed where it can be found by `pkg-config`. To install `libudev` on Debian-based Linux distributions, execute the following command: ``` sudo apt-get install libudev-dev ``` `libudev` is a Linux-specific package. It is not available for Windows, OS X, or other operating systems. ### Cross-Compiling The `libudev` crate can be used when cross-compiling to a foreign target. Details on how to cross-compile `libudev` are explained in the [`libudev-sys` crate's README](https://github.com/dcuddeback/libudev-sys#cross-compiling). ## Usage Add `udev` as a dependency in `Cargo.toml`: ```toml [dependencies] udev = "^0.9.0" ``` If you plan to support operating systems other than Linux, you'll need to add `udev` as a target-specific dependency: ```toml [target.x86_64-unknown-linux-gnu.dependencies] udev = "^0.9.0" ``` Import the `udev` crate. ```rust extern crate udev; fn main() { let mut enumerator = udev::Enumerator::new().unwrap(); enumerator.match_subsystem("tty").unwrap(); for device in enumerator.scan_devices().unwrap() { println!("found device: {:?}", device.syspath()); } } ``` ## Contributors * [drakulix](https://github.com/drakulix) * [dcuddeback](https://github.com/dcuddeback) * [mulkieran](https://github.com/mulkieran) * [Susurrus](https://github.com/Susurrus) * [woodruffw](https://github.com/woodruffw) * [Ravenslofty](https://github.com/Ravenslofty) * [sjoerdsimons](https://github.com/sjoerdsimons) * [anelson](https://github.com/anelson) * [ollpu](https://github.com/ollpu) * [a1ien](https://github.com/a1ien) * [lj94093](https://github.com/lj94093) * [patrickelectric](https://github.com/patrickelectric) * [TomzBench](https://github.com/TomzBench) ## License Copyright © 2017 Victoria Brekenfeld Copyright © 2015 David Cuddeback Copyright for portions of the project are held by [David Cuddeback, 2015] as part of the project. All other copyright for the project are held by [Victoria Brekenfeld, 2017]. Distributed under the [MIT License](LICENSE). udev-0.9.0/examples/list_devices.rs000064400000000000000000000011031046102023000154310ustar 00000000000000extern crate udev; use std::io; fn main() -> io::Result<()> { let mut enumerator = udev::Enumerator::new()?; for device in enumerator.scan_devices()? { println!(); println!("{:#?}", device); println!(" [properties]"); for property in device.properties() { println!(" - {:?} {:?}", property.name(), property.value()); } println!(" [attributes]"); for attribute in device.attributes() { println!(" - {:?} {:?}", attribute.name(), attribute.value()); } } Ok(()) } udev-0.9.0/examples/monitor.rs000064400000000000000000000112141046102023000144470ustar 00000000000000extern crate libc; #[cfg(feature = "mio06")] extern crate mio06; #[cfg(feature = "mio07")] extern crate mio07; #[cfg(feature = "mio08")] extern crate mio08; #[cfg(feature = "mio10")] extern crate mio10; extern crate udev; use std::io; #[cfg(not(any( feature = "mio06", feature = "mio07", feature = "mio08", feature = "mio10" )))] mod poll { use std::io; use std::ptr; use std::thread; use std::time::Duration; use std::os::unix::io::AsRawFd; use libc::{c_int, c_short, c_ulong, c_void}; #[repr(C)] #[allow(non_camel_case_types)] struct pollfd { fd: c_int, events: c_short, revents: c_short, } #[repr(C)] #[allow(non_camel_case_types)] struct sigset_t { __private: c_void, } #[allow(non_camel_case_types)] type nfds_t = c_ulong; const POLLIN: c_short = 0x0001; extern "C" { fn ppoll( fds: *mut pollfd, nfds: nfds_t, timeout_ts: *mut libc::timespec, sigmask: *const sigset_t, ) -> c_int; } pub fn poll(socket: udev::MonitorSocket) -> io::Result<()> { println!("Use syspoll"); let mut fds = vec![pollfd { fd: socket.as_raw_fd(), events: POLLIN, revents: 0, }]; loop { let result = unsafe { ppoll( (&mut fds[..]).as_mut_ptr(), fds.len() as nfds_t, ptr::null_mut(), ptr::null(), ) }; if result < 0 { return Err(io::Error::last_os_error()); } let event = match socket.iter().next() { Some(evt) => evt, None => { thread::sleep(Duration::from_millis(10)); continue; } }; super::print_event(event); } } } #[cfg(feature = "mio06")] mod poll { use std::io; use mio06::{Events, Poll, PollOpt, Ready, Token}; pub fn poll(mut socket: udev::MonitorSocket) -> io::Result<()> { println!("Use mio06 poll"); let poll = Poll::new()?; let mut events = Events::with_capacity(1024); poll.register( &mut socket, Token(0), Ready::readable() | Ready::writable(), PollOpt::edge(), )?; loop { poll.poll(&mut events, None)?; for event in &events { if event.token() == Token(0) && event.readiness().is_writable() { socket.iter().for_each(|x| super::print_event(x)); } } } } } #[cfg(any(feature = "mio07", feature = "mio08", feature = "mio10"))] mod poll { use std::io; #[cfg(feature = "mio07")] use mio07::{Events, Interest, Poll, Token}; #[cfg(feature = "mio08")] use mio08::{Events, Interest, Poll, Token}; #[cfg(feature = "mio10")] use mio10::{Events, Interest, Poll, Token}; pub fn poll(mut socket: udev::MonitorSocket) -> io::Result<()> { let version = if cfg!(feature = "mio07") { "mio07" } else if cfg!(feature = "mio08") { "mio08" } else if cfg!(feature = "mio10") { "mio10" } else { "mio-unknown" }; println!("Use {} poll", version); let mut poll = Poll::new()?; let mut events = Events::with_capacity(1024); poll.registry().register( &mut socket, Token(0), Interest::READABLE | Interest::WRITABLE, )?; loop { poll.poll(&mut events, None)?; for event in &events { if event.token() == Token(0) && event.is_writable() { socket.iter().for_each(|x| super::print_event(x)); } } } } } fn print_event(event: udev::Event) { println!( "{}: {} {} (subsystem={}, sysname={}, devtype={})", event.sequence_number(), event.event_type(), event.syspath().to_str().unwrap_or("---"), event .subsystem() .map_or("", |s| { s.to_str().unwrap_or("") }), event.sysname().to_str().unwrap_or(""), event.devtype().map_or("", |s| { s.to_str().unwrap_or("") }) ); } // Use `mio::poll` as poller by compile with: // `cargo run --example monitor --features "mio10"` fn main() -> io::Result<()> { let socket = udev::MonitorBuilder::new()? // .match_subsystem_devtype("usb", "usb_device")? .match_subsystem_devtype("block", "disk")? .listen()?; poll::poll(socket) } udev-0.9.0/src/device.rs000064400000000000000000000510701046102023000131740ustar 00000000000000use std::str; use std::ffi::{CStr, CString, OsStr}; use std::io::Result; use std::marker::PhantomData; use std::path::Path; use std::ptr; use std::str::FromStr; use libc::{c_char, dev_t}; use list::{Entry, EntryList}; use Udev; use {ffi, util}; use {AsRaw, FromRaw}; /// A structure that provides access to sysfs/kernel devices. pub struct Device { udev: Udev, device: *mut ffi::udev_device, } /// Permissible types of UNIX file I/O API device special file. /// /// See also [`from_devnum`][crate::Device::from_devnum]. #[repr(u8)] pub enum DeviceType { /// UNIX character-style file IO semantics. Character = b'c', /// UNIX block-style file IO semantics. Block = b'b', } impl std::fmt::Debug for Device { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Device") .field("initialized", &self.is_initialized()) .field("device_major_minor_number", &self.devnum()) .field("system_path", &self.syspath()) .field("device_path", &self.devpath()) .field("device_node", &self.devnode()) .field("subsystem_name", &self.subsystem()) .field("system_name", &self.sysname()) .field("instance_number", &self.sysnum()) .field("device_type", &self.devtype()) .field("driver", &self.driver()) .field("action", &self.action()) .field("parent", &self.parent()) .finish() } } impl Clone for Device { fn clone(&self) -> Self { Self { udev: self.udev.clone(), device: unsafe { ffi::udev_device_ref(self.device) }, } } } impl Drop for Device { fn drop(&mut self) { unsafe { ffi::udev_device_unref(self.device); } } } as_ffi_with_context!(Device, device, ffi::udev_device, ffi::udev_device_ref); /// A convenience alias for a list of properties, bound to a device. pub type Properties<'a> = EntryList<'a, Device>; /// A convenience alias for a list of attributes, bound to a device. pub struct Attributes<'a> { entries: EntryList<'a, Device>, device: &'a Device, } impl Device { /// Creates a device for a given syspath. /// /// The `syspath` parameter should be a path to the device file within the `sysfs` file system, /// e.g., `/sys/devices/virtual/tty/tty0`. pub fn from_syspath(syspath: &Path) -> Result { // Create a new Udev context for this device // It would be more efficient to allow callers to create just one context and use multiple // devices, however that would be an API-breaking change. // // When devices are enumerated using an `Enumerator`, it will use // `from_syspath_with_context` which can reuse the existing `Udev` context to avoid this // extra overhead. let udev = Udev::new()?; Self::from_syspath_with_context(udev, syspath) } /// Creates a device for a given syspath, using an existing `Udev` instance rather than /// creating one automatically. /// /// The `syspath` parameter should be a path to the device file within the `sysfs` file system, /// e.g., `/sys/devices/virtual/tty/tty0`. pub fn from_syspath_with_context(udev: Udev, syspath: &Path) -> Result { let syspath = util::os_str_to_cstring(syspath)?; let ptr = try_alloc!(unsafe { ffi::udev_device_new_from_syspath(udev.as_raw(), syspath.as_ptr()) }); Ok(Self::from_raw(udev, ptr)) } /// Create new udev device, and fill in information from the sys device /// and the udev database entry. /// /// The device is looked up by the `subsystem` and `sysname` string of the device, like "mem" / "zero", or "block" / "sda". pub fn from_subsystem_sysname(subsystem: String, sysname: String) -> Result { let subsystem = CString::new(subsystem.as_bytes()) .ok() .ok_or(std::io::Error::from_raw_os_error(libc::EINVAL))?; let sysname = CString::new(sysname.as_bytes()) .ok() .ok_or(std::io::Error::from_raw_os_error(libc::EINVAL))?; let udev = Udev::new()?; let ptr = try_alloc!(unsafe { ffi::udev_device_new_from_subsystem_sysname( udev.as_raw(), subsystem.as_ptr(), sysname.as_ptr(), ) }); Ok(Self::from_raw(udev, ptr)) } /// Create new udev device, and fill in information from the sys device /// and the udev database entry, using an existing `Udev` instance rather than /// creating a new one. /// /// The device is looked up by the `subsystem` and `sysname` string of the device, like "mem" / "zero", or "block" / "sda". pub fn from_subsystem_sysname_with_context( udev: Udev, subsystem: String, sysname: String, ) -> Result { let subsystem = CString::new(subsystem.as_bytes()) .ok() .ok_or(std::io::Error::from_raw_os_error(libc::EINVAL))?; let sysname = CString::new(sysname.as_bytes()) .ok() .ok_or(std::io::Error::from_raw_os_error(libc::EINVAL))?; let ptr = try_alloc!(unsafe { ffi::udev_device_new_from_subsystem_sysname( udev.as_raw(), subsystem.as_ptr(), sysname.as_ptr(), ) }); Ok(Self::from_raw(udev, ptr)) } /// Creates a rust udev `Device` for a given UNIX device "special file" type and number. /// /// The `dev_type` parameter indicates which of the historical UNIX file-like I/O paradigms the /// device permits, and is either [`DeviceType::Character`] or [`DeviceType::Block`]. /// /// n.b. This function follows the naming used by the underlying `libudev` function. As with /// the underlying function, there is **no** **direct** **correspondence** between this /// function's `dev_type` parameter and string values returned by [`devtype`][Self::devtype]. /// i.e. They represent different underlying concepts within the OS kernel. /// /// The `devnum` parameter is of type [`libc::dev_t`][libc::dev_t] which encodes the historical /// UNIX major and minor device numbers (see below). /// /// Typically both parameters would be determined at run-time by calling one of the `stat` /// family of system calls (or Rust std library functions which utilise them) on a filesystem /// "special file" inode (e.g. `/dev/null`) or (more commonly) on a symbolic link to such a /// file which was created by the `udevd` system daemon such as those under `/dev/disk/`. /// /// ``` /// use std::{env, fs, os::linux::fs::MetadataExt}; /// use udev::DeviceType; /// /// fn main() -> std::io::Result<()> { /// let args: Vec = env::args().collect(); /// # // Examples are automatically run as tests: provide dummy args for cargo test. /// # let args: Vec = vec!("testname".into(), "/dev/null".into()); /// let path = args.get(1).expect("No filename given"); /// let metadata = fs::metadata(path).unwrap_or_else(|_| panic!("Can't open file: {}", path)); /// let devtype = match metadata.st_mode() & libc::S_IFMT { /// libc::S_IFCHR => Some(DeviceType::Character), /// libc::S_IFBLK => Some(DeviceType::Block), /// _ => None, /// }.expect("Not a character or block special file"); /// let ud = udev::Device::from_devnum(devtype, metadata.st_rdev()) /// .expect("Couldn't construct udev from supplied path"); /// println!("syspath of {} is {:?}", path, ud.syspath()); /// let dn = ud.devnum(); /// println!("devnum: {}", dn.unwrap()); /// Ok(()) /// } /// ``` /// The user should be aware that a given device may change its major and/or minor number /// across reboots, when the hardware attached to the device is subject to hot-plug events, or /// for a variety of other reasons. /// /// The `udevd` system daemon (or equivalent) is configured to dynamically create filesystem /// symbolic links (examples of which can be seen under e.g. `/dev/disk/by-id/` on most Linux /// systems), the purpose of which is to provide a predictable and persistent means of /// identifying devices which themselves have a persistent state or identity. /// /// Code similar to the sample presented above may be used to obtain a [`udev::Device`][Self] /// corresponding to the filesystem path of the UNIX file I/O style device node or symbolic /// link. /// /// Historical UNIX systems statically allocated their internal data structures which were /// associated with devices that exposed a "file-like" user-space API (e.g. `/dev/null`). A /// device could be uniquely and persistently identified by combining its type (either /// "character" or "block"), with its major and minor device numbers. /// /// In the underlying OS kernel, a major number might be allocated to a single device driver /// such as a SCSI disk controller, and that device driver would allocate the minor device /// number (e.g. `4` might have represented the 4th SCSI device addressable by a particular /// SCSI host adapter). The `mknod` system utility would be used to create friendly filesystem /// paths in the filesystem, which corresponded with these attributes, and file permissions /// would be managed with utilities such as `chown` and `chmod` etc. and the numbers would not /// change between system reboots. /// /// As has been noted, modern UNIX-like operating systems dynamically allocate devices. To /// provide backward compatibility with existing user-space APIs, the concept of major/minor /// devices being associated with file system "special file" inodes has been retained. /// /// For udev devices which present a UNIX file I/O style interface (i.e. via `/dev/` paths), /// the Linux `udevadm` utility currently reports devices belonging to the `"block"` subsystem /// to be of type "block", and all other file I/O style udev devices to be of type "character". /// /// Those needing to compose or decompose values of type `dev_t` should refer to /// [`libc::major`], [`libc::minor`], [`libc::makedev`] and equivalent functionality from /// higher-level rust crates. pub fn from_devnum(dev_type: self::DeviceType, devnum: dev_t) -> Result { let udev = Udev::new()?; Self::from_devnum_with_context(udev, dev_type, devnum) } /// Creates a rust udev `Device` for a given UNIX device "special file" type and number. Uses /// an existing [`Udev`] instance rather than creating one automatically. /// /// See [`from_devnum`][Self::from_devnum] for detailed usage. pub fn from_devnum_with_context( udev: Udev, dev_type: self::DeviceType, devnum: dev_t, ) -> Result { let ptr = try_alloc!(unsafe { ffi::udev_device_new_from_devnum(udev.as_raw(), dev_type as c_char, devnum) }); Ok(Self::from_raw(udev, ptr)) } /// Creates a rust `Device` given an already created libudev `ffi::udev_device*` and a /// corresponding `Udev` instance from which the device was created. /// /// This guarantees that the `Udev` will live longer than the corresponding `Device` pub(crate) fn from_raw(udev: Udev, ptr: *mut ffi::udev_device) -> Self { Self { udev, device: ptr } } /// Checks whether the device has already been handled by udev. /// /// When a new device is connected to the system, udev initializes the device by setting /// permissions, renaming network devices, and possibly other initialization routines. This /// method returns `true` if udev has performed all of its work to initialize this device. /// /// This method only applies to devices with device nodes or network interfaces. All other /// devices return `true` by default. pub fn is_initialized(&self) -> bool { unsafe { ffi::udev_device_get_is_initialized(self.device) > 0 } } /// Gets the device's major/minor number. pub fn devnum(&self) -> Option { match unsafe { ffi::udev_device_get_devnum(self.device) } { 0 => None, n => Some(n), } } /// Returns the syspath of the device. /// /// The path is an absolute path and includes the sys mount point. For example, the syspath for /// `tty0` could be `/sys/devices/virtual/tty/tty0`, which includes the sys mount point, /// `/sys`. pub fn syspath(&self) -> &Path { Path::new(unsafe { util::ptr_to_os_str_unchecked(ffi::udev_device_get_syspath(self.device)) }) } /// Returns the kernel devpath value of the device. /// /// The path does not contain the sys mount point, but does start with a `/`. For example, the /// devpath for `tty0` could be `/devices/virtual/tty/tty0`. pub fn devpath(&self) -> &OsStr { unsafe { util::ptr_to_os_str_unchecked(ffi::udev_device_get_devpath(self.device)) } } /// Returns the path to the device node belonging to the device. /// /// The path is an absolute path and starts with the device directory. For example, the device /// node for `tty0` could be `/dev/tty0`. pub fn devnode(&self) -> Option<&Path> { unsafe { util::ptr_to_os_str(ffi::udev_device_get_devnode(self.device)) } .map(|path| Path::new(path)) } /// Returns the parent of the device. pub fn parent(&self) -> Option { let ptr = unsafe { ffi::udev_device_get_parent(self.device) }; if ptr.is_null() { return None; } Some(Self::from_raw(self.udev.clone(), unsafe { ffi::udev_device_ref(ptr) })) } /// Returns the parent of the device with the matching subsystem and devtype if any. pub fn parent_with_subsystem>(&self, subsystem: T) -> Result> { let subsystem = util::os_str_to_cstring(subsystem)?; let ptr = unsafe { ffi::udev_device_get_parent_with_subsystem_devtype( self.device, subsystem.as_ptr(), ptr::null(), ) }; if ptr.is_null() { return Ok(None); } Ok(Some(Self::from_raw(self.udev.clone(), unsafe { ffi::udev_device_ref(ptr) }))) } /// Returns the parent of the device with the matching subsystem and devtype if any. pub fn parent_with_subsystem_devtype, U: AsRef>( &self, subsystem: T, devtype: U, ) -> Result> { let subsystem = util::os_str_to_cstring(subsystem)?; let devtype = util::os_str_to_cstring(devtype)?; let ptr = unsafe { ffi::udev_device_get_parent_with_subsystem_devtype( self.device, subsystem.as_ptr(), devtype.as_ptr(), ) }; if ptr.is_null() { return Ok(None); } Ok(Some(Self::from_raw(self.udev.clone(), unsafe { ffi::udev_device_ref(ptr) }))) } /// Returns the subsystem name of the device. /// /// The subsystem name is a string that indicates which kernel subsystem the device belongs to. /// Examples of subsystem names are `tty`, `vtconsole`, `block`, `scsi`, and `net`. pub fn subsystem(&self) -> Option<&OsStr> { unsafe { util::ptr_to_os_str(ffi::udev_device_get_subsystem(self.device)) } } /// Returns the kernel device name for the device. /// /// The sysname is a string that differentiates the device from others in the same subsystem. /// For example, `tty0` is the sysname for a TTY device that differentiates it from others, /// such as `tty1`. pub fn sysname(&self) -> &OsStr { unsafe { util::ptr_to_os_str_unchecked(ffi::udev_device_get_sysname(self.device)) } } /// Returns the instance number of the device. /// /// The instance number is used to differentiate many devices of the same type. For example, /// `/dev/tty0` and `/dev/tty1` are both TTY devices but have instance numbers of 0 and 1, /// respectively. /// /// Some devices don't have instance numbers, such as `/dev/console`, in which case the method /// returns `None`. pub fn sysnum(&self) -> Option { let ptr = unsafe { ffi::udev_device_get_sysnum(self.device) }; if ptr.is_null() { return None; } match str::from_utf8(unsafe { CStr::from_ptr(ptr) }.to_bytes()) { Err(_) => None, Ok(s) => FromStr::from_str(s).ok(), } } /// Returns the devtype name of the device (if any), for example "disk". pub fn devtype(&self) -> Option<&OsStr> { unsafe { util::ptr_to_os_str(ffi::udev_device_get_devtype(self.device)) } } /// Returns the name of the kernel driver attached to the device. pub fn driver(&self) -> Option<&OsStr> { unsafe { util::ptr_to_os_str(ffi::udev_device_get_driver(self.device)) } } /// Retrieves the value of a device property. pub fn property_value>(&self, property: T) -> Option<&OsStr> { let prop = match util::os_str_to_cstring(property) { Ok(s) => s, Err(_) => return None, }; unsafe { util::ptr_to_os_str(ffi::udev_device_get_property_value( self.device, prop.as_ptr(), )) } } /// Retrieves the value of a device attribute. pub fn attribute_value>(&self, attribute: T) -> Option<&OsStr> { let attr = match util::os_str_to_cstring(attribute) { Ok(s) => s, Err(_) => return None, }; unsafe { util::ptr_to_os_str(ffi::udev_device_get_sysattr_value( self.device, attr.as_ptr(), )) } } /// Sets the value of a device attribute. pub fn set_attribute_value, U: AsRef>( &mut self, attribute: T, value: U, ) -> Result<()> { let attribute = util::os_str_to_cstring(attribute)?; let value = util::os_str_to_cstring(value)?; util::errno_to_result(unsafe { ffi::udev_device_set_sysattr_value( self.device, attribute.as_ptr(), value.as_ptr() as *mut c_char, ) }) } /// Returns an iterator over the device's properties. /// /// ## Example /// /// This example prints out all of a device's properties: /// /// ```no_run /// # use std::path::Path; /// # let device = udev::Device::from_syspath(Path::new("/sys/devices/virtual/tty/tty0")).unwrap(); /// for property in device.properties() { /// println!("{:?} = {:?}", property.name(), property.value()); /// } /// ``` pub fn properties(&self) -> Properties { Properties { entry: unsafe { ffi::udev_device_get_properties_list_entry(self.device) }, phantom: PhantomData, } } /// Returns an iterator over the device's attributes. /// /// ## Example /// /// This example prints out all of a device's attributes: /// /// ```no_run /// # use std::path::Path; /// # let device = udev::Device::from_syspath(Path::new("/sys/devices/virtual/tty/tty0")).unwrap(); /// for attribute in device.attributes() { /// println!("{:?} = {:?}", attribute.name(), attribute.value()); /// } /// ``` pub fn attributes(&self) -> Attributes { Attributes { entries: EntryList { entry: unsafe { ffi::udev_device_get_sysattr_list_entry(self.device) }, phantom: PhantomData, }, device: self, } } /// Returns the device action for the device. pub fn action(&self) -> Option<&OsStr> { unsafe { util::ptr_to_os_str(ffi::udev_device_get_action(self.device)) } } } impl<'a> Iterator for Attributes<'a> { type Item = Entry<'a>; // The list of sysattr entries only contains the attribute names, with // the values being empty. To get the value, each has to be queried. fn next(&mut self) -> Option> { match self.entries.next() { Some(Entry { name, value: _ }) => Some(Entry { name, value: self.device.attribute_value(name), }), None => None, } } fn size_hint(&self) -> (usize, Option) { (0, None) } } udev-0.9.0/src/enumerator.rs000064400000000000000000000202711046102023000141150ustar 00000000000000use std::ffi::OsStr; use std::io::Result; use std::marker::PhantomData; use std::path::Path; use Udev; use {ffi, list::List, util}; use {AsRaw, AsRawWithContext, Device, FromRaw}; /// An enumeration context. /// /// An Enumerator scans `/sys` for devices matching its filters. Filters are added to an Enumerator /// by calling its `match_*` and `nomatch_*` methods. After the filters are setup, the /// `scan_devices()` method finds devices in `/sys` that match the filters. pub struct Enumerator { udev: Udev, enumerator: *mut ffi::udev_enumerate, } impl Clone for Enumerator { fn clone(&self) -> Self { Self { udev: self.udev.clone(), enumerator: unsafe { ffi::udev_enumerate_ref(self.enumerator) }, } } } impl Drop for Enumerator { fn drop(&mut self) { unsafe { ffi::udev_enumerate_unref(self.enumerator) }; } } as_ffi_with_context!( Enumerator, enumerator, ffi::udev_enumerate, ffi::udev_enumerate_ref ); impl Enumerator { /// Creates a new Enumerator. pub fn new() -> Result { // Create a new Udev context for this enumeration let udev = Udev::new()?; Self::with_udev(udev) } /// Creates a new `Enumerator` with an existing `Udev` instance pub fn with_udev(udev: Udev) -> Result { let ptr = try_alloc!(unsafe { ffi::udev_enumerate_new(udev.as_raw()) }); Ok(Self { udev, enumerator: ptr, }) } /// Adds a filter that matches only initialized devices. pub fn match_is_initialized(&mut self) -> Result<()> { util::errno_to_result(unsafe { ffi::udev_enumerate_add_match_is_initialized(self.enumerator) }) } /// Adds a filter that matches only devices that belong to the given kernel subsystem. pub fn match_subsystem>(&mut self, subsystem: T) -> Result<()> { let subsystem = util::os_str_to_cstring(subsystem)?; util::errno_to_result(unsafe { ffi::udev_enumerate_add_match_subsystem(self.enumerator, subsystem.as_ptr()) }) } /// Adds a filter that matches only devices with the given attribute value. pub fn match_attribute, U: AsRef>( &mut self, attribute: T, value: U, ) -> Result<()> { let attribute = util::os_str_to_cstring(attribute)?; let value = util::os_str_to_cstring(value)?; util::errno_to_result(unsafe { ffi::udev_enumerate_add_match_sysattr( self.enumerator, attribute.as_ptr(), value.as_ptr(), ) }) } /// Adds a filter that matches only devices with the given kernel device name. pub fn match_sysname>(&mut self, sysname: T) -> Result<()> { let sysname = util::os_str_to_cstring(sysname)?; util::errno_to_result(unsafe { ffi::udev_enumerate_add_match_sysname(self.enumerator, sysname.as_ptr()) }) } /// Adds a filter that matches only devices with the given property value. pub fn match_property, U: AsRef>( &mut self, property: T, value: U, ) -> Result<()> { let property = util::os_str_to_cstring(property)?; let value = util::os_str_to_cstring(value)?; util::errno_to_result(unsafe { ffi::udev_enumerate_add_match_property( self.enumerator, property.as_ptr(), value.as_ptr(), ) }) } /// Adds a filter that matches only devices with the given tag. pub fn match_tag>(&mut self, tag: T) -> Result<()> { let tag = util::os_str_to_cstring(tag)?; util::errno_to_result(unsafe { ffi::udev_enumerate_add_match_tag(self.enumerator, tag.as_ptr()) }) } /// Includes the parent device and all devices in the subtree of the parent device. pub fn match_parent(&mut self, parent: &Device) -> Result<()> { util::errno_to_result(unsafe { ffi::udev_enumerate_add_match_parent(self.enumerator, parent.as_raw()) }) } /// Adds a filter that matches only devices that don't belong to the given kernel subsystem. pub fn nomatch_subsystem>(&mut self, subsystem: T) -> Result<()> { let subsystem = util::os_str_to_cstring(subsystem)?; util::errno_to_result(unsafe { ffi::udev_enumerate_add_nomatch_subsystem(self.enumerator, subsystem.as_ptr()) }) } /// Adds a filter that matches only devices that don't have the the given attribute value. pub fn nomatch_attribute, U: AsRef>( &mut self, attribute: T, value: U, ) -> Result<()> { let attribute = util::os_str_to_cstring(attribute)?; let value = util::os_str_to_cstring(value)?; util::errno_to_result(unsafe { ffi::udev_enumerate_add_nomatch_sysattr( self.enumerator, attribute.as_ptr(), value.as_ptr(), ) }) } /// Includes the device with the given syspath. pub fn add_syspath>(&mut self, syspath: T) -> Result<()> { let syspath = util::os_str_to_cstring(syspath)?; util::errno_to_result(unsafe { ffi::udev_enumerate_add_syspath(self.enumerator, syspath.as_ptr()) }) } /// Scans `/sys` for devices matching the attached filters. /// /// The devices will be sorted in dependency order. pub fn scan_devices(&mut self) -> Result> { util::errno_to_result(unsafe { ffi::udev_enumerate_scan_devices(self.enumerator) })?; Ok(Devices { entry: unsafe { ffi::udev_enumerate_get_list_entry(self.enumerator) }, phantom: PhantomData, }) } } /// Iterator over devices. pub type Devices<'a> = List<'a, Enumerator, Device>; impl<'a> Iterator for Devices<'a> { type Item = Device; fn next(&mut self) -> Option { while !self.entry.is_null() { let syspath = Path::new(unsafe { util::ptr_to_os_str_unchecked(ffi::udev_list_entry_get_name(self.entry)) }); self.entry = unsafe { ffi::udev_list_entry_get_next(self.entry) }; match Device::from_syspath(syspath) { Ok(d) => return Some(d), Err(_) => continue, }; } None } fn size_hint(&self) -> (usize, Option) { (0, None) } } #[cfg(test)] mod tests { use super::*; use crate::{AsRawWithContext, FromRawWithContext}; #[test] fn create_enumerator() { Enumerator::new().unwrap(); } #[test] fn round_trip_to_raw_pointers() { let enumerator = Enumerator::new().unwrap(); // Round-trip this to raw pointers and back again let (udev, ptr) = enumerator.into_raw_with_context(); let mut enumerator = unsafe { Enumerator::from_raw_with_context(udev, ptr) }; // Everything should still work just the same after round-tripping let _ = enumerator.scan_devices().unwrap().collect::>(); } #[test] fn test_enumeration() { fn find_hidraws(en: &mut Enumerator) -> Devices<'_> { en.match_is_initialized().unwrap(); en.match_subsystem("hidraw").unwrap(); en.scan_devices().unwrap() } let mut en = Enumerator::new().unwrap(); for dev in find_hidraws(&mut en) { println!("Found a hidraw at {:?}", dev.devnode()); } } // The above test which limits devices to `hidraw` did not reproduce the crash on libudev 215 // caused by the use of a bogus udev context. Clearly it's important to test all enumeration // pathways. // // This test is intended to reproduce https://github.com/Smithay/udev-rs/issues/18 when run on // a system like Debian 8 "jessie" which runs an older libudev #[test] fn test_enumerate_all() { let mut en = Enumerator::new().unwrap(); for dev in en.scan_devices().unwrap() { println!("Found a device at {:?}", dev.devnode()); } } } udev-0.9.0/src/hwdb.rs000064400000000000000000000065771046102023000126750ustar 00000000000000use std::ffi::{CString, OsStr}; use std::io::Result; use std::marker::PhantomData; use std::os::unix::ffi::OsStrExt; use libc::c_char; use ffi; use list::EntryList; use FromRaw; /// Rust wrapper for the `udev_hwdb` struct, which provides access to `udev`'s /// hardware database API. /// /// Like the `udev` struct, `udev_hwdb` is refcounted and automatically managed /// by the Rust wrapper. pub struct Hwdb { hwdb: *mut ffi::udev_hwdb, } impl Clone for Hwdb { fn clone(&self) -> Self { unsafe { Self::from_raw(ffi::udev_hwdb_ref(self.hwdb)) } } } impl Drop for Hwdb { fn drop(&mut self) { unsafe { ffi::udev_hwdb_unref(self.hwdb) }; } } as_ffi!(Hwdb, hwdb, ffi::udev_hwdb, ffi::udev_hwdb_ref); impl Hwdb { /// Creates a new Hwdb context. pub fn new() -> Result { // NOTE: udev_hwdb_new states that its first parameter is unused. // However, older versions of udev check it against NULL, so we can't just pass an // empty pointer in. Instead, we pass in a garbage pointer. let junk: *mut ffi::udev = 0x41414141_41414141 as *mut ffi::udev; let ptr = try_alloc!(unsafe { ffi::udev_hwdb_new(junk) }); Ok(unsafe { Self::from_raw(ptr) }) } /// Queries the hardware database with the given `modalias` query, /// returning an iterator over each matching entry. pub fn query>(&self, modalias: S) -> EntryList { // NOTE: This expect can fail if someone passes a string that contains an internal NUL. let modalias = CString::new(modalias.as_ref().as_bytes()) .expect("query() called with malformed modalias string"); EntryList { entry: unsafe { ffi::udev_hwdb_get_properties_list_entry( self.hwdb, modalias.as_ptr() as *const c_char, 0, ) }, phantom: PhantomData, } } /// Returns the first entry value with the given name, or `None` if no result exists. pub fn query_one>(&self, modalias: S, name: S) -> Option<&OsStr> { self.query(modalias) .find(|e| e.name == name.as_ref()) .map(|e| e.value.unwrap_or_else(|| OsStr::new(""))) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_query() { let hwdb = Hwdb::new().unwrap(); // Query the hwdb for a device that should always be known: // the Linux Foundation's USB 1.1. root hub let results: Vec<_> = hwdb.query("usb:v1D6Bp0001").collect(); assert!(results.len() >= 2); // We expect an ID_VENDOR_FROM_DATABASE and an ID_MODEL_FROM_DATABASE with corresponding // values; no order is specified by udev. assert!(results.iter().any(|e| e.name == "ID_VENDOR_FROM_DATABASE")); assert!(results.iter().any(|e| e.name == "ID_MODEL_FROM_DATABASE")); assert!(results .iter() .any(|e| e.value.unwrap_or(OsStr::new("")) == "Linux Foundation")); assert!(results .iter() .any(|e| e.value.unwrap_or(OsStr::new("")) == "1.1 root hub")); } #[test] fn test_query_one() { let hwdb = Hwdb::new().unwrap(); let value = hwdb .query_one("usb:v1D6Bp0001", "ID_MODEL_FROM_DATABASE") .unwrap(); assert_eq!(value, "1.1 root hub"); } } udev-0.9.0/src/lib.rs000064400000000000000000000154641046102023000125120ustar 00000000000000//! //! libudev Bindings for Rust //! #![warn(missing_docs)] extern crate io_lifetimes; extern crate libc; pub extern crate libudev_sys as ffi; #[cfg(feature = "mio06")] extern crate mio06; #[cfg(feature = "mio07")] extern crate mio07; #[cfg(feature = "mio08")] extern crate mio08; #[cfg(feature = "mio10")] extern crate mio10; pub use device::{Attributes, Device, DeviceType, Properties}; pub use enumerator::{Devices, Enumerator}; #[cfg(feature = "hwdb")] pub use hwdb::Hwdb; pub use list::{Entry, List}; pub use monitor::{ Builder as MonitorBuilder, Event, EventType, Socket as MonitorSocket, SocketIter as MonitorSocketIter, }; pub use udev::Udev; macro_rules! try_alloc { ($exp:expr) => {{ let ptr = $exp; if ptr.is_null() { return Err(std::io::Error::last_os_error()); } ptr }}; } /// Receive the underlying raw pointer pub trait AsRaw { /// Get a reference of the underlying struct. /// /// The reference count will not be increased. fn as_raw(&self) -> *mut T; /// Convert the object into the underlying pointer. /// /// You are responsible for freeing the object. fn into_raw(self) -> *mut T; } /// Receive the underlying raw pointer for types with an associated `udev` struct which must /// outlive them. pub trait AsRawWithContext { /// Get a reference of the underlying struct. /// /// The reference count will not be increased. fn as_raw(&self) -> *mut T; /// The `udev` context with which this struct was created. This must live at least as long as /// the struct itself or undefined behavior will result. fn udev(&self) -> &Udev; /// Convert the object into the raw `udev` pointer and the underlying pointer for this object. /// /// You are responsible for freeing both. You're also responsible for ensuring that the `udev` /// pointer is not freed until after this object's pointer is freed. fn into_raw_with_context(self) -> (*mut ffi::udev, *mut T); } /// Convert from a raw pointer pub trait FromRaw { /// Create an object from a given raw pointer. /// /// The reference count will not be increased, be sure not to free this pointer. /// /// ## Safety /// /// The pointer has to be a valid reference to the expected underlying udev-struct or undefined /// behaviour might occur. unsafe fn from_raw(ptr: *mut T) -> Self; } /// Convert from a raw pointer for types which must be associated with a `Udev` context object. pub trait FromRawWithContext { /// Create an object from a given raw pointer and `udev` context pointer. /// /// The reference count will not be increased, be sure not to free this pointer. /// /// ## Safety /// /// The `udev` pointer must correspond to the `udev` pointer used when `ptr` was created. If /// not memory corruption and undefined behavior will result. /// /// Both the `udev` and `ptr` pointers must be a valid reference to the expected underlying udev-struct or undefined /// behaviour might occur. Do NOT attempt to free either pointer; `udev_unref` and the /// corresponding `*_unref` function for `ptr` will be called automatically when this type is /// dropped. unsafe fn from_raw_with_context(udev: *mut ffi::udev, ptr: *mut T) -> Self; } /// Convert from a raw pointer and the matching context macro_rules! as_ffi { ($struct_:ident, $field:ident, $type_:ty, $ref:path) => { as_raw!($struct_, $field, $type_, $ref); from_raw!($struct_, $field, $type_); }; } macro_rules! as_ffi_with_context { ($struct_:ident, $field:ident, $type_:ty, $ref:path) => { as_raw_with_context!($struct_, $field, $type_, $ref); from_raw_with_context!($struct_, $field, $type_); }; } macro_rules! as_raw { ($struct_:ident, $field:ident, $type_:ty, $ref:path) => { impl $crate::AsRaw<$type_> for $struct_ { fn as_raw(&self) -> *mut $type_ { self.$field } fn into_raw(self) -> *mut $type_ { // Note that all `AsRaw` implementations also implement `Drop` which calls the // `_unref` function that correponds to $type_. We can't prevent this from // happening, so we have to add a reference here to ensure the returned pointer // remains allocated for the caller. unsafe { $ref(self.$field) }; self.$field } } }; } macro_rules! from_raw { ($struct_:ident, $field:ident, $type_:ty) => { impl $crate::FromRaw<$type_> for $struct_ { unsafe fn from_raw(t: *mut $type_) -> Self { Self { $field: t } } } }; } macro_rules! as_raw_with_context { ($struct_:ident, $field:ident, $type_:ty, $ref:path) => { impl $crate::AsRawWithContext<$type_> for $struct_ { fn as_raw(&self) -> *mut $type_ { self.$field } fn udev(&self) -> &Udev { &self.udev } fn into_raw_with_context(self) -> (*mut ffi::udev, *mut $type_) { // We can't call `self.udev.into_raw()` here, because that will consume // `self.udev`, which is not possible because every type that implements // `AsRawWithContext` also implements `Drop`. Of course we know that it would be // safe here to just skip the `drop()` on `Udev` and "leak" the `udev` pointer back // to the caller, but the Rust compiler doesn't know that. // // So instead we have to add a new reference to the `udev` pointer before we return // it, because as soon as we leave the scope of this function the `Udev` struct // will be dropped which will call `udev_unref` on it. If there's only once // reference left that will free the pointer and we'll end up returning a dangling // pointer to the caller. // // For much the same reason, we do the same with the pointer of type $type let udev = self.udev.as_raw(); unsafe { ffi::udev_ref(udev) }; unsafe { $ref(self.$field) }; (udev, self.$field) } } }; } macro_rules! from_raw_with_context { ($struct_:ident, $field:ident, $type_:ty) => { impl $crate::FromRawWithContext<$type_> for $struct_ { unsafe fn from_raw_with_context(udev: *mut ffi::udev, t: *mut $type_) -> Self { Self { udev: Udev::from_raw(udev), $field: t, } } } }; } mod device; mod enumerator; #[cfg(feature = "hwdb")] mod hwdb; mod list; mod monitor; mod udev; mod util; udev-0.9.0/src/list.rs000064400000000000000000000031741046102023000127120ustar 00000000000000use std::ffi::OsStr; use std::marker::PhantomData; use ffi; use util; /// Rust wrapper for the `udev_list_entry` struct, which provides sequential /// access to an associative list of string names and values. /// /// Each `List` is parametrized on the Rust wrapper type that owns its /// underlying data. For example, `List` indicates a list owned by /// some open handle to the `udev` hardware database. pub struct List<'a, T: 'a, E: 'a> { pub(crate) entry: *mut ffi::udev_list_entry, pub(crate) phantom: PhantomData<&'a (T, E)>, } pub type EntryList<'a, T> = List<'a, T, Entry<'a>>; impl<'a, T> Iterator for EntryList<'a, T> { type Item = Entry<'a>; fn next(&mut self) -> Option> { if self.entry.is_null() { None } else { let name = unsafe { util::ptr_to_os_str_unchecked(ffi::udev_list_entry_get_name(self.entry)) }; let value = unsafe { util::ptr_to_os_str(ffi::udev_list_entry_get_value(self.entry)) }; self.entry = unsafe { ffi::udev_list_entry_get_next(self.entry) }; Some(Entry { name, value }) } } fn size_hint(&self) -> (usize, Option) { (0, None) } } /// Rust wrapper for each entry in `List`, each of which contains a name and a value. pub struct Entry<'a> { pub(crate) name: &'a OsStr, pub(crate) value: Option<&'a OsStr>, } impl<'a> Entry<'a> { /// Returns the entry name. pub fn name(&self) -> &OsStr { self.name } /// Returns the entry value. pub fn value(&self) -> &OsStr { self.value.unwrap_or_else(|| OsStr::new("")) } } udev-0.9.0/src/monitor.rs000064400000000000000000000235701046102023000134300ustar 00000000000000use std::fmt; use std::ptr; use std::ffi::OsStr; use std::io::Result; use std::ops::Deref; use std::os::unix::io::{AsRawFd, RawFd}; use io_lifetimes::{AsFd, BorrowedFd}; #[cfg(feature = "mio06")] use mio06::{event::Evented, unix::EventedFd, Poll, PollOpt, Ready, Token}; #[cfg(feature = "mio07")] use mio07::{event::Source, unix::SourceFd, Interest, Registry, Token}; #[cfg(feature = "mio08")] use mio08::{event::Source, unix::SourceFd, Interest, Registry, Token}; #[cfg(feature = "mio10")] use mio10::{event::Source, unix::SourceFd, Interest, Registry, Token}; use Udev; use {ffi, util}; use {AsRaw, AsRawWithContext, Device, FromRaw}; /// Monitors for device events. /// /// A monitor communicates with the kernel over a socket. Filtering events is performed efficiently /// in the kernel, and only events that match the filters are received by the socket. Filters must /// be set up before listening for events. pub struct Builder { udev: Udev, monitor: *mut ffi::udev_monitor, } impl Clone for Builder { fn clone(&self) -> Self { Self { udev: self.udev.clone(), monitor: unsafe { ffi::udev_monitor_ref(self.monitor) }, } } } impl Drop for Builder { fn drop(&mut self) { unsafe { ffi::udev_monitor_unref(self.monitor); } } } as_ffi_with_context!(Builder, monitor, ffi::udev_monitor, ffi::udev_monitor_ref); impl Builder { /// Creates a new `Monitor`. pub fn new() -> Result { // Create a new Udev context for this monitor // It would be more efficient to allow callers to create just one context and use multiple // monitors, however that would be an API-breaking change. Self::with_udev(Udev::new()?) } /// Creates a new `Monitor` using an existing `Udev` instance pub(crate) fn with_udev(udev: Udev) -> Result { let name = b"udev\0".as_ptr() as *const libc::c_char; let ptr = try_alloc!(unsafe { ffi::udev_monitor_new_from_netlink(udev.as_raw(), name) }); Ok(Self { udev, monitor: ptr }) } /// Adds a filter that matches events for devices with the given subsystem. pub fn match_subsystem>(self, subsystem: T) -> Result { let subsystem = util::os_str_to_cstring(subsystem)?; util::errno_to_result(unsafe { ffi::udev_monitor_filter_add_match_subsystem_devtype( self.monitor, subsystem.as_ptr(), ptr::null(), ) }) .and(Ok(self)) } /// Adds a filter that matches events for devices with the given subsystem and device type. pub fn match_subsystem_devtype, U: AsRef>( self, subsystem: T, devtype: U, ) -> Result { let subsystem = util::os_str_to_cstring(subsystem)?; let devtype = util::os_str_to_cstring(devtype)?; util::errno_to_result(unsafe { ffi::udev_monitor_filter_add_match_subsystem_devtype( self.monitor, subsystem.as_ptr(), devtype.as_ptr(), ) }) .and(Ok(self)) } /// Adds a filter that matches events for devices with the given tag. pub fn match_tag>(self, tag: T) -> Result { let tag = util::os_str_to_cstring(tag)?; util::errno_to_result(unsafe { ffi::udev_monitor_filter_add_match_tag(self.monitor, tag.as_ptr()) }) .and(Ok(self)) } /// Removes all filters currently set on the monitor. pub fn clear_filters(self) -> Result { util::errno_to_result(unsafe { ffi::udev_monitor_filter_remove(self.monitor) }) .and(Ok(self)) } /// Listens for events matching the current filters. /// /// This method consumes the `Monitor`. pub fn listen(self) -> Result { util::errno_to_result(unsafe { ffi::udev_monitor_enable_receiving(self.monitor) })?; Ok(Socket { inner: self }) } } /// Provides raw access to the monitor's socket. impl AsRawFd for Builder { /// Returns the file descriptor of the monitor's socket. fn as_raw_fd(&self) -> RawFd { unsafe { ffi::udev_monitor_get_fd(self.monitor) } } } /// Provides raw access to the monitor's socket. impl AsFd for Builder { /// Returns the file descriptor of the monitor's socket. fn as_fd(&self) -> BorrowedFd<'_> { unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) } } } /// An active monitor that can receive events. /// /// The events received by a `Socket` match the filters set up by the `Monitor` that created /// the socket. /// /// Monitors are initially set up to receive events from the kernel via a nonblocking socket. A /// variant of `poll()` should be used on the file descriptor returned by the `AsRawFd` trait to /// wait for new events. pub struct Socket { inner: Builder, } impl Socket { /// Create an iterator of socket event messages pub fn iter(&self) -> SocketIter { SocketIter::new(&self) } } impl AsRaw for Socket { fn as_raw(&self) -> *mut ffi::udev_monitor { self.inner.monitor } fn into_raw(self) -> *mut ffi::udev_monitor { self.inner.monitor } } /// Provides raw access to the monitor's socket. impl AsRawFd for Socket { /// Returns the file descriptor of the monitor's socket. fn as_raw_fd(&self) -> RawFd { self.inner.as_raw_fd() } } /// Provides raw access to the monitor's socket. impl AsFd for Socket { /// Returns the file descriptor of the monitor's socket. fn as_fd(&self) -> BorrowedFd<'_> { self.inner.as_fd() } } /// Iterator of socket events pub struct SocketIter<'a> { socket: &'a Socket, } impl<'a> SocketIter<'a> { /// Create a socket by cloning the underlying udev instance fn new(socket: &'a Socket) -> SocketIter<'a> { SocketIter { socket } } } impl<'a> Iterator for SocketIter<'a> { type Item = Event; fn next(&mut self) -> Option { let ptr = unsafe { ffi::udev_monitor_receive_device(self.socket.inner.monitor) }; if ptr.is_null() { None } else { let device = Device::from_raw(self.socket.inner.udev.clone(), ptr); Some(Event { device }) } } } /// Types of events that can be received from udev. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum EventType { /// A device was added. Add, /// A device changed. Change, /// A device was removed. Remove, /// A device was bound to a driver. Bind, /// A device was unbound from a driver. Unbind, /// An unknown event occurred. Unknown, } impl Default for EventType { fn default() -> Self { EventType::Unknown } } impl fmt::Display for EventType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(match *self { EventType::Add => "add", EventType::Change => "change", EventType::Remove => "remove", EventType::Bind => "bind", EventType::Unbind => "unbind", EventType::Unknown => "unknown", }) } } /// An event that indicates a change in device state. pub struct Event { device: Device, } impl std::fmt::Debug for Event { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Event") .field("device", &self.device()) .field("event_type", &self.event_type()) .field("sequence_number", &self.sequence_number()) .finish() } } /// Provides access to the device associated with the event. impl Deref for Event { type Target = Device; fn deref(&self) -> &Device { &self.device } } impl Event { /// Returns the `EventType` corresponding to this event. pub fn event_type(&self) -> EventType { let value = match self.device.property_value("ACTION") { Some(s) => s.to_str(), None => None, }; match value { Some("add") => EventType::Add, Some("change") => EventType::Change, Some("remove") => EventType::Remove, Some("bind") => EventType::Bind, Some("unbind") => EventType::Unbind, _ => EventType::Unknown, } } /// Returns the event's sequence number. pub fn sequence_number(&self) -> u64 { unsafe { ffi::udev_device_get_seqnum(self.device.as_raw()) as u64 } } /// Returns the device associated with this event. pub fn device(&self) -> Device { self.device.clone() } } #[cfg(feature = "mio06")] impl Evented for Socket { fn register( &self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt, ) -> std::io::Result<()> { EventedFd(&self.as_raw_fd()).register(poll, token, interest, opts) } fn reregister( &self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt, ) -> std::io::Result<()> { EventedFd(&self.as_raw_fd()).reregister(poll, token, interest, opts) } fn deregister(&self, poll: &Poll) -> std::io::Result<()> { EventedFd(&self.as_raw_fd()).deregister(poll) } } #[cfg(any(feature = "mio07", feature = "mio08", feature = "mio10"))] impl Source for Socket { fn register( &mut self, registry: &Registry, token: Token, interest: Interest, ) -> std::io::Result<()> { SourceFd(&self.as_raw_fd()).register(registry, token, interest) } fn reregister( &mut self, registry: &Registry, token: Token, interest: Interest, ) -> std::io::Result<()> { SourceFd(&self.as_raw_fd()).reregister(registry, token, interest) } fn deregister(&mut self, registry: &Registry) -> std::io::Result<()> { SourceFd(&self.as_raw_fd()).deregister(registry) } } udev-0.9.0/src/udev.rs000064400000000000000000000043321046102023000126770ustar 00000000000000use std::io::Result; use ffi; use FromRaw; /// Rust wrapper for the `udev` struct which represents an opaque libudev context /// /// Most other `libudev` calls take a `struct udev*` argument, although whether or not this /// argument is actually used depends on the version of libudev. In more recent versions the /// context is ignored, therefore it sometimes works to pass a NULL or a invalid pointer for /// `udev`. However older versions, specifically 215 which shipped with Debian 8, expect this to /// be a valid `udev` struct. Thus it is not optional. /// /// `udev` is a ref-counted struct, with references added and removed with `udev_ref` and /// `udef_unref` respectively. This Rust wrapper takes advantage of that ref counting to implement /// `Clone` and `Drop`, so callers need not worry about any C-specific resource management. pub struct Udev { udev: *mut ffi::udev, } impl Clone for Udev { fn clone(&self) -> Self { unsafe { Self::from_raw(ffi::udev_ref(self.udev)) } } } impl Drop for Udev { fn drop(&mut self) { unsafe { ffi::udev_unref(self.udev) }; } } as_ffi!(Udev, udev, ffi::udev, ffi::udev_ref); impl Udev { /// Creates a new Udev context. pub fn new() -> Result { let ptr = try_alloc!(unsafe { ffi::udev_new() }); Ok(unsafe { Self::from_raw(ptr) }) } } #[cfg(test)] mod tests { use super::*; use AsRaw; #[test] fn clone_drop() { // Exercise clone/drop. We won't be able to catch a bug here that leaks memory, but a // crash due to the ref count getting out of whack would show up here. let mut udev = Udev::new().unwrap(); for _ in 0..1000 { let clone = udev.clone(); assert_eq!(udev.as_raw(), clone.as_raw()); // This will `drop()` what's in `udev`, and transfer ownership from `clone` to `udev` udev = clone; } } #[test] fn round_trip_to_raw_pointers() { // Make sure this can be made into a raw pointer, then back to a Rust type, and still works let udev = Udev::new().unwrap(); let ptr = udev.into_raw(); let udev = unsafe { Udev::from_raw(ptr) }; assert_eq!(ptr, udev.as_raw()); } } udev-0.9.0/src/util.rs000064400000000000000000000014531046102023000127120ustar 00000000000000use std::ffi::{CStr, CString, OsStr}; use std::io::Result; use libc::{c_char, c_int}; use std::os::unix::prelude::*; pub unsafe fn ptr_to_os_str<'a>(ptr: *const c_char) -> Option<&'a OsStr> { if ptr.is_null() { return None; } Some(ptr_to_os_str_unchecked(ptr)) } pub unsafe fn ptr_to_os_str_unchecked<'a>(ptr: *const c_char) -> &'a OsStr { OsStr::from_bytes(CStr::from_ptr(ptr).to_bytes()) } pub fn os_str_to_cstring>(s: T) -> Result { match CString::new(s.as_ref().as_bytes()) { Ok(s) => Ok(s), Err(_) => Err(std::io::Error::from_raw_os_error(libc::EINVAL)), } } pub fn errno_to_result(errno: c_int) -> Result<()> { match errno { x if x >= 0 => Ok(()), e => Err(std::io::Error::from_raw_os_error(-e)), } }