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