1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """
23 pyinotify
24
25 @author: Sebastien Martini
26 @license: GPLv2+
27 @contact: seb@dbzteam.org
28 """
31 """Indicates exceptions raised by a Pyinotify class."""
32
35 """
36 Raised for unsupported Python version.
37 """
39 """
40 @param version: Current Python version
41 @type version: string
42 """
43 PyinotifyError.__init__(self,
44 ('Python %s is unsupported, requires '
45 'at least Python 2.4') % version)
46
49 """
50 Raised for unsupported libc version.
51 """
53 """
54 @param version: Current Libc version
55 @type version: string
56 """
57 PyinotifyError.__init__(self,
58 ('Libc %s is unsupported, requires '
59 'at least Libc 2.4') % version)
60
61
62
63 import sys
64 if sys.version < '2.4':
65 raise UnsupportedPythonVersionError(sys.version)
66
67
68
69 import threading
70 import os
71 import select
72 import struct
73 import fcntl
74 import errno
75 import termios
76 import array
77 import logging
78 import atexit
79 from collections import deque
80 from datetime import datetime, timedelta
81 import time
82 import fnmatch
83 import re
84 import ctypes
85 import ctypes.util
86
87
88 __author__ = "seb@dbzteam.org (Sebastien Martini)"
89
90 __version__ = "0.8.6"
91
92 __metaclass__ = type
93
94
95
96 LIBC = ctypes.cdll.LoadLibrary(ctypes.util.find_library('c'))
97
98
99
100
101 LIBC.gnu_get_libc_version.restype = ctypes.c_char_p
102 LIBC_VERSION = LIBC.gnu_get_libc_version()
103 if (int(LIBC_VERSION.split('.')[0]) < 2 or
104 (int(LIBC_VERSION.split('.')[0]) == 2 and
105 int(LIBC_VERSION.split('.')[1]) < 4)):
106 raise UnsupportedLibcVersionError(LIBC_VERSION)
107
108
109
110 log = logging.getLogger("pyinotify")
111 console_handler = logging.StreamHandler()
112 console_handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
113 log.addHandler(console_handler)
114 log.setLevel(20)
115
116
117
118 try:
119 if False:
120 import psyco
121 psyco.full()
122 except ImportError:
123
124 pass
131 """
132 Access (read, write) inotify's variables through sysctl.
133
134 Examples:
135 - Read variable: myvar = max_queued_events.value
136 - Update variable: max_queued_events.value = 42
137 """
138
139 inotify_attrs = {'max_user_instances': 1,
140 'max_user_watches': 2,
141 'max_queued_events': 3}
142
147
149 """
150 @return: stored value.
151 @rtype: int
152 """
153 oldv = ctypes.c_int(0)
154 size = ctypes.c_int(ctypes.sizeof(oldv))
155 LIBC.sysctl(self._attr, 3,
156 ctypes.c_voidp(ctypes.addressof(oldv)),
157 ctypes.addressof(size),
158 None, 0)
159 return oldv.value
160
162 """
163 @param nval: set to nval.
164 @type nval: int
165 """
166 oldv = ctypes.c_int(0)
167 sizeo = ctypes.c_int(ctypes.sizeof(oldv))
168 newv = ctypes.c_int(nval)
169 sizen = ctypes.c_int(ctypes.sizeof(newv))
170 LIBC.sysctl(self._attr, 3,
171 ctypes.c_voidp(ctypes.addressof(oldv)),
172 ctypes.addressof(sizeo),
173 ctypes.c_voidp(ctypes.addressof(newv)),
174 ctypes.addressof(sizen))
175
176 value = property(get_val, set_val)
177
179 return '<%s=%d>' % (self._attrname, self.get_val())
180
181
182
183
184
185
186
187 for attrname in ('max_queued_events', 'max_user_instances', 'max_user_watches'):
188 globals()[attrname] = SysCtlINotify(attrname)
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211 -def iglob(pathname):
212 if not has_magic(pathname):
213 if hasattr(os.path, 'lexists'):
214 if os.path.lexists(pathname):
215 yield pathname
216 else:
217 if os.path.islink(pathname) or os.path.exists(pathname):
218 yield pathname
219 return
220 dirname, basename = os.path.split(pathname)
221
222 if not dirname:
223 return
224
225 if has_magic(dirname):
226 dirs = iglob(dirname)
227 else:
228 dirs = [dirname]
229 if has_magic(basename):
230 glob_in_dir = glob1
231 else:
232 glob_in_dir = glob0
233 for dirname in dirs:
234 for name in glob_in_dir(dirname, basename):
235 yield os.path.join(dirname, name)
236
237 -def glob1(dirname, pattern):
238 if not dirname:
239 dirname = os.curdir
240 try:
241 names = os.listdir(dirname)
242 except os.error:
243 return []
244 return fnmatch.filter(names, pattern)
245
246 -def glob0(dirname, basename):
247 if basename == '' and os.path.isdir(dirname):
248
249
250 return [basename]
251 if hasattr(os.path, 'lexists'):
252 if os.path.lexists(os.path.join(dirname, basename)):
253 return [basename]
254 else:
255 if (os.path.islink(os.path.join(dirname, basename)) or
256 os.path.exists(os.path.join(dirname, basename))):
257 return [basename]
258 return []
259
260 magic_check = re.compile('[*?[]')
264
271 """
272 Set of codes corresponding to each kind of events.
273 Some of these flags are used to communicate with inotify, whereas
274 the others are sent to userspace by inotify notifying some events.
275
276 @cvar IN_ACCESS: File was accessed.
277 @type IN_ACCESS: int
278 @cvar IN_MODIFY: File was modified.
279 @type IN_MODIFY: int
280 @cvar IN_ATTRIB: Metadata changed.
281 @type IN_ATTRIB: int
282 @cvar IN_CLOSE_WRITE: Writtable file was closed.
283 @type IN_CLOSE_WRITE: int
284 @cvar IN_CLOSE_NOWRITE: Unwrittable file closed.
285 @type IN_CLOSE_NOWRITE: int
286 @cvar IN_OPEN: File was opened.
287 @type IN_OPEN: int
288 @cvar IN_MOVED_FROM: File was moved from X.
289 @type IN_MOVED_FROM: int
290 @cvar IN_MOVED_TO: File was moved to Y.
291 @type IN_MOVED_TO: int
292 @cvar IN_CREATE: Subfile was created.
293 @type IN_CREATE: int
294 @cvar IN_DELETE: Subfile was deleted.
295 @type IN_DELETE: int
296 @cvar IN_DELETE_SELF: Self (watched item itself) was deleted.
297 @type IN_DELETE_SELF: int
298 @cvar IN_MOVE_SELF: Self (watched item itself) was moved.
299 @type IN_MOVE_SELF: int
300 @cvar IN_UNMOUNT: Backing fs was unmounted.
301 @type IN_UNMOUNT: int
302 @cvar IN_Q_OVERFLOW: Event queued overflowed.
303 @type IN_Q_OVERFLOW: int
304 @cvar IN_IGNORED: File was ignored.
305 @type IN_IGNORED: int
306 @cvar IN_ONLYDIR: only watch the path if it is a directory (new
307 in kernel 2.6.15).
308 @type IN_ONLYDIR: int
309 @cvar IN_DONT_FOLLOW: don't follow a symlink (new in kernel 2.6.15).
310 IN_ONLYDIR we can make sure that we don't watch
311 the target of symlinks.
312 @type IN_DONT_FOLLOW: int
313 @cvar IN_MASK_ADD: add to the mask of an already existing watch (new
314 in kernel 2.6.14).
315 @type IN_MASK_ADD: int
316 @cvar IN_ISDIR: Event occurred against dir.
317 @type IN_ISDIR: int
318 @cvar IN_ONESHOT: Only send event once.
319 @type IN_ONESHOT: int
320 @cvar ALL_EVENTS: Alias for considering all of the events.
321 @type ALL_EVENTS: int
322 """
323
324
325
326
327 FLAG_COLLECTIONS = {'OP_FLAGS': {
328 'IN_ACCESS' : 0x00000001,
329 'IN_MODIFY' : 0x00000002,
330 'IN_ATTRIB' : 0x00000004,
331 'IN_CLOSE_WRITE' : 0x00000008,
332 'IN_CLOSE_NOWRITE' : 0x00000010,
333 'IN_OPEN' : 0x00000020,
334 'IN_MOVED_FROM' : 0x00000040,
335 'IN_MOVED_TO' : 0x00000080,
336 'IN_CREATE' : 0x00000100,
337 'IN_DELETE' : 0x00000200,
338 'IN_DELETE_SELF' : 0x00000400,
339
340 'IN_MOVE_SELF' : 0x00000800,
341 },
342 'EVENT_FLAGS': {
343 'IN_UNMOUNT' : 0x00002000,
344 'IN_Q_OVERFLOW' : 0x00004000,
345 'IN_IGNORED' : 0x00008000,
346 },
347 'SPECIAL_FLAGS': {
348 'IN_ONLYDIR' : 0x01000000,
349
350 'IN_DONT_FOLLOW' : 0x02000000,
351 'IN_MASK_ADD' : 0x20000000,
352
353 'IN_ISDIR' : 0x40000000,
354 'IN_ONESHOT' : 0x80000000,
355 },
356 }
357
359 """
360 Return the event name associated to mask. IN_ISDIR is appended when
361 appropriate. Note: only one event is returned, because only one is
362 raised once at a time.
363
364 @param mask: mask.
365 @type mask: int
366 @return: event name.
367 @rtype: str
368 """
369 ms = mask
370 name = '%s'
371 if mask & IN_ISDIR:
372 ms = mask - IN_ISDIR
373 name = '%s|IN_ISDIR'
374 return name % EventsCodes.ALL_VALUES[ms]
375
376 maskname = staticmethod(maskname)
377
378
379
380 EventsCodes.ALL_FLAGS = {}
381 EventsCodes.ALL_VALUES = {}
382 for flagc, valc in EventsCodes.FLAG_COLLECTIONS.iteritems():
383
384
385 setattr(EventsCodes, flagc, valc)
386
387
388 EventsCodes.ALL_FLAGS.update(valc)
389
390
391
392 for name, val in valc.iteritems():
393 globals()[name] = val
394 EventsCodes.ALL_VALUES[val] = name
395
396
397
398 ALL_EVENTS = reduce(lambda x, y: x | y, EventsCodes.OP_FLAGS.itervalues())
399 EventsCodes.ALL_FLAGS['ALL_EVENTS'] = ALL_EVENTS
400 EventsCodes.ALL_VALUES[ALL_EVENTS] = 'ALL_EVENTS'
404 """
405 Event structure, represent events raised by the system. This
406 is the base class and should be subclassed.
407
408 """
410 """
411 Attach attributes (contained in dict_) to self.
412 """
413 for tpl in dict_.iteritems():
414 setattr(self, *tpl)
415
438
441 """
442 Raw event, it contains only the informations provided by the system.
443 It doesn't infer anything.
444 """
445 - def __init__(self, wd, mask, cookie, name):
446 """
447 @param wd: Watch Descriptor.
448 @type wd: int
449 @param mask: Bitmask of events.
450 @type mask: int
451 @param cookie: Cookie.
452 @type cookie: int
453 @param name: Basename of the file or directory against which the
454 event was raised, in case where the watched directory
455 is the parent directory. None if the event was raised
456 on the watched item itself.
457 @type name: string or None
458 """
459
460 super(_RawEvent, self).__init__({'wd': wd,
461 'mask': mask,
462 'cookie': cookie,
463 'name': name.rstrip('\0')})
464 log.debug(repr(self))
465
468 """
469 This class contains all the useful informations about the observed
470 event. However, the incorporation of each field is not guaranteed and
471 depends on the type of event. In effect, some fields are irrelevant
472 for some kind of event (for example 'cookie' is meaningless for
473 IN_CREATE whereas it is useful for IN_MOVE_TO).
474
475 The possible fields are:
476 - wd (int): Watch Descriptor.
477 - mask (int): Mask.
478 - maskname (str): Readable event name.
479 - path (str): path of the file or directory being watched.
480 - name (str): Basename of the file or directory against which the
481 event was raised, in case where the watched directory
482 is the parent directory. None if the event was raised
483 on the watched item itself. This field is always provided
484 even if the string is ''.
485 - pathname (str): absolute path of: path + name
486 - cookie (int): Cookie.
487 - dir (bool): is the event raised against directory.
488
489 """
491 """
492 Concretely, this is the raw event plus inferred infos.
493 """
494 _Event.__init__(self, raw)
495 self.maskname = EventsCodes.maskname(self.mask)
496 try:
497 if self.name:
498 self.pathname = os.path.abspath(os.path.join(self.path,
499 self.name))
500 else:
501 self.pathname = os.path.abspath(self.path)
502 except AttributeError:
503 pass
504
507 """
508 ProcessEventError Exception. Raised on ProcessEvent error.
509 """
511 """
512 @param err: Exception error description.
513 @type err: string
514 """
515 PyinotifyError.__init__(self, err)
516
519 """
520 Abstract processing event class.
521 """
523 """
524 To behave like a functor the object must be callable.
525 This method is a dispatch method. Lookup order:
526 1. process_MASKNAME method
527 2. process_FAMILY_NAME method
528 3. otherwise call process_default
529
530 @param event: Event to be processed.
531 @type event: Event object
532 @return: By convention when used from the ProcessEvent class:
533 - Returning False or None (default value) means keep on
534 executing next chained functors (see chain.py example).
535 - Returning True instead means do not execute next
536 processing functions.
537 @rtype: bool
538 @raise ProcessEventError: Event object undispatchable,
539 unknown event.
540 """
541 stripped_mask = event.mask - (event.mask & IN_ISDIR)
542 maskname = EventsCodes.ALL_VALUES.get(stripped_mask)
543 if maskname is None:
544 raise ProcessEventError("Unknown mask 0x%08x" % stripped_mask)
545
546
547 meth = getattr(self, 'process_' + maskname, None)
548 if meth is not None:
549 return meth(event)
550
551 meth = getattr(self, 'process_IN_' + maskname.split('_')[1], None)
552 if meth is not None:
553 return meth(event)
554
555 return self.process_default(event)
556
558 return '<%s>' % self.__class__.__name__
559
562 """
563 There is three kind of processing according to each event:
564
565 1. special handling (deletion from internal container, bug, ...).
566 2. default treatment: which is applied to most of events.
567 4. IN_ISDIR is never sent alone, he is piggybacked with a standart
568 event, he is not processed as the others events, instead, its
569 value is captured and appropriately aggregated to dst event.
570 """
572 """
573
574 @param wm: Watch Manager.
575 @type wm: WatchManager instance
576 @param notifier: notifier.
577 @type notifier: Instance of Notifier.
578 """
579 self._watch_manager = wm
580 self._notifier = notifier
581 self._mv_cookie = {}
582 self._mv = {}
583
585 """
586 Cleanup (delete) old (>1mn) records contained in self._mv_cookie
587 and self._mv.
588 """
589 date_cur_ = datetime.now()
590 for seq in [self._mv_cookie, self._mv]:
591 for k in seq.keys():
592 if (date_cur_ - seq[k][1]) > timedelta(minutes=1):
593 log.debug('cleanup: deleting entry %s' % seq[k][0])
594 del seq[k]
595
597 """
598 If the event concerns a directory and the auto_add flag of the
599 targetted watch is set to True, a new watch is added on this
600 new directory, with the same attributes's values than those of
601 this watch.
602 """
603 if raw_event.mask & IN_ISDIR:
604 watch_ = self._watch_manager._wmd.get(raw_event.wd)
605 if watch_.auto_add:
606 addw = self._watch_manager.add_watch
607 newwd = addw(os.path.join(watch_.path, raw_event.name),
608 watch_.mask, proc_fun=watch_.proc_fun,
609 rec=False, auto_add=watch_.auto_add)
610
611
612
613
614
615 base = os.path.join(watch_.path, raw_event.name)
616 if newwd[base] > 0:
617 for name in os.listdir(base):
618 inner = os.path.join(base, name)
619 if (os.path.isdir(inner) and
620 self._watch_manager.get_wd(inner) is None):
621
622
623 rawevent = _RawEvent(newwd[base],
624 IN_CREATE | IN_ISDIR,
625 0, name)
626 self._notifier._eventq.append(rawevent)
627 return self.process_default(raw_event)
628
630 """
631 Map the cookie with the source path (+ date for cleaning).
632 """
633 watch_ = self._watch_manager._wmd.get(raw_event.wd)
634 path_ = watch_.path
635 src_path = os.path.normpath(os.path.join(path_, raw_event.name))
636 self._mv_cookie[raw_event.cookie] = (src_path, datetime.now())
637 return self.process_default(raw_event, {'cookie': raw_event.cookie})
638
640 """
641 Map the source path with the destination path (+ date for
642 cleaning).
643 """
644 watch_ = self._watch_manager._wmd.get(raw_event.wd)
645 path_ = watch_.path
646 dst_path = os.path.normpath(os.path.join(path_, raw_event.name))
647 mv_ = self._mv_cookie.get(raw_event.cookie)
648 if mv_:
649 self._mv[mv_[0]] = (dst_path, datetime.now())
650 return self.process_default(raw_event, {'cookie': raw_event.cookie})
651
653 """
654 STATUS: the following bug has been fixed in the recent kernels (fixme:
655 which version ?). Now it raises IN_DELETE_SELF instead.
656
657 Old kernels are bugged, this event is raised when the watched item
658 was moved, so we must update its path, but under some circumstances it
659 can be impossible: if its parent directory and its destination
660 directory aren't watched. The kernel (see include/linux/fsnotify.h)
661 doesn't bring us enough informations like the destination path of
662 moved items.
663 """
664 watch_ = self._watch_manager._wmd.get(raw_event.wd)
665 src_path = watch_.path
666 mv_ = self._mv.get(src_path)
667 if mv_:
668 watch_.path = mv_[0]
669 else:
670 log.error("The path %s of this watch %s must not "
671 "be trusted anymore" % (watch_.path, watch_))
672 if not watch_.path.endswith('-wrong-path'):
673 watch_.path += '-wrong-path'
674
675 return self.process_default(raw_event)
676
678 """
679 Only signal overflow, most of the common flags are irrelevant
680 for this event (path, wd, name).
681 """
682 return Event({'mask': raw_event.mask})
683
685 """
686 The watch descriptor raised by this event is now ignored (forever),
687 it can be safely deleted from watch manager dictionary.
688 After this event we can be sure that neither the event queue
689 neither the system will raise an event associated to this wd.
690 """
691 event_ = self.process_default(raw_event)
692 try:
693 del self._watch_manager._wmd[raw_event.wd]
694 except KeyError, err:
695 log.error(err)
696 return event_
697
699 """
700 Common handling for the following events:
701
702 IN_ACCESS, IN_MODIFY, IN_ATTRIB, IN_CLOSE_WRITE, IN_CLOSE_NOWRITE,
703 IN_OPEN, IN_DELETE, IN_DELETE_SELF, IN_UNMOUNT.
704 """
705 ret = None
706 watch_ = self._watch_manager._wmd.get(raw_event.wd)
707 if raw_event.mask & (IN_DELETE_SELF | IN_MOVE_SELF):
708
709 dir_ = watch_.dir
710 else:
711 dir_ = bool(raw_event.mask & IN_ISDIR)
712 dict_ = {'wd': raw_event.wd,
713 'mask': raw_event.mask,
714 'path': watch_.path,
715 'name': raw_event.name,
716 'dir': dir_}
717 dict_.update(to_append)
718 return Event(dict_)
719
722 """
723 Process events objects, can be specialized via subclassing, thus its
724 behavior can be overriden:
725
726 Note: you should not override __init__ in your subclass instead define
727 a my_init() method, this method will be called from the constructor of
728 this class with optional parameters.
729
730 1. Provide methods, e.g. process_IN_DELETE for processing a given kind
731 of event (eg. IN_DELETE in this case).
732 2. Or/and provide methods for processing events by 'family', e.g.
733 process_IN_CLOSE method will process both IN_CLOSE_WRITE and
734 IN_CLOSE_NOWRITE events (if process_IN_CLOSE_WRITE and
735 process_IN_CLOSE_NOWRITE aren't defined).
736 3. Or/and override process_default for processing the remaining kind of
737 events.
738 """
739 pevent = None
740
741 - def __init__(self, pevent=None, **kargs):
742 """
743 Enable chaining of ProcessEvent instances.
744
745 @param pevent: optional callable object, will be called on event
746 processing (before self).
747 @type pevent: callable
748 @param kargs: optional arguments delagated to template method my_init
749 @type kargs: dict
750 """
751 self.pevent = pevent
752 self.my_init(**kargs)
753
755 """
756 Override this method when subclassing if you want to achieve
757 custom initialization of your subclass' instance. You MUST pass
758 keyword arguments. This method does nothing by default.
759
760 @param kargs: optional arguments delagated to template method my_init
761 @type kargs: dict
762 """
763 pass
764
766 stop_chaining = False
767 if self.pevent is not None:
768
769
770
771
772
773 stop_chaining = self.pevent(event)
774 if not stop_chaining:
775 return _ProcessEvent.__call__(self, event)
776
779
781 """
782 Default default processing event method. Print event
783 on standart output.
784
785 @param event: Event to be processed.
786 @type event: Event instance
787 """
788 print(repr(event))
789
792 """
793 Makes conditional chaining depending on the result of the nested
794 processing instance.
795 """
798
800 return not self._func(event)
801
802
803 -class Stats(ProcessEvent):
805 self._start_time = time.time()
806 self._stats = {}
807 self._stats_lock = threading.Lock()
808
810 self._stats_lock.acquire()
811 try:
812 events = event.maskname.split('|')
813 for event_name in events:
814 count = self._stats.get(event_name, 0)
815 self._stats[event_name] = count + 1
816 finally:
817 self._stats_lock.release()
818
820 self._stats_lock.acquire()
821 try:
822 return self._stats.copy()
823 finally:
824 self._stats_lock.release()
825
827 stats = self._stats_copy()
828
829 t = int(time.time() - self._start_time)
830 if t < 60:
831 ts = str(t) + 'sec'
832 elif 60 <= t < 3600:
833 ts = '%dmn%dsec' % (t / 60, t % 60)
834 elif 3600 <= t < 86400:
835 ts = '%dh%dmn' % (t / 3600, (t % 3600) / 60)
836 elif t >= 86400:
837 ts = '%dd%dh' % (t / 86400, (t % 86400) / 3600)
838 stats['ElapsedTime'] = ts
839
840 l = []
841 for ev, value in sorted(stats.items(), key=lambda x: x[0]):
842 l.append(' %s=%s' % (Color.FieldName(ev),
843 Color.FieldValue(value)))
844 s = '<%s%s >' % (Color.ClassName(self.__class__.__name__),
845 ''.join(l))
846 return s
847
848 - def dump(self, filename):
849 fo = file(filename, 'wb')
850 try:
851 fo.write(str(self))
852 finally:
853 fo.close()
854
856 stats = self._stats_copy()
857 if not stats:
858 return ''
859
860 m = max(stats.values())
861 unity = int(round(float(m) / scale)) or 1
862 fmt = '%%-26s%%-%ds%%s' % (len(Color.FieldValue('@' * scale))
863 + 1)
864 def func(x):
865 return fmt % (Color.FieldName(x[0]),
866 Color.FieldValue('@' * (x[1] / unity)),
867 Color.Simple('%d' % x[1], 'yellow'))
868 s = '\n'.join(map(func, sorted(stats.items(), key=lambda x: x[0])))
869 return s
870
873 """
874 Notifier Exception. Raised on Notifier error.
875
876 """
878 """
879 @param err: Exception string's description.
880 @type err: string
881 """
882 PyinotifyError.__init__(self, err)
883
886 """
887 Read notifications, process events.
888
889 """
890 - def __init__(self, watch_manager, default_proc_fun=ProcessEvent(),
891 read_freq=0, treshold=0, timeout=None):
892 """
893 Initialization. read_freq, treshold and timeout parameters are used
894 when looping.
895
896 @param watch_manager: Watch Manager.
897 @type watch_manager: WatchManager instance
898 @param default_proc_fun: Default processing method.
899 @type default_proc_fun: instance of ProcessEvent
900 @param read_freq: if read_freq == 0, events are read asap,
901 if read_freq is > 0, this thread sleeps
902 max(0, read_freq - timeout) seconds. But if
903 timeout is None it can be different because
904 poll is blocking waiting for something to read.
905 @type read_freq: int
906 @param treshold: File descriptor will be read only if its size to
907 read is >= treshold. If != 0, you likely want to
908 use it in combination with read_freq because
909 without that you keep looping without really reading
910 anything and that until the amount to read
911 is >= treshold. At least with read_freq you may sleep.
912 @type treshold: int
913 @param timeout:
914 http://docs.python.org/lib/poll-objects.html#poll-objects
915 @type timeout: int
916 """
917
918 self._watch_manager = watch_manager
919
920 self._fd = self._watch_manager._fd
921
922 self._pollobj = select.poll()
923 self._pollobj.register(self._fd, select.POLLIN)
924
925 self._pipe = (-1, -1)
926
927 self._eventq = deque()
928
929 self._sys_proc_fun = _SysProcessEvent(self._watch_manager, self)
930
931 self._default_proc_fun = default_proc_fun
932
933 self._read_freq = read_freq
934 self._treshold = treshold
935 self._timeout = timeout
936
938 return self._default_proc_fun
939
941 """
942 Check for new events available to read, blocks up to timeout
943 milliseconds.
944
945 @return: New events to read.
946 @rtype: bool
947 """
948 while True:
949 try:
950
951 ret = self._pollobj.poll(self._timeout)
952 except select.error, err:
953 if err[0] == errno.EINTR:
954 continue
955 else:
956 raise
957 else:
958 break
959
960 if not ret or (self._pipe[0] == ret[0][0]):
961 return False
962
963 return ret[0][1] & select.POLLIN
964
966 """
967 Read events from device, build _RawEvents, and enqueue them.
968 """
969 buf_ = array.array('i', [0])
970
971 if fcntl.ioctl(self._fd, termios.FIONREAD, buf_, 1) == -1:
972 return
973 queue_size = buf_[0]
974 if queue_size < self._treshold:
975 log.debug('(fd: %d) %d bytes available to read but '
976 'treshold is fixed to %d bytes' % (self._fd,
977 queue_size,
978 self._treshold))
979 return
980
981 try:
982
983 r = os.read(self._fd, queue_size)
984 except Exception, msg:
985 raise NotifierError(msg)
986 log.debug('event queue size: %d' % queue_size)
987 rsum = 0
988 while rsum < queue_size:
989 s_size = 16
990
991 s_ = struct.unpack('iIII', r[rsum:rsum+s_size])
992
993 fname_len = s_[3]
994
995 s_ = s_[:-1]
996
997 s_ += struct.unpack('%ds' % fname_len,
998 r[rsum + s_size:rsum + s_size + fname_len])
999 self._eventq.append(_RawEvent(*s_))
1000 rsum += s_size + fname_len
1001
1003 """
1004 Routine for processing events from queue by calling their
1005 associated proccessing function (instance of ProcessEvent).
1006 It also do internal processings, to keep the system updated.
1007 """
1008 while self._eventq:
1009 raw_event = self._eventq.popleft()
1010 watch_ = self._watch_manager._wmd.get(raw_event.wd)
1011 revent = self._sys_proc_fun(raw_event)
1012 if watch_ and watch_.proc_fun:
1013 watch_.proc_fun(revent)
1014 else:
1015 self._default_proc_fun(revent)
1016 self._sys_proc_fun.cleanup()
1017
1018
1019 - def __daemonize(self, pid_file=None, force_kill=False, stdin=os.devnull,
1020 stdout=os.devnull, stderr=os.devnull):
1021 """
1022 pid_file: file to which pid will be written.
1023 force_kill: if True kill the process associated to pid_file.
1024 stdin, stdout, stderr: files associated to common streams.
1025 """
1026 if pid_file is None:
1027 dirname = '/var/run/'
1028 basename = sys.argv[0] or 'pyinotify'
1029 pid_file = os.path.join(dirname, basename + '.pid')
1030
1031 if os.path.exists(pid_file):
1032 fo = file(pid_file, 'rb')
1033 try:
1034 try:
1035 pid = int(fo.read())
1036 except ValueError:
1037 pid = None
1038 if pid is not None:
1039 try:
1040 os.kill(pid, 0)
1041 except OSError, err:
1042 pass
1043 else:
1044 if not force_kill:
1045 s = 'There is already a pid file %s with pid %d'
1046 raise NotifierError(s % (pid_file, pid))
1047 else:
1048 os.kill(pid, 9)
1049 finally:
1050 fo.close()
1051
1052
1053 def fork_daemon():
1054
1055 pid = os.fork()
1056 if (pid == 0):
1057
1058 os.setsid()
1059 pid = os.fork()
1060 if (pid == 0):
1061
1062 os.chdir('/')
1063 os.umask(0)
1064 else:
1065
1066 os._exit(0)
1067 else:
1068
1069 os._exit(0)
1070
1071 fd_inp = os.open(stdin, os.O_RDONLY)
1072 os.dup2(fd_inp, 0)
1073 fd_out = os.open(stdout, os.O_WRONLY|os.O_CREAT)
1074 os.dup2(fd_out, 1)
1075 fd_err = os.open(stderr, os.O_WRONLY|os.O_CREAT)
1076 os.dup2(fd_err, 2)
1077
1078
1079 fork_daemon()
1080
1081
1082 fo = file(pid_file, 'wb')
1083 try:
1084 fo.write(str(os.getpid()) + '\n')
1085 finally:
1086 fo.close()
1087
1088 atexit.register(lambda : os.unlink(pid_file))
1089
1090
1092
1093 if self._read_freq > 0:
1094 cur_time = time.time()
1095 sleep_amount = self._read_freq - (cur_time - ref_time)
1096 if sleep_amount > 0:
1097 log.debug('Now sleeping %d seconds' % sleep_amount)
1098 time.sleep(sleep_amount)
1099
1100
1101 - def loop(self, callback=None, daemonize=False, **args):
1102 """
1103 Events are read only once time every min(read_freq, timeout)
1104 seconds at best and only if the size to read is >= treshold.
1105
1106 @param callback: Functor called after each event processing. Expects
1107 to receive notifier object (self) as first parameter.
1108 @type callback: callable
1109 @param daemonize: This thread is daemonized if set to True.
1110 @type daemonize: boolean
1111 """
1112 if daemonize:
1113 self.__daemonize(**args)
1114
1115
1116 while 1:
1117 try:
1118 self.process_events()
1119 if callback is not None:
1120 callback(self)
1121 ref_time = time.time()
1122
1123 if self.check_events():
1124 self._sleep(ref_time)
1125 self.read_events()
1126 except KeyboardInterrupt:
1127
1128 log.debug('Pyinotify stops monitoring.')
1129
1130 self.stop()
1131 break
1132
1134 """
1135 Close the inotify's instance (close its file descriptor).
1136 It destroys all existing watches, pending events,...
1137 """
1138 self._pollobj.unregister(self._fd)
1139 os.close(self._fd)
1140
1143 """
1144 This notifier inherits from threading.Thread for instantiating a separate
1145 thread, and also inherits from Notifier, because it is a threaded notifier.
1146
1147 Note that everything possible with this class is also possible through
1148 Notifier. Moreover Notifier is _better_ under many aspects: not threaded,
1149 can be easily daemonized.
1150 """
1151 - def __init__(self, watch_manager, default_proc_fun=ProcessEvent(),
1152 read_freq=0, treshold=0, timeout=None):
1153 """
1154 Initialization, initialize base classes. read_freq, treshold and
1155 timeout parameters are used when looping.
1156
1157 @param watch_manager: Watch Manager.
1158 @type watch_manager: WatchManager instance
1159 @param default_proc_fun: Default processing method.
1160 @type default_proc_fun: instance of ProcessEvent
1161 @param read_freq: if read_freq == 0, events are read asap,
1162 if read_freq is > 0, this thread sleeps
1163 max(0, read_freq - timeout) seconds.
1164 @type read_freq: int
1165 @param treshold: File descriptor will be read only if its size to
1166 read is >= treshold. If != 0, you likely want to
1167 use it in combination with read_freq because
1168 without that you keep looping without really reading
1169 anything and that until the amount to read
1170 is >= treshold. At least with read_freq you may sleep.
1171 @type treshold: int
1172 @param timeout:
1173 see http://docs.python.org/lib/poll-objects.html#poll-objects
1174 Read the corresponding comment in the source code before changing
1175 it.
1176 @type timeout: int
1177 """
1178
1179 threading.Thread.__init__(self)
1180
1181 self._stop_event = threading.Event()
1182
1183 Notifier.__init__(self, watch_manager, default_proc_fun, read_freq,
1184 treshold, timeout)
1185
1186 self._pipe = os.pipe()
1187 self._pollobj.register(self._pipe[0], select.POLLIN)
1188
1190 """
1191 Stop the notifier's loop. Stop notification. Join the thread.
1192 """
1193 self._stop_event.set()
1194 os.write(self._pipe[1], 'stop')
1195 threading.Thread.join(self)
1196 Notifier.stop(self)
1197 self._pollobj.unregister(self._pipe[0])
1198 os.close(self._pipe[0])
1199 os.close(self._pipe[1])
1200
1202 """
1203 Thread's main loop. Don't meant to be called by user directly.
1204 Call start() instead.
1205
1206 Events are read only once time every min(read_freq, timeout)
1207 seconds at best and only if the size of events to read is >= treshold.
1208 """
1209
1210
1211
1212
1213 while not self._stop_event.isSet():
1214 self.process_events()
1215 ref_time = time.time()
1216 if self.check_events():
1217 self._sleep(ref_time)
1218 self.read_events()
1219
1221 """
1222 Start the thread's loop: read and process events until the method
1223 stop() is called.
1224 Never call this method directly, instead call the start() method
1225 inherited from threading.Thread, which then will call run().
1226 """
1227 self.loop()
1228
1231 """
1232 Represent a watch, i.e. a file or directory being watched.
1233
1234 """
1236 """
1237 Initializations.
1238
1239 @param wd: Watch descriptor.
1240 @type wd: int
1241 @param path: Path of the file or directory being watched.
1242 @type path: str
1243 @param mask: Mask.
1244 @type mask: int
1245 @param proc_fun: Processing callable object.
1246 @type proc_fun:
1247 @param auto_add: Automatically add watches on new directories.
1248 @type auto_add: bool
1249 """
1250 for k, v in keys.iteritems():
1251 setattr(self, k, v)
1252 self.dir = os.path.isdir(self.path)
1253
1269
1272 """
1273 ExcludeFilter is an exclusion filter.
1274 """
1275
1277 """
1278 @param arg_lst: is either a list or dict of patterns:
1279 [pattern1, ..., patternn]
1280 {'filename1': (list1, listn), ...} where list1 is
1281 a list of patterns
1282 @type arg_lst: list or dict
1283 """
1284 if isinstance(arg_lst, dict):
1285 lst = self._load_patterns(arg_lst)
1286 elif isinstance(arg_lst, list):
1287 lst = arg_lst
1288 else:
1289 raise TypeError
1290
1291 self._lregex = []
1292 for regex in lst:
1293 self._lregex.append(re.compile(regex, re.UNICODE))
1294
1296 lst = []
1297 for path, varnames in dct.iteritems():
1298 loc = {}
1299 execfile(path, {}, loc)
1300 for varname in varnames:
1301 lst.extend(loc.get(varname, []))
1302 return lst
1303
1304 - def _match(self, regex, path):
1305 return regex.match(path) is not None
1306
1308 """
1309 @param path: path to match against regexps.
1310 @type path: str
1311 @return: return True is path has been matched and should
1312 be excluded, False otherwise.
1313 @rtype: bool
1314 """
1315 for regex in self._lregex:
1316 if self._match(regex, path):
1317 return True
1318 return False
1319
1322 """
1323 WatchManager Exception. Raised on error encountered on watches
1324 operations.
1325
1326 """
1328 """
1329 @param msg: Exception string's description.
1330 @type msg: string
1331 @param wmd: Results of previous operations made by the same function
1332 on previous wd or paths. It also contains the item which
1333 raised this exception.
1334 @type wmd: dict
1335 """
1336 self.wmd = wmd
1337 Exception.__init__(self, msg)
1338
1341 """
1342 Provide operations for watching files and directories. Integrated
1343 dictionary is used to reference watched items.
1344 """
1345 - def __init__(self, exclude_filter=lambda path: False):
1346 """
1347 Initialization: init inotify, init watch manager dictionary.
1348 Raise OSError if initialization fails.
1349
1350 @param exclude_filter: boolean function, returns True if current
1351 path must be excluded from being watched.
1352 Convenient for providing a common exclusion
1353 filter for every call to add_watch.
1354 @type exclude_filter: bool
1355 """
1356 self._exclude_filter = exclude_filter
1357 self._wmd = {}
1358 self._fd = LIBC.inotify_init()
1359 if self._fd < 0:
1360 raise OSError()
1361
1362 - def __add_watch(self, path, mask, proc_fun, auto_add):
1363 """
1364 Add a watch on path, build a Watch object and insert it in the
1365 watch manager dictionary. Return the wd value.
1366 """
1367 wd_ = LIBC.inotify_add_watch(self._fd,
1368 ctypes.create_string_buffer(path),
1369 mask)
1370 if wd_ < 0:
1371 return wd_
1372 watch_ = Watch(wd=wd_, path=os.path.normpath(path), mask=mask,
1373 proc_fun=proc_fun, auto_add=auto_add)
1374 self._wmd[wd_] = watch_
1375 log.debug('New %s' % watch_)
1376 return wd_
1377
1378 - def __glob(self, path, do_glob):
1379 if do_glob:
1380 return iglob(path)
1381 else:
1382 return [path]
1383
1384 - def add_watch(self, path, mask, proc_fun=None, rec=False,
1385 auto_add=False, do_glob=False, quiet=True,
1386 exclude_filter=None):
1387 """
1388 Add watch(s) on given path(s) with the specified mask and
1389 optionnally with a processing function and recursive flag.
1390
1391 @param path: Path to watch, the path can either be a file or a
1392 directory. Also accepts a sequence (list) of paths.
1393 @type path: string or list of string
1394 @param mask: Bitmask of events.
1395 @type mask: int
1396 @param proc_fun: Processing object.
1397 @type proc_fun: function or ProcessEvent instance or instance of
1398 one of its subclasses or callable object.
1399 @param rec: Recursively add watches from path on all its
1400 subdirectories, set to False by default (doesn't
1401 follows symlinks).
1402 @type rec: bool
1403 @param auto_add: Automatically add watches on newly created
1404 directories in the watch's path.
1405 @type auto_add: bool
1406 @param do_glob: Do globbing on pathname.
1407 @type do_glob: bool
1408 @param quiet: if True raise an WatchManagerError exception on
1409 error. See example not_quiet.py
1410 @type quiet: bool
1411 @param exclude_filter: boolean function, returns True if current
1412 path must be excluded from being watched.
1413 Has precedence on exclude_filter defined
1414 into __init__.
1415 @type exclude_filter: bool
1416 @return: dict of paths associated to watch descriptors. A wd value
1417 is positive if the watch has been sucessfully added,
1418 otherwise the value is negative. If the path is invalid
1419 it will be not included into this dict.
1420 @rtype: dict of {str: int}
1421 """
1422 ret_ = {}
1423
1424 if exclude_filter is None:
1425 exclude_filter = self._exclude_filter
1426
1427
1428 for npath in self.__format_param(path):
1429
1430 for apath in self.__glob(npath, do_glob):
1431
1432 for rpath in self.__walk_rec(apath, rec):
1433 if not exclude_filter(rpath):
1434 wd = ret_[rpath] = self.__add_watch(rpath, mask,
1435 proc_fun,
1436 auto_add)
1437 if wd < 0:
1438 err = 'add_watch: cannot watch %s (WD=%d)'
1439 err = err % (rpath, wd)
1440 if quiet:
1441 log.error(err)
1442 else:
1443 raise WatchManagerError(err, ret_)
1444 else:
1445
1446
1447 ret_[rpath] = -2
1448 return ret_
1449
1451 """
1452 Get every wd from self._wmd if its path is under the path of
1453 one (at least) of those in lpath. Doesn't follow symlinks.
1454
1455 @param lpath: list of watch descriptor
1456 @type lpath: list of int
1457 @return: list of watch descriptor
1458 @rtype: list of int
1459 """
1460 for d in lpath:
1461 root = self.get_path(d)
1462 if root:
1463
1464 yield d
1465 else:
1466
1467 continue
1468
1469
1470 if not os.path.isdir(root):
1471 continue
1472
1473
1474 root = os.path.normpath(root)
1475
1476 lend = len(root)
1477 for iwd in self._wmd.items():
1478 cur = iwd[1].path
1479 pref = os.path.commonprefix([root, cur])
1480 if root == os.sep or (len(pref) == lend and \
1481 len(cur) > lend and \
1482 cur[lend] == os.sep):
1483 yield iwd[1].wd
1484
1485 - def update_watch(self, wd, mask=None, proc_fun=None, rec=False,
1486 auto_add=False, quiet=True):
1487 """
1488 Update existing watch(s). Both the mask and the processing
1489 object can be modified.
1490
1491 @param wd: Watch Descriptor to update. Also accepts a list of
1492 watch descriptors.
1493 @type wd: int or list of int
1494 @param mask: Optional new bitmask of events.
1495 @type mask: int
1496 @param proc_fun: Optional new processing function.
1497 @type proc_fun: function or ProcessEvent instance or instance of
1498 one of its subclasses or callable object.
1499 @param rec: Recursively update watches on every already watched
1500 subdirectories and subfiles.
1501 @type rec: bool
1502 @param auto_add: Automatically add watches on newly created
1503 directories in the watch's path.
1504 @type auto_add: bool
1505 @param quiet: if True raise an WatchManagerError exception on
1506 error. See example not_quiet.py
1507 @type quiet: bool
1508 @return: dict of watch descriptors associated to booleans values.
1509 True if the corresponding wd has been successfully
1510 updated, False otherwise.
1511 @rtype: dict of int: bool
1512 """
1513 lwd = self.__format_param(wd)
1514 if rec:
1515 lwd = self.__get_sub_rec(lwd)
1516
1517 ret_ = {}
1518 for awd in lwd:
1519 apath = self.get_path(awd)
1520 if not apath or awd < 0:
1521 err = 'update_watch: invalid WD=%d' % awd
1522 if quiet:
1523 log.error(err)
1524 continue
1525 raise WatchManagerError(err, ret_)
1526
1527 if mask:
1528 addw = LIBC.inotify_add_watch
1529 wd_ = addw(self._fd,
1530 ctypes.create_string_buffer(apath),
1531 mask)
1532 if wd_ < 0:
1533 ret_[awd] = False
1534 err = 'update_watch: cannot update WD=%d (%s)' % (wd_,
1535 apath)
1536 if quiet:
1537 log.error(err)
1538 continue
1539 raise WatchManagerError(err, ret_)
1540
1541 assert(awd == wd_)
1542
1543 if proc_fun or auto_add:
1544 watch_ = self._wmd[awd]
1545
1546 if proc_fun:
1547 watch_.proc_fun = proc_fun
1548
1549 if auto_add:
1550 watch_.proc_fun = auto_add
1551
1552 ret_[awd] = True
1553 log.debug('Updated watch - %s' % self._wmd[awd])
1554 return ret_
1555
1568
1570 """
1571 Returns the watch descriptor associated to path. This method
1572 has an prohibitive cost, always prefer to keep the WD.
1573 If path is unknown None is returned.
1574
1575 @param path: path.
1576 @type path: str
1577 @return: WD or None.
1578 @rtype: int or None
1579 """
1580 path = os.path.normpath(path)
1581 for iwd in self._wmd.iteritems():
1582 if iwd[1].path == path:
1583 return iwd[0]
1584 log.debug('get_wd: unknown path %s' % path)
1585
1587 """
1588 Returns the path associated to WD, if WD is unknown
1589 None is returned.
1590
1591 @param wd: watch descriptor.
1592 @type wd: int
1593 @return: path or None.
1594 @rtype: string or None
1595 """
1596 watch_ = self._wmd.get(wd)
1597 if watch_:
1598 return watch_.path
1599 log.debug('get_path: unknown WD %d' % wd)
1600
1602 """
1603 Yields each subdirectories of top, doesn't follow symlinks.
1604 If rec is false, only yield top.
1605
1606 @param top: root directory.
1607 @type top: string
1608 @param rec: recursive flag.
1609 @type rec: bool
1610 @return: path of one subdirectory.
1611 @rtype: string
1612 """
1613 if not rec or os.path.islink(top) or not os.path.isdir(top):
1614 yield top
1615 else:
1616 for root, dirs, files in os.walk(top):
1617 yield root
1618
1619 - def rm_watch(self, wd, rec=False, quiet=True):
1620 """
1621 Removes watch(s).
1622
1623 @param wd: Watch Descriptor of the file or directory to unwatch.
1624 Also accepts a list of WDs.
1625 @type wd: int or list of int.
1626 @param rec: Recursively removes watches on every already watched
1627 subdirectories and subfiles.
1628 @type rec: bool
1629 @param quiet: if True raise an WatchManagerError exception on
1630 error. See example not_quiet.py
1631 @type quiet: bool
1632 @return: dict of watch descriptors associated to booleans values.
1633 True if the corresponding wd has been successfully
1634 removed, False otherwise.
1635 @rtype: dict of int: bool
1636 """
1637 lwd = self.__format_param(wd)
1638 if rec:
1639 lwd = self.__get_sub_rec(lwd)
1640
1641 ret_ = {}
1642 for awd in lwd:
1643
1644 wd_ = LIBC.inotify_rm_watch(self._fd, awd)
1645 if wd_ < 0:
1646 ret_[awd] = False
1647 err = 'rm_watch: cannot remove WD=%d' % awd
1648 if quiet:
1649 log.error(err)
1650 continue
1651 raise WatchManagerError(err, ret_)
1652
1653 ret_[awd] = True
1654 log.debug('watch WD=%d (%s) removed' % (awd, self.get_path(awd)))
1655 return ret_
1656
1657
1659 """
1660 Watch a transient file, which will be created and deleted frequently
1661 over time (e.g. pid file).
1662
1663 @attention: Under the call to this function it will be impossible
1664 to correctly watch the events triggered into the same
1665 base directory than the directory where is located this watched
1666 transient file. For instance it would actually be wrong to make these
1667 two successive calls: wm.watch_transient_file('/var/run/foo.pid', ...)
1668 and wm.add_watch('/var/run/', ...)
1669
1670 @param filename: Filename.
1671 @type filename: string
1672 @param mask: Bitmask of events, should contain IN_CREATE and IN_DELETE.
1673 @type mask: int
1674 @param proc_class: ProcessEvent (or of one of its subclass), beware of
1675 accepting a ProcessEvent's instance as argument into
1676 __init__, see transient_file.py example for more
1677 details.
1678 @type proc_class: ProcessEvent's instance or of one of its subclasses.
1679 @return: See add_watch().
1680 @rtype: See add_watch().
1681 """
1682 dirname = os.path.dirname(filename)
1683 if dirname == '':
1684 return {}
1685 basename = os.path.basename(filename)
1686
1687 mask |= IN_CREATE | IN_DELETE
1688
1689 def cmp_name(event):
1690 return basename == event.name
1691 return self.add_watch(dirname, mask,
1692 proc_fun=proc_class(ChainIfTrue(func=cmp_name)),
1693 rec=False,
1694 auto_add=False, do_glob=False)
1695
1698 normal = "\033[0m"
1699 black = "\033[30m"
1700 red = "\033[31m"
1701 green = "\033[32m"
1702 yellow = "\033[33m"
1703 blue = "\033[34m"
1704 purple = "\033[35m"
1705 cyan = "\033[36m"
1706 bold = "\033[1m"
1707 uline = "\033[4m"
1708 blink = "\033[5m"
1709 invert = "\033[7m"
1710
1711 @staticmethod
1714
1715 @staticmethod
1720
1721 @staticmethod
1724
1725 @staticmethod
1728
1729 @staticmethod
1731 if not isinstance(s, str):
1732 s = str(s)
1733 try:
1734 color_attr = getattr(Color, color)
1735 except AttributeError:
1736 return s
1737 return color_attr + s + Color.normal
1738
1741
1742
1743
1744
1745
1746 from optparse import OptionParser
1747
1748 usage = "usage: %prog [options] [path1] [path2] [pathn]"
1749
1750 parser = OptionParser(usage=usage)
1751 parser.add_option("-v", "--verbose", action="store_true",
1752 dest="verbose", help="Verbose mode")
1753 parser.add_option("-r", "--recursive", action="store_true",
1754 dest="recursive",
1755 help="Add watches recursively on paths")
1756 parser.add_option("-a", "--auto_add", action="store_true",
1757 dest="auto_add",
1758 help="Automatically add watches on new directories")
1759 parser.add_option("-e", "--events-list", metavar="EVENT[,...]",
1760 dest="events_list",
1761 help=("A comma-separated list of events to watch for - "
1762 "see the documentation for valid options (defaults"
1763 " to everything)"))
1764 parser.add_option("-s", "--stats", action="store_true",
1765 dest="stats",
1766 help="Display statistics")
1767
1768 (options, args) = parser.parse_args()
1769
1770 if options.verbose:
1771 log.setLevel(10)
1772
1773 if len(args) < 1:
1774 path = '/tmp'
1775 else:
1776 path = args
1777
1778
1779 wm = WatchManager()
1780
1781 if options.stats:
1782 notifier = Notifier(wm, default_proc_fun=Stats(), read_freq=5)
1783 else:
1784 notifier = Notifier(wm)
1785
1786
1787 mask = 0
1788 if options.events_list:
1789 events_list = options.events_list.split(',')
1790 for ev in events_list:
1791 evcode = EventsCodes.ALL_FLAGS.get(ev, 0)
1792 if evcode:
1793 mask |= evcode
1794 else:
1795 parser.error("The event '%s' specified with option -e"
1796 " is not valid" % ev)
1797 else:
1798 mask = ALL_EVENTS
1799
1800
1801 cb_fun = None
1802 if options.stats:
1803 def cb(s):
1804 print('%s\n%s\n' % (repr(s.proc_fun()),
1805 s.proc_fun()))
1806 cb_fun = cb
1807
1808 log.debug('Start monitoring %s, (press c^c to halt pyinotify)' % path)
1809
1810 wm.add_watch(path, mask, rec=options.recursive, auto_add=options.auto_add)
1811
1812 notifier.loop(callback=cb_fun)
1813
1814
1815 if __name__ == '__main__':
1816 command_line()
1817