inotify-0.9.6/.cargo_vcs_info.json 0000644 00000000112 00000000001 0012523 0 ustar {
"git": {
"sha1": "4b774c9be96f5fe9a08ad8c9be5fa73810d8eed2"
}
}
inotify-0.9.6/.github/workflows/rust.yml 0000644 0000000 0000000 00000001152 00726746425 0016465 0 ustar 0000000 0000000 name: Rust
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Install rust
uses: actions-rs/toolchain@v1
with:
toolchain: 1.47.0
override: true
- name: Check rust and cargo version
run: rustc -V && cargo -V
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
- name: Build (no default features)
run: cargo build --verbose --no-default-features
- name: Run tests (no default features)
run: cargo test --verbose --no-default-features
inotify-0.9.6/.gitignore 0000644 0000000 0000000 00000000060 00726746425 0013335 0 ustar 0000000 0000000 Cargo.lock
/**/target
*.sublime-workspace
*.swp
inotify-0.9.6/CHANGELOG.md 0000644 0000000 0000000 00000006201 00726746425 0013161 0 ustar 0000000 0000000 ### v0.9.6 (2021-10-07)
- Fix build status badge in README ([#185])
- Add `get_buffer_size`/`get_absolute_path_buffer_size` ([#187])
[#185]: https://github.com/hannobraun/inotify-rs/pull/185
[#187]: https://github.com/hannobraun/inotify-rs/pull/187
### v0.9.5 (2021-10-07)
- Implement `Ord`/`PartialOrd` for `WatchDescriptor` ([#183])
[#183]: https://github.com/hannobraun/inotify-rs/pull/183
### v0.9.4 (2021-09-22)
- Make `Event::into_owned` always available ([#179])
- Implement missing `Debug` implementations ([#180])
[#179]: https://github.com/hannobraun/inotify-rs/pull/179
[#180]: https://github.com/hannobraun/inotify-rs/pull/180
### v0.9.3 (2021-05-12)
- Improve documentation ([#167], [#169])
- Add missing check for invalid file descriptor ([#168])
- Fix unsound use of buffers due to misalignment ([#171])
- Add missing error checks ([#173])
[#167]: https://github.com/hannobraun/inotify-rs/pull/167
[#168]: https://github.com/hannobraun/inotify-rs/pull/168
[#169]: https://github.com/hannobraun/inotify-rs/pull/169
[#171]: https://github.com/hannobraun/inotify-rs/pull/171
[#173]: https://github.com/hannobraun/inotify-rs/pull/173
### v0.9.2 (2020-12-30)
- Upgrade to Tokio 1.0 ([#165])
[#165]: https://github.com/hannobraun/inotify/pull/165
### v0.9.1 (2020-11-09)
- Fix take wake-up ([#161])
[#161]: https://github.com/hannobraun/inotify/pull/161
### v0.9.0 (2020-11-06)
- Update minimum supported Rust version to version 1.47 ([#154])
- Fix documentation: `Inotify::read_events` doesn't handle all events ([#157])
- Update to tokio 0.3 ([#158])
[#154]: https://github.com/hannobraun/inotify/pull/154
[#157]: https://github.com/hannobraun/inotify/pull/157
[#158]: https://github.com/hannobraun/inotify/pull/158
### v0.8.3 (2020-06-05)
- Avoid using `inotify_init1` ([#146])
[#146]: https://github.com/hannobraun/inotify/pull/146
### v0.8.2 (2020-01-25)
- Ensure file descriptor is closed on drop ([#140])
[#140]: https://github.com/inotify-rs/inotify/pull/140
### v0.8.1 (2020-01-23)
No changes, due to a mistake made while releasing this version.
### v0.8.0 (2019-12-04)
- Update to tokio 0.2 and futures 0.3 ([#134])
[#134]: https://github.com/inotify-rs/inotify/pull/134
### v0.7.1 (2020-06-05)
- backport: Avoid using `inotify_init1` ([#146])
[#146]: https://github.com/hannobraun/inotify/pull/146
### v0.7.0 (2019-02-09)
#### Features
* Make stream API more flexible in regards to buffers ([ea3e7a394bf34a6ccce4f2136c0991fe7e8f1f42](ea3e7a394bf34a6ccce4f2136c0991fe7e8f1f42)) (breaking change)
### v0.6.1 (2018-08-28)
#### Bug Fixes
* Don't return spurious filenames ([2f37560f](2f37560f))
## v0.6.0 (2018-08-16)
#### Features
* Handle closing of inotify instance better ([824160fe](824160fe))
* Implement `EventStream` using `mio` ([ba4cb8c7](ba4cb8c7))
### v0.5.1 (2018-02-27)
#### Features
* Add future-based async API ([569e65a7](569e65a7), closes [#49](49))
inotify-0.9.6/CONTRIBUTING.md 0000644 0000000 0000000 00000003010 00726746425 0013574 0 ustar 0000000 0000000 # Contributing to inotify-rs
Thank you for considering to work on inotify-rs. We're always happy to see outside contributions, small or large.
You probably found this document in the repository of either the [inotify] or [inotify-sys] crate. Both are part of the same project, so this guide is valid for both (in fact, the documents in either repository should be identical).
## Opening issues
If you found a problem with inotify-rs, please open an issue to let us know. If you're not sure whether you found a problem or not, just open an issue anyway. We'd rather close a few invalid issues than miss real problems.
Issues are tracked on GitHub, in the repository for the respective crate:
- [Open an inotify issue](https://github.com/inotify-rs/inotify/issues/new)
- [Open an inotify-sys issue](https://github.com/inotify-rs/inotify-sys/issues/new)
If you're unsure where to open your issue, just open it in the [inotify] repository.
## Contributing changes
If you want to make a change to the inotify-rs code, please open a pull request on the respective repository. The best way to open a pull request is usually to just push a branch to your fork, and click the button that should appear near the top of your fork's GitHub page.
If you're having any problems with completing your change, feel free to open a pull request anyway and ask any questions there. We're happy to help with getting changes across the finish line.
[inotify]: https://github.com/hannobraun/inotify
[inotify-sys]: https://github.com/hannobraun/inotify-sys
inotify-0.9.6/Cargo.lock 0000644 00000021636 00000000001 0010514 0 ustar # This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "futures-core"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0402f765d8a89a26043b889b26ce3c4679d268fa6bb22cd7c6aad98340e179d1"
[[package]]
name = "futures-macro"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4c40298486cdf52cc00cd6d6987892ba502c7656a16a4192a9992b1ccedd121"
dependencies = [
"autocfg",
"proc-macro-hack",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-task"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a16bef9fc1a4dddb5bee51c989e3fbba26569cbb0e31f5b303c184e3dd33dae"
[[package]]
name = "futures-util"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "feb5c238d27e2bf94ffdfd27b2c29e3df4a68c4193bb6427384259e2bf191967"
dependencies = [
"autocfg",
"futures-core",
"futures-macro",
"futures-task",
"pin-project-lite",
"pin-utils",
"proc-macro-hack",
"proc-macro-nested",
"slab",
]
[[package]]
name = "getrandom"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hermit-abi"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
dependencies = [
"libc",
]
[[package]]
name = "inotify"
version = "0.9.6"
dependencies = [
"bitflags",
"futures-core",
"futures-util",
"inotify-sys",
"libc",
"tempfile",
"tokio",
]
[[package]]
name = "inotify-sys"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
dependencies = [
"libc",
]
[[package]]
name = "libc"
version = "0.2.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e"
[[package]]
name = "log"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
[[package]]
name = "mio"
version = "0.7.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956"
dependencies = [
"libc",
"log",
"miow",
"ntapi",
"winapi",
]
[[package]]
name = "miow"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
dependencies = [
"winapi",
]
[[package]]
name = "ntapi"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
dependencies = [
"winapi",
]
[[package]]
name = "num_cpus"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "pin-project-lite"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "ppv-lite86"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "proc-macro-nested"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086"
[[package]]
name = "proc-macro2"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
name = "rand_chacha"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
dependencies = [
"rand_core",
]
[[package]]
name = "redox_syscall"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc"
dependencies = [
"bitflags",
]
[[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.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527"
[[package]]
name = "syn"
version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "tempfile"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
dependencies = [
"cfg-if",
"libc",
"rand",
"redox_syscall",
"remove_dir_all",
"winapi",
]
[[package]]
name = "tokio"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83f0c8e7c0addab50b663055baf787d0af7f413a46e6e7fb9559a4e4db7137a5"
dependencies = [
"autocfg",
"libc",
"mio",
"num_cpus",
"pin-project-lite",
"tokio-macros",
]
[[package]]
name = "tokio-macros"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[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"
inotify-0.9.6/Cargo.toml 0000644 00000003257 00000000001 0010536 0 ustar # 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]
edition = "2018"
name = "inotify"
version = "0.9.6"
authors = ["Hanno Braun ", "Félix Saparelli ", "Cristian Kubis ", "Frank Denis "]
exclude = ["/.travis.yml", "/inotify-rs.sublime-project"]
description = "Idiomatic wrapper for inotify"
documentation = "https://docs.rs/inotify"
readme = "README.md"
keywords = ["inotify", "linux"]
categories = ["api-bindings", "filesystem"]
license = "ISC"
repository = "https://github.com/hannobraun/inotify"
[[example]]
name = "stream"
required-features = ["stream"]
[[example]]
name = "watch"
[dependencies.bitflags]
version = "1"
[dependencies.futures-core]
version = "0.3.1"
optional = true
[dependencies.inotify-sys]
version = "0.1.3"
[dependencies.libc]
version = "0.2"
[dependencies.tokio]
version = "1.0.1"
features = ["net"]
optional = true
[dev-dependencies.futures-util]
version = "0.3.1"
[dev-dependencies.tempfile]
version = "3.1.0"
[dev-dependencies.tokio]
version = "1.0.1"
features = ["macros", "rt-multi-thread"]
[features]
default = ["stream"]
stream = ["futures-core", "tokio"]
[badges.maintenance]
status = "actively-developed"
[badges.travis-ci]
repository = "inotify-rs/inotify"
inotify-0.9.6/Cargo.toml.orig 0000644 0000000 0000000 00000002276 00726746425 0014247 0 ustar 0000000 0000000 [package]
name = "inotify"
version = "0.9.6"
authors = [
"Hanno Braun ",
"Félix Saparelli ",
"Cristian Kubis ",
"Frank Denis "
]
edition = "2018"
description = "Idiomatic wrapper for inotify"
documentation = "https://docs.rs/inotify"
repository = "https://github.com/hannobraun/inotify"
license = "ISC"
readme = "README.md"
keywords = ["inotify", "linux"]
categories = ["api-bindings", "filesystem"]
exclude = ["/.travis.yml", "/inotify-rs.sublime-project"]
[badges]
maintenance = { status = "actively-developed" }
travis-ci = { repository = "inotify-rs/inotify" }
[features]
default = ["stream"]
stream = ["futures-core", "tokio"]
[dependencies]
bitflags = "1"
futures-core = { version = "0.3.1", optional = true }
inotify-sys = "0.1.3"
libc = "0.2"
tokio = { version = "1.0.1", optional = true, features = ["net"] }
[dev-dependencies]
tempfile = "3.1.0"
futures-util = "0.3.1"
tokio = { version = "1.0.1", features = ["macros", "rt-multi-thread"] }
[[example]]
name = "stream"
required-features = ["stream"]
[[example]]
name = "watch"
inotify-0.9.6/LICENSE 0000644 0000000 0000000 00000001345 00726746425 0012361 0 ustar 0000000 0000000 Copyright (c) Hanno Braun and contributors
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
inotify-0.9.6/README.md 0000644 0000000 0000000 00000010302 00726746425 0012624 0 ustar 0000000 0000000 # inotify-rs [](https://crates.io/crates/inotify) [](https://docs.rs/inotify) [](https://github.com/hannobraun/inotify-rs/actions/workflows/rust.yml)
Idiomatic [inotify] wrapper for the [Rust programming language].
```Rust
extern crate inotify;
use std::env;
use inotify::{
EventMask,
WatchMask,
Inotify,
};
fn main() {
let mut inotify = Inotify::init()
.expect("Failed to initialize inotify");
let current_dir = env::current_dir()
.expect("Failed to determine current directory");
inotify
.add_watch(
current_dir,
WatchMask::MODIFY | WatchMask::CREATE | WatchMask::DELETE,
)
.expect("Failed to add inotify watch");
println!("Watching current directory for activity...");
let mut buffer = [0u8; 4096];
loop {
let events = inotify
.read_events_blocking(&mut buffer)
.expect("Failed to read inotify events");
for event in events {
if event.mask.contains(EventMask::CREATE) {
if event.mask.contains(EventMask::ISDIR) {
println!("Directory created: {:?}", event.name);
} else {
println!("File created: {:?}", event.name);
}
} else if event.mask.contains(EventMask::DELETE) {
if event.mask.contains(EventMask::ISDIR) {
println!("Directory deleted: {:?}", event.name);
} else {
println!("File deleted: {:?}", event.name);
}
} else if event.mask.contains(EventMask::MODIFY) {
if event.mask.contains(EventMask::ISDIR) {
println!("Directory modified: {:?}", event.name);
} else {
println!("File modified: {:?}", event.name);
}
}
}
}
}
```
## Usage
Include it in your `Cargo.toml`:
```toml
[dependencies]
inotify = "0.9"
```
Please refer to the [documentation] and the example above, for information on how to use it in your code.
Please note that inotify-rs is a relatively low-level wrapper around the original inotify API. And, of course, it is Linux-specific, just like inotify itself. If you are looking for a higher-level and platform-independent file system notification library, please consider [notify].
If you need to access inotify in a way that this wrapper doesn't support, consider using [inotify-sys] instead.
## Documentation
The most important piece of documentation for inotify-rs is the **[API reference]**, as it contains a thorough description of the complete API, as well as examples.
Additional examples can be found in the **[examples directory]**.
Please also make sure to read the **[inotify man page]**. Inotify use can be hard to get right, and this low-level wrapper won't protect you from all mistakes.
## License
Copyright (c) Hanno Braun and contributors
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
[inotify]: http://en.wikipedia.org/wiki/Inotify
[Rust programming language]: http://rust-lang.org/
[documentation]: https://docs.rs/inotify
[notify]: https://crates.io/crates/notify
[inotify-sys]: https://crates.io/crates/inotify-sys
[API reference]: https://docs.rs/inotify
[examples directory]: https://github.com/inotify-rs/inotify/tree/master/examples
[inotify man page]: http://man7.org/linux/man-pages/man7/inotify.7.html
inotify-0.9.6/examples/stream.rs 0000644 0000000 0000000 00000001506 00726746425 0015032 0 ustar 0000000 0000000 use std::{
fs::File,
io,
thread,
time::Duration,
};
use futures_util::StreamExt;
use inotify::{
Inotify,
WatchMask,
};
use tempfile::TempDir;
#[tokio::main]
async fn main() -> Result<(), io::Error> {
let mut inotify = Inotify::init()
.expect("Failed to initialize inotify");
let dir = TempDir::new()?;
inotify.add_watch(dir.path(), WatchMask::CREATE | WatchMask::MODIFY)?;
thread::spawn::<_, Result<(), io::Error>>(move || {
loop {
File::create(dir.path().join("file"))?;
thread::sleep(Duration::from_millis(500));
}
});
let mut buffer = [0; 1024];
let mut stream = inotify.event_stream(&mut buffer)?;
while let Some(event_or_error) = stream.next().await {
println!("event: {:?}", event_or_error?);
}
Ok(())
}
inotify-0.9.6/examples/watch.rs 0000644 0000000 0000000 00000003165 00726746425 0014650 0 ustar 0000000 0000000 use std::env;
use inotify::{
EventMask,
Inotify,
WatchMask,
};
fn main() {
let mut inotify = Inotify::init()
.expect("Failed to initialize inotify");
let current_dir = env::current_dir()
.expect("Failed to determine current directory");
inotify
.add_watch(
current_dir,
WatchMask::MODIFY | WatchMask::CREATE | WatchMask::DELETE,
)
.expect("Failed to add inotify watch");
println!("Watching current directory for activity...");
let mut buffer = [0u8; 4096];
loop {
let events = inotify
.read_events_blocking(&mut buffer)
.expect("Failed to read inotify events");
for event in events {
if event.mask.contains(EventMask::CREATE) {
if event.mask.contains(EventMask::ISDIR) {
println!("Directory created: {:?}", event.name);
} else {
println!("File created: {:?}", event.name);
}
} else if event.mask.contains(EventMask::DELETE) {
if event.mask.contains(EventMask::ISDIR) {
println!("Directory deleted: {:?}", event.name);
} else {
println!("File deleted: {:?}", event.name);
}
} else if event.mask.contains(EventMask::MODIFY) {
if event.mask.contains(EventMask::ISDIR) {
println!("Directory modified: {:?}", event.name);
} else {
println!("File modified: {:?}", event.name);
}
}
}
}
}
inotify-0.9.6/src/events.rs 0000644 0000000 0000000 00000035315 00726746425 0014021 0 ustar 0000000 0000000 use std::{
ffi::{
OsStr,
OsString,
},
mem,
os::unix::ffi::OsStrExt,
sync::Weak,
};
use inotify_sys as ffi;
use crate::fd_guard::FdGuard;
use crate::watches::WatchDescriptor;
use crate::util::align_buffer;
/// Iterator over inotify events
///
/// Allows for iteration over the events returned by
/// [`Inotify::read_events_blocking`] or [`Inotify::read_events`].
///
/// [`Inotify::read_events_blocking`]: struct.Inotify.html#method.read_events_blocking
/// [`Inotify::read_events`]: struct.Inotify.html#method.read_events
#[derive(Debug)]
pub struct Events<'a> {
fd : Weak,
buffer : &'a [u8],
num_bytes: usize,
pos : usize,
}
impl<'a> Events<'a> {
pub(crate) fn new(fd: Weak, buffer: &'a [u8], num_bytes: usize)
-> Self
{
Events {
fd : fd,
buffer : buffer,
num_bytes: num_bytes,
pos : 0,
}
}
}
impl<'a> Iterator for Events<'a> {
type Item = Event<&'a OsStr>;
fn next(&mut self) -> Option {
if self.pos < self.num_bytes {
let (step, event) = Event::from_buffer(self.fd.clone(), &self.buffer[self.pos..]);
self.pos += step;
Some(event)
}
else {
None
}
}
}
/// An inotify event
///
/// A file system event that describes a change that the user previously
/// registered interest in. To watch for events, call [`Inotify::add_watch`]. To
/// retrieve events, call [`Inotify::read_events_blocking`] or
/// [`Inotify::read_events`].
///
/// [`Inotify::add_watch`]: struct.Inotify.html#method.add_watch
/// [`Inotify::read_events_blocking`]: struct.Inotify.html#method.read_events_blocking
/// [`Inotify::read_events`]: struct.Inotify.html#method.read_events
#[derive(Clone, Debug)]
pub struct Event {
/// Identifies the watch this event originates from
///
/// This [`WatchDescriptor`] is equal to the one that [`Inotify::add_watch`]
/// returned when interest for this event was registered. The
/// [`WatchDescriptor`] can be used to remove the watch using
/// [`Inotify::rm_watch`], thereby preventing future events of this type
/// from being created.
///
/// [`WatchDescriptor`]: struct.WatchDescriptor.html
/// [`Inotify::add_watch`]: struct.Inotify.html#method.add_watch
/// [`Inotify::rm_watch`]: struct.Inotify.html#method.rm_watch
pub wd: WatchDescriptor,
/// Indicates what kind of event this is
pub mask: EventMask,
/// Connects related events to each other
///
/// When a file is renamed, this results two events: [`MOVED_FROM`] and
/// [`MOVED_TO`]. The `cookie` field will be the same for both of them,
/// thereby making is possible to connect the event pair.
///
/// [`MOVED_FROM`]: event_mask/constant.MOVED_FROM.html
/// [`MOVED_TO`]: event_mask/constant.MOVED_TO.html
pub cookie: u32,
/// The name of the file the event originates from
///
/// This field is set only if the subject of the event is a file or directory in a
/// watched directory. If the event concerns a file or directory that is
/// watched directly, `name` will be `None`.
pub name: Option,
}
impl<'a> Event<&'a OsStr> {
fn new(fd: Weak, event: &ffi::inotify_event, name: &'a OsStr)
-> Self
{
let mask = EventMask::from_bits(event.mask)
.expect("Failed to convert event mask. This indicates a bug.");
let wd = crate::WatchDescriptor {
id: event.wd,
fd,
};
let name = if name == "" {
None
}
else {
Some(name)
};
Event {
wd,
mask,
cookie: event.cookie,
name,
}
}
/// Create an `Event` from a buffer
///
/// Assumes that a full `inotify_event` plus its name is located at the
/// beginning of `buffer`.
///
/// Returns the number of bytes used from the buffer, and the event.
///
/// # Panics
///
/// Panics if the buffer does not contain a full event, including its name.
pub(crate) fn from_buffer(
fd : Weak,
buffer: &'a [u8],
)
-> (usize, Self)
{
let event_size = mem::size_of::();
let event_align = mem::align_of::();
// Make sure that the buffer can satisfy the alignment requirements for `inotify_event`
assert!(buffer.len() >= event_align);
// Discard the unaligned portion, if any, of the supplied buffer
let buffer = align_buffer(buffer);
// Make sure that the aligned buffer is big enough to contain an event, without
// the name. Otherwise we can't safely convert it to an `inotify_event`.
assert!(buffer.len() >= event_size);
let event = buffer.as_ptr() as *const ffi::inotify_event;
// We have a pointer to an `inotify_event`, pointing to the beginning of
// `buffer`. Since we know, as per the assertion above, that there are
// enough bytes in the buffer for at least one event, we can safely
// convert that pointer into a reference.
let event = unsafe { &*event };
// The name's length is given by `event.len`. There should always be
// enough bytes left in the buffer to fit the name. Let's make sure that
// is the case.
let bytes_left_in_buffer = buffer.len() - event_size;
assert!(bytes_left_in_buffer >= event.len as usize);
// Directly after the event struct should be a name, if there's one
// associated with the event. Let's make a new slice that starts with
// that name. If there's no name, this slice might have a length of `0`.
let bytes_consumed = event_size + event.len as usize;
let name = &buffer[event_size..bytes_consumed];
// Remove trailing '\0' bytes
//
// The events in the buffer are aligned, and `name` is filled up
// with '\0' up to the alignment boundary. Here we remove those
// additional bytes.
//
// The `unwrap` here is safe, because `splitn` always returns at
// least one result, even if the original slice contains no '\0'.
let name = name
.splitn(2, |b| b == &0u8)
.next()
.unwrap();
let event = Event::new(
fd,
event,
OsStr::from_bytes(name),
);
(bytes_consumed, event)
}
/// Returns an owned copy of the event.
#[must_use = "cloning is often expensive and is not expected to have side effects"]
pub fn into_owned(&self) -> EventOwned {
Event {
wd: self.wd.clone(),
mask: self.mask,
cookie: self.cookie,
name: self.name.map(OsStr::to_os_string),
}
}
}
/// An owned version of `Event`
pub type EventOwned = Event;
bitflags! {
/// Indicates the type of an event
///
/// This struct can be retrieved from an [`Event`] via its `mask` field.
/// You can determine the [`Event`]'s type by comparing the `EventMask` to
/// its associated constants.
///
/// Please refer to the documentation of [`Event`] for a usage example.
///
/// [`Event`]: struct.Event.html
pub struct EventMask: u32 {
/// File was accessed
///
/// When watching a directory, this event is only triggered for objects
/// inside the directory, not the directory itself.
///
/// See [`inotify_sys::IN_ACCESS`].
///
/// [`inotify_sys::IN_ACCESS`]: ../inotify_sys/constant.IN_ACCESS.html
const ACCESS = ffi::IN_ACCESS;
/// Metadata (permissions, timestamps, ...) changed
///
/// When watching a directory, this event can be triggered for the
/// directory itself, as well as objects inside the directory.
///
/// See [`inotify_sys::IN_ATTRIB`].
///
/// [`inotify_sys::IN_ATTRIB`]: ../inotify_sys/constant.IN_ATTRIB.html
const ATTRIB = ffi::IN_ATTRIB;
/// File opened for writing was closed
///
/// When watching a directory, this event is only triggered for objects
/// inside the directory, not the directory itself.
///
/// See [`inotify_sys::IN_CLOSE_WRITE`].
///
/// [`inotify_sys::IN_CLOSE_WRITE`]: ../inotify_sys/constant.IN_CLOSE_WRITE.html
const CLOSE_WRITE = ffi::IN_CLOSE_WRITE;
/// File or directory not opened for writing was closed
///
/// When watching a directory, this event can be triggered for the
/// directory itself, as well as objects inside the directory.
///
/// See [`inotify_sys::IN_CLOSE_NOWRITE`].
///
/// [`inotify_sys::IN_CLOSE_NOWRITE`]: ../inotify_sys/constant.IN_CLOSE_NOWRITE.html
const CLOSE_NOWRITE = ffi::IN_CLOSE_NOWRITE;
/// File/directory created in watched directory
///
/// When watching a directory, this event is only triggered for objects
/// inside the directory, not the directory itself.
///
/// See [`inotify_sys::IN_CREATE`].
///
/// [`inotify_sys::IN_CREATE`]: ../inotify_sys/constant.IN_CREATE.html
const CREATE = ffi::IN_CREATE;
/// File/directory deleted from watched directory
///
/// When watching a directory, this event is only triggered for objects
/// inside the directory, not the directory itself.
///
/// See [`inotify_sys::IN_DELETE`].
///
/// [`inotify_sys::IN_DELETE`]: ../inotify_sys/constant.IN_DELETE.html
const DELETE = ffi::IN_DELETE;
/// Watched file/directory was deleted
///
/// See [`inotify_sys::IN_DELETE_SELF`].
///
/// [`inotify_sys::IN_DELETE_SELF`]: ../inotify_sys/constant.IN_DELETE_SELF.html
const DELETE_SELF = ffi::IN_DELETE_SELF;
/// File was modified
///
/// When watching a directory, this event is only triggered for objects
/// inside the directory, not the directory itself.
///
/// See [`inotify_sys::IN_MODIFY`].
///
/// [`inotify_sys::IN_MODIFY`]: ../inotify_sys/constant.IN_MODIFY.html
const MODIFY = ffi::IN_MODIFY;
/// Watched file/directory was moved
///
/// See [`inotify_sys::IN_MOVE_SELF`].
///
/// [`inotify_sys::IN_MOVE_SELF`]: ../inotify_sys/constant.IN_MOVE_SELF.html
const MOVE_SELF = ffi::IN_MOVE_SELF;
/// File was renamed/moved; watched directory contained old name
///
/// When watching a directory, this event is only triggered for objects
/// inside the directory, not the directory itself.
///
/// See [`inotify_sys::IN_MOVED_FROM`].
///
/// [`inotify_sys::IN_MOVED_FROM`]: ../inotify_sys/constant.IN_MOVED_FROM.html
const MOVED_FROM = ffi::IN_MOVED_FROM;
/// File was renamed/moved; watched directory contains new name
///
/// When watching a directory, this event is only triggered for objects
/// inside the directory, not the directory itself.
///
/// See [`inotify_sys::IN_MOVED_TO`].
///
/// [`inotify_sys::IN_MOVED_TO`]: ../inotify_sys/constant.IN_MOVED_TO.html
const MOVED_TO = ffi::IN_MOVED_TO;
/// File or directory was opened
///
/// When watching a directory, this event can be triggered for the
/// directory itself, as well as objects inside the directory.
///
/// See [`inotify_sys::IN_OPEN`].
///
/// [`inotify_sys::IN_OPEN`]: ../inotify_sys/constant.IN_OPEN.html
const OPEN = ffi::IN_OPEN;
/// Watch was removed
///
/// This event will be generated, if the watch was removed explicitly
/// (via [`Inotify::rm_watch`]), or automatically (because the file was
/// deleted or the file system was unmounted).
///
/// See [`inotify_sys::IN_IGNORED`].
///
/// [`inotify_sys::IN_IGNORED`]: ../inotify_sys/constant.IN_IGNORED.html
const IGNORED = ffi::IN_IGNORED;
/// Event related to a directory
///
/// The subject of the event is a directory.
///
/// See [`inotify_sys::IN_ISDIR`].
///
/// [`inotify_sys::IN_ISDIR`]: ../inotify_sys/constant.IN_ISDIR.html
const ISDIR = ffi::IN_ISDIR;
/// Event queue overflowed
///
/// The event queue has overflowed and events have presumably been lost.
///
/// See [`inotify_sys::IN_Q_OVERFLOW`].
///
/// [`inotify_sys::IN_Q_OVERFLOW`]: ../inotify_sys/constant.IN_Q_OVERFLOW.html
const Q_OVERFLOW = ffi::IN_Q_OVERFLOW;
/// File system containing watched object was unmounted.
/// File system was unmounted
///
/// The file system that contained the watched object has been
/// unmounted. An event with [`WatchMask::IGNORED`] will subsequently be
/// generated for the same watch descriptor.
///
/// See [`inotify_sys::IN_UNMOUNT`].
///
/// [`WatchMask::IGNORED`]: #associatedconstant.IGNORED
/// [`inotify_sys::IN_UNMOUNT`]: ../inotify_sys/constant.IN_UNMOUNT.html
const UNMOUNT = ffi::IN_UNMOUNT;
}
}
#[cfg(test)]
mod tests {
use std::{
io::prelude::*,
mem,
slice,
sync,
};
use crate::util;
use inotify_sys as ffi;
use super::Event;
#[test]
fn from_buffer_should_not_mistake_next_event_for_name_of_previous_event() {
let mut buffer = [0u8; 1024];
// Make sure the buffer is properly aligned before writing raw events into it
let buffer = util::align_buffer_mut(&mut buffer);
// First, put a normal event into the buffer
let event = ffi::inotify_event {
wd: 0,
mask: 0,
cookie: 0,
len: 0, // no name following after event
};
let event = unsafe {
slice::from_raw_parts(
&event as *const _ as *const u8,
mem::size_of_val(&event),
)
};
(&mut buffer[..]).write(event)
.expect("Failed to write into buffer");
// After that event, simulate an event that starts with a non-zero byte.
buffer[mem::size_of_val(&event)] = 1;
// Now create the event and verify that the name is actually `None`, as
// dictated by the value `len` above.
let (_, event) = Event::from_buffer(
sync::Weak::new(),
&buffer,
);
assert_eq!(event.name, None);
}
}
inotify-0.9.6/src/fd_guard.rs 0000644 0000000 0000000 00000003250 00726746425 0014261 0 ustar 0000000 0000000 use std::{
ops::Deref,
os::unix::io::{
AsRawFd,
FromRawFd,
IntoRawFd,
RawFd,
},
sync::atomic::{
AtomicBool,
Ordering,
},
};
use inotify_sys as ffi;
/// A RAII guard around a `RawFd` that closes it automatically on drop.
#[derive(Debug)]
pub struct FdGuard {
pub(crate) fd : RawFd,
pub(crate) close_on_drop: AtomicBool,
}
impl FdGuard {
/// Indicate that the wrapped file descriptor should _not_ be closed
/// when the guard is dropped.
///
/// This should be called in cases where ownership of the wrapped file
/// descriptor has been "moved" out of the guard.
///
/// This is factored out into a separate function to ensure that it's
/// always used consistently.
#[inline]
pub fn should_not_close(&self) {
self.close_on_drop.store(false, Ordering::Release);
}
}
impl Deref for FdGuard {
type Target = RawFd;
#[inline]
fn deref(&self) -> &Self::Target {
&self.fd
}
}
impl Drop for FdGuard {
fn drop(&mut self) {
if self.close_on_drop.load(Ordering::Acquire) {
unsafe { ffi::close(self.fd); }
}
}
}
impl FromRawFd for FdGuard {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
FdGuard {
fd,
close_on_drop: AtomicBool::new(true),
}
}
}
impl IntoRawFd for FdGuard {
fn into_raw_fd(self) -> RawFd {
self.should_not_close();
self.fd
}
}
impl AsRawFd for FdGuard {
fn as_raw_fd(&self) -> RawFd {
self.fd
}
}
impl PartialEq for FdGuard {
fn eq(&self, other: &FdGuard) -> bool {
self.fd == other.fd
}
}
inotify-0.9.6/src/inotify.rs 0000644 0000000 0000000 00000041534 00726746425 0014176 0 ustar 0000000 0000000 use std::{
ffi::CString,
io,
os::unix::ffi::OsStrExt,
os::unix::io::{
AsRawFd,
FromRawFd,
IntoRawFd,
RawFd,
},
path::Path,
sync::{
atomic::AtomicBool,
Arc,
}
};
use inotify_sys as ffi;
use libc::{
F_GETFL,
F_SETFD,
F_SETFL,
FD_CLOEXEC,
O_NONBLOCK,
fcntl,
};
use crate::events::Events;
use crate::fd_guard::FdGuard;
use crate::util::read_into_buffer;
use crate::watches::{
WatchDescriptor,
WatchMask,
};
#[cfg(feature = "stream")]
use crate::stream::EventStream;
/// Idiomatic Rust wrapper around Linux's inotify API
///
/// `Inotify` is a wrapper around an inotify instance. It generally tries to
/// adhere to the underlying inotify API closely, while making access to it
/// safe and convenient.
///
/// Please refer to the [top-level documentation] for further details and a
/// usage example.
///
/// [top-level documentation]: index.html
#[derive(Debug)]
pub struct Inotify {
fd: Arc,
}
impl Inotify {
/// Creates an [`Inotify`] instance
///
/// Initializes an inotify instance by calling [`inotify_init1`].
///
/// This method passes both flags accepted by [`inotify_init1`], not giving
/// the user any choice in the matter, as not passing the flags would be
/// inappropriate in the context of this wrapper:
///
/// - [`IN_CLOEXEC`] prevents leaking file descriptors to other processes.
/// - [`IN_NONBLOCK`] controls the blocking behavior of the inotify API,
/// which is entirely managed by this wrapper.
///
/// # Errors
///
/// Directly returns the error from the call to [`inotify_init1`], without
/// adding any error conditions of its own.
///
/// # Examples
///
/// ```
/// use inotify::Inotify;
///
/// let inotify = Inotify::init()
/// .expect("Failed to initialize an inotify instance");
/// ```
///
/// [`Inotify`]: struct.Inotify.html
/// [`inotify_init1`]: ../inotify_sys/fn.inotify_init1.html
/// [`IN_CLOEXEC`]: ../inotify_sys/constant.IN_CLOEXEC.html
/// [`IN_NONBLOCK`]: ../inotify_sys/constant.IN_NONBLOCK.html
pub fn init() -> io::Result {
// Initialize inotify and set CLOEXEC and NONBLOCK flags.
//
// NONBLOCK is needed, because `Inotify` manages blocking behavior for
// the API consumer, and the way we do that is to make everything non-
// blocking by default and later override that as required.
//
// CLOEXEC prevents leaking file descriptors to processes executed by
// this process and seems to be a best practice. I don't grasp this
// issue completely and failed to find any authoritative sources on the
// topic. There's some discussion in the open(2) and fcntl(2) man pages,
// but I didn't find that helpful in understanding the issue of leaked
// file descriptors. For what it's worth, there's a Rust issue about
// this:
// https://github.com/rust-lang/rust/issues/12148
let fd = unsafe {
let fd = ffi::inotify_init();
if fd == -1 {
return Err(io::Error::last_os_error());
}
if fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 {
return Err(io::Error::last_os_error());
}
if fcntl(fd, F_SETFL, O_NONBLOCK) == -1 {
return Err(io::Error::last_os_error());
}
fd
};
Ok(Inotify {
fd: Arc::new(FdGuard {
fd,
close_on_drop: AtomicBool::new(true),
}),
})
}
/// Adds or updates a watch for the given path
///
/// Adds a new watch or updates an existing one for the file referred to by
/// `path`. Returns a watch descriptor that can be used to refer to this
/// watch later.
///
/// The `mask` argument defines what kind of changes the file should be
/// watched for, and how to do that. See the documentation of [`WatchMask`]
/// for details.
///
/// If this method is used to add a new watch, a new [`WatchDescriptor`] is
/// returned. If it is used to update an existing watch, a
/// [`WatchDescriptor`] that equals the previously returned
/// [`WatchDescriptor`] for that watch is returned instead.
///
/// Under the hood, this method just calls [`inotify_add_watch`] and does
/// some trivial translation between the types on the Rust side and the C
/// side.
///
/// # Attention: Updating watches and hardlinks
///
/// As mentioned above, this method can be used to update an existing watch.
/// This is usually done by calling this method with the same `path`
/// argument that it has been called with before. But less obviously, it can
/// also happen if the method is called with a different path that happens
/// to link to the same inode.
///
/// You can detect this by keeping track of [`WatchDescriptor`]s and the
/// paths they have been returned for. If the same [`WatchDescriptor`] is
/// returned for a different path (and you haven't freed the
/// [`WatchDescriptor`] by removing the watch), you know you have two paths
/// pointing to the same inode, being watched by the same watch.
///
/// # Errors
///
/// Directly returns the error from the call to
/// [`inotify_add_watch`][`inotify_add_watch`] (translated into an
/// `io::Error`), without adding any error conditions of
/// its own.
///
/// # Examples
///
/// ```
/// use inotify::{
/// Inotify,
/// WatchMask,
/// };
///
/// let mut inotify = Inotify::init()
/// .expect("Failed to initialize an inotify instance");
///
/// # // Create a temporary file, so `add_watch` won't return an error.
/// # use std::fs::File;
/// # File::create("/tmp/inotify-rs-test-file")
/// # .expect("Failed to create test file");
/// #
/// inotify.add_watch("/tmp/inotify-rs-test-file", WatchMask::MODIFY)
/// .expect("Failed to add file watch");
///
/// // Handle events for the file here
/// ```
///
/// [`inotify_add_watch`]: ../inotify_sys/fn.inotify_add_watch.html
/// [`WatchMask`]: struct.WatchMask.html
/// [`WatchDescriptor`]: struct.WatchDescriptor.html
pub fn add_watch
(&mut self, path: P, mask: WatchMask)
-> io::Result
where P: AsRef
{
let path = CString::new(path.as_ref().as_os_str().as_bytes())?;
let wd = unsafe {
ffi::inotify_add_watch(
**self.fd,
path.as_ptr() as *const _,
mask.bits(),
)
};
match wd {
-1 => Err(io::Error::last_os_error()),
_ => Ok(WatchDescriptor{ id: wd, fd: Arc::downgrade(&self.fd) }),
}
}
/// Stops watching a file
///
/// Removes the watch represented by the provided [`WatchDescriptor`] by
/// calling [`inotify_rm_watch`]. [`WatchDescriptor`]s can be obtained via
/// [`Inotify::add_watch`], or from the `wd` field of [`Event`].
///
/// # Errors
///
/// Directly returns the error from the call to [`inotify_rm_watch`].
/// Returns an [`io::Error`] with [`ErrorKind`]`::InvalidInput`, if the given
/// [`WatchDescriptor`] did not originate from this [`Inotify`] instance.
///
/// # Examples
///
/// ```
/// use inotify::Inotify;
///
/// let mut inotify = Inotify::init()
/// .expect("Failed to initialize an inotify instance");
///
/// # // Create a temporary file, so `add_watch` won't return an error.
/// # use std::fs::File;
/// # let mut test_file = File::create("/tmp/inotify-rs-test-file")
/// # .expect("Failed to create test file");
/// #
/// # // Add a watch and modify the file, so the code below doesn't block
/// # // forever.
/// # use inotify::WatchMask;
/// # inotify.add_watch("/tmp/inotify-rs-test-file", WatchMask::MODIFY)
/// # .expect("Failed to add file watch");
/// # use std::io::Write;
/// # write!(&mut test_file, "something\n")
/// # .expect("Failed to write something to test file");
/// #
/// let mut buffer = [0; 1024];
/// let events = inotify
/// .read_events_blocking(&mut buffer)
/// .expect("Error while waiting for events");
///
/// for event in events {
/// inotify.rm_watch(event.wd);
/// }
/// ```
///
/// [`WatchDescriptor`]: struct.WatchDescriptor.html
/// [`inotify_rm_watch`]: ../inotify_sys/fn.inotify_rm_watch.html
/// [`Inotify::add_watch`]: struct.Inotify.html#method.add_watch
/// [`Event`]: struct.Event.html
/// [`Inotify`]: struct.Inotify.html
/// [`io::Error`]: https://doc.rust-lang.org/std/io/struct.Error.html
/// [`ErrorKind`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html
pub fn rm_watch(&mut self, wd: WatchDescriptor) -> io::Result<()> {
if wd.fd.upgrade().as_ref() != Some(&self.fd) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid WatchDescriptor",
));
}
let result = unsafe { ffi::inotify_rm_watch(**self.fd, wd.id) };
match result {
0 => Ok(()),
-1 => Err(io::Error::last_os_error()),
_ => panic!(
"unexpected return code from inotify_rm_watch ({})", result)
}
}
/// Waits until events are available, then returns them
///
/// Blocks the current thread until at least one event is available. If this
/// is not desirable, please consider [`Inotify::read_events`].
///
/// This method calls [`Inotify::read_events`] internally and behaves
/// essentially the same, apart from the blocking behavior. Please refer to
/// the documentation of [`Inotify::read_events`] for more information.
///
/// [`Inotify::read_events`]: struct.Inotify.html#method.read_events
/// [`read`]: ../libc/fn.read.html
pub fn read_events_blocking<'a>(&mut self, buffer: &'a mut [u8])
-> io::Result>
{
unsafe {
let res = fcntl(**self.fd, F_GETFL);
if res == -1 {
return Err(io::Error::last_os_error());
}
if fcntl(**self.fd, F_SETFL, res & !O_NONBLOCK) == -1 {
return Err(io::Error::last_os_error());
}
};
let result = self.read_events(buffer);
unsafe {
let res = fcntl(**self.fd, F_GETFL);
if res == -1 {
return Err(io::Error::last_os_error());
}
if fcntl(**self.fd, F_SETFL, res | O_NONBLOCK) == -1 {
return Err(io::Error::last_os_error());
}
};
result
}
/// Returns one buffer's worth of available events
///
/// Reads as many events as possible into `buffer`, and returns an iterator
/// over them. If no events are available, an iterator is still returned. If
/// you need a method that will block until at least one event is available,
/// please consider [`read_events_blocking`].
///
/// Please note that inotify will merge identical successive unread events
/// into a single event. This means this method can not be used to count the
/// number of file system events.
///
/// The `buffer` argument, as the name indicates, is used as a buffer for
/// the inotify events. Its contents may be overwritten.
///
/// # Errors
///
/// This function directly returns all errors from the call to [`read`]
/// (except EGAIN/EWOULDBLOCK, which result in an empty iterator). In
/// addition, [`ErrorKind::UnexpectedEof`] is returned, if the call to
/// [`read`] returns `0`, signaling end-of-file.
///
/// If `buffer` is too small, this will result in an error with
/// [`ErrorKind::InvalidInput`]. On very old Linux kernels,
/// [`ErrorKind::UnexpectedEof`] will be returned instead.
///
/// # Examples
///
/// ```
/// use inotify::Inotify;
///
/// let mut inotify = Inotify::init()
/// .expect("Failed to initialize an inotify instance");
///
/// let mut buffer = [0; 1024];
/// let events = inotify.read_events(&mut buffer)
/// .expect("Error while reading events");
///
/// for event in events {
/// // Handle event
/// }
/// ```
///
/// [`read_events_blocking`]: struct.Inotify.html#method.read_events_blocking
/// [`read`]: ../libc/fn.read.html
/// [`ErrorKind::UnexpectedEof`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.UnexpectedEof
/// [`ErrorKind::InvalidInput`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.InvalidInput
pub fn read_events<'a>(&mut self, buffer: &'a mut [u8])
-> io::Result>
{
let num_bytes = read_into_buffer(**self.fd, buffer);
let num_bytes = match num_bytes {
0 => {
return Err(
io::Error::new(
io::ErrorKind::UnexpectedEof,
"`read` return `0`, signaling end-of-file"
)
);
}
-1 => {
let error = io::Error::last_os_error();
if error.kind() == io::ErrorKind::WouldBlock {
return Ok(Events::new(Arc::downgrade(&self.fd), buffer, 0));
}
else {
return Err(error);
}
},
_ if num_bytes < 0 => {
panic!("{} {} {} {} {} {}",
"Unexpected return value from `read`. Received a negative",
"value that was not `-1`. According to the `read` man page",
"this shouldn't happen, as either `-1` is returned on",
"error, `0` on end-of-file, or a positive value for the",
"number of bytes read. Returned value:",
num_bytes,
);
}
_ => {
// The value returned by `read` should be `isize`. Let's quickly
// verify this with the following assignment, so we can be sure
// our cast below is valid.
let num_bytes: isize = num_bytes;
// The type returned by `read` is `isize`, and we've ruled out
// all negative values with the match arms above. This means we
// can safely cast to `usize`.
debug_assert!(num_bytes > 0);
num_bytes as usize
}
};
Ok(Events::new(Arc::downgrade(&self.fd), buffer, num_bytes))
}
/// Create a stream which collects events
///
/// Returns a `Stream` over all events that are available. This stream is an
/// infinite source of events.
///
/// An internal buffer which can hold the largest possible event is used.
#[cfg(feature = "stream")]
pub fn event_stream(&mut self, buffer: T)
-> io::Result>
where
T: AsMut<[u8]> + AsRef<[u8]>,
{
EventStream::new(self.fd.clone(), buffer)
}
/// Closes the inotify instance
///
/// Closes the file descriptor referring to the inotify instance. The user
/// usually doesn't have to call this function, as the underlying inotify
/// instance is closed automatically, when [`Inotify`] is dropped.
///
/// # Errors
///
/// Directly returns the error from the call to [`close`], without adding any
/// error conditions of its own.
///
/// # Examples
///
/// ```
/// use inotify::Inotify;
///
/// let mut inotify = Inotify::init()
/// .expect("Failed to initialize an inotify instance");
///
/// inotify.close()
/// .expect("Failed to close inotify instance");
/// ```
///
/// [`Inotify`]: struct.Inotify.html
/// [`close`]: ../libc/fn.close.html
pub fn close(self) -> io::Result<()> {
// `self` will be dropped when this method returns. If this is the only
// owner of `fd`, the `Arc` will also be dropped. The `Drop`
// implementation for `FdGuard` will attempt to close the file descriptor
// again, unless this flag here is cleared.
self.fd.should_not_close();
match unsafe { ffi::close(**self.fd) } {
0 => Ok(()),
_ => Err(io::Error::last_os_error()),
}
}
}
impl AsRawFd for Inotify {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.fd.as_raw_fd()
}
}
impl FromRawFd for Inotify {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Inotify {
fd: Arc::new(FdGuard::from_raw_fd(fd))
}
}
}
impl IntoRawFd for Inotify {
#[inline]
fn into_raw_fd(self) -> RawFd {
self.fd.should_not_close();
self.fd.fd
}
}
inotify-0.9.6/src/lib.rs 0000644 0000000 0000000 00000005631 00726746425 0013261 0 ustar 0000000 0000000 //! Idiomatic inotify wrapper for the Rust programming language
//!
//! # About
//!
//! [inotify-rs] is an idiomatic wrapper around the Linux kernel's [inotify] API
//! for the Rust programming language. It can be used for monitoring changes to
//! files or directories.
//!
//! The [`Inotify`] struct is the main entry point into the API.
//!
//! # Example
//!
//! ```
//! use inotify::{
//! Inotify,
//! WatchMask,
//! };
//!
//! let mut inotify = Inotify::init()
//! .expect("Error while initializing inotify instance");
//!
//! # // Create a temporary file, so `add_watch` won't return an error.
//! # use std::fs::File;
//! # let mut test_file = File::create("/tmp/inotify-rs-test-file")
//! # .expect("Failed to create test file");
//! #
//! // Watch for modify and close events.
//! inotify
//! .add_watch(
//! "/tmp/inotify-rs-test-file",
//! WatchMask::MODIFY | WatchMask::CLOSE,
//! )
//! .expect("Failed to add file watch");
//!
//! # // Modify file, so the following `read_events_blocking` won't block.
//! # use std::io::Write;
//! # write!(&mut test_file, "something\n")
//! # .expect("Failed to write something to test file");
//! #
//! // Read events that were added with `add_watch` above.
//! let mut buffer = [0; 1024];
//! let events = inotify.read_events_blocking(&mut buffer)
//! .expect("Error while reading events");
//!
//! for event in events {
//! // Handle event
//! }
//! ```
//!
//! # Attention: inotify gotchas
//!
//! inotify (as in, the Linux API, not this wrapper) has many edge cases, making
//! it hard to use correctly. This can lead to weird and hard to find bugs in
//! applications that are based on it. inotify-rs does its best to fix these
//! issues, but sometimes this would require an amount of runtime overhead that
//! is just unacceptable for a low-level wrapper such as this.
//!
//! We've documented any issues that inotify-rs has inherited from inotify, as
//! far as we are aware of them. Please watch out for any further warnings
//! throughout this documentation. If you want to be on the safe side, in case
//! we have missed something, please read the [inotify man pages] carefully.
//!
//! [inotify-rs]: https://crates.io/crates/inotify
//! [inotify]: https://en.wikipedia.org/wiki/Inotify
//! [`Inotify`]: struct.Inotify.html
//! [inotify man pages]: http://man7.org/linux/man-pages/man7/inotify.7.html
#![deny(missing_docs)]
#![deny(warnings)]
#![deny(missing_debug_implementations)]
#[macro_use]
extern crate bitflags;
mod events;
mod fd_guard;
mod inotify;
mod util;
mod watches;
#[cfg(feature = "stream")]
mod stream;
pub use crate::events::{
Event,
EventMask,
EventOwned,
Events,
};
pub use crate::inotify::Inotify;
pub use crate::util::{
get_buffer_size,
get_absolute_path_buffer_size,
};
pub use crate::watches::{
WatchDescriptor,
WatchMask,
};
#[cfg(feature = "stream")]
pub use self::stream::EventStream;
inotify-0.9.6/src/stream.rs 0000644 0000000 0000000 00000006054 00726746425 0014006 0 ustar 0000000 0000000 use std::{
io,
os::unix::io::{AsRawFd, RawFd},
pin::Pin,
sync::Arc,
task::{Context, Poll},
};
use futures_core::{ready, Stream};
use tokio::io::unix::AsyncFd;
use crate::events::{Event, EventOwned};
use crate::fd_guard::FdGuard;
use crate::util::read_into_buffer;
/// Stream of inotify events
///
/// Allows for streaming events returned by [`Inotify::event_stream`].
///
/// [`Inotify::event_stream`]: struct.Inotify.html#method.event_stream
#[derive(Debug)]
pub struct EventStream {
fd: AsyncFd,
buffer: T,
buffer_pos: usize,
unused_bytes: usize,
}
impl EventStream
where
T: AsMut<[u8]> + AsRef<[u8]>,
{
/// Returns a new `EventStream` associated with the default reactor.
pub(crate) fn new(fd: Arc, buffer: T) -> io::Result {
Ok(EventStream {
fd: AsyncFd::new(ArcFdGuard(fd))?,
buffer: buffer,
buffer_pos: 0,
unused_bytes: 0,
})
}
}
impl Stream for EventStream
where
T: AsMut<[u8]> + AsRef<[u8]>,
{
type Item = io::Result;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll