Viewing file: _inotify.py (3.41 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
# -*- test-case-name: twisted.internet.test.test_inotify -*- # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details.
""" Very low-level ctypes-based interface to Linux inotify(7).
ctypes and a version of libc which supports inotify system calls are required. """
import ctypes import ctypes.util from typing import cast
from twisted.python.filepath import FilePath
class INotifyError(Exception): """ Unify all the possible exceptions that can be raised by the INotify API. """
def init() -> int: """ Create an inotify instance and return the associated file descriptor. """ fd = cast(int, libc.inotify_init()) if fd < 0: raise INotifyError("INotify initialization error.") return fd
def add(fd: int, path: FilePath, mask: int) -> int: """ Add a watch for the given path to the inotify file descriptor, and return the watch descriptor.
@param fd: The file descriptor returned by C{libc.inotify_init}. @param path: The path to watch via inotify. @param mask: Bitmask specifying the events that inotify should monitor. """ wd = cast(int, libc.inotify_add_watch(fd, path.asBytesMode().path, mask)) if wd < 0: raise INotifyError(f"Failed to add watch on '{path!r}' - ({wd!r})") return wd
def remove(fd: int, wd: int) -> None: """ Remove the given watch descriptor from the inotify file descriptor. """ # When inotify_rm_watch returns -1 there's an error: # The errno for this call can be either one of the following: # EBADF: fd is not a valid file descriptor. # EINVAL: The watch descriptor wd is not valid; or fd is # not an inotify file descriptor. # # if we can't access the errno here we cannot even raise # an exception and we need to ignore the problem, one of # the most common cases is when you remove a directory from # the filesystem and that directory is observed. When inotify # tries to call inotify_rm_watch with a non existing directory # either of the 2 errors might come up because the files inside # it might have events generated way before they were handled. # Unfortunately only ctypes in Python 2.6 supports accessing errno: # http://bugs.python.org/issue1798 and in order to solve # the problem for previous versions we need to introduce # code that is quite complex: # http://stackoverflow.com/questions/661017/access-to-errno-from-python # # See #4310 for future resolution of this issue. libc.inotify_rm_watch(fd, wd)
def initializeModule(libc: ctypes.CDLL) -> None: """ Initialize the module, checking if the expected APIs exist and setting the argtypes and restype for C{inotify_init}, C{inotify_add_watch}, and C{inotify_rm_watch}. """ for function in ("inotify_add_watch", "inotify_init", "inotify_rm_watch"): if getattr(libc, function, None) is None: raise ImportError("libc6 2.4 or higher needed") libc.inotify_init.argtypes = [] libc.inotify_init.restype = ctypes.c_int
libc.inotify_rm_watch.argtypes = [ctypes.c_int, ctypes.c_int] libc.inotify_rm_watch.restype = ctypes.c_int
libc.inotify_add_watch.argtypes = [ctypes.c_int, ctypes.c_char_p, ctypes.c_uint32] libc.inotify_add_watch.restype = ctypes.c_int
name = ctypes.util.find_library("c") if not name: raise ImportError("Can't find C library.") libc = ctypes.cdll.LoadLibrary(name) initializeModule(libc)
|