Giter Site home page Giter Site logo

inotify-rs's Introduction

inotify-rs crates.io Documentation Rust

Idiomatic inotify wrapper for the Rust programming language.

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
        .watches()
        .add(
            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:

[dependencies]
inotify = "0.10"

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-rs's People

Contributors

arsalan86 avatar benschulz avatar blaenk avatar blckngm avatar daboross avatar danieldg avatar dead10ck avatar fenrirwolf avatar guillaumegomez avatar hannobraun avatar hawkw avatar htrefil avatar i509vcb avatar joshtriplett avatar jurodr avatar kiron1 avatar leoniephiline avatar lhallam avatar mathstuf avatar neuschaefer avatar paholg avatar palfrey avatar passcod avatar raoulhc avatar talklittle avatar themasch avatar tokenrove avatar tomasvdw avatar tshepang avatar tsurai avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

inotify-rs's Issues

Remove GitCop integration

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.

Review `Inotify::add_watch` and `WatchMask`

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.

event.name is always "" when add_watch is called with a filename instead of a dir

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.

Look into automatic changelog generation

I've heard of projects, for example Hyper, automatically generating changelogs from Git commits. I haven't looked into the details yet, but I understand that GitCop can be used to enforce the commit message format that's required for that.

too many open files

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);
}


add a read_events_with_timeout()

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.

Implement `RawFd`-related traits

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.

Review documentation

The documentation is relatively complete, but it could be organized better. Some ideas:

  • Move high-level overview from Inotify to root module
  • Make sure inotify-sys documentation and inotify man pages are referenced, where appropriate.

I'm sure there are other aspects that could be improved.

Decide what to do about unstable warnings

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.

linking with `cc` failed

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)

`WatchDescriptor` usage is not safe

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 implement Drop for watch descriptor!

Improve documentation of `Inotify::add_watch`

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.

Review representation of `mask` in `Event`

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.

wrapper abstraction does not lend itself to multithreading

When one thread reads from the inotify fd and another closes it, bad things can happen.

  • The reading thread gets stuck in wait_for_events forever.
  • The inotify fd becomes invalid (⇒ bad file descriptor).
  • The inotify fd is recycled by the OS (⇒ undefined bahavior).

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)

Base API on futures-rs

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).

Consider using information from /proc

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.

`Eq` implementation of `WatchDescriptor` is wrong

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.

Add tags for released versions

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.

WatchDescriptors returned by Inotify are arbitrary integers, not Unix File Descriptors

The inotify-rs wrapper makes use of the std::os::unix::io::RawFd type to describe Inotify WatchDescriptors.

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.

Consider unifying traditional and future-based APIs

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!

Document unsafe usage

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.

Readme example incorrect

rm_watch seems to have been changed to take a Watch as returned by add_watch, so the commented-out example is incorrect.

Async/Await syntax support

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.

Getting the full path to a modified file from an event

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 ?

Look into inotify gotchas and what can be done about them

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.

Consider removing the watch when `WatchDescriptor` is dropped

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:

  • Currently WatchDescriptor implements Clone and Copy. This would probably no longer be desirable, if we wanted it to implement Drop.
  • The 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.
  • If we allow only one 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.
  • What would happen if the user called 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.

On Linux directory watcher stops after emitting first couple of events

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.

Document event deduplication

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.

Unable to watch files on Ubuntu

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)
          }

}

Non-blocking INotify.event

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.

Multiple `EventStream`s can take each other's events

It is possible to create multiple EventStreams 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:

  1. Duplicate add_watch/rm_watch on EventStream. That way, EventStream would effectively become a replacement for Inotify that uses a different method for reading events.
  2. Split 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.

"No space left on device"

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

Issue with detecting Move to and Move from

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

Address watch descriptor reuse

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.

Consider redesign of the field `name` in `Event`

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:

  • Clearly explain this in the documentation of the field.
  • Consider changing the type of the field to Option<&'a OsStr>, to make it clear that the name is not always set.

Build failure

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.

Update bitflags dependency

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.