hannobraun / inotify-rs Goto Github PK
View Code? Open in Web Editor NEWIdiomatic inotify wrapper for the Rust programming language
License: ISC License
Idiomatic inotify wrapper for the Rust programming language
License: ISC License
On OSX I'm able to run a notifier for a specific file. When I run on Ubuntu/Linux I get an OS error telling me the path is not a directory (ENOTDIR - Err(Io(Error { repr: Os(20) }))).
Here's some code that demonstrates the problem:
extern crate notify;
use notify::{RecommendedWatcher, Error, Watcher};
use std::sync::mpsc::channel;
use std::path::Path;
fn main() {
println!("Hello, world!");
let path = "/tmp/foo.db";
let (tx, rx) = channel();
// Automatically select the best implementation for your platform.
// You can also access each implementation directly e.g. INotifyWatcher.
let w: Result<RecommendedWatcher, Error> = Watcher::new(tx);
match w {
Ok(mut watcher) => {
println!("created watcher!");
// Add a path to be watched. All files and directories at that path and
// below will be monitored for changes.
let x = watcher.watch(&Path::new(&path));
println!("watch result: {:?}", x);
// You'll probably want to do that in a loop. The type to match for is
// notify::Event, look at src/lib.rs for details.
loop {
match rx.recv() {
_ => {
println!("received and event!!!");
}
}
}
},
Err(e) => println!("failed to create watcher, error {:?}", e)
}
}
It is possible to create multiple EventStream
s from a single Inotify
instance. Those streams will read
from the same inotify file descriptor. Depending on the timing of the reads, one EventStream
could end up with all the events, or both could end up with some of them. (Note: I haven't tested this. It's just my own reasoning).
It would be possible to guarantee there is only one EventStream
per Inotify
instance, by consuming or mutably borrowing Inotify
when creating an EventStream
. However, both those solutions would make add_watch
/rm_watch
inaccessible.
I have two ideas for how to solve this:
add_watch
/rm_watch
on EventStream
. That way, EventStream
would effectively become a replacement for Inotify
that uses a different method for reading events.Inotify
into two parts, one for managing watches, one for reading events.I don't think solution 1 is flexible enough. Stream
has a lot of methods that move the stream, which would make add_watch
/rm_watch
inaccessible again. This isn't really a solution, it's just kicking the problem one step down the line.
Solution 2 deserves a bit more elaboration:
Inotify
would become a struct with two fields, watches
and events
.watches
would provide add_watch
/rm_watch
(which could be shortened to add
/remove
).events
would provide the various read_*
methods.events
could be moved or borrowed when creating an EventStream
, without affecting watches
.Right now I'm unsure what to do, although I like solution 2. I'm going to let this issue sit for a while before attempting an implementation. In the meantime, feedback is highly welcome.
The documentation for Inotify::event_stream
is out of date.
The examples should document what kinds of values can be passes as a buffer. See this comment in #124 and this comment in #125 for more information.
can you push a new crate version including #23's changes when you get a chance?
That would imply to use a select on the inotify's fd waiting for data in the fd or the timeout.
The workaround would be to use the nix crate with Inotify's RawFD and do the select in the application.
When adding a watch using Inotify::add_watch
, a WatchMask
must be passed. This WatchMask
defines which events should be watched and how to watch them. These two things, which events to watch for, and the configuration of that watch, could be considered separate concerns.
I'm thinking it may be better to split WatchMask
into two parameters, but I'm not sure at this point.
From the inotify man page:
When a watch descriptor is removed by calling inotify_rm_watch(2) (or because a watch file is deleted or the filesystem that contains it is unmounted), any pending unread events for that watch descriptor remain available to read. As watch descriptors are subsequently allocated with inotify_add_watch(2), the kernel cycles through the range of possible watch descriptors (0 to INT_MAX) incrementally. When allocating a free watch descriptor, no check is made to see whether that watch descriptor number has any pending unread events in the inotify queue. Thus, it can happen that a watch descriptor is reallocated even when pending unread events exist for a previous incarnation of that watch descriptor number, with the result that the application might then read those events and interpret them as belonging to the file associated with the newly recycled watch descriptor. In practice, the likelihood of hitting this bug may be extremely low, since it requires that an application cycle through INT_MAX watch descriptors, release a watch descriptor while leaving unread events for that watch descriptor in the queue, and then recycle that watch descriptor. For this reason, and because there have been no reports of the bug occurring in real-world applications, as of Linux 3.15, no kernel changes have yet been made to eliminate this possible bug.
I think a solution to this problem would be to force the user to read the full event queue before adding another watch. In principle, it should be relatively straight-forward to enforce this at compile-time, but that would require us to rethink Inotify
's API.
It is legal requirement of various Linux distributions. Thanks!
Currently, there are a lot of warnings about the use of unstable functionality in the code. If my understanding is correct, nothing unstable will be shipped with stable Rust versions following the release of Rust 1.0. I think it's very desirable for this library to work with stable versions, too.
The best decision for now is probably to wait. A lot of the unstable stuff might still become stable before 1.0. However, if there are still warnings shortly before the 1.0 release, we should probably attempt to replace the code in question with something else, if possible.
It's no longer as useful, as #120 is fixed now. It should be removed, and the technique it shows should be integrated into the API documentation.
It seems to be that it's possible to watch multiple directories using the same inotify object.
However, I haven't found a way to extract the full path to a file/directory once an event occurs.
For example I could be watching two directories:
~/A
~/B
Both of them contain the file test
Now, assuming I modify the test in either A or B an get a notification I can get the name of the file as such:
let mut modified = event.name; //Will be the OsStr "test"
Which sadly enough doesn't seem to tell me what directory 'test' is a part of.
Is it possible to get this information in any way without using separate inotify objects for each directory ? Or is the intended usage having a different inotify for each dir ?
Implement the RawFd
-related traits for WatchDescriptor
and possibly Inotify
.
Inotify
currently has an fd
method that allows access to the file descriptor. I've made that method unsafe
, to signal that it could mess with the Inotify
internals. I'm not sure now whether that's a good use of unsafe
.
Maybe the method should just be replaced with AsRawFd
/IntoRawFd
implementations. FromRawFd
should be implemented in any case.
The code contains quite a few unsafe
blocks. As this is a wrapper around a C API, this can't be avoided. However, all unsafe
blocks should be audited and it should be documented why the code in them is valid.
Currently, a WatchDescriptor
can exist pretty much independently of the actual inotify watch it represents. This means the user has to manually remove any watches (by calling Inotify::rm_watch
) that they no longer need. If the user doesn't do this, this can result in lots of useless events being fired.
I'm not sure if this is something we can solve, or if this is one of those issues that has to be left to a higher-level library. Some thoughts:
WatchDescriptor
implements Clone
and Copy
. This would probably no longer be desirable, if we wanted it to implement Drop
.Clone
implementation could be kept, if WatchDescriptor
did reference counting, for example using Rc
. This would require a heap allocation per watch. As an application might watch thousands of files and directories, this seems like excessive overhead for a low-level wrapper.WatchDescriptor
instance per watch, then Event
could no longer contain a WatchDescriptor
. Maybe the current WatchDescriptor
could be renamed to Watch
, implement Drop
, and Event
would carry a WatchDescriptor
whose sole purpose it was to be compared to a Watch
.Inotify::add_watch
to modify an already existing watch? This can happen by calling add_watch
for an already watched path, or even for a different path that refers to an already watched inode. If we naively created a new Watch
in that case, then dropping that Watch
would also remove the watch for the first Watch
, if that was still around.Of course all problems could be solved if Inotify
did extensive internal bookkeeping on what watch descriptors are being used. That kind of overhead seems out of the question, especially since any higher-level library would probably want to duplicate all that work in a different way on top.
If this issue is going to be closed without a solution, the problem should be properly documented.
From Stack Overflow:
The library does nothing to highlight major gotchas of inotify: event queue overflows,
IN_IGNORED
and hard links sharing the same inode (and the same inotify watch descriptors!)
It's true that inotify has major gotchas that make it hard to use. Since inotify-rs is intended to be a low-level wrapper, to be used as a building block for higher-level libraries, I'm not sure whether all of those can be reasonably handled in this library.
However, I think it's a good idea to look into each of the issues mentioned in the above quote (and possibly other issues mentioned in the man pages) and either
a) find a way to reasonably handle them within inotify-rs, or
b) clearly document that they exist and are not handled by inotify-rs.
They state that "This function directly returns all errors from the call to read. " - however EAGAIN/EWOULDBLOCK is signaled by returning an empty slice.
From the inotify man page:
The set of watch descriptors that is being monitored via an inotify file descriptor can be viewed via the entry for the inotify file descriptor in the process's /proc/[pid]/fdinfo directory. See proc(5) for further details.
We're not currently using this information, but it might be useful. For example, it might be possible to detect whether inotify_add_watch
added a new watch or updated an existing one. This might have implications for #74.
I'm not currently aware what kind of performance overhead is associated with access to the /proc filesystem. This should be a consideration when making a decision about this issue.
From Stack Overflow:
There is nothing to ensure, that inotify watches are closed before closing the inotify descriptor. There is nothing to ensure that watch descriptors don't get reused behind your back (they can be reused after watch descriptor number wraps). Those issues probably won't bite you immediately, but… IMO, the entire inotify-rs should have been declared
unsafe
. It does not add any actual safety on top of inotify, just some sugar and pointless wrappers. It does not even implementDrop
for watch descriptor!
Inotify::add_watch
can not only be used to create a new watch. It can also modify an already existing watch. This will happen, if add_watch
is called with a path it has already been called with before, or if it is called with any path that refers to an inode that is already being watched.
Modifying an existing watch will not allocate a new watch descriptor and will return an existing one instead.
Since this behavior can be highly unexpected, especially in the presence of hardlinks, it should be highlighted in the documentation.
This repository doesn't contain tags for any released versions. At least new versions should be tagged, but ideally we'd add tags for old versions, too.
One thing to note is that this repository contains two crates now, so the tag name should probably include the crate name. Something like inotify_v1.2.3
/inotify-sys_v1.2.3
.
From Stack Overflow:
As noted above, inotify watch descriptor values are scoped per parent inotify file descriptor. This makes the use of [derive(Eq)] on WatchDescriptor highly questionable. Looks like a straight-up bug.
I haven't had the chance to look into the details myself so far, but this seems legit.
Bitflags 1.0 has been released, now using associated constants. This looks like an improvement, but is a breaking change, so I think we should upgrade sooner rather than later.
From reading the man pages, I get the impression that distinct event objects are generated for each type of event, and that therefore most bits in the event mask can't coexist. If that is the case, maybe it makes sense to create a new enum, EventKind
, to represent the type of event.
One exception to this case is IN_ISDIR
, which can be set in addition to other bits that identify the type of event. A new bool
field could be created specifically for this flag.
In any case, before anything like this can be merged, more careful reading of the man pages and probably some experimentation is required.
We're still depending on bigflags 0.8, but version 0.9 has been out for a while.
As I'm writing this, an error is returned, which might not be the best thing to do. See discussion in #14.
When one thread reads from the inotify fd and another closes it, bad things can happen.
wait_for_events
forever.I'd be happy to submit a PR to fix this. However, any solution I can come up with involves at least some API breakage. Therefore I'd really like to get some input first.
(ref notify-rs/notify#37)
At least repository
and travis-ci
are out of date.
Using the exact code in the README.md and adding the following:
else if event.mask.contains(event_mask::MOVED_FROM) {
println!("Moved from {:?}", event.name);
} else if event.mask.contains(event_mask::MOVED_TO) {
println!("Moved to {:?}", event.name);
} else {
println!("Other event\n");
}
I am unable to detect the moving of a file (or at least some other event symbolized by the last else when I mv a file).
Using inotifywait on the same directory yields the 'correct' events:
MOVED_FROM A
MOVED_TO B
When I run mv A B
rm_watch
seems to have been changed to take a Watch as returned by add_watch
, so the commented-out example is incorrect.
The current API offers both synchronous and asynchronous operation, but it would be better to do that based on futures-rs. I've looked into it, but decided to hold off for the moment, until I had a chance to learn a bit more about futures-rs and experience it from a user's perspective (as opposed to that of a library author).
Using rustc 1.0.0-nightly (ea6f65c5f 2015-01-06 19:47:08 +0000)
, it fails to build:
skyler at skyler-0345 in ~/src/rust/inotify-rs (master)
$ cargo build
Compiling inotify v0.1.1 (file:///Users/skyler/src/rust/inotify-rs)
/Users/skyler/src/rust/inotify-rs/src/wrapper.rs:20:2: 20:9 error: unresolved import `std::c_str::CString`. Could not find `c_str` in `std`
/Users/skyler/src/rust/inotify-rs/src/wrapper.rs:20 CString,
^~~~~~~
/Users/skyler/src/rust/inotify-rs/src/wrapper.rs:21:2: 21:8 error: unresolved import `std::c_str::ToCStr`. Could not find `c_str` in `std`
/Users/skyler/src/rust/inotify-rs/src/wrapper.rs:21 ToCStr,
^~~~~~
error: aborting due to 2 previous errors
Could not compile `inotify`.
To learn more, run the command again with --verbose.
Currently there are two parallel APIs: The original one, and the new future-based one. I think it would be ideal if we could unify those.
One problem I see is efficiency. The stream-based API requires one additional heap allocation for every event that has a name
. The reason for this are lifetime issues that probably can't be resolved, at least not in safe Rust.
I think it's best for now to keep things as they are, while keeping a look at how futures develop. Feedback is very welcome!
Futures 0.3 will support the Async/Await syntax.
One it reaches GA, Tokio will be next.
We should investigate what it will require of us to support the new syntax and prepare ourselves for it.
Hi, just wondering if a release on crates.io is planned.
Experience has shown that the commit style it enforces is not really useful for generating a changelog, at least not without further manual enforcement around Git commit messages. All in all, it's more trouble than it's worth. This is a reminder for me to remove it.
Hi~
When I run the example, it gave me some error:
error: linking with `cc` failed: exit code: 1
|
= note: "cc" "-m64" "-L" "/Users/suhanyu/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/rs_inotify-3eb434efa42c26bf.1owus55qvpsfqs60.rcgu.o" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/rs_inotify-3eb434efa42c26bf.1qt0p25ty25mug09.rcgu.o" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/rs_inotify-3eb434efa42c26bf.259u8q3fukleu4l8.rcgu.o" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/rs_inotify-3eb434efa42c26bf.26xtl3mixibro044.rcgu.o" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/rs_inotify-3eb434efa42c26bf.2vxq5dm32jsou234.rcgu.o" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/rs_inotify-3eb434efa42c26bf.384qytb7ad5lurrp.rcgu.o" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/rs_inotify-3eb434efa42c26bf.3atgdi55ak1j2nng.rcgu.o" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/rs_inotify-3eb434efa42c26bf.3lcd43w2w8fpbcr4.rcgu.o" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/rs_inotify-3eb434efa42c26bf.3voj73xxznplxyo.rcgu.o" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/rs_inotify-3eb434efa42c26bf.43shqjee4jvp1zxo.rcgu.o" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/rs_inotify-3eb434efa42c26bf.4mui56p6mslglwav.rcgu.o" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/rs_inotify-3eb434efa42c26bf.4z9o7y5w0p7tfjbs.rcgu.o" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/rs_inotify-3eb434efa42c26bf.58oz6dvxg5xkz4gx.rcgu.o" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/rs_inotify-3eb434efa42c26bf.5agolatez8b95yjw.rcgu.o" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/rs_inotify-3eb434efa42c26bf.ix6j80fgd0dlmoa.rcgu.o" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/rs_inotify-3eb434efa42c26bf.nwf0mxaszxab87b.rcgu.o" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/rs_inotify-3eb434efa42c26bf.qkoh5jkqwg09ty0.rcgu.o" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/rs_inotify-3eb434efa42c26bf.w60uhcyrnbw26s6.rcgu.o" "-o" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/rs_inotify-3eb434efa42c26bf" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/rs_inotify-3eb434efa42c26bf.dssoovpbv7e9uxm.rcgu.o" "-Wl,-dead_strip" "-nodefaultlibs" "-L" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps" "-L" "/Users/suhanyu/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/libinotify-77e5eecc10c3475a.rlib" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/libfutures_core-bdffb28e46e4dfa3.rlib" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/libtokio-9ab056c1b73239a9.rlib" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/libbytes-02007a3b2af2e8f1.rlib" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/libpin_project_lite-3c1a2e829136ef28.rlib" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/libmio-4ab4e1343dc398e5.rlib" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/liblog-e49ec6733c6aa0fe.rlib" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/libslab-1dc456b6c6d33a5b.rlib" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/libiovec-14649cac03119d92.rlib" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/libnet2-42f667f8a1328f2e.rlib" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/libcfg_if-9e348a9fb2b89a89.rlib" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/libinotify_sys-767ab7331cfd2b81.rlib" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/liblibc-7fa5eb974e65c1aa.rlib" "/Volumes/su2/userSa/www2/tech/repo/rust/some-tool/rs_inotify/target/debug/deps/libbitflags-8e32dd99ef981b02.rlib" "/Users/suhanyu/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib/libstd-7b7c1aa360707106.rlib" "/Users/suhanyu/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib/libpanic_unwind-0a60af874e0f1312.rlib" "/Users/suhanyu/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib/libhashbrown-43d0d9d231bd7cce.rlib" "/Users/suhanyu/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib/librustc_std_workspace_alloc-100c6a8c3adebc50.rlib" "/Users/suhanyu/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib/libbacktrace-aeb2baf0c92f9f6d.rlib" "/Users/suhanyu/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib/libbacktrace_sys-01413be072bcbd80.rlib" "/Users/suhanyu/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib/librustc_demangle-d1c10f9c63e29f57.rlib" "/Users/suhanyu/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib/libunwind-462ae417fcbdfe75.rlib" "/Users/suhanyu/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib/libcfg_if-ef96c6ee0108ea8f.rlib" "/Users/suhanyu/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib/liblibc-f39561e1a04f71f4.rlib" "/Users/suhanyu/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib/liballoc-cece2afa99061033.rlib" "/Users/suhanyu/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib/librustc_std_workspace_core-d44b42c36a93f649.rlib" "/Users/suhanyu/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib/libcore-50adb13cf7b41db0.rlib" "/Users/suhanyu/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib/libcompiler_builtins-57f4887058fe2807.rlib" "-lSystem" "-lresolv" "-lc" "-lm"
= note: Undefined symbols for architecture x86_64:
"_inotify_init1", referenced from:
inotify::inotify::Inotify::init::h3e720c6e3888706b in libinotify-77e5eecc10c3475a.rlib(inotify-77e5eecc10c3475a.inotify.buwczvik-cgu.3.rcgu.o)
"_inotify_add_watch", referenced from:
inotify::inotify::Inotify::add_watch::h3d2fb8b5e0db507e in rs_inotify-3eb434efa42c26bf.43shqjee4jvp1zxo.rcgu.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I've tried the example on my $HOME directory with 125k folders.
fn watch() {
// Create a channel to receive the events.
let (tx, rx) = channel();
// Automatically select the best implementation for your platform.
// You can also access each implementation directly e.g. INotifyWatcher.
let w: Result<RecommendedWatcher, Error> = Watcher::new(tx);
match w {
Ok(mut watcher) => {
// Add a path to be watched. All files and directories at that path and
// below will be monitored for changes.
let mut r = watcher.watch("/home/oever");
while let Ok(()) = r {
// You'll probably want to do that in a loop. The type to match for is
// notify::Event, look at src/lib.rs for details.
match rx.recv() {
Ok(notify::Event{path:Some(path),op:Ok(op)}) => {
println!("{:?} {:?}", op, path);
},
Err(e) => println!("watch error {}", e),
_ => ()
}
println!("next round");
r = watcher.watch("/home/oever/src");
}
if let Err(err) = r {
println!("Error?: {:?}", err);
}
},
Err(err) => println!("Error!: {:?}", err)
}
}
prints out
Error?: Io(Error { repr: Os { code: 28, message: "No space left on device" } })
The error occurred in the watch() loop. Inotify had plenty of handles left:
$ cat /proc/sys/fs/inotify/max_user_watches
524288
The name, as returned by inotify, is only set, if the watch refers to a directory, and the event is for a file within that directory. This means, if you're watching a file directly, the name will be empty.
As this has lead to confusion among users in the past, I propose the following two measures:
Option<&'a OsStr>
, to make it clear that the name is not always set.I'm using notify
in a personal, downstream rust hotloading project, where the src
directory is watched recursively in order to re-build the lib if a change happens to any of the src files.
I'm running into an issue where the watcher will only emit events for the first two or three file events that occur when writing to src/lib.rs
, and then will suddenly stop reporting any events at all.
This same issue seems to occur on both versions 4 and 5-pre, both for raw and debounced watchers. I haven't yet tested master.
I haven't yet honed in on whether or not there is an event in particular that causes the stoppage. I haven't yet tested if this occurs with non-recursive mode.
I noticed the shade_runner
crate also uses notify for its file events but it does not seem to have this issue. One major difference worth investigating is that shade_runner
watches individual files rather than a directory, so perhaps the issue has something to do with watching directories in particular.
I'm going to do some digging today to see if I can work out exactly what's going on.
see http://stackoverflow.com/questions/38078936/borrowing-reference-in-structure for a user having trouble with the API.
I suggest to split the method into two. One to fetch the events and one to read the events that were fetched.
Not everyone uses Sublime text!
The documentation is relatively complete, but it could be organized better. Some ideas:
Inotify
to root moduleI'm sure there are other aspects that could be improved.
The inotify-rs wrapper makes use of the std::os::unix::io::RawFd
type to describe Inotify WatchDescriptor
s.
Inotify itself describes WatchDescriptors as an integer:
struct inotify_event {
int wd; /* Watch descriptor */
uint32_t mask; /* Mask describing event */
uint32_t cookie; /* Unique cookie associating related
events (for rename(2)) */
uint32_t len; /* Size of name field */
char name[]; /* Optional null-terminated name */
};
sourced from: http://man7.org/linux/man-pages/man7/inotify.7.html
A closer examination of the Inotify source reveals that WatchDescriptors are, in fact, arbitrary integers used to identify Events with the calls to add_watch() that generated them, as outlined in this stackoverflow answer
The inotify-rs wrapper should not use the RawFD
type to refer to WatchDescriptors, and should use an i32
instead.
Turns out I've been duplicating some functionality in the wrapper code that already exists in the Rust standard library, namely last_error: http://static.rust-lang.org/doc/master/std/io/struct.IoError.html
The INotify interface should be changed to return IoResult.
I've been trying to get this to work, but have been unsuccessful. I'm writing a program where I want to monitor a large number of files spread across different paths in the filesystem.
If I pass /path/to/file to add_watch(), the returned event for that watch always contains an empty name field.
I have been able to replicate this with on a variety of environments, with many kinds of target files (with assorted permissions). I have also tried many different watch masks.
As it stands, this library seems broken for use with single file targets, as opposed to dirs.
From the inotify man page:
If successive output inotify events produced on the inotify file descriptor are identical (same wd, mask, cookie, and name), then they are coalesced into a single event if the older event has not yet been read (but see BUGS). This reduces the amount of kernel memory required for the event queue, but also means that an application can't use inotify to reliably count file events.
This should be clearly documented in Inotify::read_events
/Inotify::read_events_blocking
.
I'd like to call INotify.event() in a non-blocking way, so my program can have a single main loop that checks for INotify events alongside normal processing. Unfortunately, the current implementation appears to block waiting for an event. Further, there doesn't appear to be an easy way to check for pending events so I might only make the blocking call if an event is pending.
Am I just missing something? Fairly new to Rust so this is quite likely. :)
Thanks.
See #106 (comment).
#119 adds an example for working around this issue.
on Centos7
rust version rustc 1.37.0 (eae3437df 2019-08-13)
inotify = "0.7"
extern crate inotify;
use std::process::Command;
use std::path::PathBuf;
use std::path::Path;
use std::env;
use std::os::unix::io::AsRawFd;
pub fn system_get_file_handle(file: &std::path::Path) -> usize {
let process_id = std::process::id();
let command = format!("lsof -n| grep {:?} | grep {:?} | wc -l", process_id, file);
let stdout = std::process::Command::new("sh").arg("-c").arg(command).output().expect("output").stdout;
let data = String::from_utf8(stdout).expect("utf8");
let split: Vec<&str> = data.trim().split(' ').collect();
split.get(0).expect("get 0").to_string().parse().expect("num")
}
fn test(p: &Path) {
std::thread::spawn(move || {
let mut inotify = inotify::Inotify::init().expect("inotify");
});
assert_eq!(0, system_get_file_handle(p));
let file = std::fs::File::open(p);
assert_eq!(1, system_get_file_handle(p));
}
fn test1(p: &Path) {
std::thread::spawn(move || {
let mut inotify = inotify::Inotify::init().expect("inotify");
loop {
}
});
assert_eq!(0, system_get_file_handle(p));
let file = std::fs::File::open(p);
assert_eq!(2, system_get_file_handle(p));
}
fn test3(p: &Path) {
for _ in 0..10 {
std::thread::spawn(move || {
let mut inotify = inotify::Inotify::init().expect("inotify");
loop {
}
});
}
assert_eq!(0, system_get_file_handle(p));
let file = std::fs::File::open(p);
assert_eq!(11, system_get_file_handle(p));
}
fn main() {
let path = Path::new("./src/main.rs");
test3(&path);
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.