1# vim:fileencoding=utf-8:noet 2from __future__ import (unicode_literals, division, absolute_import, print_function) 3 4import sys 5import os 6import errno 7import ctypes 8import struct 9 10from ctypes.util import find_library 11 12from powerline.lib.encoding import get_preferred_file_name_encoding 13 14 15__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>' 16__docformat__ = 'restructuredtext en' 17 18 19class INotifyError(Exception): 20 pass 21 22 23_inotify = None 24 25 26def load_inotify(): 27 ''' Initialize the inotify library ''' 28 global _inotify 29 if _inotify is None: 30 if hasattr(sys, 'getwindowsversion'): 31 # On windows abort before loading the C library. Windows has 32 # multiple, incompatible C runtimes, and we have no way of knowing 33 # if the one chosen by ctypes is compatible with the currently 34 # loaded one. 35 raise INotifyError('INotify not available on windows') 36 if sys.platform == 'darwin': 37 raise INotifyError('INotify not available on OS X') 38 if not hasattr(ctypes, 'c_ssize_t'): 39 raise INotifyError('You need python >= 2.7 to use inotify') 40 name = find_library('c') 41 if not name: 42 raise INotifyError('Cannot find C library') 43 libc = ctypes.CDLL(name, use_errno=True) 44 for function in ('inotify_add_watch', 'inotify_init1', 'inotify_rm_watch'): 45 if not hasattr(libc, function): 46 raise INotifyError('libc is too old') 47 # inotify_init1() 48 prototype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, use_errno=True) 49 init1 = prototype(('inotify_init1', libc), ((1, 'flags', 0),)) 50 51 # inotify_add_watch() 52 prototype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_char_p, ctypes.c_uint32, use_errno=True) 53 add_watch = prototype(('inotify_add_watch', libc), ( 54 (1, 'fd'), (1, 'pathname'), (1, 'mask'))) 55 56 # inotify_rm_watch() 57 prototype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int, use_errno=True) 58 rm_watch = prototype(('inotify_rm_watch', libc), ( 59 (1, 'fd'), (1, 'wd'))) 60 61 # read() 62 prototype = ctypes.CFUNCTYPE(ctypes.c_ssize_t, ctypes.c_int, ctypes.c_void_p, ctypes.c_size_t, use_errno=True) 63 read = prototype(('read', libc), ( 64 (1, 'fd'), (1, 'buf'), (1, 'count'))) 65 _inotify = (init1, add_watch, rm_watch, read) 66 return _inotify 67 68 69class INotify(object): 70 71 # See <sys/inotify.h> for the flags defined below 72 73 # Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH. 74 ACCESS = 0x00000001 # File was accessed. 75 MODIFY = 0x00000002 # File was modified. 76 ATTRIB = 0x00000004 # Metadata changed. 77 CLOSE_WRITE = 0x00000008 # Writtable file was closed. 78 CLOSE_NOWRITE = 0x00000010 # Unwrittable file closed. 79 OPEN = 0x00000020 # File was opened. 80 MOVED_FROM = 0x00000040 # File was moved from X. 81 MOVED_TO = 0x00000080 # File was moved to Y. 82 CREATE = 0x00000100 # Subfile was created. 83 DELETE = 0x00000200 # Subfile was deleted. 84 DELETE_SELF = 0x00000400 # Self was deleted. 85 MOVE_SELF = 0x00000800 # Self was moved. 86 87 # Events sent by the kernel. 88 UNMOUNT = 0x00002000 # Backing fs was unmounted. 89 Q_OVERFLOW = 0x00004000 # Event queued overflowed. 90 IGNORED = 0x00008000 # File was ignored. 91 92 # Helper events. 93 CLOSE = (CLOSE_WRITE | CLOSE_NOWRITE) # Close. 94 MOVE = (MOVED_FROM | MOVED_TO) # Moves. 95 96 # Special flags. 97 ONLYDIR = 0x01000000 # Only watch the path if it is a directory. 98 DONT_FOLLOW = 0x02000000 # Do not follow a sym link. 99 EXCL_UNLINK = 0x04000000 # Exclude events on unlinked objects. 100 MASK_ADD = 0x20000000 # Add to the mask of an already existing watch. 101 ISDIR = 0x40000000 # Event occurred against dir. 102 ONESHOT = 0x80000000 # Only send event once. 103 104 # All events which a program can wait on. 105 ALL_EVENTS = ( 106 ACCESS | MODIFY | ATTRIB | CLOSE_WRITE | CLOSE_NOWRITE | OPEN | 107 MOVED_FROM | MOVED_TO | CREATE | DELETE | DELETE_SELF | MOVE_SELF 108 ) 109 110 # See <bits/inotify.h> 111 CLOEXEC = 0x80000 112 NONBLOCK = 0x800 113 114 def __init__(self, cloexec=True, nonblock=True): 115 self._init1, self._add_watch, self._rm_watch, self._read = load_inotify() 116 flags = 0 117 if cloexec: 118 flags |= self.CLOEXEC 119 if nonblock: 120 flags |= self.NONBLOCK 121 self._inotify_fd = self._init1(flags) 122 if self._inotify_fd == -1: 123 raise INotifyError(os.strerror(ctypes.get_errno())) 124 125 self._buf = ctypes.create_string_buffer(5000) 126 self.fenc = get_preferred_file_name_encoding() 127 self.hdr = struct.Struct(b'iIII') 128 # We keep a reference to os to prevent it from being deleted 129 # during interpreter shutdown, which would lead to errors in the 130 # __del__ method 131 self.os = os 132 133 def handle_error(self): 134 eno = ctypes.get_errno() 135 extra = '' 136 if eno == errno.ENOSPC: 137 extra = 'You may need to increase the inotify limits on your system, via /proc/sys/fs/inotify/max_user_*' 138 raise OSError(eno, self.os.strerror(eno) + str(extra)) 139 140 def __del__(self): 141 # This method can be called during interpreter shutdown, which means we 142 # must do the absolute minimum here. Note that there could be running 143 # daemon threads that are trying to call other methods on this object. 144 try: 145 self.os.close(self._inotify_fd) 146 except (AttributeError, TypeError): 147 pass 148 149 def close(self): 150 if hasattr(self, '_inotify_fd'): 151 self.os.close(self._inotify_fd) 152 del self.os 153 del self._add_watch 154 del self._rm_watch 155 del self._inotify_fd 156 157 def read(self, get_name=True): 158 buf = [] 159 while True: 160 num = self._read(self._inotify_fd, self._buf, len(self._buf)) 161 if num == 0: 162 break 163 if num < 0: 164 en = ctypes.get_errno() 165 if en == errno.EAGAIN: 166 break # No more data 167 if en == errno.EINTR: 168 continue # Interrupted, try again 169 raise OSError(en, self.os.strerror(en)) 170 buf.append(self._buf.raw[:num]) 171 raw = b''.join(buf) 172 pos = 0 173 lraw = len(raw) 174 while lraw - pos >= self.hdr.size: 175 wd, mask, cookie, name_len = self.hdr.unpack_from(raw, pos) 176 pos += self.hdr.size 177 name = None 178 if get_name: 179 name = raw[pos:pos + name_len].rstrip(b'\0') 180 pos += name_len 181 self.process_event(wd, mask, cookie, name) 182 183 def process_event(self, *args): 184 raise NotImplementedError() 185