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