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