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 PyinotifyError.__init__(self,
46 ('Python %s is unsupported, requires '
47 'at least Python 3.0') % version)
48
51 """
52 Raised on unsupported libc versions.
53 """
55 """
56 @param version: Current Libc version
57 @type version: string
58 """
59 PyinotifyError.__init__(self,
60 ('Libc %s is not supported, requires '
61 'at least Libc 2.4') % version)
62
63
64
65 import sys
66 if sys.version < '3.0':
67 raise UnsupportedPythonVersionError(sys.version)
68
69
70
71 import threading
72 import os
73 import select
74 import struct
75 import fcntl
76 import errno
77 import termios
78 import array
79 import logging
80 import atexit
81 from collections import deque
82 from datetime import datetime, timedelta
83 import time
84 import fnmatch
85 import re
86 import ctypes
87 import ctypes.util
88 import asyncore
89 import glob
90
91 try:
92 from functools import reduce
93 except ImportError:
94 pass
95
96 __author__ = "seb@dbzteam.org (Sebastien Martini)"
97
98 __version__ = "0.9.0"
99
100
101
102
103
104 COMPATIBILITY_MODE = False
105
106
107
108 LIBC = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True)
111 code = ctypes.get_errno()
112 return '%s (%s)' % (os.strerror(code), errno.errorcode[code])
113
114
115
116
117 LIBC.gnu_get_libc_version.restype = ctypes.c_char_p
118 LIBC_VERSION = LIBC.gnu_get_libc_version()
119 if not isinstance(LIBC_VERSION, str):
120 LIBC_VERSION = LIBC_VERSION.decode()
121 if (int(LIBC_VERSION.split('.')[0]) < 2 or
122 (int(LIBC_VERSION.split('.')[0]) == 2 and
123 int(LIBC_VERSION.split('.')[1]) < 4)):
124 raise UnsupportedLibcVersionError(LIBC_VERSION)
128 """
129 Pyinotify logger used for logging unicode strings.
130 """
131 - def makeRecord(self, name, level, fn, lno, msg, args, exc_info, func=None,
132 extra=None):
133 rv = UnicodeLogRecord(name, level, fn, lno, msg, args, exc_info, func)
134 if extra is not None:
135 for key in extra:
136 if (key in ["message", "asctime"]) or (key in rv.__dict__):
137 raise KeyError("Attempt to overwrite %r in LogRecord" % key)
138 rv.__dict__[key] = extra[key]
139 return rv
140
144 """Initialize logger instance."""
145 log = logging.getLogger("pyinotify")
146 console_handler = logging.StreamHandler()
147 console_handler.setFormatter(
148 logging.Formatter("[Pyinotify %(levelname)s] %(message)s"))
149 log.addHandler(console_handler)
150 log.setLevel(20)
151 return log
152
153 log = logger_init()
158 """
159 Access (read, write) inotify's variables through sysctl. Usually it
160 requires administrator rights to update them.
161
162 Examples:
163 - Read max_queued_events attribute: myvar = max_queued_events.value
164 - Update max_queued_events attribute: max_queued_events.value = 42
165 """
166
167 inotify_attrs = {'max_user_instances': 1,
168 'max_user_watches': 2,
169 'max_queued_events': 3}
170
175
177 """
178 Gets attribute's value.
179
180 @return: stored value.
181 @rtype: int
182 """
183 oldv = ctypes.c_int(0)
184 size = ctypes.c_int(ctypes.sizeof(oldv))
185 LIBC.sysctl(self._attr, 3,
186 ctypes.c_voidp(ctypes.addressof(oldv)),
187 ctypes.addressof(size),
188 None, 0)
189 return oldv.value
190
192 """
193 Sets new attribute's value.
194
195 @param nval: replaces current value by nval.
196 @type nval: int
197 """
198 oldv = ctypes.c_int(0)
199 sizeo = ctypes.c_int(ctypes.sizeof(oldv))
200 newv = ctypes.c_int(nval)
201 sizen = ctypes.c_int(ctypes.sizeof(newv))
202 LIBC.sysctl(self._attr, 3,
203 ctypes.c_voidp(ctypes.addressof(oldv)),
204 ctypes.addressof(sizeo),
205 ctypes.c_voidp(ctypes.addressof(newv)),
206 ctypes.addressof(sizen))
207
208 value = property(get_val, set_val)
209
211 return '<%s=%d>' % (self._attrname, self.get_val())
212
213
214
215
216
217
218
219 for attrname in ('max_queued_events', 'max_user_instances', 'max_user_watches'):
220 globals()[attrname] = SysCtlINotify(attrname)
224 """
225 Set of codes corresponding to each kind of events.
226 Some of these flags are used to communicate with inotify, whereas
227 the others are sent to userspace by inotify notifying some events.
228
229 @cvar IN_ACCESS: File was accessed.
230 @type IN_ACCESS: int
231 @cvar IN_MODIFY: File was modified.
232 @type IN_MODIFY: int
233 @cvar IN_ATTRIB: Metadata changed.
234 @type IN_ATTRIB: int
235 @cvar IN_CLOSE_WRITE: Writtable file was closed.
236 @type IN_CLOSE_WRITE: int
237 @cvar IN_CLOSE_NOWRITE: Unwrittable file closed.
238 @type IN_CLOSE_NOWRITE: int
239 @cvar IN_OPEN: File was opened.
240 @type IN_OPEN: int
241 @cvar IN_MOVED_FROM: File was moved from X.
242 @type IN_MOVED_FROM: int
243 @cvar IN_MOVED_TO: File was moved to Y.
244 @type IN_MOVED_TO: int
245 @cvar IN_CREATE: Subfile was created.
246 @type IN_CREATE: int
247 @cvar IN_DELETE: Subfile was deleted.
248 @type IN_DELETE: int
249 @cvar IN_DELETE_SELF: Self (watched item itself) was deleted.
250 @type IN_DELETE_SELF: int
251 @cvar IN_MOVE_SELF: Self (watched item itself) was moved.
252 @type IN_MOVE_SELF: int
253 @cvar IN_UNMOUNT: Backing fs was unmounted.
254 @type IN_UNMOUNT: int
255 @cvar IN_Q_OVERFLOW: Event queued overflowed.
256 @type IN_Q_OVERFLOW: int
257 @cvar IN_IGNORED: File was ignored.
258 @type IN_IGNORED: int
259 @cvar IN_ONLYDIR: only watch the path if it is a directory (new
260 in kernel 2.6.15).
261 @type IN_ONLYDIR: int
262 @cvar IN_DONT_FOLLOW: don't follow a symlink (new in kernel 2.6.15).
263 IN_ONLYDIR we can make sure that we don't watch
264 the target of symlinks.
265 @type IN_DONT_FOLLOW: int
266 @cvar IN_MASK_ADD: add to the mask of an already existing watch (new
267 in kernel 2.6.14).
268 @type IN_MASK_ADD: int
269 @cvar IN_ISDIR: Event occurred against dir.
270 @type IN_ISDIR: int
271 @cvar IN_ONESHOT: Only send event once.
272 @type IN_ONESHOT: int
273 @cvar ALL_EVENTS: Alias for considering all of the events.
274 @type ALL_EVENTS: int
275 """
276
277
278
279
280 FLAG_COLLECTIONS = {'OP_FLAGS': {
281 'IN_ACCESS' : 0x00000001,
282 'IN_MODIFY' : 0x00000002,
283 'IN_ATTRIB' : 0x00000004,
284 'IN_CLOSE_WRITE' : 0x00000008,
285 'IN_CLOSE_NOWRITE' : 0x00000010,
286 'IN_OPEN' : 0x00000020,
287 'IN_MOVED_FROM' : 0x00000040,
288 'IN_MOVED_TO' : 0x00000080,
289 'IN_CREATE' : 0x00000100,
290 'IN_DELETE' : 0x00000200,
291 'IN_DELETE_SELF' : 0x00000400,
292
293 'IN_MOVE_SELF' : 0x00000800,
294 },
295 'EVENT_FLAGS': {
296 'IN_UNMOUNT' : 0x00002000,
297 'IN_Q_OVERFLOW' : 0x00004000,
298 'IN_IGNORED' : 0x00008000,
299 },
300 'SPECIAL_FLAGS': {
301 'IN_ONLYDIR' : 0x01000000,
302
303 'IN_DONT_FOLLOW' : 0x02000000,
304 'IN_MASK_ADD' : 0x20000000,
305
306 'IN_ISDIR' : 0x40000000,
307 'IN_ONESHOT' : 0x80000000,
308 },
309 }
310
312 """
313 Returns the event name associated to mask. IN_ISDIR is appended to
314 the result when appropriate. Note: only one event is returned, because
315 only one event can be raised at a given time.
316
317 @param mask: mask.
318 @type mask: int
319 @return: event name.
320 @rtype: str
321 """
322 ms = mask
323 name = '%s'
324 if mask & IN_ISDIR:
325 ms = mask - IN_ISDIR
326 name = '%s|IN_ISDIR'
327 return name % EventsCodes.ALL_VALUES[ms]
328
329 maskname = staticmethod(maskname)
330
331
332
333 EventsCodes.ALL_FLAGS = {}
334 EventsCodes.ALL_VALUES = {}
335 for flagc, valc in EventsCodes.FLAG_COLLECTIONS.items():
336
337
338 setattr(EventsCodes, flagc, valc)
339
340
341 EventsCodes.ALL_FLAGS.update(valc)
342
343
344
345 for name, val in valc.items():
346 globals()[name] = val
347 EventsCodes.ALL_VALUES[val] = name
348
349
350
351 ALL_EVENTS = reduce(lambda x, y: x | y, EventsCodes.OP_FLAGS.values())
352 EventsCodes.ALL_FLAGS['ALL_EVENTS'] = ALL_EVENTS
353 EventsCodes.ALL_VALUES[ALL_EVENTS] = 'ALL_EVENTS'
357 """
358 Event structure, represent events raised by the system. This
359 is the base class and should be subclassed.
360
361 """
363 """
364 Attach attributes (contained in dict_) to self.
365
366 @param dict_: Set of attributes.
367 @type dict_: dictionary
368 """
369 for tpl in dict_.items():
370 setattr(self, *tpl)
371
394
397 """
398 Raw event, it contains only the informations provided by the system.
399 It doesn't infer anything.
400 """
401 - def __init__(self, wd, mask, cookie, name):
402 """
403 @param wd: Watch Descriptor.
404 @type wd: int
405 @param mask: Bitmask of events.
406 @type mask: int
407 @param cookie: Cookie.
408 @type cookie: int
409 @param name: Basename of the file or directory against which the
410 event was raised in case where the watched directory
411 is the parent directory. None if the event was raised
412 on the watched item itself.
413 @type name: string or None
414 """
415
416 _Event.__init__(self, {'wd': wd,
417 'mask': mask,
418 'cookie': cookie,
419 'name': name.rstrip('\0')})
420 log.debug(repr(self))
421
422 self._str = None
423
425 if self._str is None:
426 self._str = '%s %s %s %s' % (str(self.wd), str(self.mask),
427 str(self.cookie), self.name)
428 return self._str
429
432 """
433 This class contains all the useful informations about the observed
434 event. However, the presence of each field is not guaranteed and
435 depends on the type of event. In effect, some fields are irrelevant
436 for some kind of event (for example 'cookie' is meaningless for
437 IN_CREATE whereas it is mandatory for IN_MOVE_TO).
438
439 The possible fields are:
440 - wd (int): Watch Descriptor.
441 - mask (int): Mask.
442 - maskname (str): Readable event name.
443 - path (str): path of the file or directory being watched.
444 - name (str): Basename of the file or directory against which the
445 event was raised in case where the watched directory
446 is the parent directory. None if the event was raised
447 on the watched item itself. This field is always provided
448 even if the string is ''.
449 - pathname (str): Concatenation of 'path' and 'name'.
450 - src_pathname (str): Only present for IN_MOVED_TO events and only in
451 the case where IN_MOVED_FROM events are watched too. Holds the
452 source pathname from where pathname was moved from.
453 - cookie (int): Cookie.
454 - dir (bool): True if the event was raised against a directory.
455
456 """
458 """
459 Concretely, this is the raw event plus inferred infos.
460 """
461 _Event.__init__(self, raw)
462 self.maskname = EventsCodes.maskname(self.mask)
463 if COMPATIBILITY_MODE:
464 self.event_name = self.maskname
465 try:
466 if self.name:
467 self.pathname = os.path.abspath(os.path.join(self.path,
468 self.name))
469 else:
470 self.pathname = os.path.abspath(self.path)
471 except AttributeError as err:
472
473
474 log.debug(err)
475
478 """
479 ProcessEventError Exception. Raised on ProcessEvent error.
480 """
482 """
483 @param err: Exception error description.
484 @type err: string
485 """
486 PyinotifyError.__init__(self, err)
487
490 """
491 Abstract processing event class.
492 """
494 """
495 To behave like a functor the object must be callable.
496 This method is a dispatch method. Its lookup order is:
497 1. process_MASKNAME method
498 2. process_FAMILY_NAME method
499 3. otherwise calls process_default
500
501 @param event: Event to be processed.
502 @type event: Event object
503 @return: By convention when used from the ProcessEvent class:
504 - Returning False or None (default value) means keep on
505 executing next chained functors (see chain.py example).
506 - Returning True instead means do not execute next
507 processing functions.
508 @rtype: bool
509 @raise ProcessEventError: Event object undispatchable,
510 unknown event.
511 """
512 stripped_mask = event.mask - (event.mask & IN_ISDIR)
513 maskname = EventsCodes.ALL_VALUES.get(stripped_mask)
514 if maskname is None:
515 raise ProcessEventError("Unknown mask 0x%08x" % stripped_mask)
516
517
518 meth = getattr(self, 'process_' + maskname, None)
519 if meth is not None:
520 return meth(event)
521
522 meth = getattr(self, 'process_IN_' + maskname.split('_')[1], None)
523 if meth is not None:
524 return meth(event)
525
526 return self.process_default(event)
527
529 return '<%s>' % self.__class__.__name__
530
533 """
534 There is three kind of processing according to each event:
535
536 1. special handling (deletion from internal container, bug, ...).
537 2. default treatment: which is applied to the majority of events.
538 3. IN_ISDIR is never sent alone, he is piggybacked with a standard
539 event, he is not processed as the others events, instead, its
540 value is captured and appropriately aggregated to dst event.
541 """
543 """
544
545 @param wm: Watch Manager.
546 @type wm: WatchManager instance
547 @param notifier: Notifier.
548 @type notifier: Notifier instance
549 """
550 self._watch_manager = wm
551 self._notifier = notifier
552 self._mv_cookie = {}
553 self._mv = {}
554
556 """
557 Cleanup (delete) old (>1mn) records contained in self._mv_cookie
558 and self._mv.
559 """
560 date_cur_ = datetime.now()
561 for seq in (self._mv_cookie, self._mv):
562 for k in list(seq.keys()):
563 if (date_cur_ - seq[k][1]) > timedelta(minutes=1):
564 log.debug('Cleanup: deleting entry %s', seq[k][0])
565 del seq[k]
566
568 """
569 If the event affects a directory and the auto_add flag of the
570 targetted watch is set to True, a new watch is added on this
571 new directory, with the same attribute values than those of
572 this watch.
573 """
574 if raw_event.mask & IN_ISDIR:
575 watch_ = self._watch_manager.get_watch(raw_event.wd)
576 created_dir = os.path.join(watch_.path, raw_event.name)
577 if watch_.auto_add and not watch_.exclude_filter(created_dir):
578 addw = self._watch_manager.add_watch
579
580
581 addw_ret = addw(created_dir, watch_.mask,
582 proc_fun=watch_.proc_fun,
583 rec=False, auto_add=watch_.auto_add,
584 exclude_filter=watch_.exclude_filter)
585
586
587
588
589
590 created_dir_wd = addw_ret.get(created_dir)
591 if (created_dir_wd is not None) and created_dir_wd > 0:
592 for name in os.listdir(created_dir):
593 inner = os.path.join(created_dir, name)
594 if (os.path.isdir(inner) and
595 self._watch_manager.get_wd(inner) is None):
596
597
598 rawevent = _RawEvent(created_dir_wd,
599 IN_CREATE | IN_ISDIR,
600 0, name)
601 self._notifier.append_event(rawevent)
602 return self.process_default(raw_event)
603
605 """
606 Map the cookie with the source path (+ date for cleaning).
607 """
608 watch_ = self._watch_manager.get_watch(raw_event.wd)
609 path_ = watch_.path
610 src_path = os.path.normpath(os.path.join(path_, raw_event.name))
611 self._mv_cookie[raw_event.cookie] = (src_path, datetime.now())
612 return self.process_default(raw_event, {'cookie': raw_event.cookie})
613
615 """
616 Map the source path with the destination path (+ date for
617 cleaning).
618 """
619 watch_ = self._watch_manager.get_watch(raw_event.wd)
620 path_ = watch_.path
621 dst_path = os.path.normpath(os.path.join(path_, raw_event.name))
622 mv_ = self._mv_cookie.get(raw_event.cookie)
623 to_append = {'cookie': raw_event.cookie}
624 if mv_ is not None:
625 self._mv[mv_[0]] = (dst_path, datetime.now())
626
627
628
629
630
631 to_append['src_pathname'] = mv_[0]
632 elif (raw_event.mask & IN_ISDIR and watch_.auto_add and
633 not watch_.exclude_filter(dst_path)):
634
635
636
637
638 self._watch_manager.add_watch(dst_path, watch_.mask,
639 proc_fun=watch_.proc_fun,
640 rec=True, auto_add=True,
641 exclude_filter=watch_.exclude_filter)
642 return self.process_default(raw_event, to_append)
643
645 """
646 STATUS: the following bug has been fixed in recent kernels (FIXME:
647 which version ?). Now it raises IN_DELETE_SELF instead.
648
649 Old kernels were bugged, this event raised when the watched item
650 were moved, so we had to update its path, but under some circumstances
651 it was impossible: if its parent directory and its destination
652 directory wasn't watched. The kernel (see include/linux/fsnotify.h)
653 doesn't bring us enough informations like the destination path of
654 moved items.
655 """
656 watch_ = self._watch_manager.get_watch(raw_event.wd)
657 src_path = watch_.path
658 mv_ = self._mv.get(src_path)
659 if mv_:
660 dest_path = mv_[0]
661 watch_.path = dest_path
662
663
664 src_path += os.path.sep
665 src_path_len = len(src_path)
666
667
668
669 for w in self._watch_manager.watches.values():
670 if w.path.startswith(src_path):
671
672 w.path = os.path.join(dest_path, w.path[src_path_len:])
673 else:
674 log.error("The pathname '%s' of this watch %s has probably changed "
675 "and couldn't be updated, so it cannot be trusted "
676 "anymore. To fix this error move directories/files only "
677 "between watched parents directories, in this case e.g. "
678 "put a watch on '%s'.",
679 watch_.path, watch_,
680 os.path.normpath(os.path.join(watch_.path,
681 os.path.pardir)))
682 if not watch_.path.endswith('-unknown-path'):
683 watch_.path += '-unknown-path'
684 return self.process_default(raw_event)
685
687 """
688 Only signal an overflow, most of the common flags are irrelevant
689 for this event (path, wd, name).
690 """
691 return Event({'mask': raw_event.mask})
692
694 """
695 The watch descriptor raised by this event is now ignored (forever),
696 it can be safely deleted from the watch manager dictionary.
697 After this event we can be sure that neither the event queue nor
698 the system will raise an event associated to this wd again.
699 """
700 event_ = self.process_default(raw_event)
701 self._watch_manager.del_watch(raw_event.wd)
702 return event_
703
705 """
706 Commons handling for the followings events:
707
708 IN_ACCESS, IN_MODIFY, IN_ATTRIB, IN_CLOSE_WRITE, IN_CLOSE_NOWRITE,
709 IN_OPEN, IN_DELETE, IN_DELETE_SELF, IN_UNMOUNT.
710 """
711 watch_ = self._watch_manager.get_watch(raw_event.wd)
712 if raw_event.mask & (IN_DELETE_SELF | IN_MOVE_SELF):
713
714 dir_ = watch_.dir
715 else:
716 dir_ = bool(raw_event.mask & IN_ISDIR)
717 dict_ = {'wd': raw_event.wd,
718 'mask': raw_event.mask,
719 'path': watch_.path,
720 'name': raw_event.name,
721 'dir': dir_}
722 if COMPATIBILITY_MODE:
723 dict_['is_dir'] = dir_
724 if to_append is not None:
725 dict_.update(to_append)
726 return Event(dict_)
727
730 """
731 Process events objects, can be specialized via subclassing, thus its
732 behavior can be overriden:
733
734 Note: you should not override __init__ in your subclass instead define
735 a my_init() method, this method will be called automatically from the
736 constructor of this class with its optionals parameters.
737
738 1. Provide specialized individual methods, e.g. process_IN_DELETE for
739 processing a precise type of event (e.g. IN_DELETE in this case).
740 2. Or/and provide methods for processing events by 'family', e.g.
741 process_IN_CLOSE method will process both IN_CLOSE_WRITE and
742 IN_CLOSE_NOWRITE events (if process_IN_CLOSE_WRITE and
743 process_IN_CLOSE_NOWRITE aren't defined though).
744 3. Or/and override process_default for catching and processing all
745 the remaining types of events.
746 """
747 pevent = None
748
749 - def __init__(self, pevent=None, **kargs):
750 """
751 Enable chaining of ProcessEvent instances.
752
753 @param pevent: Optional callable object, will be called on event
754 processing (before self).
755 @type pevent: callable
756 @param kargs: This constructor is implemented as a template method
757 delegating its optionals keyworded arguments to the
758 method my_init().
759 @type kargs: dict
760 """
761 self.pevent = pevent
762 self.my_init(**kargs)
763
765 """
766 This method is called from ProcessEvent.__init__(). This method is
767 empty here and must be redefined to be useful. In effect, if you
768 need to specifically initialize your subclass' instance then you
769 just have to override this method in your subclass. Then all the
770 keyworded arguments passed to ProcessEvent.__init__() will be
771 transmitted as parameters to this method. Beware you MUST pass
772 keyword arguments though.
773
774 @param kargs: optional delegated arguments from __init__().
775 @type kargs: dict
776 """
777 pass
778
780 stop_chaining = False
781 if self.pevent is not None:
782
783
784
785
786
787 stop_chaining = self.pevent(event)
788 if not stop_chaining:
789 return _ProcessEvent.__call__(self, event)
790
793
795 """
796 By default this method only reports warning messages, you can overredide
797 it by subclassing ProcessEvent and implement your own
798 process_IN_Q_OVERFLOW method. The actions you can take on receiving this
799 event is either to update the variable max_queued_events in order to
800 handle more simultaneous events or to modify your code in order to
801 accomplish a better filtering diminishing the number of raised events.
802 Because this method is defined, IN_Q_OVERFLOW will never get
803 transmitted as arguments to process_default calls.
804
805 @param event: IN_Q_OVERFLOW event.
806 @type event: dict
807 """
808 log.warning('Event queue overflowed.')
809
811 """
812 Default processing event method. By default does nothing. Subclass
813 ProcessEvent and redefine this method in order to modify its behavior.
814
815 @param event: Event to be processed. Can be of any type of events but
816 IN_Q_OVERFLOW events (see method process_IN_Q_OVERFLOW).
817 @type event: Event instance
818 """
819 pass
820
823 """
824 Dummy class used to print events strings representations. For instance this
825 class is used from command line to print all received events to stdout.
826 """
828 """
829 @param out: Where events will be written.
830 @type out: Object providing a valid file object interface.
831 """
832 if out is None:
833 out = sys.stdout
834 self._out = out
835
837 """
838 Writes event string representation to file object provided to
839 my_init().
840
841 @param event: Event to be processed. Can be of any type of events but
842 IN_Q_OVERFLOW events (see method process_IN_Q_OVERFLOW).
843 @type event: Event instance
844 """
845 self._out.write(repr(event))
846 self._out.write('\n')
847
850 """
851 Makes conditional chaining depending on the result of the nested
852 processing instance.
853 """
855 """
856 Method automatically called from base class constructor.
857 """
858 self._func = func
859
861 return not self._func(event)
862
863
864 -class Stats(ProcessEvent):
865 """
866 Compute and display trivial statistics about processed events.
867 """
869 """
870 Method automatically called from base class constructor.
871 """
872 self._start_time = time.time()
873 self._stats = {}
874 self._stats_lock = threading.Lock()
875
877 """
878 Processes |event|.
879 """
880 self._stats_lock.acquire()
881 try:
882 events = event.maskname.split('|')
883 for event_name in events:
884 count = self._stats.get(event_name, 0)
885 self._stats[event_name] = count + 1
886 finally:
887 self._stats_lock.release()
888
890 self._stats_lock.acquire()
891 try:
892 return self._stats.copy()
893 finally:
894 self._stats_lock.release()
895
897 stats = self._stats_copy()
898
899 elapsed = int(time.time() - self._start_time)
900 elapsed_str = ''
901 if elapsed < 60:
902 elapsed_str = str(elapsed) + 'sec'
903 elif 60 <= elapsed < 3600:
904 elapsed_str = '%dmn%dsec' % (elapsed / 60, elapsed % 60)
905 elif 3600 <= elapsed < 86400:
906 elapsed_str = '%dh%dmn' % (elapsed / 3600, (elapsed % 3600) / 60)
907 elif elapsed >= 86400:
908 elapsed_str = '%dd%dh' % (elapsed / 86400, (elapsed % 86400) / 3600)
909 stats['ElapsedTime'] = elapsed_str
910
911 l = []
912 for ev, value in sorted(stats.items(), key=lambda x: x[0]):
913 l.append(' %s=%s' % (Color.field_name(ev),
914 Color.field_value(value)))
915 s = '<%s%s >' % (Color.class_name(self.__class__.__name__),
916 ''.join(l))
917 return s
918
919 - def dump(self, filename):
920 """
921 Dumps statistics to file |filename|.
922
923 @param filename: pathname.
924 @type filename: string
925 """
926 with open(filename, 'w') as file_obj:
927 file_obj.write(str(self))
928
942 s = '\n'.join(map(func, sorted(stats.items(), key=lambda x: x[0])))
943 return s
944
947 """
948 Notifier Exception. Raised on Notifier error.
949
950 """
952 """
953 @param err: Exception string's description.
954 @type err: string
955 """
956 PyinotifyError.__init__(self, err)
957
960 """
961 Read notifications, process events.
962
963 """
964 - def __init__(self, watch_manager, default_proc_fun=None, read_freq=0,
965 threshold=0, timeout=None):
966 """
967 Initialization. read_freq, threshold and timeout parameters are used
968 when looping.
969
970 @param watch_manager: Watch Manager.
971 @type watch_manager: WatchManager instance
972 @param default_proc_fun: Default processing method. If None, a new
973 instance of PrintAllEvents will be assigned.
974 @type default_proc_fun: instance of ProcessEvent
975 @param read_freq: if read_freq == 0, events are read asap,
976 if read_freq is > 0, this thread sleeps
977 max(0, read_freq - timeout) seconds. But if
978 timeout is None it may be different because
979 poll is blocking waiting for something to read.
980 @type read_freq: int
981 @param threshold: File descriptor will be read only if the accumulated
982 size to read becomes >= threshold. If != 0, you likely
983 want to use it in combination with an appropriate
984 value for read_freq because without that you would
985 keep looping without really reading anything and that
986 until the amount of events to read is >= threshold.
987 At least with read_freq set you might sleep.
988 @type threshold: int
989 @param timeout:
990 http://docs.python.org/lib/poll-objects.html#poll-objects
991 @type timeout: int
992 """
993
994 self._watch_manager = watch_manager
995
996 self._fd = self._watch_manager.get_fd()
997
998 self._pollobj = select.poll()
999 self._pollobj.register(self._fd, select.POLLIN)
1000
1001 self._pipe = (-1, -1)
1002
1003 self._eventq = deque()
1004
1005 self._sys_proc_fun = _SysProcessEvent(self._watch_manager, self)
1006
1007 self._default_proc_fun = default_proc_fun
1008 if default_proc_fun is None:
1009 self._default_proc_fun = PrintAllEvents()
1010
1011 self._read_freq = read_freq
1012 self._threshold = threshold
1013 self._timeout = timeout
1014
1015 self._coalesce = False
1016
1017 self._eventset = set()
1018
1020 """
1021 Append a raw event to the event queue.
1022
1023 @param event: An event.
1024 @type event: _RawEvent instance.
1025 """
1026 self._eventq.append(event)
1027
1029 return self._default_proc_fun
1030
1032 """
1033 Coalescing events. Events are usually processed by batchs, their size
1034 depend on various factors. Thus, before processing them, events received
1035 from inotify are aggregated in a fifo queue. If this coalescing
1036 option is enabled events are filtered based on their unicity, only
1037 unique events are enqueued, doublons are discarded. An event is unique
1038 when the combination of its fields (wd, mask, cookie, name) is unique
1039 among events of a same batch. After a batch of events is processed any
1040 events is accepted again. By default this option is disabled, you have
1041 to explictly call this function to turn it on.
1042
1043 @param coalesce: Optional new coalescing value. True by default.
1044 @type coalesce: Bool
1045 """
1046 self._coalesce = coalesce
1047 if not coalesce:
1048 self._eventset.clear()
1049
1051 """
1052 Check for new events available to read, blocks up to timeout
1053 milliseconds.
1054
1055 @param timeout: If specified it overrides the corresponding instance
1056 attribute _timeout.
1057 @type timeout: int
1058
1059 @return: New events to read.
1060 @rtype: bool
1061 """
1062 while True:
1063 try:
1064
1065 if timeout is None:
1066 timeout = self._timeout
1067 ret = self._pollobj.poll(timeout)
1068 except select.error as err:
1069 if err.errno == errno.EINTR:
1070 continue
1071 else:
1072 raise
1073 else:
1074 break
1075
1076 if not ret or (self._pipe[0] == ret[0][0]):
1077 return False
1078
1079 return ret[0][1] & select.POLLIN
1080
1082 """
1083 Read events from device, build _RawEvents, and enqueue them.
1084 """
1085 buf_ = array.array('i', [0])
1086
1087 if fcntl.ioctl(self._fd, termios.FIONREAD, buf_, 1) == -1:
1088 return
1089 queue_size = buf_[0]
1090 if queue_size < self._threshold:
1091 log.debug('(fd: %d) %d bytes available to read but threshold is '
1092 'fixed to %d bytes', self._fd, queue_size,
1093 self._threshold)
1094 return
1095
1096 try:
1097
1098 r = os.read(self._fd, queue_size)
1099 except Exception as msg:
1100 raise NotifierError(msg)
1101 log.debug('Event queue size: %d', queue_size)
1102 rsum = 0
1103 while rsum < queue_size:
1104 s_size = 16
1105
1106 wd, mask, cookie, fname_len = struct.unpack('iIII',
1107 r[rsum:rsum+s_size])
1108
1109 bname, = struct.unpack('%ds' % fname_len,
1110 r[rsum + s_size:rsum + s_size + fname_len])
1111
1112 uname = bname.decode()
1113 rawevent = _RawEvent(wd, mask, cookie, uname)
1114 if self._coalesce:
1115
1116 raweventstr = str(rawevent)
1117 if raweventstr not in self._eventset:
1118 self._eventset.add(raweventstr)
1119 self._eventq.append(rawevent)
1120 else:
1121 self._eventq.append(rawevent)
1122 rsum += s_size + fname_len
1123
1125 """
1126 Routine for processing events from queue by calling their
1127 associated proccessing method (an instance of ProcessEvent).
1128 It also does internal processings, to keep the system updated.
1129 """
1130 while self._eventq:
1131 raw_event = self._eventq.popleft()
1132 watch_ = self._watch_manager.get_watch(raw_event.wd)
1133 if watch_ is None:
1134
1135
1136
1137 log.warning("Unable to retrieve Watch object associated to %s",
1138 repr(raw_event))
1139 continue
1140 revent = self._sys_proc_fun(raw_event)
1141 if watch_ and watch_.proc_fun:
1142 watch_.proc_fun(revent)
1143 else:
1144 self._default_proc_fun(revent)
1145 self._sys_proc_fun.cleanup()
1146 if self._coalesce:
1147 self._eventset.clear()
1148
1149 - def __daemonize(self, pid_file=None, force_kill=False, stdin=os.devnull,
1150 stdout=os.devnull, stderr=os.devnull):
1151 """
1152 pid_file: file to which the pid will be written.
1153 force_kill: if True kill the process associated to pid_file.
1154 stdin, stdout, stderr: files associated to common streams.
1155 """
1156 if pid_file is None:
1157 dirname = '/var/run/'
1158 basename = os.path.basename(sys.argv[0]) or 'pyinotify'
1159 pid_file = os.path.join(dirname, basename + '.pid')
1160
1161 if os.path.exists(pid_file):
1162 with open(pid_file, 'r') as fo:
1163 try:
1164 pid = int(fo.read())
1165 except ValueError:
1166 pid = None
1167 if pid is not None:
1168 try:
1169 os.kill(pid, 0)
1170 except OSError as err:
1171 if err.errno == errno.ESRCH:
1172 log.debug(err)
1173 else:
1174 log.error(err)
1175 else:
1176 if not force_kill:
1177 s = 'There is already a pid file %s with pid %d'
1178 raise NotifierError(s % (pid_file, pid))
1179 else:
1180 os.kill(pid, 9)
1181
1182
1183 def fork_daemon():
1184
1185
1186 pid = os.fork()
1187 if (pid == 0):
1188
1189 os.setsid()
1190 pid = os.fork()
1191 if (pid == 0):
1192
1193 os.chdir('/')
1194 os.umask(0)
1195 else:
1196
1197 os._exit(0)
1198 else:
1199
1200 os._exit(0)
1201
1202 fd_inp = open(stdin, 'r')
1203 os.dup2(fd_inp.fileno(), 0)
1204 fd_out = open(stdout, 'w')
1205 os.dup2(fd_out.fileno(), 1)
1206 fd_err = open(stderr, 'w')
1207 os.dup2(fd_err.fileno(), 2)
1208
1209
1210 fork_daemon()
1211
1212
1213 with open(pid_file, 'w') as file_obj:
1214 file_obj.write(str(os.getpid()) + '\n')
1215
1216 atexit.register(lambda : os.unlink(pid_file))
1217
1218
1220
1221 if self._read_freq > 0:
1222 cur_time = time.time()
1223 sleep_amount = self._read_freq - (cur_time - ref_time)
1224 if sleep_amount > 0:
1225 log.debug('Now sleeping %d seconds', sleep_amount)
1226 time.sleep(sleep_amount)
1227
1228
1229 - def loop(self, callback=None, daemonize=False, **args):
1230 """
1231 Events are read only one time every min(read_freq, timeout)
1232 seconds at best and only if the size to read is >= threshold.
1233 After this method returns it must not be called again for the same
1234 instance.
1235
1236 @param callback: Functor called after each event processing iteration.
1237 Expects to receive the notifier object (self) as first
1238 parameter. If this function returns True the loop is
1239 immediately terminated otherwise the loop method keeps
1240 looping.
1241 @type callback: callable object or function
1242 @param daemonize: This thread is daemonized if set to True.
1243 @type daemonize: boolean
1244 @param args: Optional and relevant only if daemonize is True. Remaining
1245 keyworded arguments are directly passed to daemonize see
1246 __daemonize() method.
1247 @type args: various
1248 """
1249 if daemonize:
1250 self.__daemonize(**args)
1251
1252
1253 while 1:
1254 try:
1255 self.process_events()
1256 if (callback is not None) and (callback(self) is True):
1257 break
1258 ref_time = time.time()
1259
1260 if self.check_events():
1261 self._sleep(ref_time)
1262 self.read_events()
1263 except KeyboardInterrupt:
1264
1265 log.debug('Pyinotify stops monitoring.')
1266 break
1267
1268 self.stop()
1269
1270
1272 """
1273 Close inotify's instance (close its file descriptor).
1274 It destroys all existing watches, pending events,...
1275 This method is automatically called at the end of loop().
1276 """
1277 self._pollobj.unregister(self._fd)
1278 os.close(self._fd)
1279
1282 """
1283 This notifier inherits from threading.Thread for instanciating a separate
1284 thread, and also inherits from Notifier, because it is a threaded notifier.
1285
1286 Note that every functionality provided by this class is also provided
1287 through Notifier class. Moreover Notifier should be considered first because
1288 it is not threaded and could be easily daemonized.
1289 """
1290 - def __init__(self, watch_manager, default_proc_fun=None, read_freq=0,
1291 threshold=0, timeout=None):
1292 """
1293 Initialization, initialize base classes. read_freq, threshold and
1294 timeout parameters are used when looping.
1295
1296 @param watch_manager: Watch Manager.
1297 @type watch_manager: WatchManager instance
1298 @param default_proc_fun: Default processing method. See base class.
1299 @type default_proc_fun: instance of ProcessEvent
1300 @param read_freq: if read_freq == 0, events are read asap,
1301 if read_freq is > 0, this thread sleeps
1302 max(0, read_freq - timeout) seconds.
1303 @type read_freq: int
1304 @param threshold: File descriptor will be read only if the accumulated
1305 size to read becomes >= threshold. If != 0, you likely
1306 want to use it in combination with an appropriate
1307 value set for read_freq because without that you would
1308 keep looping without really reading anything and that
1309 until the amount of events to read is >= threshold. At
1310 least with read_freq you might sleep.
1311 @type threshold: int
1312 @param timeout:
1313 see http://docs.python.org/lib/poll-objects.html#poll-objects
1314 @type timeout: int
1315 """
1316
1317 threading.Thread.__init__(self)
1318
1319 self._stop_event = threading.Event()
1320
1321 Notifier.__init__(self, watch_manager, default_proc_fun, read_freq,
1322 threshold, timeout)
1323
1324 self._pipe = os.pipe()
1325 self._pollobj.register(self._pipe[0], select.POLLIN)
1326
1328 """
1329 Stop notifier's loop. Stop notification. Join the thread.
1330 """
1331 self._stop_event.set()
1332 os.write(self._pipe[1], b'stop')
1333 threading.Thread.join(self)
1334 Notifier.stop(self)
1335 self._pollobj.unregister(self._pipe[0])
1336 os.close(self._pipe[0])
1337 os.close(self._pipe[1])
1338
1340 """
1341 Thread's main loop. Don't meant to be called by user directly.
1342 Call inherited start() method instead.
1343
1344 Events are read only once time every min(read_freq, timeout)
1345 seconds at best and only if the size of events to read is >= threshold.
1346 """
1347
1348
1349
1350
1351 while not self._stop_event.isSet():
1352 self.process_events()
1353 ref_time = time.time()
1354 if self.check_events():
1355 self._sleep(ref_time)
1356 self.read_events()
1357
1359 """
1360 Start thread's loop: read and process events until the method
1361 stop() is called.
1362 Never call this method directly, instead call the start() method
1363 inherited from threading.Thread, which then will call run() in
1364 its turn.
1365 """
1366 self.loop()
1367
1370 """
1371 This notifier inherits from asyncore.file_dispatcher in order to be able to
1372 use pyinotify along with the asyncore framework.
1373
1374 """
1375 - def __init__(self, watch_manager, default_proc_fun=None, read_freq=0,
1376 threshold=0, timeout=None, channel_map=None):
1377 """
1378 Initializes the async notifier. The only additional parameter is
1379 'channel_map' which is the optional asyncore private map. See
1380 Notifier class for the meaning of the others parameters.
1381
1382 """
1383 Notifier.__init__(self, watch_manager, default_proc_fun, read_freq,
1384 threshold, timeout)
1385 asyncore.file_dispatcher.__init__(self, self._fd, channel_map)
1386
1388 """
1389 When asyncore tells us we can read from the fd, we proceed processing
1390 events. This method can be overridden for handling a notification
1391 differently.
1392
1393 """
1394 self.read_events()
1395 self.process_events()
1396
1399 """
1400 Represent a watch, i.e. a file or directory being watched.
1401
1402 """
1403 - def __init__(self, wd, path, mask, proc_fun, auto_add, exclude_filter):
1404 """
1405 Initializations.
1406
1407 @param wd: Watch descriptor.
1408 @type wd: int
1409 @param path: Path of the file or directory being watched.
1410 @type path: str
1411 @param mask: Mask.
1412 @type mask: int
1413 @param proc_fun: Processing callable object.
1414 @type proc_fun:
1415 @param auto_add: Automatically add watches on new directories.
1416 @type auto_add: bool
1417 @param exclude_filter: Boolean function, used to exclude new
1418 directories from being automatically watched.
1419 See WatchManager.__init__
1420 @type exclude_filter: callable object
1421 """
1422 self.wd = wd
1423 self.path = path
1424 self.mask = mask
1425 self.proc_fun = proc_fun
1426 self.auto_add = auto_add
1427 self.exclude_filter = exclude_filter
1428 self.dir = os.path.isdir(self.path)
1429
1445
1448 """
1449 ExcludeFilter is an exclusion filter.
1450 """
1452 """
1453 Examples:
1454 ef1 = ExcludeFilter(["^/etc/rc.*", "^/etc/hostname"])
1455 ef2 = ExcludeFilter("/my/path/exclude.lst")
1456 Where exclude.lst contains:
1457 ^/etc/rc.*
1458 ^/etc/hostname
1459
1460 @param arg_lst: is either a list of patterns or a filename from which
1461 patterns will be loaded.
1462 @type arg_lst: list of str or str
1463 """
1464 if isinstance(arg_lst, str):
1465 lst = self._load_patterns_from_file(arg_lst)
1466 elif isinstance(arg_lst, list):
1467 lst = arg_lst
1468 else:
1469 raise TypeError
1470
1471 self._lregex = []
1472 for regex in lst:
1473 self._lregex.append(re.compile(regex, re.UNICODE))
1474
1476 lst = []
1477 with open(filename, 'r') as file_obj:
1478 for line in file_obj.readlines():
1479
1480 pattern = line.strip()
1481 if not pattern or pattern.startswith('#'):
1482 continue
1483 lst.append(pattern)
1484 return lst
1485
1486 - def _match(self, regex, path):
1487 return regex.match(path) is not None
1488
1490 """
1491 @param path: Path to match against provided regexps.
1492 @type path: str
1493 @return: Return True if path has been matched and should
1494 be excluded, False otherwise.
1495 @rtype: bool
1496 """
1497 for regex in self._lregex:
1498 if self._match(regex, path):
1499 return True
1500 return False
1501
1504 """
1505 WatchManager Exception. Raised on error encountered on watches
1506 operations.
1507
1508 """
1510 """
1511 @param msg: Exception string's description.
1512 @type msg: string
1513 @param wmd: This dictionary contains the wd assigned to paths of the
1514 same call for which watches were successfully added.
1515 @type wmd: dict
1516 """
1517 self.wmd = wmd
1518 Exception.__init__(self, msg)
1519
1522 """
1523 Provide operations for watching files and directories. Its internal
1524 dictionary is used to reference watched items. When used inside
1525 threaded code, one must instanciate as many WatchManager instances as
1526 there are ThreadedNotifier instances.
1527
1528 """
1529 - def __init__(self, exclude_filter=lambda path: False):
1530 """
1531 Initialization: init inotify, init watch manager dictionary.
1532 Raise OSError if initialization fails.
1533
1534 @param exclude_filter: boolean function, returns True if current
1535 path must be excluded from being watched.
1536 Convenient for providing a common exclusion
1537 filter for every call to add_watch.
1538 @type exclude_filter: callable object
1539 """
1540 self._exclude_filter = exclude_filter
1541 self._wmd = {}
1542 self._fd = LIBC.inotify_init()
1543 if self._fd < 0:
1544 err = 'Cannot initialize new instance of inotify Errno=%s'
1545 raise OSError(err % STRERRNO())
1546
1548 """
1549 Return assigned inotify's file descriptor.
1550
1551 @return: File descriptor.
1552 @rtype: int
1553 """
1554 return self._fd
1555
1557 """
1558 Get watch from provided watch descriptor wd.
1559
1560 @param wd: Watch descriptor.
1561 @type wd: int
1562 """
1563 return self._wmd.get(wd)
1564
1566 """
1567 Remove watch entry associated to watch descriptor wd.
1568
1569 @param wd: Watch descriptor.
1570 @type wd: int
1571 """
1572 try:
1573 del self._wmd[wd]
1574 except KeyError as err:
1575 log.error(str(err))
1576
1577 @property
1579 """
1580 Get a reference on the internal watch manager dictionary.
1581
1582 @return: Internal watch manager dictionary.
1583 @rtype: dict
1584 """
1585 return self._wmd
1586
1593
1594 - def __add_watch(self, path, mask, proc_fun, auto_add, exclude_filter):
1595 """
1596 Add a watch on path, build a Watch object and insert it in the
1597 watch manager dictionary. Return the wd value.
1598 """
1599 path = self.__format_path(path)
1600
1601
1602
1603
1604
1605
1606
1607 byte_path = path.encode(sys.getfilesystemencoding())
1608 wd_ = LIBC.inotify_add_watch(self._fd,
1609 ctypes.create_string_buffer(byte_path),
1610 mask)
1611 if wd_ < 0:
1612 return wd_
1613 watch_ = Watch(wd=wd_, path=path, mask=mask, proc_fun=proc_fun,
1614 auto_add=auto_add, exclude_filter=exclude_filter)
1615 self._wmd[wd_] = watch_
1616 log.debug('New %s', watch_)
1617 return wd_
1618
1619 - def __glob(self, path, do_glob):
1620 if do_glob:
1621 return glob.iglob(path)
1622 else:
1623 return [path]
1624
1625 - def add_watch(self, path, mask, proc_fun=None, rec=False,
1626 auto_add=False, do_glob=False, quiet=True,
1627 exclude_filter=None):
1628 """
1629 Add watch(s) on the provided |path|(s) with associated |mask| flag
1630 value and optionally with a processing |proc_fun| function and
1631 recursive flag |rec| set to True.
1632 All |path| components _must_ be str (i.e. unicode) objects.
1633 If |path| is already watched it is ignored, but if it is called with
1634 option rec=True a watch is put on each one of its not-watched
1635 subdirectory.
1636
1637 @param path: Path to watch, the path can either be a file or a
1638 directory. Also accepts a sequence (list) of paths.
1639 @type path: string or list of strings
1640 @param mask: Bitmask of events.
1641 @type mask: int
1642 @param proc_fun: Processing object.
1643 @type proc_fun: function or ProcessEvent instance or instance of
1644 one of its subclasses or callable object.
1645 @param rec: Recursively add watches from path on all its
1646 subdirectories, set to False by default (doesn't
1647 follows symlinks in any case).
1648 @type rec: bool
1649 @param auto_add: Automatically add watches on newly created
1650 directories in watched parent |path| directory.
1651 @type auto_add: bool
1652 @param do_glob: Do globbing on pathname (see standard globbing
1653 module for more informations).
1654 @type do_glob: bool
1655 @param quiet: if False raises a WatchManagerError exception on
1656 error. See example not_quiet.py.
1657 @type quiet: bool
1658 @param exclude_filter: predicate (boolean function), which returns
1659 True if the current path must be excluded
1660 from being watched. This argument has
1661 precedence over exclude_filter passed to
1662 the class' constructor.
1663 @type exclude_filter: callable object
1664 @return: dict of paths associated to watch descriptors. A wd value
1665 is positive if the watch was added sucessfully, otherwise
1666 the value is negative. If the path was invalid or was already
1667 watched it is not included into this returned dictionary.
1668 @rtype: dict of {str: int}
1669 """
1670 ret_ = {}
1671
1672 if exclude_filter is None:
1673 exclude_filter = self._exclude_filter
1674
1675
1676 for npath in self.__format_param(path):
1677
1678 if not isinstance(npath, str):
1679 ret_[path] = -3
1680 continue
1681
1682
1683 for apath in self.__glob(npath, do_glob):
1684
1685 for rpath in self.__walk_rec(apath, rec):
1686 if self.get_wd(rpath) is not None:
1687
1688
1689
1690 continue
1691 if not exclude_filter(rpath):
1692 wd = ret_[rpath] = self.__add_watch(rpath, mask,
1693 proc_fun,
1694 auto_add,
1695 exclude_filter)
1696 if wd < 0:
1697 err = 'add_watch: cannot watch %s WD=%d Errno=%s'
1698 err = err % (rpath, wd, STRERRNO())
1699 if quiet:
1700 log.error(err)
1701 else:
1702 raise WatchManagerError(err, ret_)
1703 else:
1704
1705
1706 ret_[rpath] = -2
1707 return ret_
1708
1710 """
1711 Get every wd from self._wmd if its path is under the path of
1712 one (at least) of those in lpath. Doesn't follow symlinks.
1713
1714 @param lpath: list of watch descriptor
1715 @type lpath: list of int
1716 @return: list of watch descriptor
1717 @rtype: list of int
1718 """
1719 for d in lpath:
1720 root = self.get_path(d)
1721 if root is not None:
1722
1723 yield d
1724 else:
1725
1726 continue
1727
1728
1729 if not os.path.isdir(root):
1730 continue
1731
1732
1733 root = os.path.normpath(root)
1734
1735 lend = len(root)
1736 for iwd in self._wmd.items():
1737 cur = iwd[1].path
1738 pref = os.path.commonprefix([root, cur])
1739 if root == os.sep or (len(pref) == lend and \
1740 len(cur) > lend and \
1741 cur[lend] == os.sep):
1742 yield iwd[1].wd
1743
1744 - def update_watch(self, wd, mask=None, proc_fun=None, rec=False,
1745 auto_add=False, quiet=True):
1746 """
1747 Update existing watch descriptors |wd|. The |mask| value, the
1748 processing object |proc_fun|, the recursive param |rec| and the
1749 |auto_add| and |quiet| flags can all be updated.
1750
1751 @param wd: Watch Descriptor to update. Also accepts a list of
1752 watch descriptors.
1753 @type wd: int or list of int
1754 @param mask: Optional new bitmask of events.
1755 @type mask: int
1756 @param proc_fun: Optional new processing function.
1757 @type proc_fun: function or ProcessEvent instance or instance of
1758 one of its subclasses or callable object.
1759 @param rec: Optionally adds watches recursively on all
1760 subdirectories contained into |wd| directory.
1761 @type rec: bool
1762 @param auto_add: Automatically adds watches on newly created
1763 directories in the watch's path corresponding to
1764 |wd|.
1765 @type auto_add: bool
1766 @param quiet: If False raises a WatchManagerError exception on
1767 error. See example not_quiet.py
1768 @type quiet: bool
1769 @return: dict of watch descriptors associated to booleans values.
1770 True if the corresponding wd has been successfully
1771 updated, False otherwise.
1772 @rtype: dict of {int: bool}
1773 """
1774 lwd = self.__format_param(wd)
1775 if rec:
1776 lwd = self.__get_sub_rec(lwd)
1777
1778 ret_ = {}
1779 for awd in lwd:
1780 apath = self.get_path(awd)
1781 if not apath or awd < 0:
1782 err = 'update_watch: invalid WD=%d' % awd
1783 if quiet:
1784 log.error(err)
1785 continue
1786 raise WatchManagerError(err, ret_)
1787
1788 if mask:
1789 addw = LIBC.inotify_add_watch
1790
1791
1792 byte_path = apath.encode(sys.getfilesystemencoding())
1793 wd_ = addw(self._fd, ctypes.create_string_buffer(byte_path),
1794 mask)
1795 if wd_ < 0:
1796 ret_[awd] = False
1797 err = 'update_watch: cannot update %s WD=%d Errno=%s'
1798 err = err % (apath, wd_, STRERRNO())
1799 if quiet:
1800 log.error(err)
1801 continue
1802 raise WatchManagerError(err, ret_)
1803
1804 assert(awd == wd_)
1805
1806 if proc_fun or auto_add:
1807 watch_ = self._wmd[awd]
1808
1809 if proc_fun:
1810 watch_.proc_fun = proc_fun
1811
1812 if auto_add:
1813 watch_.auto_add = auto_add
1814
1815 ret_[awd] = True
1816 log.debug('Updated watch - %s', self._wmd[awd])
1817 return ret_
1818
1831
1833 """
1834 Returns the watch descriptor associated to path. This method
1835 presents a prohibitive cost, always prefer to keep the WD
1836 returned by add_watch(). If the path is unknown it returns None.
1837
1838 @param path: Path.
1839 @type path: str
1840 @return: WD or None.
1841 @rtype: int or None
1842 """
1843 path = self.__format_path(path)
1844 for iwd in self._wmd.items():
1845 if iwd[1].path == path:
1846 return iwd[0]
1847
1849 """
1850 Returns the path associated to WD, if WD is unknown it returns None.
1851
1852 @param wd: Watch descriptor.
1853 @type wd: int
1854 @return: Path or None.
1855 @rtype: string or None
1856 """
1857 watch_ = self._wmd.get(wd)
1858 if watch_ is not None:
1859 return watch_.path
1860
1862 """
1863 Yields each subdirectories of top, doesn't follow symlinks.
1864 If rec is false, only yield top.
1865
1866 @param top: root directory.
1867 @type top: string
1868 @param rec: recursive flag.
1869 @type rec: bool
1870 @return: path of one subdirectory.
1871 @rtype: string
1872 """
1873 if not rec or os.path.islink(top) or not os.path.isdir(top):
1874 yield top
1875 else:
1876 for root, dirs, files in os.walk(top):
1877 yield root
1878
1879 - def rm_watch(self, wd, rec=False, quiet=True):
1880 """
1881 Removes watch(s).
1882
1883 @param wd: Watch Descriptor of the file or directory to unwatch.
1884 Also accepts a list of WDs.
1885 @type wd: int or list of int.
1886 @param rec: Recursively removes watches on every already watched
1887 subdirectories and subfiles.
1888 @type rec: bool
1889 @param quiet: If False raises a WatchManagerError exception on
1890 error. See example not_quiet.py
1891 @type quiet: bool
1892 @return: dict of watch descriptors associated to booleans values.
1893 True if the corresponding wd has been successfully
1894 removed, False otherwise.
1895 @rtype: dict of {int: bool}
1896 """
1897 lwd = self.__format_param(wd)
1898 if rec:
1899 lwd = self.__get_sub_rec(lwd)
1900
1901 ret_ = {}
1902 for awd in lwd:
1903
1904 wd_ = LIBC.inotify_rm_watch(self._fd, awd)
1905 if wd_ < 0:
1906 ret_[awd] = False
1907 err = 'rm_watch: cannot remove WD=%d Errno=%s' % (awd, STRERRNO())
1908 if quiet:
1909 log.error(err)
1910 continue
1911 raise WatchManagerError(err, ret_)
1912
1913 ret_[awd] = True
1914 log.debug('Watch WD=%d (%s) removed', awd, self.get_path(awd))
1915 return ret_
1916
1917
1919 """
1920 Watch a transient file, which will be created and deleted frequently
1921 over time (e.g. pid file).
1922
1923 @attention: Currently under the call to this function it is not
1924 possible to correctly watch the events triggered into the same
1925 base directory than the directory where is located this watched
1926 transient file. For instance it would be wrong to make these
1927 two successive calls: wm.watch_transient_file('/var/run/foo.pid', ...)
1928 and wm.add_watch('/var/run/', ...)
1929
1930 @param filename: Filename.
1931 @type filename: string
1932 @param mask: Bitmask of events, should contain IN_CREATE and IN_DELETE.
1933 @type mask: int
1934 @param proc_class: ProcessEvent (or of one of its subclass), beware of
1935 accepting a ProcessEvent's instance as argument into
1936 __init__, see transient_file.py example for more
1937 details.
1938 @type proc_class: ProcessEvent's instance or of one of its subclasses.
1939 @return: Same as add_watch().
1940 @rtype: Same as add_watch().
1941 """
1942 dirname = os.path.dirname(filename)
1943 if dirname == '':
1944 return {}
1945 basename = os.path.basename(filename)
1946
1947 mask |= IN_CREATE | IN_DELETE
1948
1949 def cmp_name(event):
1950 if getattr(event, 'name') is None:
1951 return False
1952 return basename == event.name
1953 return self.add_watch(dirname, mask,
1954 proc_fun=proc_class(ChainIfTrue(func=cmp_name)),
1955 rec=False,
1956 auto_add=False, do_glob=False,
1957 exclude_filter=lambda path: False)
1958
1961 """
1962 Internal class. Provide fancy colors used by string representations.
1963 """
1964 normal = "\033[0m"
1965 black = "\033[30m"
1966 red = "\033[31m"
1967 green = "\033[32m"
1968 yellow = "\033[33m"
1969 blue = "\033[34m"
1970 purple = "\033[35m"
1971 cyan = "\033[36m"
1972 bold = "\033[1m"
1973 uline = "\033[4m"
1974 blink = "\033[5m"
1975 invert = "\033[7m"
1976
1977 @staticmethod
1981
1982 @staticmethod
1984 """Field value color."""
1985 if not isinstance(s, str):
1986 s = str(s)
1987 return Color.purple + s + Color.normal
1988
1989 @staticmethod
1993
1994 @staticmethod
1998
1999 @staticmethod
2001 if not isinstance(s, str):
2002 s = str(s)
2003 try:
2004 color_attr = getattr(Color, color)
2005 except AttributeError:
2006 return s
2007 return color_attr + s + Color.normal
2008
2011 """
2012 Use this function to turn on the compatibility mode. The compatibility
2013 mode is used to improve compatibility with Pyinotify 0.7.1 (or older)
2014 programs. The compatibility mode provides additional variables 'is_dir',
2015 'event_name', 'EventsCodes.IN_*' and 'EventsCodes.ALL_EVENTS' as
2016 Pyinotify 0.7.1 provided. Do not call this function from new programs!!
2017 Especially if there are developped for Pyinotify >= 0.8.x.
2018 """
2019 setattr(EventsCodes, 'ALL_EVENTS', ALL_EVENTS)
2020 for evname in globals():
2021 if evname.startswith('IN_'):
2022 setattr(EventsCodes, evname, globals()[evname])
2023 global COMPATIBILITY_MODE
2024 COMPATIBILITY_MODE = True
2025
2028 """
2029 By default the watched path is '/tmp' and all types of events are
2030 monitored. Events monitoring serves forever, type c^c to stop it.
2031 """
2032 from optparse import OptionParser
2033
2034 usage = "usage: %prog [options] [path1] [path2] [pathn]"
2035
2036 parser = OptionParser(usage=usage)
2037 parser.add_option("-v", "--verbose", action="store_true",
2038 dest="verbose", help="Verbose mode")
2039 parser.add_option("-r", "--recursive", action="store_true",
2040 dest="recursive",
2041 help="Add watches recursively on paths")
2042 parser.add_option("-a", "--auto_add", action="store_true",
2043 dest="auto_add",
2044 help="Automatically add watches on new directories")
2045 parser.add_option("-e", "--events-list", metavar="EVENT[,...]",
2046 dest="events_list",
2047 help=("A comma-separated list of events to watch for - "
2048 "see the documentation for valid options (defaults"
2049 " to everything)"))
2050 parser.add_option("-s", "--stats", action="store_true",
2051 dest="stats",
2052 help="Display dummy statistics")
2053
2054 (options, args) = parser.parse_args()
2055
2056 if options.verbose:
2057 log.setLevel(10)
2058
2059 if len(args) < 1:
2060 path = '/tmp'
2061 else:
2062 path = args
2063
2064
2065 wm = WatchManager()
2066
2067 if options.stats:
2068 notifier = Notifier(wm, default_proc_fun=Stats(), read_freq=5)
2069 else:
2070 notifier = Notifier(wm, default_proc_fun=PrintAllEvents())
2071
2072
2073 mask = 0
2074 if options.events_list:
2075 events_list = options.events_list.split(',')
2076 for ev in events_list:
2077 evcode = EventsCodes.ALL_FLAGS.get(ev, 0)
2078 if evcode:
2079 mask |= evcode
2080 else:
2081 parser.error("The event '%s' specified with option -e"
2082 " is not valid" % ev)
2083 else:
2084 mask = ALL_EVENTS
2085
2086
2087 cb_fun = None
2088 if options.stats:
2089 def cb(s):
2090 print('%s\n%s\n' % (repr(s.proc_fun()),
2091 s.proc_fun()))
2092 cb_fun = cb
2093
2094 log.debug('Start monitoring %s, (press c^c to halt pyinotify)' % path)
2095
2096 wm.add_watch(path, mask, rec=options.recursive, auto_add=options.auto_add)
2097
2098 notifier.loop(callback=cb_fun)
2099
2100
2101 if __name__ == '__main__':
2102 command_line()
2103