Package elisa :: Package extern :: Package coherence :: Module inotify
[hide private]
[frames] | no frames]

Source Code for Module elisa.extern.coherence.inotify

  1  # Licensed under the MIT license 
  2  # http://opensource.org/licenses/mit-license.php 
  3   
  4  # Copyright 2006,2007 Frank Scholz <coherence@beebits.net> 
  5  # Modified by Colin Laplace, added is_watched() function 
  6   
  7  import os 
  8  import struct 
  9   
 10  try: 
 11      import ctypes 
 12  except ImportError: 
 13      ctypes = None 
 14   
 15   
 16  from twisted.internet import reactor 
 17  from twisted.internet.abstract import FileDescriptor 
 18  from twisted.internet import fdesc, protocol 
 19   
 20  from twisted.python.filepath import FilePath 
 21   
 22   
 23  # from /usr/src/linux/include/linux/inotify.h 
 24   
 25  IN_ACCESS =         0x00000001L     # File was accessed 
 26  IN_MODIFY =         0x00000002L     # File was modified 
 27  IN_ATTRIB =         0x00000004L     # Metadata changed 
 28  IN_CLOSE_WRITE =    0x00000008L     # Writtable file was closed 
 29  IN_CLOSE_NOWRITE =  0x00000010L     # Unwrittable file closed 
 30  IN_OPEN =           0x00000020L     # File was opened 
 31  IN_MOVED_FROM =     0x00000040L     # File was moved from X 
 32  IN_MOVED_TO =       0x00000080L     # File was moved to Y 
 33  IN_CREATE =         0x00000100L     # Subfile was created 
 34  IN_DELETE =         0x00000200L     # Subfile was delete 
 35  IN_DELETE_SELF =    0x00000400L     # Self was deleted 
 36  IN_MOVE_SELF =      0x00000800L     # Self was moved 
 37  IN_UNMOUNT =        0x00002000L     # Backing fs was unmounted 
 38  IN_Q_OVERFLOW =     0x00004000L     # Event queued overflowed 
 39  IN_IGNORED =        0x00008000L     # File was ignored 
 40   
 41  IN_ONLYDIR =         0x01000000      # only watch the path if it is a directory 
 42  IN_DONT_FOLLOW =     0x02000000      # don't follow a sym link 
 43  IN_MASK_ADD =        0x20000000      # add to the mask of an already existing watch 
 44  IN_ISDIR =           0x40000000      # event occurred against dir 
 45  IN_ONESHOT =         0x80000000      # only send event once 
 46   
 47  IN_CLOSE =      IN_CLOSE_WRITE | IN_CLOSE_NOWRITE   # closes 
 48  IN_MOVED =      IN_MOVED_FROM | IN_MOVED_TO         # moves 
 49  IN_CHANGED =    IN_MODIFY | IN_ATTRIB               # changes 
 50   
 51  IN_WATCH_MASK = IN_MODIFY | IN_ATTRIB | \ 
 52                  IN_CREATE | IN_DELETE | \ 
 53                  IN_DELETE_SELF | IN_MOVE_SELF | \ 
 54                  IN_UNMOUNT | IN_MOVED_FROM | IN_MOVED_TO 
 55   
 56   
 57  _flag_to_human = { 
 58      IN_ACCESS: 'access', 
 59      IN_MODIFY: 'modify', 
 60      IN_ATTRIB: 'attrib', 
 61      IN_CLOSE_WRITE: 'close_write', 
 62      IN_CLOSE_NOWRITE: 'close_nowrite', 
 63      IN_OPEN: 'open', 
 64      IN_MOVED_FROM: 'moved_from', 
 65      IN_MOVED_TO: 'moved_to', 
 66      IN_CREATE: 'create', 
 67      IN_DELETE: 'delete', 
 68      IN_DELETE_SELF: 'delete_self', 
 69      IN_MOVE_SELF: 'move_self', 
 70      IN_UNMOUNT: 'unmount', 
 71      IN_Q_OVERFLOW: 'queue_overflow', 
 72      IN_IGNORED: 'ignored', 
 73      IN_ONLYDIR: 'only_dir', 
 74      IN_DONT_FOLLOW: 'dont_follow', 
 75      IN_MASK_ADD: 'mask_add', 
 76      IN_ISDIR: 'is_dir', 
 77      IN_ONESHOT: 'one_shot'} 
 78   
 79  # system call numbers are architecture-specific 
 80  # see /usr/include/linux/asm/unistd.h and look for inotify 
 81  _inotify_syscalls = { 'i386': (291,292,293),  # FIXME, there has to be a better way for this 
 82                        'i486': (291,292,293), 
 83                        'i586': (291,292,293), 
 84                        'i686': (291,292,293), 
 85                        'x86_64': (253,254,255), # gotten from FC-6 and F-7 
 86                        'armv6l':(316,317,318),              # Nokia N800 
 87                        'armv5tej1':(316,317,318),           # Nokia N770 
 88                        'ppc': (275,276,277),                # PPC, like PS3 
 89                        } 
 90   
 91   
