Linux file system events with C, Python and Ruby

来源:互联网 发布:建一个淘宝白菜群佣金 编辑:程序博客网 时间:2024/05/01 21:27

Some applications (like file managers, monitoring tools, etc) need to know about events in the file system, for example when a file was created, opened or deleted.

With Linux, you can use the inotify mechanism to react to those events (with kernel 2.6.13 or above). In this article, I show you examples for it’s usage with C, Python and Ruby. In an upcoming post, we use Java 7 to monitor file events.

The C version

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <stdio.h>
#include <sys/inotify.h>
#include <stdlib.h>
#include <limits.h>
 
 
// hard coded directory and file to watch. don't do this in production code
#define DIR_TO_WATCH "/tmp/notify-dir"
#define FILE_TO_WATCH "/tmp/notify-dir/notify-file.txt"
 
#define EVENT_SIZE (sizeof (struct inotify_event))
 
// define large enough buffer
#define EVENT_BUFFER_LENGTH (1024 * EVENT_SIZE + NAME_MAX + 1)
 
voidprint_event(structinotify_event *event) {
 
    if(event->mask & IN_CREATE)
        printf("file created in directory\n");
    if(event->mask & IN_DELETE)
        printf("file deleted in directory\n");
    if(event->mask & IN_ACCESS)
        printf("file accessed\n");
    if(event->mask & IN_CLOSE)
        printf("file closed after reading or writing \n");
    if(event->mask & IN_OPEN)
        printf("file opened\n");
 
    if(event->len)
        printf("name: %s\n", event->name);
 
}
 
int main(int argc,char** argv) {
 
    intnotify_fd;
    intwatch_fd;
    longinput_len;
    char*ptr;
    charbuffer[EVENT_BUFFER_LENGTH];
    structinotify_event *event;
 
    notify_fd = inotify_init();
    if(notify_fd < 0) {
        perror("cannot init inotify");
        exit(EXIT_FAILURE);
    }
 
    watch_fd = inotify_add_watch(notify_fd, DIR_TO_WATCH, IN_CREATE | IN_DELETE);
    if(watch_fd < 0) {
        perror("cannot add directory");
        exit(EXIT_FAILURE);
    }
    watch_fd = inotify_add_watch(notify_fd, FILE_TO_WATCH, IN_ACCESS | IN_CLOSE | IN_OPEN);
    if(watch_fd < 0) {
        perror("cannot add file");
        exit(EXIT_FAILURE);
    }
 
    while(1) {
        input_len = read(notify_fd, buffer, EVENT_BUFFER_LENGTH);
        if(input_len <= 0) {
            perror("error reading from inotify fd");
            exit(EXIT_FAILURE);
        }
 
        ptr = buffer;
        while(ptr < buffer + input_len) {
            event = (structinotify_event *) ptr;
            print_event(event);
            ptr +=sizeof (structinotify_event) +event->len;
        }
    }
 
    exit(EXIT_SUCCESS);
}

This code is relatively straightforward. You start the mechanism mit inotify_init(), add watches withinotify_add_watch, read the events from the inotify file descriptor and call theprint_event function to print some information on stdout. Of course, depending on your software, you will do something completely different for each event.
The meaning of the events should be clear from the names of the constants. IN_CLOSE is used for closing a file after reading it or writing to it. You can also use two different events for those types of closing (IN_CLOSE_WRITE, IN_CLOSE_NOWRITE, see Python example below).
The inotify mechanism supports many more events than used in this example. See the man page for inotify for details.

If you use C++, you may want to have a look at this:
inotify C++ interface

Ruby Version

1
2
3
4
5
6
7
8
9
10
11
12
require "rb-inotify"
 
DIR_TO_WATCH= "/tmp/notify-dir"
 
notifier = INotify::Notifier.new
 
notifier.watch(DIR_TO_WATCH,:create, :delete) do|event|
  puts"Create event for: #{event.name}" if event.flags.include?(:create)
  puts"Delete event for: #{event.name}" if event.flags.include?(:delete)
end
 
notifier.run

This uses the rb-inotify Ruby library. In this example, Ruby 1.9 was used.
To keep the example short, the Ruby version only watches events for a given directory (when a file is created or deleted). That should be enough to show you how the library works.
If you watch only one event, you don’t need the if behind the puts. I added this because I watch for several events but wanted different output for each event.
In order to watch for file events like in the C version, do the same thing for a file and use different events.
More information about the Ruby rb-inotify library can be found here:
http://rdoc.info/projects/nex3/rb-inotify

Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import pyinotify
 
DIR_TO_WATCH="/tmp/notify-dir"
FILE_TO_WATCH="/tmp/notify-dir/notify-file.txt"
 
wm =pyinotify.WatchManager()
 
dir_events = pyinotify.IN_DELETE | pyinotify.IN_CREATE
file_events = pyinotify.IN_OPEN | pyinotify.IN_CLOSE_WRITE | pyinotify.IN_CLOSE_NOWRITE
 
classEventHandler(pyinotify.ProcessEvent):
    defprocess_IN_DELETE(self, event):
        print("File %s was deleted"% event.pathname) #python 3 style print function
    defprocess_IN_CREATE(self, event):
        print("File %s was created"% event.pathname)
    defprocess_IN_OPEN(self, event):
        print("File %s was opened"% event.pathname)
    defprocess_IN_CLOSE_WRITE(self, event):
        print("File %s was closed after writing"% event.pathname)
    defprocess_IN_CLOSE_NOWRITE(self, event):
        print("File %s was closed after reading"% event.pathname)
 
event_handler = EventHandler()
notifier = pyinotify.Notifier(wm, event_handler)
 
wm.add_watch(DIR_TO_WATCH, dir_events)
wm.add_watch(FILE_TO_WATCH, file_events)
 
notifier.loop()

The Python example used Python 3 (version 3.2 in my machine) as you can see by the way print is used (as a function).
In the Python example I used different handlers for IN_CLOSE_WRITE (used after a file was closed after writing something to it) and IN_CLOSE_NOWRITE (used after a file was closed after just reading the content).
You could write only one callback method process_IN_CLOSE to handle both events, but I wanted different output messages. And sometimes it is better to write a little more code to make it cleaer.

The pynotify module is available for both Python 2 and Python 3 and is very easy to use. More information can be found here:
https://github.com/seb-m/pyinotify/wiki

Conclusion

As you can see, listening to different file system events on Linux is not difficult using C, C++, Python or Ruby. The inotify mechanism is also available for other languages (for example, seehere for a Haskell Version).

I prefer to use the Ruby or Python version over the C version as the source code is considerably shorter and easier to understand (as it is often the case with shorter code).
Of course it depends on your project. If you use C for your project, you have to go with the C version. If you just need a short script, for example for monitoring, I recommend going with the Ruby or Python solution (or aPerl implementation)

These examples only show some basic functionality of the inotify mechanism. For example, the Python version has different notifiers, for example a ThreadedNotifier. For more details, check the man pages and the documentation listed above. I hope this example serves as a starting point for your own programs.

Those modules and libraries are specific to Linux and won’t work on other operating systems. If you are a Java developer, you can useinotify-java, but this won’t be platform independent (which is often a goal for Java software).
In an upcoming posting, I will show you how to use the latest features of Java 7 to monitor file system events.

0 0