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