Welcome, guest | Sign In | My Account | Store | Cart

This module is meant to be as simple and straightforward as it gets.

Python, 72 lines
 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
'''
    Low level inotify wrapper
'''

from os import read, close
from struct import unpack
from fcntl import ioctl
from termios import FIONREAD
from time import sleep
from ctypes import cdll, c_int, POINTER
from errno import errorcode

libc = cdll.LoadLibrary('libc.so.6')
libc.__errno_location.restype = POINTER(c_int)
def geterr(): return errorcode[libc.__errno_location().contents.value]

class Inotify(object):
    def __init__(self):
        self.fd = libc.inotify_init()
        if self.fd == -1:
            print 'inotify INIT err :', geterr()
            raise OSError()
    def read(self):
        size_int = c_int()
        while ioctl(self.fd, FIONREAD, size_int)==-1: sleep(1)
        size = size_int.value
        if not size: return
        data = read(self.fd, size)
        deb = 0
        while deb < size:
            fin = deb+16
            wd, mask, cookie, name_len = unpack('iIII', data[deb:fin])
            deb, fin = fin, fin+name_len
            name = unpack('%ds' % name_len, data[deb:fin])
            name = name[0].rstrip('\0')
            deb = fin
            yield wd, mask, cookie, name
    def add_watch(self, path, mask):
        wd = libc.inotify_add_watch(self.fd, path, mask)
        if wd == -1: print 'inotify ADD err :', geterr()
        return wd
    def rm_watch(self, wd):
        ret = libc.inotify_rm_watch(self.fd, wd)
        if ret == -1: print 'inotify RM err :', geterr()
    def close(self):
        close(self.fd)

FLAGS = {
    'ACCESS'      : 0x00000001, # IN_ACCESS
    'MODIFY'      : 0x00000002, # IN_MODIFY
    'ATTRIB'      : 0x00000004, # IN_ATTRIB
    'WRITE'       : 0x00000008, # IN_CLOSE_WRITE
    'CLOSE'       : 0x00000010, # IN_CLOSE_NOWRITE
    'OPEN'        : 0x00000020, # IN_OPEN
    'MOVED_FROM'  : 0x00000040, # IN_MOVED_FROM
    'MOVED_TO'    : 0x00000080, # IN_MOVED_TO
    'CREATE'      : 0x00000100, # IN_CREATE
    'DELETE'      : 0x00000200, # IN_DELETE
    'DELETE_SELF' : 0x00000400, # IN_DELETE_SELF
    'MOVE_SELF'   : 0x00000800, # IN_MOVE_SELF
    'UNMOUNT'     : 0x00002000, # IN_UNMOUNT
    'Q_OVERFLOW'  : 0x00004000, # IN_Q_OVERFLOW
    'IGNORED'     : 0x00008000, # IN_IGNORED
    'ONLYDIR'     : 0x01000000, # IN_ONLYDIR
    'DONT_FOLLOW' : 0x02000000, # IN_DONT_FOLLOW
    'MASK_ADD'    : 0x20000000, # IN_MASK_ADD
    'ISDIR'       : 0x40000000, # IN_ISDIR
    'ONESHOT'     : 0x80000000, # IN_ONESHOT
}

def mask_str(mask):
    return ' | '.join(name for name, val in FLAGS.items() if val & mask)

This module is not intended to be used as is, it should be used to build higher level modules, like the one you can see here

3 comments

Sok Ann Yap 14 years, 11 months ago  # | flag

For some reason, this has stopped working for me since kernel 2.6.28 (Gentoo machines), with the following exception:

Exception in thread Thread-2:                                                                                                                     
Traceback (most recent call last):                                                                                                                
  File "/usr/lib64/python2.5/threading.py", line 486, in __bootstrap_inner                                                                        
    self.run()                                                                                                                                    
  File "/usr/lib64/python2.5/threading.py", line 446, in run                                                                                      
    self.__target(*self.__args, **self.__kwargs)                                                                                                  
  File "/path/to/watch.py", line 67, in _push                                              
    for wd, mask, cookie, name in self.inotify.read():                                                                                            
  File "/path/to/inotify.py", line 28, in read                                             
    data = read(self.fd, size)                                                                                                                    
OSError: [Errno 22] Invalid argument

I need to apply the following patch to get it to work again:

--- inotify.py.orig
+++ inotify.py
@@ -7,7 +7,7 @@
 from fcntl import ioctl
 from termios import FIONREAD
 from time import sleep
-from ctypes import cdll, c_int, POINTER
+from ctypes import cdll, create_string_buffer, c_int, POINTER
 from errno import errorcode

 libc = cdll.LoadLibrary('libc.so.6')
@@ -24,7 +24,9 @@
         size_int = c_int()
         while ioctl(self.fd, FIONREAD, size_int)==-1: sleep(1)
         size = size_int.value
-        data = read(self.fd, size)
+        buf = create_string_buffer(size)
+        libc.read(self.fd, buf, size)
+        data = buf.raw
         deb = 0
         while deb < size:
             fin = deb+16
ivan 14 years, 8 months ago  # | flag

I had the exact same problem as above since Ubuntu 8.10's 2.6.27-14-server (possibly earlier). The patch worked. Thanks.

Louis RIVIERE (author) 10 years, 8 months ago  # | flag

Benjamin Peterson : "Linux is just not accepting "0" as a valid size argument to read(). You don't see this using libc.read because you don't check errno."

Thanks Benjamin. Corrected.