92 -class IWatchPoint:
93
94 - def __init__(self, path, mask = IN_WATCH_MASK, auto_add = False, callbacks=[]):
95 self.path = path 96 self.mask = mask 97 self.auto_add = auto_add 98 self.callbacks = [] 99 if callbacks != None: 100 if not isinstance(callbacks, list): 101 callbacks = [callbacks] 102 self.callbacks = callbacks
103
104 - def add_callback(self, callback, parameter=None):
105 self.callbacks.append((callback,parameter))
106
107 - def remove_callback(self, callback):
108 try: 109 del self.callbacks[callback] 110 except: 111 pass
112
113 - def notify(self, filename, events):
114 for callback in self.callbacks: 115 if callback != None: 116 callback[0](self, filename, events, callback[1])
117
118 -class INotify(FileDescriptor, object):
119 _instance_ = None # Singleton 120
121 - def __new__(cls, *args, **kwargs):
122 obj = getattr(cls,'_instance_',None) 123 if obj is not None: 124 return obj 125 else: 126 127 if ctypes == None: 128 raise SystemError, "ctypes not detected on this system, INotify support disabled" 129 130 obj = super(INotify, cls).__new__(cls, *args, **kwargs) 131 try: 132 obj.libc = ctypes.CDLL("libc.so.6") 133 except: 134 raise SystemError, "libc not found, INotify support disabled" 135 136 try: 137 obj.inotify_init = obj.libc.inotify_init 138 #print "horray, we have a libc with inotify support" 139 obj.inotify_add_watch = obj.libc_inotify_add_watch 140 obj.inotify_rm_watch = obj.libc_inotify_rm_watch 141 except: 142 import platform 143 machine = platform.machine() 144 try: 145 obj._init_syscall_id = _inotify_syscalls[machine][0] 146 obj._add_watch_syscall_id = _inotify_syscalls[machine][1] 147 obj._rm_watch_syscall_id = _inotify_syscalls[machine][2] 148 except: 149 raise SystemError, "unknown system '%s', INotify support disabled" % machine 150 151 FileDescriptor.__init__(obj) 152 153 obj._fd = obj.inotify_init() 154 if obj._fd < 0: 155 raise SystemError, "INotify support not detected on this system." 156 157 fdesc.setNonBlocking(obj._fd) # FIXME do we need this? 158 159 reactor.addReader(obj) 160 161 obj._buffer = '' 162 obj._watchpoints = {} 163 cls._instance_ = obj 164 return obj
165
166 - def release(self):
167 reactor.removeReader(self) 168 if hasattr(self, '_fd') and self._fd >= 0: 169 try: 170 os.close(self._fd) 171 except OSError: 172 pass 173 174 if hasattr(INotify, '_instance_'): 175 del INotify._instance_
176 177 __del__ = release 178
179 - def inotify_init(self):
180 return self.libc.syscall(self._init_syscall_id)
181
182 - def inotify_add_watch(self, path, mask):
183 if type(path) is unicode: 184 path = path.encode('utf-8') 185 self.libc.syscall.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_char_p, ctypes.c_int] 186 else: 187 self.libc.syscall.argtypes = None 188 return self.libc.syscall(self._add_watch_syscall_id, self._fd, path, mask)
189
190 - def inotify_rm_watch(self, wd):
191 return self.libc.syscall(self._rm_watch_syscall_id, self._fd, wd)
192
193 - def libc_inotify_add_watch(self, path, mask):
194 if type(path) is unicode: 195 path = path.encode('utf-8') 196 self.libc.inotify_add_watch.argtypes = [ctypes.c_int, ctypes.c_char_p, ctypes.c_int] 197 else: 198 self.libc.inotify_add_watch.argtypes = None 199 return self.libc.inotify_add_watch(self._fd, path, mask)
200
201 - def libc_inotify_rm_watch(self, wd):
202 return self.libc.inotify_rm_watch(self._fd, wd)
203
204 - def fileno(self):
205 return self._fd
206
207 - def flag_to_human(self, mask):
208 s = [] 209 for (k, v) in _flag_to_human.iteritems(): 210 if k & mask: 211 s.append(v) 212 return s
213
214 - def notify(self, iwp, filename, mask, parameter=None):
215 print "event %s on %s %s" % ( 216 ', '.join(self.flag_to_human(mask)), iwp.path, filename)
217
218 - def doRead(self):
219 self._buffer += os.read(self._fd, 1024) 220 221 while True: 222 if len(self._buffer) < 16: 223 break 224 225 wd, mask, cookie, size = struct.unpack("=LLLL", self._buffer[0:16]) 226 if size: 227 name = self._buffer[16:16+size].rstrip('\0') 228 else: 229 name = None 230 231 self._buffer = self._buffer[16+size:] 232 233 try: 234 iwp = self._watchpoints[wd] 235 except: 236 continue # can this happen? 237 238 path = iwp.path 239 if name: 240 name = unicode(name, 'utf-8') 241 path = os.path.join(path, name) 242 243 iwp.notify( name, mask) 244 245 if( iwp.auto_add and mask & IN_ISDIR and mask & IN_CREATE): 246 self.watch(path, mask = iwp.mask, auto_add = True, callbacks=iwp.callbacks) 247 248 if mask & IN_DELETE_SELF: 249 # watch point got deleted, remove its data. 250 del self._watchpoints[wd]
251 252
253 - def watch(self, path, mask = IN_WATCH_MASK, auto_add = None, callbacks=[], recursive=False):
254 if recursive: 255 for root, dirs, files in os.walk(path): 256 self.watch(root, mask, auto_add, callbacks, False) 257 else: 258 if isinstance(path, FilePath): 259 path = path.path 260 path = os.path.realpath(path) 261 for wd, iwp in self._watchpoints.items(): 262 if iwp.path == path: 263 return wd 264 265 mask = mask | IN_DELETE_SELF 266 267 #print "add watch for", path, ', '.join(self.flag_to_human(mask)) 268 wd = self.inotify_add_watch(path, mask) 269 if wd < 0: 270 raise IOError, "Failed to add watch on '%s' - (%r)" % (path.encode('ascii', 'ignore'),wd) 271 272 iwp = IWatchPoint(path, mask, auto_add, callbacks) 273 self._watchpoints[wd] = iwp
274
275 - def ignore(self, path):
276 path = os.path.realpath(path) 277 found_wd = None 278 for wd, iwp in self._watchpoints.items(): 279 if iwp.path == path: 280 self.inotify_rm_watch(wd) 281 del self._watchpoints[wd] 282 break
283
284 - def is_watched(self, path):
285 for wd, iwp in self._watchpoints.items(): 286 if iwp.path == path: 287 return True 288 return False
289 290 if __name__ == '__main__': 291 292 i = INotify() 293 print i 294 i.watch(unicode('/tmp'), auto_add = True, callbacks=(i.notify,None), recursive=True) 295 296 i2 = INotify() 297 print i2 298 i2.watch('/', auto_add = True, callbacks=(i2.notify,None), recursive=False) 299 reactor.run() 300