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