1#!/usr/bin/env python
2
3# pyinotify.py - python interface to inotify
4# Copyright (c) 2005-2015 Sebastien Martini <seb@dbzteam.org>
5#
6# Permission is hereby granted, free of charge, to any person obtaining a copy
7# of this software and associated documentation files (the "Software"), to deal
8# in the Software without restriction, including without limitation the rights
9# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10# copies of the Software, and to permit persons to whom the Software is
11# furnished to do so, subject to the following conditions:
12#
13# The above copyright notice and this permission notice shall be included in
14# all copies or substantial portions of the Software.
15#
16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22# THE SOFTWARE.
23"""
24pyinotify
25
26@author: Sebastien Martini
27@license: MIT License
28@contact: seb@dbzteam.org
29"""
30
31class PyinotifyError(Exception):
32    """Indicates exceptions raised by a Pyinotify class."""
33    pass
34
35
36class UnsupportedPythonVersionError(PyinotifyError):
37    """
38    Raised on unsupported Python versions.
39    """
40    def __init__(self, version):
41        """
42        @param version: Current Python version
43        @type version: string
44        """
45        PyinotifyError.__init__(self,
46                                ('Python %s is unsupported, requires '
47                                 'at least Python 3.0') % version)
48
49
50# Check Python version
51import sys
52if sys.version_info < (3, 0):
53    raise UnsupportedPythonVersionError(sys.version)
54
55
56# Import directives
57import threading
58import os
59import select
60import struct
61import fcntl
62import errno
63import termios
64import array
65import logging
66import atexit
67from collections import deque
68from datetime import datetime, timedelta
69import time
70import re
71import asyncore
72import glob
73import locale
74import subprocess
75
76try:
77    from functools import reduce
78except ImportError:
79    pass  # Will fail on Python 2.4 which has reduce() builtin anyway.
80
81try:
82    import ctypes
83    import ctypes.util
84except ImportError:
85    ctypes = None
86
87try:
88    import inotify_syscalls
89except ImportError:
90    inotify_syscalls = None
91
92
93__author__ = "seb@dbzteam.org (Sebastien Martini)"
94
95__version__ = "0.9.6"
96
97
98# Compatibity mode: set to True to improve compatibility with
99# Pyinotify 0.7.1. Do not set this variable yourself, call the
100# function compatibility_mode() instead.
101COMPATIBILITY_MODE = False
102
103
104class InotifyBindingNotFoundError(PyinotifyError):
105    """
106    Raised when no inotify support couldn't be found.
107    """
108    def __init__(self):
109        err = "Couldn't find any inotify binding"
110        PyinotifyError.__init__(self, err)
111
112
113class INotifyWrapper:
114    """
115    Abstract class wrapping access to inotify's functions. This is an
116    internal class.
117    """
118    @staticmethod
119    def create():
120        """
121        Factory method instanciating and returning the right wrapper.
122        """
123        # First, try to use ctypes.
124        if ctypes:
125            inotify = _CtypesLibcINotifyWrapper()
126            if inotify.init():
127                return inotify
128        # Second, see if C extension is compiled.
129        if inotify_syscalls:
130            inotify = _INotifySyscallsWrapper()
131            if inotify.init():
132                return inotify
133
134    def get_errno(self):
135        """
136        Return None is no errno code is available.
137        """
138        return self._get_errno()
139
140    def str_errno(self):
141        code = self.get_errno()
142        if code is None:
143            return 'Errno: no errno support'
144        return 'Errno=%s (%s)' % (os.strerror(code), errno.errorcode[code])
145
146    def inotify_init(self):
147        return self._inotify_init()
148
149    def inotify_add_watch(self, fd, pathname, mask):
150        # Unicode strings must be encoded to string prior to calling this
151        # method.
152        assert isinstance(pathname, str)
153        return self._inotify_add_watch(fd, pathname, mask)
154
155    def inotify_rm_watch(self, fd, wd):
156        return self._inotify_rm_watch(fd, wd)
157
158
159class _INotifySyscallsWrapper(INotifyWrapper):
160    def __init__(self):
161        # Stores the last errno value.
162        self._last_errno = None
163
164    def init(self):
165        assert inotify_syscalls
166        return True
167
168    def _get_errno(self):
169        return self._last_errno
170
171    def _inotify_init(self):
172        try:
173            fd = inotify_syscalls.inotify_init()
174        except IOError as err:
175            self._last_errno = err.errno
176            return -1
177        return fd
178
179    def _inotify_add_watch(self, fd, pathname, mask):
180        try:
181            wd = inotify_syscalls.inotify_add_watch(fd, pathname, mask)
182        except IOError as err:
183            self._last_errno = err.errno
184            return -1
185        return wd
186
187    def _inotify_rm_watch(self, fd, wd):
188        try:
189            ret = inotify_syscalls.inotify_rm_watch(fd, wd)
190        except IOError as err:
191            self._last_errno = err.errno
192            return -1
193        return ret
194
195
196class _CtypesLibcINotifyWrapper(INotifyWrapper):
197    def __init__(self):
198        self._libc = None
199        self._get_errno_func = None
200
201    def init(self):
202        assert ctypes
203
204        try_libc_name = 'c'
205        if sys.platform.startswith('freebsd') or sys.platform.startswith('dragonfly'):
206            try_libc_name = 'inotify'
207
208        libc_name = None
209        try:
210            libc_name = ctypes.util.find_library(try_libc_name)
211        except (OSError, IOError):
212            pass  # Will attemp to load it with None anyway.
213
214        self._libc = ctypes.CDLL(libc_name, use_errno=True)
215        self._get_errno_func = ctypes.get_errno
216
217        # Eventually check that libc has needed inotify bindings.
218        if (not hasattr(self._libc, 'inotify_init') or
219            not hasattr(self._libc, 'inotify_add_watch') or
220            not hasattr(self._libc, 'inotify_rm_watch')):
221            return False
222
223        self._libc.inotify_init.argtypes = []
224        self._libc.inotify_init.restype = ctypes.c_int
225        self._libc.inotify_add_watch.argtypes = [ctypes.c_int, ctypes.c_char_p,
226                                                 ctypes.c_uint32]
227        self._libc.inotify_add_watch.restype = ctypes.c_int
228        self._libc.inotify_rm_watch.argtypes = [ctypes.c_int, ctypes.c_int]
229        self._libc.inotify_rm_watch.restype = ctypes.c_int
230        return True
231
232    def _get_errno(self):
233        assert self._get_errno_func
234        return self._get_errno_func()
235
236    def _inotify_init(self):
237        assert self._libc is not None
238        return self._libc.inotify_init()
239
240    def _inotify_add_watch(self, fd, pathname, mask):
241        assert self._libc is not None
242        # Encodes path to a bytes string. This conversion seems required because
243        # ctypes.create_string_buffer seems to manipulate bytes internally.
244        # Moreover it seems that inotify_add_watch does not work very well when
245        # it receives an ctypes.create_unicode_buffer instance as argument.
246        pathname = pathname.encode(sys.getfilesystemencoding())
247        pathname = ctypes.create_string_buffer(pathname)
248        return self._libc.inotify_add_watch(fd, pathname, mask)
249
250    def _inotify_rm_watch(self, fd, wd):
251        assert self._libc is not None
252        return self._libc.inotify_rm_watch(fd, wd)
253
254
255# Logging
256def logger_init():
257    """Initialize logger instance."""
258    log = logging.getLogger("pyinotify")
259    console_handler = logging.StreamHandler()
260    console_handler.setFormatter(
261        logging.Formatter("[%(asctime)s %(name)s %(levelname)s] %(message)s"))
262    log.addHandler(console_handler)
263    log.setLevel(20)
264    return log
265
266log = logger_init()
267
268
269# inotify's variables
270class ProcINotify:
271    """
272    Access (read, write) inotify's variables through /proc/sys/. Note that
273    usually it requires administrator rights to update them.
274
275    Examples:
276      - Read max_queued_events attribute: myvar = max_queued_events.value
277      - Update max_queued_events attribute: max_queued_events.value = 42
278    """
279    def __init__(self, attr):
280        self._base = "/proc/sys/fs/inotify"
281        self._attr = attr
282
283    def get_val(self):
284        """
285        Gets attribute's value.
286
287        @return: stored value.
288        @rtype: int
289        @raise IOError: if corresponding file in /proc/sys cannot be read.
290        """
291        with open(os.path.join(self._base, self._attr), 'r') as file_obj:
292            return int(file_obj.readline())
293
294    def set_val(self, nval):
295        """
296        Sets new attribute's value.
297
298        @param nval: replaces current value by nval.
299        @type nval: int
300        @raise IOError: if corresponding file in /proc/sys cannot be written.
301        """
302        with open(os.path.join(self._base, self._attr), 'w') as file_obj:
303            file_obj.write(str(nval) + '\n')
304
305    value = property(get_val, set_val)
306
307    def __repr__(self):
308        return '<%s=%d>' % (self._attr, self.get_val())
309
310
311# Inotify's variables
312#
313# Note: may raise IOError if the corresponding value in /proc/sys
314#       cannot be accessed.
315#
316# Examples:
317#  - read: myvar = max_queued_events.value
318#  - update: max_queued_events.value = 42
319#
320for attrname in ('max_queued_events', 'max_user_instances', 'max_user_watches'):
321    globals()[attrname] = ProcINotify(attrname)
322
323
324class EventsCodes:
325    """
326    Set of codes corresponding to each kind of events.
327    Some of these flags are used to communicate with inotify, whereas
328    the others are sent to userspace by inotify notifying some events.
329
330    @cvar IN_ACCESS: File was accessed.
331    @type IN_ACCESS: int
332    @cvar IN_MODIFY: File was modified.
333    @type IN_MODIFY: int
334    @cvar IN_ATTRIB: Metadata changed.
335    @type IN_ATTRIB: int
336    @cvar IN_CLOSE_WRITE: Writtable file was closed.
337    @type IN_CLOSE_WRITE: int
338    @cvar IN_CLOSE_NOWRITE: Unwrittable file closed.
339    @type IN_CLOSE_NOWRITE: int
340    @cvar IN_OPEN: File was opened.
341    @type IN_OPEN: int
342    @cvar IN_MOVED_FROM: File was moved from X.
343    @type IN_MOVED_FROM: int
344    @cvar IN_MOVED_TO: File was moved to Y.
345    @type IN_MOVED_TO: int
346    @cvar IN_CREATE: Subfile was created.
347    @type IN_CREATE: int
348    @cvar IN_DELETE: Subfile was deleted.
349    @type IN_DELETE: int
350    @cvar IN_DELETE_SELF: Self (watched item itself) was deleted.
351    @type IN_DELETE_SELF: int
352    @cvar IN_MOVE_SELF: Self (watched item itself) was moved.
353    @type IN_MOVE_SELF: int
354    @cvar IN_UNMOUNT: Backing fs was unmounted.
355    @type IN_UNMOUNT: int
356    @cvar IN_Q_OVERFLOW: Event queued overflowed.
357    @type IN_Q_OVERFLOW: int
358    @cvar IN_IGNORED: File was ignored.
359    @type IN_IGNORED: int
360    @cvar IN_ONLYDIR: only watch the path if it is a directory (new
361                      in kernel 2.6.15).
362    @type IN_ONLYDIR: int
363    @cvar IN_DONT_FOLLOW: don't follow a symlink (new in kernel 2.6.15).
364                          IN_ONLYDIR we can make sure that we don't watch
365                          the target of symlinks.
366    @type IN_DONT_FOLLOW: int
367    @cvar IN_EXCL_UNLINK: Events are not generated for children after they
368                          have been unlinked from the watched directory.
369                          (new in kernel 2.6.36).
370    @type IN_EXCL_UNLINK: int
371    @cvar IN_MASK_ADD: add to the mask of an already existing watch (new
372                       in kernel 2.6.14).
373    @type IN_MASK_ADD: int
374    @cvar IN_ISDIR: Event occurred against dir.
375    @type IN_ISDIR: int
376    @cvar IN_ONESHOT: Only send event once.
377    @type IN_ONESHOT: int
378    @cvar ALL_EVENTS: Alias for considering all of the events.
379    @type ALL_EVENTS: int
380    """
381
382    # The idea here is 'configuration-as-code' - this way, we get our nice class
383    # constants, but we also get nice human-friendly text mappings to do lookups
384    # against as well, for free:
385    FLAG_COLLECTIONS = {'OP_FLAGS': {
386        'IN_ACCESS'        : 0x00000001,  # File was accessed
387        'IN_MODIFY'        : 0x00000002,  # File was modified
388        'IN_ATTRIB'        : 0x00000004,  # Metadata changed
389        'IN_CLOSE_WRITE'   : 0x00000008,  # Writable file was closed
390        'IN_CLOSE_NOWRITE' : 0x00000010,  # Unwritable file closed
391        'IN_OPEN'          : 0x00000020,  # File was opened
392        'IN_MOVED_FROM'    : 0x00000040,  # File was moved from X
393        'IN_MOVED_TO'      : 0x00000080,  # File was moved to Y
394        'IN_CREATE'        : 0x00000100,  # Subfile was created
395        'IN_DELETE'        : 0x00000200,  # Subfile was deleted
396        'IN_DELETE_SELF'   : 0x00000400,  # Self (watched item itself)
397                                          # was deleted
398        'IN_MOVE_SELF'     : 0x00000800,  # Self (watched item itself) was moved
399        },
400                        'EVENT_FLAGS': {
401        'IN_UNMOUNT'       : 0x00002000,  # Backing fs was unmounted
402        'IN_Q_OVERFLOW'    : 0x00004000,  # Event queued overflowed
403        'IN_IGNORED'       : 0x00008000,  # File was ignored
404        },
405                        'SPECIAL_FLAGS': {
406        'IN_ONLYDIR'       : 0x01000000,  # only watch the path if it is a
407                                          # directory
408        'IN_DONT_FOLLOW'   : 0x02000000,  # don't follow a symlink
409        'IN_EXCL_UNLINK'   : 0x04000000,  # exclude events on unlinked objects
410        'IN_MASK_ADD'      : 0x20000000,  # add to the mask of an already
411                                          # existing watch
412        'IN_ISDIR'         : 0x40000000,  # event occurred against dir
413        'IN_ONESHOT'       : 0x80000000,  # only send event once
414        },
415                        }
416
417    def maskname(mask):
418        """
419        Returns the event name associated to mask. IN_ISDIR is appended to
420        the result when appropriate. Note: only one event is returned, because
421        only one event can be raised at a given time.
422
423        @param mask: mask.
424        @type mask: int
425        @return: event name.
426        @rtype: str
427        """
428        ms = mask
429        name = '%s'
430        if mask & IN_ISDIR:
431            ms = mask - IN_ISDIR
432            name = '%s|IN_ISDIR'
433        return name % EventsCodes.ALL_VALUES[ms]
434
435    maskname = staticmethod(maskname)
436
437
438# So let's now turn the configuration into code
439EventsCodes.ALL_FLAGS = {}
440EventsCodes.ALL_VALUES = {}
441for flagc, valc in EventsCodes.FLAG_COLLECTIONS.items():
442    # Make the collections' members directly accessible through the
443    # class dictionary
444    setattr(EventsCodes, flagc, valc)
445
446    # Collect all the flags under a common umbrella
447    EventsCodes.ALL_FLAGS.update(valc)
448
449    # Make the individual masks accessible as 'constants' at globals() scope
450    # and masknames accessible by values.
451    for name, val in valc.items():
452        globals()[name] = val
453        EventsCodes.ALL_VALUES[val] = name
454
455
456# all 'normal' events
457ALL_EVENTS = reduce(lambda x, y: x | y, EventsCodes.OP_FLAGS.values())
458EventsCodes.ALL_FLAGS['ALL_EVENTS'] = ALL_EVENTS
459EventsCodes.ALL_VALUES[ALL_EVENTS] = 'ALL_EVENTS'
460
461
462class _Event:
463    """
464    Event structure, represent events raised by the system. This
465    is the base class and should be subclassed.
466
467    """
468    def __init__(self, dict_):
469        """
470        Attach attributes (contained in dict_) to self.
471
472        @param dict_: Set of attributes.
473        @type dict_: dictionary
474        """
475        for tpl in dict_.items():
476            setattr(self, *tpl)
477
478    def __repr__(self):
479        """
480        @return: Generic event string representation.
481        @rtype: str
482        """
483        s = ''
484        for attr, value in sorted(self.__dict__.items(), key=lambda x: x[0]):
485            if attr.startswith('_'):
486                continue
487            if attr == 'mask':
488                value = hex(getattr(self, attr))
489            elif isinstance(value, str) and not value:
490                value = "''"
491            s += ' %s%s%s' % (output_format.field_name(attr),
492                              output_format.punctuation('='),
493                              output_format.field_value(value))
494
495        s = '%s%s%s %s' % (output_format.punctuation('<'),
496                           output_format.class_name(self.__class__.__name__),
497                           s,
498                           output_format.punctuation('>'))
499        return s
500
501    def __str__(self):
502        return repr(self)
503
504
505class _RawEvent(_Event):
506    """
507    Raw event, it contains only the informations provided by the system.
508    It doesn't infer anything.
509    """
510    def __init__(self, wd, mask, cookie, name):
511        """
512        @param wd: Watch Descriptor.
513        @type wd: int
514        @param mask: Bitmask of events.
515        @type mask: int
516        @param cookie: Cookie.
517        @type cookie: int
518        @param name: Basename of the file or directory against which the
519                     event was raised in case where the watched directory
520                     is the parent directory. None if the event was raised
521                     on the watched item itself.
522        @type name: string or None
523        """
524        # Use this variable to cache the result of str(self), this object
525        # is immutable.
526        self._str = None
527        # name: remove trailing '\0'
528        d = {'wd': wd,
529             'mask': mask,
530             'cookie': cookie,
531             'name': name.rstrip('\0')}
532        _Event.__init__(self, d)
533        log.debug(str(self))
534
535    def __str__(self):
536        if self._str is None:
537            self._str = _Event.__str__(self)
538        return self._str
539
540
541class Event(_Event):
542    """
543    This class contains all the useful informations about the observed
544    event. However, the presence of each field is not guaranteed and
545    depends on the type of event. In effect, some fields are irrelevant
546    for some kind of event (for example 'cookie' is meaningless for
547    IN_CREATE whereas it is mandatory for IN_MOVE_TO).
548
549    The possible fields are:
550      - wd (int): Watch Descriptor.
551      - mask (int): Mask.
552      - maskname (str): Readable event name.
553      - path (str): path of the file or directory being watched.
554      - name (str): Basename of the file or directory against which the
555              event was raised in case where the watched directory
556              is the parent directory. None if the event was raised
557              on the watched item itself. This field is always provided
558              even if the string is ''.
559      - pathname (str): Concatenation of 'path' and 'name'.
560      - src_pathname (str): Only present for IN_MOVED_TO events and only in
561              the case where IN_MOVED_FROM events are watched too. Holds the
562              source pathname from where pathname was moved from.
563      - cookie (int): Cookie.
564      - dir (bool): True if the event was raised against a directory.
565
566    """
567    def __init__(self, raw):
568        """
569        Concretely, this is the raw event plus inferred infos.
570        """
571        _Event.__init__(self, raw)
572        self.maskname = EventsCodes.maskname(self.mask)
573        if COMPATIBILITY_MODE:
574            self.event_name = self.maskname
575        try:
576            if self.name:
577                self.pathname = os.path.abspath(os.path.join(self.path,
578                                                             self.name))
579            else:
580                self.pathname = os.path.abspath(self.path)
581        except AttributeError as err:
582            # Usually it is not an error some events are perfectly valids
583            # despite the lack of these attributes.
584            log.debug(err)
585
586
587class ProcessEventError(PyinotifyError):
588    """
589    ProcessEventError Exception. Raised on ProcessEvent error.
590    """
591    def __init__(self, err):
592        """
593        @param err: Exception error description.
594        @type err: string
595        """
596        PyinotifyError.__init__(self, err)
597
598
599class _ProcessEvent:
600    """
601    Abstract processing event class.
602    """
603    def __call__(self, event):
604        """
605        To behave like a functor the object must be callable.
606        This method is a dispatch method. Its lookup order is:
607          1. process_MASKNAME method
608          2. process_FAMILY_NAME method
609          3. otherwise calls process_default
610
611        @param event: Event to be processed.
612        @type event: Event object
613        @return: By convention when used from the ProcessEvent class:
614                 - Returning False or None (default value) means keep on
615                 executing next chained functors (see chain.py example).
616                 - Returning True instead means do not execute next
617                   processing functions.
618        @rtype: bool
619        @raise ProcessEventError: Event object undispatchable,
620                                  unknown event.
621        """
622        stripped_mask = event.mask - (event.mask & IN_ISDIR)
623        maskname = EventsCodes.ALL_VALUES.get(stripped_mask)
624        if maskname is None:
625            raise ProcessEventError("Unknown mask 0x%08x" % stripped_mask)
626
627        # 1- look for process_MASKNAME
628        meth = getattr(self, 'process_' + maskname, None)
629        if meth is not None:
630            return meth(event)
631        # 2- look for process_FAMILY_NAME
632        meth = getattr(self, 'process_IN_' + maskname.split('_')[1], None)
633        if meth is not None:
634            return meth(event)
635        # 3- default call method process_default
636        return self.process_default(event)
637
638    def __repr__(self):
639        return '<%s>' % self.__class__.__name__
640
641
642class _SysProcessEvent(_ProcessEvent):
643    """
644    There is three kind of processing according to each event:
645
646      1. special handling (deletion from internal container, bug, ...).
647      2. default treatment: which is applied to the majority of events.
648      3. IN_ISDIR is never sent alone, he is piggybacked with a standard
649         event, he is not processed as the others events, instead, its
650         value is captured and appropriately aggregated to dst event.
651    """
652    def __init__(self, wm, notifier):
653        """
654
655        @param wm: Watch Manager.
656        @type wm: WatchManager instance
657        @param notifier: Notifier.
658        @type notifier: Notifier instance
659        """
660        self._watch_manager = wm  # watch manager
661        self._notifier = notifier  # notifier
662        self._mv_cookie = {}  # {cookie(int): (src_path(str), date), ...}
663        self._mv = {}  # {src_path(str): (dst_path(str), date), ...}
664
665    def cleanup(self):
666        """
667        Cleanup (delete) old (>1mn) records contained in self._mv_cookie
668        and self._mv.
669        """
670        date_cur_ = datetime.now()
671        for seq in (self._mv_cookie, self._mv):
672            for k in list(seq.keys()):
673                if (date_cur_ - seq[k][1]) > timedelta(minutes=1):
674                    log.debug('Cleanup: deleting entry %s', seq[k][0])
675                    del seq[k]
676
677    def process_IN_CREATE(self, raw_event):
678        """
679        If the event affects a directory and the auto_add flag of the
680        targetted watch is set to True, a new watch is added on this
681        new directory, with the same attribute values than those of
682        this watch.
683        """
684        if raw_event.mask & IN_ISDIR:
685            watch_ = self._watch_manager.get_watch(raw_event.wd)
686            created_dir = os.path.join(watch_.path, raw_event.name)
687            if watch_.auto_add and not watch_.exclude_filter(created_dir):
688                addw = self._watch_manager.add_watch
689                # The newly monitored directory inherits attributes from its
690                # parent directory.
691                addw_ret = addw(created_dir, watch_.mask,
692                                proc_fun=watch_.proc_fun,
693                                rec=False, auto_add=watch_.auto_add,
694                                exclude_filter=watch_.exclude_filter)
695
696                # Trick to handle mkdir -p /d1/d2/t3 where d1 is watched and
697                # d2 and t3 (directory or file) are created.
698                # Since the directory d2 is new, then everything inside it must
699                # also be new.
700                created_dir_wd = addw_ret.get(created_dir)
701                if ((created_dir_wd is not None) and (created_dir_wd > 0) and
702                    os.path.isdir(created_dir)):
703                    try:
704                        for name in os.listdir(created_dir):
705                            inner = os.path.join(created_dir, name)
706                            if self._watch_manager.get_wd(inner) is not None:
707                                continue
708                            # Generate (simulate) creation events for sub-
709                            # directories and files.
710                            if os.path.isfile(inner):
711                                # symlinks are handled as files.
712                                flags = IN_CREATE
713                            elif os.path.isdir(inner):
714                                flags = IN_CREATE | IN_ISDIR
715                            else:
716                                # This path should not be taken.
717                                continue
718                            rawevent = _RawEvent(created_dir_wd, flags, 0, name)
719                            self._notifier.append_event(rawevent)
720                    except OSError as err:
721                        msg = "process_IN_CREATE, invalid directory: %s"
722                        log.debug(msg % str(err))
723        return self.process_default(raw_event)
724
725    def process_IN_MOVED_FROM(self, raw_event):
726        """
727        Map the cookie with the source path (+ date for cleaning).
728        """
729        watch_ = self._watch_manager.get_watch(raw_event.wd)
730        path_ = watch_.path
731        src_path = os.path.normpath(os.path.join(path_, raw_event.name))
732        self._mv_cookie[raw_event.cookie] = (src_path, datetime.now())
733        return self.process_default(raw_event, {'cookie': raw_event.cookie})
734
735    def process_IN_MOVED_TO(self, raw_event):
736        """
737        Map the source path with the destination path (+ date for
738        cleaning).
739        """
740        watch_ = self._watch_manager.get_watch(raw_event.wd)
741        path_ = watch_.path
742        dst_path = os.path.normpath(os.path.join(path_, raw_event.name))
743        mv_ = self._mv_cookie.get(raw_event.cookie)
744        to_append = {'cookie': raw_event.cookie}
745        if mv_ is not None:
746            self._mv[mv_[0]] = (dst_path, datetime.now())
747            # Let's assume that IN_MOVED_FROM event is always queued before
748            # that its associated (they share a common cookie) IN_MOVED_TO
749            # event is queued itself. It is then possible in that scenario
750            # to provide as additional information to the IN_MOVED_TO event
751            # the original pathname of the moved file/directory.
752            to_append['src_pathname'] = mv_[0]
753        elif (raw_event.mask & IN_ISDIR and watch_.auto_add and
754              not watch_.exclude_filter(dst_path)):
755            # We got a diretory that's "moved in" from an unknown source and
756            # auto_add is enabled. Manually add watches to the inner subtrees.
757            # The newly monitored directory inherits attributes from its
758            # parent directory.
759            self._watch_manager.add_watch(dst_path, watch_.mask,
760                                          proc_fun=watch_.proc_fun,
761                                          rec=True, auto_add=True,
762                                          exclude_filter=watch_.exclude_filter)
763        return self.process_default(raw_event, to_append)
764
765    def process_IN_MOVE_SELF(self, raw_event):
766        """
767        STATUS: the following bug has been fixed in recent kernels (FIXME:
768        which version ?). Now it raises IN_DELETE_SELF instead.
769
770        Old kernels were bugged, this event raised when the watched item
771        were moved, so we had to update its path, but under some circumstances
772        it was impossible: if its parent directory and its destination
773        directory wasn't watched. The kernel (see include/linux/fsnotify.h)
774        doesn't bring us enough informations like the destination path of
775        moved items.
776        """
777        watch_ = self._watch_manager.get_watch(raw_event.wd)
778        src_path = watch_.path
779        mv_ = self._mv.get(src_path)
780        if mv_:
781            dest_path = mv_[0]
782            watch_.path = dest_path
783            # add the separator to the source path to avoid overlapping
784            # path issue when testing with startswith()
785            src_path += os.path.sep
786            src_path_len = len(src_path)
787            # The next loop renames all watches with src_path as base path.
788            # It seems that IN_MOVE_SELF does not provide IN_ISDIR information
789            # therefore the next loop is iterated even if raw_event is a file.
790            for w in self._watch_manager.watches.values():
791                if w.path.startswith(src_path):
792                    # Note that dest_path is a normalized path.
793                    w.path = os.path.join(dest_path, w.path[src_path_len:])
794        else:
795            log.error("The pathname '%s' of this watch %s has probably changed "
796                      "and couldn't be updated, so it cannot be trusted "
797                      "anymore. To fix this error move directories/files only "
798                      "between watched parents directories, in this case e.g. "
799                      "put a watch on '%s'.",
800                      watch_.path, watch_,
801                      os.path.normpath(os.path.join(watch_.path,
802                                                    os.path.pardir)))
803            if not watch_.path.endswith('-unknown-path'):
804                watch_.path += '-unknown-path'
805        return self.process_default(raw_event)
806
807    def process_IN_Q_OVERFLOW(self, raw_event):
808        """
809        Only signal an overflow, most of the common flags are irrelevant
810        for this event (path, wd, name).
811        """
812        return Event({'mask': raw_event.mask})
813
814    def process_IN_IGNORED(self, raw_event):
815        """
816        The watch descriptor raised by this event is now ignored (forever),
817        it can be safely deleted from the watch manager dictionary.
818        After this event we can be sure that neither the event queue nor
819        the system will raise an event associated to this wd again.
820        """
821        event_ = self.process_default(raw_event)
822        self._watch_manager.del_watch(raw_event.wd)
823        return event_
824
825    def process_default(self, raw_event, to_append=None):
826        """
827        Commons handling for the followings events:
828
829        IN_ACCESS, IN_MODIFY, IN_ATTRIB, IN_CLOSE_WRITE, IN_CLOSE_NOWRITE,
830        IN_OPEN, IN_DELETE, IN_DELETE_SELF, IN_UNMOUNT.
831        """
832        watch_ = self._watch_manager.get_watch(raw_event.wd)
833        if raw_event.mask & (IN_DELETE_SELF | IN_MOVE_SELF):
834            # Unfornulately this information is not provided by the kernel
835            dir_ = watch_.dir
836        else:
837            dir_ = bool(raw_event.mask & IN_ISDIR)
838        dict_ = {'wd': raw_event.wd,
839                 'mask': raw_event.mask,
840                 'path': watch_.path,
841                 'name': raw_event.name,
842                 'dir': dir_}
843        if COMPATIBILITY_MODE:
844            dict_['is_dir'] = dir_
845        if to_append is not None:
846            dict_.update(to_append)
847        return Event(dict_)
848
849
850class ProcessEvent(_ProcessEvent):
851    """
852    Process events objects, can be specialized via subclassing, thus its
853    behavior can be overriden:
854
855    Note: you should not override __init__ in your subclass instead define
856    a my_init() method, this method will be called automatically from the
857    constructor of this class with its optionals parameters.
858
859      1. Provide specialized individual methods, e.g. process_IN_DELETE for
860         processing a precise type of event (e.g. IN_DELETE in this case).
861      2. Or/and provide methods for processing events by 'family', e.g.
862         process_IN_CLOSE method will process both IN_CLOSE_WRITE and
863         IN_CLOSE_NOWRITE events (if process_IN_CLOSE_WRITE and
864         process_IN_CLOSE_NOWRITE aren't defined though).
865      3. Or/and override process_default for catching and processing all
866         the remaining types of events.
867    """
868    pevent = None
869
870    def __init__(self, pevent=None, **kargs):
871        """
872        Enable chaining of ProcessEvent instances.
873
874        @param pevent: Optional callable object, will be called on event
875                       processing (before self).
876        @type pevent: callable
877        @param kargs: This constructor is implemented as a template method
878                      delegating its optionals keyworded arguments to the
879                      method my_init().
880        @type kargs: dict
881        """
882        self.pevent = pevent
883        self.my_init(**kargs)
884
885    def my_init(self, **kargs):
886        """
887        This method is called from ProcessEvent.__init__(). This method is
888        empty here and must be redefined to be useful. In effect, if you
889        need to specifically initialize your subclass' instance then you
890        just have to override this method in your subclass. Then all the
891        keyworded arguments passed to ProcessEvent.__init__() will be
892        transmitted as parameters to this method. Beware you MUST pass
893        keyword arguments though.
894
895        @param kargs: optional delegated arguments from __init__().
896        @type kargs: dict
897        """
898        pass
899
900    def __call__(self, event):
901        stop_chaining = False
902        if self.pevent is not None:
903            # By default methods return None so we set as guideline
904            # that methods asking for stop chaining must explicitely
905            # return non None or non False values, otherwise the default
906            # behavior will be to accept chain call to the corresponding
907            # local method.
908            stop_chaining = self.pevent(event)
909        if not stop_chaining:
910            return _ProcessEvent.__call__(self, event)
911
912    def nested_pevent(self):
913        return self.pevent
914
915    def process_IN_Q_OVERFLOW(self, event):
916        """
917        By default this method only reports warning messages, you can overredide
918        it by subclassing ProcessEvent and implement your own
919        process_IN_Q_OVERFLOW method. The actions you can take on receiving this
920        event is either to update the variable max_queued_events in order to
921        handle more simultaneous events or to modify your code in order to
922        accomplish a better filtering diminishing the number of raised events.
923        Because this method is defined, IN_Q_OVERFLOW will never get
924        transmitted as arguments to process_default calls.
925
926        @param event: IN_Q_OVERFLOW event.
927        @type event: dict
928        """
929        log.warning('Event queue overflowed.')
930
931    def process_default(self, event):
932        """
933        Default processing event method. By default does nothing. Subclass
934        ProcessEvent and redefine this method in order to modify its behavior.
935
936        @param event: Event to be processed. Can be of any type of events but
937                      IN_Q_OVERFLOW events (see method process_IN_Q_OVERFLOW).
938        @type event: Event instance
939        """
940        pass
941
942
943class PrintAllEvents(ProcessEvent):
944    """
945    Dummy class used to print events strings representations. For instance this
946    class is used from command line to print all received events to stdout.
947    """
948    def my_init(self, out=None):
949        """
950        @param out: Where events will be written.
951        @type out: Object providing a valid file object interface.
952        """
953        if out is None:
954            out = sys.stdout
955        self._out = out
956
957    def process_default(self, event):
958        """
959        Writes event string representation to file object provided to
960        my_init().
961
962        @param event: Event to be processed. Can be of any type of events but
963                      IN_Q_OVERFLOW events (see method process_IN_Q_OVERFLOW).
964        @type event: Event instance
965        """
966        self._out.write(str(event))
967        self._out.write('\n')
968        self._out.flush()
969
970
971class ChainIfTrue(ProcessEvent):
972    """
973    Makes conditional chaining depending on the result of the nested
974    processing instance.
975    """
976    def my_init(self, func):
977        """
978        Method automatically called from base class constructor.
979        """
980        self._func = func
981
982    def process_default(self, event):
983        return not self._func(event)
984
985
986class Stats(ProcessEvent):
987    """
988    Compute and display trivial statistics about processed events.
989    """
990    def my_init(self):
991        """
992        Method automatically called from base class constructor.
993        """
994        self._start_time = time.time()
995        self._stats = {}
996        self._stats_lock = threading.Lock()
997
998    def process_default(self, event):
999        """
1000        Processes |event|.
1001        """
1002        self._stats_lock.acquire()
1003        try:
1004            events = event.maskname.split('|')
1005            for event_name in events:
1006                count = self._stats.get(event_name, 0)
1007                self._stats[event_name] = count + 1
1008        finally:
1009            self._stats_lock.release()
1010
1011    def _stats_copy(self):
1012        self._stats_lock.acquire()
1013        try:
1014            return self._stats.copy()
1015        finally:
1016            self._stats_lock.release()
1017
1018    def __repr__(self):
1019        stats = self._stats_copy()
1020
1021        elapsed = int(time.time() - self._start_time)
1022        elapsed_str = ''
1023        if elapsed < 60:
1024            elapsed_str = str(elapsed) + 'sec'
1025        elif 60 <= elapsed < 3600:
1026            elapsed_str = '%dmn%dsec' % (elapsed / 60, elapsed % 60)
1027        elif 3600 <= elapsed < 86400:
1028            elapsed_str = '%dh%dmn' % (elapsed / 3600, (elapsed % 3600) / 60)
1029        elif elapsed >= 86400:
1030            elapsed_str = '%dd%dh' % (elapsed / 86400, (elapsed % 86400) / 3600)
1031        stats['ElapsedTime'] = elapsed_str
1032
1033        l = []
1034        for ev, value in sorted(stats.items(), key=lambda x: x[0]):
1035            l.append(' %s=%s' % (output_format.field_name(ev),
1036                                 output_format.field_value(value)))
1037        s = '<%s%s >' % (output_format.class_name(self.__class__.__name__),
1038                         ''.join(l))
1039        return s
1040
1041    def dump(self, filename):
1042        """
1043        Dumps statistics.
1044
1045        @param filename: filename where stats will be dumped, filename is
1046                         created and must not exist prior to this call.
1047        @type filename: string
1048        """
1049        flags = os.O_WRONLY|os.O_CREAT|os.O_NOFOLLOW|os.O_EXCL
1050        fd = os.open(filename, flags, 0o0600)
1051        os.write(fd, bytes(self.__str__(), locale.getpreferredencoding()))
1052        os.close(fd)
1053
1054    def __str__(self, scale=45):
1055        stats = self._stats_copy()
1056        if not stats:
1057            return ''
1058
1059        m = max(stats.values())
1060        unity = scale / m
1061        fmt = '%%-26s%%-%ds%%s' % (len(output_format.field_value('@' * scale))
1062                                   + 1)
1063        def func(x):
1064            return fmt % (output_format.field_name(x[0]),
1065                          output_format.field_value('@' * int(x[1] * unity)),
1066                          output_format.simple('%d' % x[1], 'yellow'))
1067        s = '\n'.join(map(func, sorted(stats.items(), key=lambda x: x[0])))
1068        return s
1069
1070
1071class NotifierError(PyinotifyError):
1072    """
1073    Notifier Exception. Raised on Notifier error.
1074
1075    """
1076    def __init__(self, err):
1077        """
1078        @param err: Exception string's description.
1079        @type err: string
1080        """
1081        PyinotifyError.__init__(self, err)
1082
1083
1084class Notifier:
1085    """
1086    Read notifications, process events.
1087
1088    """
1089    def __init__(self, watch_manager, default_proc_fun=None, read_freq=0,
1090                 threshold=0, timeout=None):
1091        """
1092        Initialization. read_freq, threshold and timeout parameters are used
1093        when looping.
1094
1095        @param watch_manager: Watch Manager.
1096        @type watch_manager: WatchManager instance
1097        @param default_proc_fun: Default processing method. If None, a new
1098                                 instance of PrintAllEvents will be assigned.
1099        @type default_proc_fun: instance of ProcessEvent
1100        @param read_freq: if read_freq == 0, events are read asap,
1101                          if read_freq is > 0, this thread sleeps
1102                          max(0, read_freq - (timeout / 1000)) seconds. But if
1103                          timeout is None it may be different because
1104                          poll is blocking waiting for something to read.
1105        @type read_freq: int
1106        @param threshold: File descriptor will be read only if the accumulated
1107                          size to read becomes >= threshold. If != 0, you likely
1108                          want to use it in combination with an appropriate
1109                          value for read_freq because without that you would
1110                          keep looping without really reading anything and that
1111                          until the amount of events to read is >= threshold.
1112                          At least with read_freq set you might sleep.
1113        @type threshold: int
1114        @param timeout: see read_freq above. If provided, it must be set in
1115                        milliseconds. See
1116                        https://docs.python.org/3/library/select.html#select.poll.poll
1117        @type timeout: int
1118        """
1119        # Watch Manager instance
1120        self._watch_manager = watch_manager
1121        # File descriptor
1122        self._fd = self._watch_manager.get_fd()
1123        # Poll object and registration
1124        self._pollobj = select.poll()
1125        self._pollobj.register(self._fd, select.POLLIN)
1126        # This pipe is correctely initialized and used by ThreadedNotifier
1127        self._pipe = (-1, -1)
1128        # Event queue
1129        self._eventq = deque()
1130        # System processing functor, common to all events
1131        self._sys_proc_fun = _SysProcessEvent(self._watch_manager, self)
1132        # Default processing method
1133        self._default_proc_fun = default_proc_fun
1134        if default_proc_fun is None:
1135            self._default_proc_fun = PrintAllEvents()
1136        # Loop parameters
1137        self._read_freq = read_freq
1138        self._threshold = threshold
1139        self._timeout = timeout
1140        # Coalesce events option
1141        self._coalesce = False
1142        # set of str(raw_event), only used when coalesce option is True
1143        self._eventset = set()
1144
1145    def append_event(self, event):
1146        """
1147        Append a raw event to the event queue.
1148
1149        @param event: An event.
1150        @type event: _RawEvent instance.
1151        """
1152        self._eventq.append(event)
1153
1154    def proc_fun(self):
1155        return self._default_proc_fun
1156
1157    def coalesce_events(self, coalesce=True):
1158        """
1159        Coalescing events. Events are usually processed by batchs, their size
1160        depend on various factors. Thus, before processing them, events received
1161        from inotify are aggregated in a fifo queue. If this coalescing
1162        option is enabled events are filtered based on their unicity, only
1163        unique events are enqueued, doublons are discarded. An event is unique
1164        when the combination of its fields (wd, mask, cookie, name) is unique
1165        among events of a same batch. After a batch of events is processed any
1166        events is accepted again. By default this option is disabled, you have
1167        to explictly call this function to turn it on.
1168
1169        @param coalesce: Optional new coalescing value. True by default.
1170        @type coalesce: Bool
1171        """
1172        self._coalesce = coalesce
1173        if not coalesce:
1174            self._eventset.clear()
1175
1176    def check_events(self, timeout=None):
1177        """
1178        Check for new events available to read, blocks up to timeout
1179        milliseconds.
1180
1181        @param timeout: If specified it overrides the corresponding instance
1182                        attribute _timeout. timeout must be sepcified in
1183                        milliseconds.
1184        @type timeout: int
1185
1186        @return: New events to read.
1187        @rtype: bool
1188        """
1189        while True:
1190            try:
1191                # blocks up to 'timeout' milliseconds
1192                if timeout is None:
1193                    timeout = self._timeout
1194                ret = self._pollobj.poll(timeout)
1195            except select.error as err:
1196                if err.args[0] == errno.EINTR:
1197                    continue # interrupted, retry
1198                else:
1199                    raise
1200            else:
1201                break
1202
1203        if not ret or (self._pipe[0] == ret[0][0]):
1204            return False
1205        # only one fd is polled
1206        return ret[0][1] & select.POLLIN
1207
1208    def read_events(self):
1209        """
1210        Read events from device, build _RawEvents, and enqueue them.
1211        """
1212        buf_ = array.array('i', [0])
1213        # get event queue size
1214        if fcntl.ioctl(self._fd, termios.FIONREAD, buf_, 1) == -1:
1215            return
1216        queue_size = buf_[0]
1217        if queue_size < self._threshold:
1218            log.debug('(fd: %d) %d bytes available to read but threshold is '
1219                      'fixed to %d bytes', self._fd, queue_size,
1220                      self._threshold)
1221            return
1222
1223        try:
1224            # Read content from file
1225            r = os.read(self._fd, queue_size)
1226        except Exception as msg:
1227            raise NotifierError(msg)
1228        log.debug('Event queue size: %d', queue_size)
1229        rsum = 0  # counter
1230        while rsum < queue_size:
1231            s_size = 16
1232            # Retrieve wd, mask, cookie and fname_len
1233            wd, mask, cookie, fname_len = struct.unpack('iIII',
1234                                                        r[rsum:rsum+s_size])
1235            # Retrieve name
1236            bname, = struct.unpack('%ds' % fname_len,
1237                                   r[rsum + s_size:rsum + s_size + fname_len])
1238            # FIXME: should we explictly call sys.getdefaultencoding() here ??
1239            uname = bname.decode()
1240            rawevent = _RawEvent(wd, mask, cookie, uname)
1241            if self._coalesce:
1242                # Only enqueue new (unique) events.
1243                raweventstr = str(rawevent)
1244                if raweventstr not in self._eventset:
1245                    self._eventset.add(raweventstr)
1246                    self._eventq.append(rawevent)
1247            else:
1248                self._eventq.append(rawevent)
1249            rsum += s_size + fname_len
1250
1251    def process_events(self):
1252        """
1253        Routine for processing events from queue by calling their
1254        associated proccessing method (an instance of ProcessEvent).
1255        It also does internal processings, to keep the system updated.
1256        """
1257        while self._eventq:
1258            raw_event = self._eventq.popleft()  # pop next event
1259            if self._watch_manager.ignore_events:
1260                log.debug("Event ignored: %s" % repr(raw_event))
1261                continue
1262            watch_ = self._watch_manager.get_watch(raw_event.wd)
1263            if (watch_ is None) and not (raw_event.mask & IN_Q_OVERFLOW):
1264                if not (raw_event.mask & IN_IGNORED):
1265                    # Not really sure how we ended up here, nor how we should
1266                    # handle these types of events and if it is appropriate to
1267                    # completly skip them (like we are doing here).
1268                    log.warning("Unable to retrieve Watch object associated to %s",
1269                                repr(raw_event))
1270                continue
1271            revent = self._sys_proc_fun(raw_event)  # system processings
1272            if watch_ and watch_.proc_fun:
1273                watch_.proc_fun(revent)  # user processings
1274            else:
1275                self._default_proc_fun(revent)
1276        self._sys_proc_fun.cleanup()  # remove olds MOVED_* events records
1277        if self._coalesce:
1278            self._eventset.clear()
1279
1280    def __daemonize(self, pid_file=None, stdin=os.devnull, stdout=os.devnull,
1281                    stderr=os.devnull):
1282        """
1283        pid_file: file where the pid will be written. If pid_file=None the pid
1284                  is written to /var/run/<sys.argv[0]|pyinotify>.pid, if
1285                  pid_file=False no pid_file is written.
1286        stdin, stdout, stderr: files associated to common streams.
1287        """
1288        if pid_file is None:
1289            dirname = '/var/run/'
1290            basename = os.path.basename(sys.argv[0]) or 'pyinotify'
1291            pid_file = os.path.join(dirname, basename + '.pid')
1292
1293        if pid_file != False and os.path.lexists(pid_file):
1294            err = 'Cannot daemonize: pid file %s already exists.' % pid_file
1295            raise NotifierError(err)
1296
1297        def fork_daemon():
1298            # Adapted from Chad J. Schroeder's recipe
1299            # @see http://code.activestate.com/recipes/278731/
1300            pid = os.fork()
1301            if (pid == 0):
1302                # parent 2
1303                os.setsid()
1304                pid = os.fork()
1305                if (pid == 0):
1306                    # child
1307                    os.chdir('/')
1308                    os.umask(0o022)
1309                else:
1310                    # parent 2
1311                    os._exit(0)
1312            else:
1313                # parent 1
1314                os._exit(0)
1315
1316            fd_inp = os.open(stdin, os.O_RDONLY)
1317            os.dup2(fd_inp, 0)
1318            fd_out = os.open(stdout, os.O_WRONLY|os.O_CREAT, 0o0600)
1319            os.dup2(fd_out, 1)
1320            fd_err = os.open(stderr, os.O_WRONLY|os.O_CREAT, 0o0600)
1321            os.dup2(fd_err, 2)
1322
1323        # Detach task
1324        fork_daemon()
1325
1326        # Write pid
1327        if pid_file != False:
1328            flags = os.O_WRONLY|os.O_CREAT|os.O_NOFOLLOW|os.O_EXCL
1329            fd_pid = os.open(pid_file, flags, 0o0600)
1330            os.write(fd_pid,  bytes(str(os.getpid()) + '\n',
1331                                    locale.getpreferredencoding()))
1332            os.close(fd_pid)
1333            # Register unlink function
1334            atexit.register(lambda : os.unlink(pid_file))
1335
1336    def _sleep(self, ref_time):
1337        # Only consider sleeping if read_freq is > 0
1338        if self._read_freq > 0:
1339            cur_time = time.time()
1340            sleep_amount = self._read_freq - (cur_time - ref_time)
1341            if sleep_amount > 0:
1342                log.debug('Now sleeping %d seconds', sleep_amount)
1343                time.sleep(sleep_amount)
1344
1345    def loop(self, callback=None, daemonize=False, **args):
1346        """
1347        Events are read only one time every min(read_freq, timeout)
1348        seconds at best and only if the size to read is >= threshold.
1349        After this method returns it must not be called again for the same
1350        instance.
1351
1352        @param callback: Functor called after each event processing iteration.
1353                         Expects to receive the notifier object (self) as first
1354                         parameter. If this function returns True the loop is
1355                         immediately terminated otherwise the loop method keeps
1356                         looping.
1357        @type callback: callable object or function
1358        @param daemonize: This thread is daemonized if set to True.
1359        @type daemonize: boolean
1360        @param args: Optional and relevant only if daemonize is True. Remaining
1361                     keyworded arguments are directly passed to daemonize see
1362                     __daemonize() method. If pid_file=None or is set to a
1363                     pathname the caller must ensure the file does not exist
1364                     before this method is called otherwise an exception
1365                     pyinotify.NotifierError will be raised. If pid_file=False
1366                     it is still daemonized but the pid is not written in any
1367                     file.
1368        @type args: various
1369        """
1370        if daemonize:
1371            self.__daemonize(**args)
1372
1373        # Read and process events forever
1374        while 1:
1375            try:
1376                self.process_events()
1377                if (callback is not None) and (callback(self) is True):
1378                    break
1379                ref_time = time.time()
1380                # check_events is blocking
1381                if self.check_events():
1382                    self._sleep(ref_time)
1383                    self.read_events()
1384            except KeyboardInterrupt:
1385                # Stop monitoring if sigint is caught (Control-C).
1386                log.debug('Pyinotify stops monitoring.')
1387                break
1388        # Close internals
1389        self.stop()
1390
1391    def stop(self):
1392        """
1393        Close inotify's instance (close its file descriptor).
1394        It destroys all existing watches, pending events,...
1395        This method is automatically called at the end of loop().
1396        Afterward it is invalid to access this instance.
1397        """
1398        if self._fd is not None:
1399            self._pollobj.unregister(self._fd)
1400            os.close(self._fd)
1401            self._fd = None
1402        self._sys_proc_fun = None
1403
1404
1405class ThreadedNotifier(threading.Thread, Notifier):
1406    """
1407    This notifier inherits from threading.Thread for instanciating a separate
1408    thread, and also inherits from Notifier, because it is a threaded notifier.
1409
1410    Note that every functionality provided by this class is also provided
1411    through Notifier class. Moreover Notifier should be considered first because
1412    it is not threaded and could be easily daemonized.
1413    """
1414    def __init__(self, watch_manager, default_proc_fun=None, read_freq=0,
1415                 threshold=0, timeout=None):
1416        """
1417        Initialization, initialize base classes. read_freq, threshold and
1418        timeout parameters are used when looping.
1419
1420        @param watch_manager: Watch Manager.
1421        @type watch_manager: WatchManager instance
1422        @param default_proc_fun: Default processing method. See base class.
1423        @type default_proc_fun: instance of ProcessEvent
1424        @param read_freq: if read_freq == 0, events are read asap,
1425                          if read_freq is > 0, this thread sleeps
1426                          max(0, read_freq - (timeout / 1000)) seconds.
1427        @type read_freq: int
1428        @param threshold: File descriptor will be read only if the accumulated
1429                          size to read becomes >= threshold. If != 0, you likely
1430                          want to use it in combination with an appropriate
1431                          value set for read_freq because without that you would
1432                          keep looping without really reading anything and that
1433                          until the amount of events to read is >= threshold. At
1434                          least with read_freq you might sleep.
1435        @type threshold: int
1436        @param timeout: see read_freq above. If provided, it must be set in
1437                        milliseconds. See
1438                        https://docs.python.org/3/library/select.html#select.poll.poll
1439        @type timeout: int
1440        """
1441        # Init threading base class
1442        threading.Thread.__init__(self)
1443        # Stop condition
1444        self._stop_event = threading.Event()
1445        # Init Notifier base class
1446        Notifier.__init__(self, watch_manager, default_proc_fun, read_freq,
1447                          threshold, timeout)
1448        # Create a new pipe used for thread termination
1449        self._pipe = os.pipe()
1450        self._pollobj.register(self._pipe[0], select.POLLIN)
1451
1452    def stop(self):
1453        """
1454        Stop notifier's loop. Stop notification. Join the thread.
1455        """
1456        self._stop_event.set()
1457        os.write(self._pipe[1], b'stop')
1458        threading.Thread.join(self)
1459        Notifier.stop(self)
1460        self._pollobj.unregister(self._pipe[0])
1461        os.close(self._pipe[0])
1462        os.close(self._pipe[1])
1463
1464    def loop(self):
1465        """
1466        Thread's main loop. Don't meant to be called by user directly.
1467        Call inherited start() method instead.
1468
1469        Events are read only once time every min(read_freq, timeout)
1470        seconds at best and only if the size of events to read is >= threshold.
1471        """
1472        # When the loop must be terminated .stop() is called, 'stop'
1473        # is written to pipe fd so poll() returns and .check_events()
1474        # returns False which make evaluate the While's stop condition
1475        # ._stop_event.isSet() wich put an end to the thread's execution.
1476        while not self._stop_event.isSet():
1477            self.process_events()
1478            ref_time = time.time()
1479            if self.check_events():
1480                self._sleep(ref_time)
1481                self.read_events()
1482
1483    def run(self):
1484        """
1485        Start thread's loop: read and process events until the method
1486        stop() is called.
1487        Never call this method directly, instead call the start() method
1488        inherited from threading.Thread, which then will call run() in
1489        its turn.
1490        """
1491        self.loop()
1492
1493
1494class AsyncNotifier(asyncore.file_dispatcher, Notifier):
1495    """
1496    This notifier inherits from asyncore.file_dispatcher in order to be able to
1497    use pyinotify along with the asyncore framework.
1498
1499    """
1500    def __init__(self, watch_manager, default_proc_fun=None, read_freq=0,
1501                 threshold=0, timeout=None, channel_map=None):
1502        """
1503        Initializes the async notifier. The only additional parameter is
1504        'channel_map' which is the optional asyncore private map. See
1505        Notifier class for the meaning of the others parameters.
1506
1507        """
1508        Notifier.__init__(self, watch_manager, default_proc_fun, read_freq,
1509                          threshold, timeout)
1510        asyncore.file_dispatcher.__init__(self, self._fd, channel_map)
1511
1512    def handle_read(self):
1513        """
1514        When asyncore tells us we can read from the fd, we proceed processing
1515        events. This method can be overridden for handling a notification
1516        differently.
1517
1518        """
1519        self.read_events()
1520        self.process_events()
1521
1522
1523class TornadoAsyncNotifier(Notifier):
1524    """
1525    Tornado ioloop adapter.
1526
1527    """
1528    def __init__(self, watch_manager, ioloop, callback=None,
1529                 default_proc_fun=None, read_freq=0, threshold=0, timeout=None,
1530                 channel_map=None):
1531        """
1532        Note that if later you must call ioloop.close() be sure to let the
1533        default parameter to all_fds=False.
1534
1535        See example tornado_notifier.py for an example using this notifier.
1536
1537        @param ioloop: Tornado's IO loop.
1538        @type ioloop: tornado.ioloop.IOLoop instance.
1539        @param callback: Functor called at the end of each call to handle_read
1540                         (IOLoop's read handler). Expects to receive the
1541                         notifier object (self) as single parameter.
1542        @type callback: callable object or function
1543        """
1544        self.io_loop = ioloop
1545        self.handle_read_callback = callback
1546        Notifier.__init__(self, watch_manager, default_proc_fun, read_freq,
1547                          threshold, timeout)
1548        ioloop.add_handler(self._fd, self.handle_read, ioloop.READ)
1549
1550    def stop(self):
1551        self.io_loop.remove_handler(self._fd)
1552        Notifier.stop(self)
1553
1554    def handle_read(self, *args, **kwargs):
1555        """
1556        See comment in AsyncNotifier.
1557
1558        """
1559        self.read_events()
1560        self.process_events()
1561        if self.handle_read_callback is not None:
1562            self.handle_read_callback(self)
1563
1564
1565class AsyncioNotifier(Notifier):
1566    """
1567
1568    asyncio/trollius event loop adapter.
1569
1570    """
1571    def __init__(self, watch_manager, loop, callback=None,
1572                 default_proc_fun=None, read_freq=0, threshold=0, timeout=None):
1573        """
1574
1575        See examples/asyncio_notifier.py for an example usage.
1576
1577        @param loop: asyncio or trollius event loop instance.
1578        @type loop: asyncio.BaseEventLoop or trollius.BaseEventLoop instance.
1579        @param callback: Functor called at the end of each call to handle_read.
1580                         Expects to receive the notifier object (self) as
1581                         single parameter.
1582        @type callback: callable object or function
1583
1584        """
1585        self.loop = loop
1586        self.handle_read_callback = callback
1587        Notifier.__init__(self, watch_manager, default_proc_fun, read_freq,
1588                          threshold, timeout)
1589        loop.add_reader(self._fd, self.handle_read)
1590
1591    def stop(self):
1592        self.loop.remove_reader(self._fd)
1593        Notifier.stop(self)
1594
1595    def handle_read(self, *args, **kwargs):
1596        self.read_events()
1597        self.process_events()
1598        if self.handle_read_callback is not None:
1599            self.handle_read_callback(self)
1600
1601
1602class Watch:
1603    """
1604    Represent a watch, i.e. a file or directory being watched.
1605
1606    """
1607    __slots__ = ('wd', 'path', 'mask', 'proc_fun', 'auto_add',
1608                 'exclude_filter', 'dir')
1609
1610    def __init__(self, wd, path, mask, proc_fun, auto_add, exclude_filter):
1611        """
1612        Initializations.
1613
1614        @param wd: Watch descriptor.
1615        @type wd: int
1616        @param path: Path of the file or directory being watched.
1617        @type path: str
1618        @param mask: Mask.
1619        @type mask: int
1620        @param proc_fun: Processing callable object.
1621        @type proc_fun:
1622        @param auto_add: Automatically add watches on new directories.
1623        @type auto_add: bool
1624        @param exclude_filter: Boolean function, used to exclude new
1625                               directories from being automatically watched.
1626                               See WatchManager.__init__
1627        @type exclude_filter: callable object
1628        """
1629        self.wd = wd
1630        self.path = path
1631        self.mask = mask
1632        self.proc_fun = proc_fun
1633        self.auto_add = auto_add
1634        self.exclude_filter = exclude_filter
1635        self.dir = os.path.isdir(self.path)
1636
1637    def __repr__(self):
1638        """
1639        @return: String representation.
1640        @rtype: str
1641        """
1642        s = ' '.join(['%s%s%s' % (output_format.field_name(attr),
1643                                  output_format.punctuation('='),
1644                                  output_format.field_value(getattr(self,
1645                                                                    attr))) \
1646                      for attr in self.__slots__ if not attr.startswith('_')])
1647
1648        s = '%s%s %s %s' % (output_format.punctuation('<'),
1649                            output_format.class_name(self.__class__.__name__),
1650                            s,
1651                            output_format.punctuation('>'))
1652        return s
1653
1654
1655class ExcludeFilter:
1656    """
1657    ExcludeFilter is an exclusion filter.
1658    """
1659    def __init__(self, arg_lst):
1660        """
1661        Examples:
1662          ef1 = ExcludeFilter(["/etc/rc.*", "/etc/hostname"])
1663          ef2 = ExcludeFilter("/my/path/exclude.lst")
1664          Where exclude.lst contains:
1665          /etc/rc.*
1666          /etc/hostname
1667
1668        Note: it is not possible to exclude a file if its encapsulating
1669              directory is itself watched. See this issue for more details
1670              https://github.com/seb-m/pyinotify/issues/31
1671
1672        @param arg_lst: is either a list of patterns or a filename from which
1673                        patterns will be loaded.
1674        @type arg_lst: list of str or str
1675        """
1676        if isinstance(arg_lst, str):
1677            lst = self._load_patterns_from_file(arg_lst)
1678        elif isinstance(arg_lst, list):
1679            lst = arg_lst
1680        else:
1681            raise TypeError
1682
1683        self._lregex = []
1684        for regex in lst:
1685            self._lregex.append(re.compile(regex, re.UNICODE))
1686
1687    def _load_patterns_from_file(self, filename):
1688        lst = []
1689        with open(filename, 'r') as file_obj:
1690            for line in file_obj.readlines():
1691                # Trim leading an trailing whitespaces
1692                pattern = line.strip()
1693                if not pattern or pattern.startswith('#'):
1694                    continue
1695                lst.append(pattern)
1696        return lst
1697
1698    def _match(self, regex, path):
1699        return regex.match(path) is not None
1700
1701    def __call__(self, path):
1702        """
1703        @param path: Path to match against provided regexps.
1704        @type path: str
1705        @return: Return True if path has been matched and should
1706                 be excluded, False otherwise.
1707        @rtype: bool
1708        """
1709        for regex in self._lregex:
1710            if self._match(regex, path):
1711                return True
1712        return False
1713
1714
1715class WatchManagerError(Exception):
1716    """
1717    WatchManager Exception. Raised on error encountered on watches
1718    operations.
1719    """
1720    def __init__(self, msg, wmd):
1721        """
1722        @param msg: Exception string's description.
1723        @type msg: string
1724        @param wmd: This dictionary contains the wd assigned to paths of the
1725                    same call for which watches were successfully added.
1726        @type wmd: dict
1727        """
1728        self.wmd = wmd
1729        Exception.__init__(self, msg)
1730
1731
1732class WatchManager:
1733    """
1734    Provide operations for watching files and directories. Its internal
1735    dictionary is used to reference watched items. When used inside
1736    threaded code, one must instanciate as many WatchManager instances as
1737    there are ThreadedNotifier instances.
1738
1739    """
1740    def __init__(self, exclude_filter=lambda path: False):
1741        """
1742        Initialization: init inotify, init watch manager dictionary.
1743        Raise OSError if initialization fails, raise InotifyBindingNotFoundError
1744        if no inotify binding was found (through ctypes or from direct access to
1745        syscalls).
1746
1747        @param exclude_filter: boolean function, returns True if current
1748                               path must be excluded from being watched.
1749                               Convenient for providing a common exclusion
1750                               filter for every call to add_watch.
1751        @type exclude_filter: callable object
1752        """
1753        self._ignore_events = False
1754        self._exclude_filter = exclude_filter
1755        self._wmd = {}  # watch dict key: watch descriptor, value: watch
1756
1757        self._inotify_wrapper = INotifyWrapper.create()
1758        if self._inotify_wrapper is None:
1759            raise InotifyBindingNotFoundError()
1760
1761        self._fd = self._inotify_wrapper.inotify_init() # file descriptor
1762        if self._fd < 0:
1763            err = 'Cannot initialize new instance of inotify, %s'
1764            raise OSError(err % self._inotify_wrapper.str_errno())
1765
1766    def close(self):
1767        """
1768        Close inotify's file descriptor, this action will also automatically
1769        remove (i.e. stop watching) all its associated watch descriptors.
1770        After a call to this method the WatchManager's instance become useless
1771        and cannot be reused, a new instance must then be instanciated. It
1772        makes sense to call this method in few situations for instance if
1773        several independant WatchManager must be instanciated or if all watches
1774        must be removed and no other watches need to be added.
1775        """
1776        os.close(self._fd)
1777
1778    def get_fd(self):
1779        """
1780        Return assigned inotify's file descriptor.
1781
1782        @return: File descriptor.
1783        @rtype: int
1784        """
1785        return self._fd
1786
1787    def get_watch(self, wd):
1788        """
1789        Get watch from provided watch descriptor wd.
1790
1791        @param wd: Watch descriptor.
1792        @type wd: int
1793        """
1794        return self._wmd.get(wd)
1795
1796    def del_watch(self, wd):
1797        """
1798        Remove watch entry associated to watch descriptor wd.
1799
1800        @param wd: Watch descriptor.
1801        @type wd: int
1802        """
1803        try:
1804            del self._wmd[wd]
1805        except KeyError as err:
1806            log.error('Cannot delete unknown watch descriptor %s' % str(err))
1807
1808    @property
1809    def watches(self):
1810        """
1811        Get a reference on the internal watch manager dictionary.
1812
1813        @return: Internal watch manager dictionary.
1814        @rtype: dict
1815        """
1816        return self._wmd
1817
1818    def __format_path(self, path):
1819        """
1820        Format path to its internal (stored in watch manager) representation.
1821        """
1822        # path must be a unicode string (str) and is just normalized.
1823        return os.path.normpath(path)
1824
1825    def __add_watch(self, path, mask, proc_fun, auto_add, exclude_filter):
1826        """
1827        Add a watch on path, build a Watch object and insert it in the
1828        watch manager dictionary. Return the wd value.
1829        """
1830        path = self.__format_path(path)
1831        if auto_add and not mask & IN_CREATE:
1832            mask |= IN_CREATE
1833        wd = self._inotify_wrapper.inotify_add_watch(self._fd, path, mask)
1834        if wd < 0:
1835            return wd
1836        watch = Watch(wd=wd, path=path, mask=mask, proc_fun=proc_fun,
1837                      auto_add=auto_add, exclude_filter=exclude_filter)
1838        # wd are _always_ indexed with their original unicode paths in wmd.
1839        self._wmd[wd] = watch
1840        log.debug('New %s', watch)
1841        return wd
1842
1843    def __glob(self, path, do_glob):
1844        if do_glob:
1845            return glob.iglob(path)
1846        else:
1847            return [path]
1848
1849    def add_watch(self, path, mask, proc_fun=None, rec=False,
1850                  auto_add=False, do_glob=False, quiet=True,
1851                  exclude_filter=None):
1852        """
1853        Add watch(s) on the provided |path|(s) with associated |mask| flag
1854        value and optionally with a processing |proc_fun| function and
1855        recursive flag |rec| set to True.
1856        All |path| components _must_ be str (i.e. unicode) objects.
1857        If |path| is already watched it is ignored, but if it is called with
1858        option rec=True a watch is put on each one of its not-watched
1859        subdirectory.
1860
1861        @param path: Path to watch, the path can either be a file or a
1862                     directory. Also accepts a sequence (list) of paths.
1863        @type path: string or list of strings
1864        @param mask: Bitmask of events.
1865        @type mask: int
1866        @param proc_fun: Processing object.
1867        @type proc_fun: function or ProcessEvent instance or instance of
1868                        one of its subclasses or callable object.
1869        @param rec: Recursively add watches from path on all its
1870                    subdirectories, set to False by default (doesn't
1871                    follows symlinks in any case).
1872        @type rec: bool
1873        @param auto_add: Automatically add watches on newly created
1874                         directories in watched parent |path| directory.
1875                         If |auto_add| is True, IN_CREATE is ored with |mask|
1876                         when the watch is added.
1877        @type auto_add: bool
1878        @param do_glob: Do globbing on pathname (see standard globbing
1879                        module for more informations).
1880        @type do_glob: bool
1881        @param quiet: if False raises a WatchManagerError exception on
1882                      error. See example not_quiet.py.
1883        @type quiet: bool
1884        @param exclude_filter: predicate (boolean function), which returns
1885                               True if the current path must be excluded
1886                               from being watched. This argument has
1887                               precedence over exclude_filter passed to
1888                               the class' constructor.
1889        @type exclude_filter: callable object
1890        @return: dict of paths associated to watch descriptors. A wd value
1891                 is positive if the watch was added sucessfully, otherwise
1892                 the value is negative. If the path was invalid or was already
1893                 watched it is not included into this returned dictionary.
1894        @rtype: dict of {str: int}
1895        """
1896        ret_ = {} # return {path: wd, ...}
1897
1898        if exclude_filter is None:
1899            exclude_filter = self._exclude_filter
1900
1901        # normalize args as list elements
1902        for npath in self.__format_param(path):
1903            # Require that path be a unicode string
1904            if not isinstance(npath, str):
1905                ret_[path] = -3
1906                continue
1907
1908            # unix pathname pattern expansion
1909            for apath in self.__glob(npath, do_glob):
1910                # recursively list subdirs according to rec param
1911                for rpath in self.__walk_rec(apath, rec):
1912                    if not exclude_filter(rpath):
1913                        wd = ret_[rpath] = self.__add_watch(rpath, mask,
1914                                                            proc_fun,
1915                                                            auto_add,
1916                                                            exclude_filter)
1917                        if wd < 0:
1918                            err = ('add_watch: cannot watch %s WD=%d, %s' % \
1919                                       (rpath, wd,
1920                                        self._inotify_wrapper.str_errno()))
1921                            if quiet:
1922                                log.error(err)
1923                            else:
1924                                raise WatchManagerError(err, ret_)
1925                    else:
1926                        # Let's say -2 means 'explicitely excluded
1927                        # from watching'.
1928                        ret_[rpath] = -2
1929        return ret_
1930
1931    def __get_sub_rec(self, lpath):
1932        """
1933        Get every wd from self._wmd if its path is under the path of
1934        one (at least) of those in lpath. Doesn't follow symlinks.
1935
1936        @param lpath: list of watch descriptor
1937        @type lpath: list of int
1938        @return: list of watch descriptor
1939        @rtype: list of int
1940        """
1941        for d in lpath:
1942            root = self.get_path(d)
1943            if root is not None:
1944                # always keep root
1945                yield d
1946            else:
1947                # if invalid
1948                continue
1949
1950            # nothing else to expect
1951            if not os.path.isdir(root):
1952                continue
1953
1954            # normalization
1955            root = os.path.normpath(root)
1956            # recursion
1957            lend = len(root)
1958            for iwd in self._wmd.items():
1959                cur = iwd[1].path
1960                pref = os.path.commonprefix([root, cur])
1961                if root == os.sep or (len(pref) == lend and \
1962                                      len(cur) > lend and \
1963                                      cur[lend] == os.sep):
1964                    yield iwd[1].wd
1965
1966    def update_watch(self, wd, mask=None, proc_fun=None, rec=False,
1967                     auto_add=False, quiet=True):
1968        """
1969        Update existing watch descriptors |wd|. The |mask| value, the
1970        processing object |proc_fun|, the recursive param |rec| and the
1971        |auto_add| and |quiet| flags can all be updated.
1972
1973        @param wd: Watch Descriptor to update. Also accepts a list of
1974                   watch descriptors.
1975        @type wd: int or list of int
1976        @param mask: Optional new bitmask of events.
1977        @type mask: int
1978        @param proc_fun: Optional new processing function.
1979        @type proc_fun: function or ProcessEvent instance or instance of
1980                        one of its subclasses or callable object.
1981        @param rec: Optionally adds watches recursively on all
1982                    subdirectories contained into |wd| directory.
1983        @type rec: bool
1984        @param auto_add: Automatically adds watches on newly created
1985                         directories in the watch's path corresponding to |wd|.
1986                         If |auto_add| is True, IN_CREATE is ored with |mask|
1987                         when the watch is updated.
1988        @type auto_add: bool
1989        @param quiet: If False raises a WatchManagerError exception on
1990                      error. See example not_quiet.py
1991        @type quiet: bool
1992        @return: dict of watch descriptors associated to booleans values.
1993                 True if the corresponding wd has been successfully
1994                 updated, False otherwise.
1995        @rtype: dict of {int: bool}
1996        """
1997        lwd = self.__format_param(wd)
1998        if rec:
1999            lwd = self.__get_sub_rec(lwd)
2000
2001        ret_ = {}  # return {wd: bool, ...}
2002        for awd in lwd:
2003            apath = self.get_path(awd)
2004            if not apath or awd < 0:
2005                err = 'update_watch: invalid WD=%d' % awd
2006                if quiet:
2007                    log.error(err)
2008                    continue
2009                raise WatchManagerError(err, ret_)
2010
2011            if mask:
2012                wd_ = self._inotify_wrapper.inotify_add_watch(self._fd, apath,
2013                                                              mask)
2014                if wd_ < 0:
2015                    ret_[awd] = False
2016                    err = ('update_watch: cannot update %s WD=%d, %s' % \
2017                               (apath, wd_, self._inotify_wrapper.str_errno()))
2018                    if quiet:
2019                        log.error(err)
2020                        continue
2021                    raise WatchManagerError(err, ret_)
2022
2023                assert(awd == wd_)
2024
2025            if proc_fun or auto_add:
2026                watch_ = self._wmd[awd]
2027
2028            if proc_fun:
2029                watch_.proc_fun = proc_fun
2030
2031            if auto_add:
2032                watch_.auto_add = auto_add
2033
2034            ret_[awd] = True
2035            log.debug('Updated watch - %s', self._wmd[awd])
2036        return ret_
2037
2038    def __format_param(self, param):
2039        """
2040        @param param: Parameter.
2041        @type param: string or int
2042        @return: wrap param.
2043        @rtype: list of type(param)
2044        """
2045        if isinstance(param, list):
2046            for p_ in param:
2047                yield p_
2048        else:
2049            yield param
2050
2051    def get_wd(self, path):
2052        """
2053        Returns the watch descriptor associated to path. This method
2054        presents a prohibitive cost, always prefer to keep the WD
2055        returned by add_watch(). If the path is unknown it returns None.
2056
2057        @param path: Path.
2058        @type path: str
2059        @return: WD or None.
2060        @rtype: int or None
2061        """
2062        path = self.__format_path(path)
2063        for iwd in self._wmd.items():
2064            if iwd[1].path == path:
2065                return iwd[0]
2066
2067    def get_path(self, wd):
2068        """
2069        Returns the path associated to WD, if WD is unknown it returns None.
2070
2071        @param wd: Watch descriptor.
2072        @type wd: int
2073        @return: Path or None.
2074        @rtype: string or None
2075        """
2076        watch_ = self._wmd.get(wd)
2077        if watch_ is not None:
2078            return watch_.path
2079
2080    def __walk_rec(self, top, rec):
2081        """
2082        Yields each subdirectories of top, doesn't follow symlinks.
2083        If rec is false, only yield top.
2084
2085        @param top: root directory.
2086        @type top: string
2087        @param rec: recursive flag.
2088        @type rec: bool
2089        @return: path of one subdirectory.
2090        @rtype: string
2091        """
2092        if not rec or os.path.islink(top) or not os.path.isdir(top):
2093            yield top
2094        else:
2095            for root, dirs, files in os.walk(top):
2096                yield root
2097
2098    def rm_watch(self, wd, rec=False, quiet=True):
2099        """
2100        Removes watch(s).
2101
2102        @param wd: Watch Descriptor of the file or directory to unwatch.
2103                   Also accepts a list of WDs.
2104        @type wd: int or list of int.
2105        @param rec: Recursively removes watches on every already watched
2106                    subdirectories and subfiles.
2107        @type rec: bool
2108        @param quiet: If False raises a WatchManagerError exception on
2109                      error. See example not_quiet.py
2110        @type quiet: bool
2111        @return: dict of watch descriptors associated to booleans values.
2112                 True if the corresponding wd has been successfully
2113                 removed, False otherwise.
2114        @rtype: dict of {int: bool}
2115        """
2116        lwd = self.__format_param(wd)
2117        if rec:
2118            lwd = self.__get_sub_rec(lwd)
2119
2120        ret_ = {}  # return {wd: bool, ...}
2121        for awd in lwd:
2122            # remove watch
2123            wd_ = self._inotify_wrapper.inotify_rm_watch(self._fd, awd)
2124            if wd_ < 0:
2125                ret_[awd] = False
2126                err = ('rm_watch: cannot remove WD=%d, %s' % \
2127                           (awd, self._inotify_wrapper.str_errno()))
2128                if quiet:
2129                    log.error(err)
2130                    continue
2131                raise WatchManagerError(err, ret_)
2132
2133            # Remove watch from our dictionary
2134            if awd in self._wmd:
2135                del self._wmd[awd]
2136            ret_[awd] = True
2137            log.debug('Watch WD=%d (%s) removed', awd, self.get_path(awd))
2138        return ret_
2139
2140
2141    def watch_transient_file(self, filename, mask, proc_class):
2142        """
2143        Watch a transient file, which will be created and deleted frequently
2144        over time (e.g. pid file).
2145
2146        @attention: Currently under the call to this function it is not
2147        possible to correctly watch the events triggered into the same
2148        base directory than the directory where is located this watched
2149        transient file. For instance it would be wrong to make these
2150        two successive calls: wm.watch_transient_file('/var/run/foo.pid', ...)
2151        and wm.add_watch('/var/run/', ...)
2152
2153        @param filename: Filename.
2154        @type filename: string
2155        @param mask: Bitmask of events, should contain IN_CREATE and IN_DELETE.
2156        @type mask: int
2157        @param proc_class: ProcessEvent (or of one of its subclass), beware of
2158                           accepting a ProcessEvent's instance as argument into
2159                           __init__, see transient_file.py example for more
2160                           details.
2161        @type proc_class: ProcessEvent's instance or of one of its subclasses.
2162        @return: Same as add_watch().
2163        @rtype: Same as add_watch().
2164        """
2165        dirname = os.path.dirname(filename)
2166        if dirname == '':
2167            return {}  # Maintains coherence with add_watch()
2168        basename = os.path.basename(filename)
2169        # Assuming we are watching at least for IN_CREATE and IN_DELETE
2170        mask |= IN_CREATE | IN_DELETE
2171
2172        def cmp_name(event):
2173            if getattr(event, 'name') is None:
2174                return False
2175            return basename == event.name
2176        return self.add_watch(dirname, mask,
2177                              proc_fun=proc_class(ChainIfTrue(func=cmp_name)),
2178                              rec=False,
2179                              auto_add=False, do_glob=False,
2180                              exclude_filter=lambda path: False)
2181
2182    def get_ignore_events(self):
2183        return self._ignore_events
2184
2185    def set_ignore_events(self, nval):
2186        self._ignore_events = nval
2187
2188    ignore_events = property(get_ignore_events, set_ignore_events,
2189                             "Make watch manager ignoring new events.")
2190
2191
2192class RawOutputFormat:
2193    """
2194    Format string representations.
2195    """
2196    def __init__(self, format=None):
2197        self.format = format or {}
2198
2199    def simple(self, s, attribute):
2200        if not isinstance(s, str):
2201            s = str(s)
2202        return (self.format.get(attribute, '') + s +
2203                self.format.get('normal', ''))
2204
2205    def punctuation(self, s):
2206        """Punctuation color."""
2207        return self.simple(s, 'normal')
2208
2209    def field_value(self, s):
2210        """Field value color."""
2211        return self.simple(s, 'purple')
2212
2213    def field_name(self, s):
2214        """Field name color."""
2215        return self.simple(s, 'blue')
2216
2217    def class_name(self, s):
2218        """Class name color."""
2219        return self.format.get('red', '') + self.simple(s, 'bold')
2220
2221output_format = RawOutputFormat()
2222
2223class ColoredOutputFormat(RawOutputFormat):
2224    """
2225    Format colored string representations.
2226    """
2227    def __init__(self):
2228        f = {'normal': '\033[0m',
2229             'black': '\033[30m',
2230             'red': '\033[31m',
2231             'green': '\033[32m',
2232             'yellow': '\033[33m',
2233             'blue': '\033[34m',
2234             'purple': '\033[35m',
2235             'cyan': '\033[36m',
2236             'bold': '\033[1m',
2237             'uline': '\033[4m',
2238             'blink': '\033[5m',
2239             'invert': '\033[7m'}
2240        RawOutputFormat.__init__(self, f)
2241
2242
2243def compatibility_mode():
2244    """
2245    Use this function to turn on the compatibility mode. The compatibility
2246    mode is used to improve compatibility with Pyinotify 0.7.1 (or older)
2247    programs. The compatibility mode provides additional variables 'is_dir',
2248    'event_name', 'EventsCodes.IN_*' and 'EventsCodes.ALL_EVENTS' as
2249    Pyinotify 0.7.1 provided. Do not call this function from new programs!!
2250    Especially if there are developped for Pyinotify >= 0.8.x.
2251    """
2252    setattr(EventsCodes, 'ALL_EVENTS', ALL_EVENTS)
2253    for evname in globals():
2254        if evname.startswith('IN_'):
2255            setattr(EventsCodes, evname, globals()[evname])
2256    global COMPATIBILITY_MODE
2257    COMPATIBILITY_MODE = True
2258
2259
2260def command_line():
2261    """
2262    By default the watched path is '/tmp' and all types of events are
2263    monitored. Events monitoring serves forever, type c^c to stop it.
2264    """
2265    from optparse import OptionParser
2266
2267    usage = "usage: %prog [options] [path1] [path2] [pathn]"
2268
2269    parser = OptionParser(usage=usage)
2270    parser.add_option("-v", "--verbose", action="store_true",
2271                      dest="verbose", help="Verbose mode")
2272    parser.add_option("-r", "--recursive", action="store_true",
2273                      dest="recursive",
2274                      help="Add watches recursively on paths")
2275    parser.add_option("-a", "--auto_add", action="store_true",
2276                      dest="auto_add",
2277                      help="Automatically add watches on new directories")
2278    parser.add_option("-g", "--glob", action="store_true",
2279                      dest="glob",
2280                      help="Treat paths as globs")
2281    parser.add_option("-e", "--events-list", metavar="EVENT[,...]",
2282                      dest="events_list",
2283                      help=("A comma-separated list of events to watch for - "
2284                           "see the documentation for valid options (defaults"
2285                           " to everything)"))
2286    parser.add_option("-s", "--stats", action="store_true",
2287                      dest="stats",
2288                      help="Display dummy statistics")
2289    parser.add_option("-V", "--version", action="store_true",
2290                      dest="version",  help="Pyinotify version")
2291    parser.add_option("-f", "--raw-format", action="store_true",
2292                      dest="raw_format",
2293                      help="Disable enhanced output format.")
2294    parser.add_option("-c", "--command", action="store",
2295                      dest="command",
2296                      help="Shell command to run upon event")
2297
2298    (options, args) = parser.parse_args()
2299
2300    if options.verbose:
2301        log.setLevel(10)
2302
2303    if options.version:
2304        print(__version__)
2305
2306    if not options.raw_format:
2307        global output_format
2308        output_format = ColoredOutputFormat()
2309
2310    if len(args) < 1:
2311        path = '/tmp'  # default watched path
2312    else:
2313        path = args
2314
2315    # watch manager instance
2316    wm = WatchManager()
2317    # notifier instance and init
2318    if options.stats:
2319        notifier = Notifier(wm, default_proc_fun=Stats(), read_freq=5)
2320    else:
2321        notifier = Notifier(wm, default_proc_fun=PrintAllEvents())
2322
2323    # What mask to apply
2324    mask = 0
2325    if options.events_list:
2326        events_list = options.events_list.split(',')
2327        for ev in events_list:
2328            evcode = EventsCodes.ALL_FLAGS.get(ev, 0)
2329            if evcode:
2330                mask |= evcode
2331            else:
2332                parser.error("The event '%s' specified with option -e"
2333                             " is not valid" % ev)
2334    else:
2335        mask = ALL_EVENTS
2336
2337    # stats
2338    cb_fun = None
2339    if options.stats:
2340        def cb(s):
2341            sys.stdout.write(repr(s.proc_fun()))
2342            sys.stdout.write('\n')
2343            sys.stdout.write(str(s.proc_fun()))
2344            sys.stdout.write('\n')
2345            sys.stdout.flush()
2346        cb_fun = cb
2347
2348    # External command
2349    if options.command:
2350        def cb(s):
2351            subprocess.Popen(options.command, shell=True)
2352        cb_fun = cb
2353
2354    log.debug('Start monitoring %s, (press c^c to halt pyinotify)' % path)
2355
2356    wm.add_watch(path, mask, rec=options.recursive, auto_add=options.auto_add, do_glob=options.glob)
2357    # Loop forever (until sigint signal get caught)
2358    notifier.loop(callback=cb_fun)
2359
2360
2361if __name__ == '__main__':
2362    command_line()
2363