1#!/usr/bin/env python 2 3# pyinotify.py - python interface to inotify 4# Copyright (c) 2005-2015 Sebastien Martini <seb@dbzteam.org> 5# 6# Permission is hereby granted, free of charge, to any person obtaining a copy 7# of this software and associated documentation files (the "Software"), to deal 8# in the Software without restriction, including without limitation the rights 9# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10# copies of the Software, and to permit persons to whom the Software is 11# furnished to do so, subject to the following conditions: 12# 13# The above copyright notice and this permission notice shall be included in 14# all copies or substantial portions of the Software. 15# 16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22# THE SOFTWARE. 23""" 24pyinotify 25 26@author: Sebastien Martini 27@license: MIT License 28@contact: seb@dbzteam.org 29""" 30 31class PyinotifyError(Exception): 32 """Indicates exceptions raised by a Pyinotify class.""" 33 pass 34 35 36class UnsupportedPythonVersionError(PyinotifyError): 37 """ 38 Raised on unsupported Python versions. 39 """ 40 def __init__(self, version): 41 """ 42 @param version: Current Python version 43 @type version: string 44 """ 45 PyinotifyError.__init__(self, 46 ('Python %s is unsupported, requires ' 47 'at least Python 3.0') % version) 48 49 50# Check Python version 51import sys 52if sys.version_info < (3, 0): 53 raise UnsupportedPythonVersionError(sys.version) 54 55 56# Import directives 57import threading 58import os 59import select 60import struct 61import fcntl 62import errno 63import termios 64import array 65import logging 66import atexit 67from collections import deque 68from datetime import datetime, timedelta 69import time 70import re 71import asyncore 72import glob 73import locale 74import subprocess 75 76try: 77 from functools import reduce 78except ImportError: 79 pass # Will fail on Python 2.4 which has reduce() builtin anyway. 80 81try: 82 import ctypes 83 import ctypes.util 84except ImportError: 85 ctypes = None 86 87try: 88 import inotify_syscalls 89except ImportError: 90 inotify_syscalls = None 91 92 93__author__ = "seb@dbzteam.org (Sebastien Martini)" 94 95__version__ = "0.9.6" 96 97 98# Compatibity mode: set to True to improve compatibility with 99# Pyinotify 0.7.1. Do not set this variable yourself, call the 100# function compatibility_mode() instead. 101COMPATIBILITY_MODE = False 102 103 104class InotifyBindingNotFoundError(PyinotifyError): 105 """ 106 Raised when no inotify support couldn't be found. 107 """ 108 def __init__(self): 109 err = "Couldn't find any inotify binding" 110 PyinotifyError.__init__(self, err) 111 112 113class INotifyWrapper: 114 """ 115 Abstract class wrapping access to inotify's functions. This is an 116 internal class. 117 """ 118 @staticmethod 119 def create(): 120 """ 121 Factory method instanciating and returning the right wrapper. 122 """ 123 # First, try to use ctypes. 124 if ctypes: 125 inotify = _CtypesLibcINotifyWrapper() 126 if inotify.init(): 127 return inotify 128 # Second, see if C extension is compiled. 129 if inotify_syscalls: 130 inotify = _INotifySyscallsWrapper() 131 if inotify.init(): 132 return inotify 133 134 def get_errno(self): 135 """ 136 Return None is no errno code is available. 137 """ 138 return self._get_errno() 139 140 def str_errno(self): 141 code = self.get_errno() 142 if code is None: 143 return 'Errno: no errno support' 144 return 'Errno=%s (%s)' % (os.strerror(code), errno.errorcode[code]) 145 146 def inotify_init(self): 147 return self._inotify_init() 148 149 def inotify_add_watch(self, fd, pathname, mask): 150 # Unicode strings must be encoded to string prior to calling this 151 # method. 152 assert isinstance(pathname, str) 153 return self._inotify_add_watch(fd, pathname, mask) 154 155 def inotify_rm_watch(self, fd, wd): 156 return self._inotify_rm_watch(fd, wd) 157 158 159class _INotifySyscallsWrapper(INotifyWrapper): 160 def __init__(self): 161 # Stores the last errno value. 162 self._last_errno = None 163 164 def init(self): 165 assert inotify_syscalls 166 return True 167 168 def _get_errno(self): 169 return self._last_errno 170 171 def _inotify_init(self): 172 try: 173 fd = inotify_syscalls.inotify_init() 174 except IOError as err: 175 self._last_errno = err.errno 176 return -1 177 return fd 178 179 def _inotify_add_watch(self, fd, pathname, mask): 180 try: 181 wd = inotify_syscalls.inotify_add_watch(fd, pathname, mask) 182 except IOError as err: 183 self._last_errno = err.errno 184 return -1 185 return wd 186 187 def _inotify_rm_watch(self, fd, wd): 188 try: 189 ret = inotify_syscalls.inotify_rm_watch(fd, wd) 190 except IOError as err: 191 self._last_errno = err.errno 192 return -1 193 return ret 194 195 196class _CtypesLibcINotifyWrapper(INotifyWrapper): 197 def __init__(self): 198 self._libc = None 199 self._get_errno_func = None 200 201 def init(self): 202 assert ctypes 203 204 try_libc_name = 'c' 205 if sys.platform.startswith('freebsd') or sys.platform.startswith('dragonfly'): 206 try_libc_name = 'inotify' 207 208 libc_name = None 209 try: 210 libc_name = ctypes.util.find_library(try_libc_name) 211 except (OSError, IOError): 212 pass # Will attemp to load it with None anyway. 213 214 self._libc = ctypes.CDLL(libc_name, use_errno=True) 215 self._get_errno_func = ctypes.get_errno 216 217 # Eventually check that libc has needed inotify bindings. 218 if (not hasattr(self._libc, 'inotify_init') or 219 not hasattr(self._libc, 'inotify_add_watch') or 220 not hasattr(self._libc, 'inotify_rm_watch')): 221 return False 222 223 self._libc.inotify_init.argtypes = [] 224 self._libc.inotify_init.restype = ctypes.c_int 225 self._libc.inotify_add_watch.argtypes = [ctypes.c_int, ctypes.c_char_p, 226 ctypes.c_uint32] 227 self._libc.inotify_add_watch.restype = ctypes.c_int 228 self._libc.inotify_rm_watch.argtypes = [ctypes.c_int, ctypes.c_int] 229 self._libc.inotify_rm_watch.restype = ctypes.c_int 230 return True 231 232 def _get_errno(self): 233 assert self._get_errno_func 234 return self._get_errno_func() 235 236 def _inotify_init(self): 237 assert self._libc is not None 238 return self._libc.inotify_init() 239 240 def _inotify_add_watch(self, fd, pathname, mask): 241 assert self._libc is not None 242 # Encodes path to a bytes string. This conversion seems required because 243 # ctypes.create_string_buffer seems to manipulate bytes internally. 244 # Moreover it seems that inotify_add_watch does not work very well when 245 # it receives an ctypes.create_unicode_buffer instance as argument. 246 pathname = pathname.encode(sys.getfilesystemencoding()) 247 pathname = ctypes.create_string_buffer(pathname) 248 return self._libc.inotify_add_watch(fd, pathname, mask) 249 250 def _inotify_rm_watch(self, fd, wd): 251 assert self._libc is not None 252 return self._libc.inotify_rm_watch(fd, wd) 253 254 255# Logging 256def logger_init(): 257 """Initialize logger instance.""" 258 log = logging.getLogger("pyinotify") 259 console_handler = logging.StreamHandler() 260 console_handler.setFormatter( 261 logging.Formatter("[%(asctime)s %(name)s %(levelname)s] %(message)s")) 262 log.addHandler(console_handler) 263 log.setLevel(20) 264 return log 265 266log = logger_init() 267 268 269# inotify's variables 270class ProcINotify: 271 """ 272 Access (read, write) inotify's variables through /proc/sys/. Note that 273 usually it requires administrator rights to update them. 274 275 Examples: 276 - Read max_queued_events attribute: myvar = max_queued_events.value 277 - Update max_queued_events attribute: max_queued_events.value = 42 278 """ 279 def __init__(self, attr): 280 self._base = "/proc/sys/fs/inotify" 281 self._attr = attr 282 283 def get_val(self): 284 """ 285 Gets attribute's value. 286 287 @return: stored value. 288 @rtype: int 289 @raise IOError: if corresponding file in /proc/sys cannot be read. 290 """ 291 with open(os.path.join(self._base, self._attr), 'r') as file_obj: 292 return int(file_obj.readline()) 293 294 def set_val(self, nval): 295 """ 296 Sets new attribute's value. 297 298 @param nval: replaces current value by nval. 299 @type nval: int 300 @raise IOError: if corresponding file in /proc/sys cannot be written. 301 """ 302 with open(os.path.join(self._base, self._attr), 'w') as file_obj: 303 file_obj.write(str(nval) + '\n') 304 305 value = property(get_val, set_val) 306 307 def __repr__(self): 308 return '<%s=%d>' % (self._attr, self.get_val()) 309 310 311# Inotify's variables 312# 313# Note: may raise IOError if the corresponding value in /proc/sys 314# cannot be accessed. 315# 316# Examples: 317# - read: myvar = max_queued_events.value 318# - update: max_queued_events.value = 42 319# 320for attrname in ('max_queued_events', 'max_user_instances', 'max_user_watches'): 321 globals()[attrname] = ProcINotify(attrname) 322 323 324class EventsCodes: 325 """ 326 Set of codes corresponding to each kind of events. 327 Some of these flags are used to communicate with inotify, whereas 328 the others are sent to userspace by inotify notifying some events. 329 330 @cvar IN_ACCESS: File was accessed. 331 @type IN_ACCESS: int 332 @cvar IN_MODIFY: File was modified. 333 @type IN_MODIFY: int 334 @cvar IN_ATTRIB: Metadata changed. 335 @type IN_ATTRIB: int 336 @cvar IN_CLOSE_WRITE: Writtable file was closed. 337 @type IN_CLOSE_WRITE: int 338 @cvar IN_CLOSE_NOWRITE: Unwrittable file closed. 339 @type IN_CLOSE_NOWRITE: int 340 @cvar IN_OPEN: File was opened. 341 @type IN_OPEN: int 342 @cvar IN_MOVED_FROM: File was moved from X. 343 @type IN_MOVED_FROM: int 344 @cvar IN_MOVED_TO: File was moved to Y. 345 @type IN_MOVED_TO: int 346 @cvar IN_CREATE: Subfile was created. 347 @type IN_CREATE: int 348 @cvar IN_DELETE: Subfile was deleted. 349 @type IN_DELETE: int 350 @cvar IN_DELETE_SELF: Self (watched item itself) was deleted. 351 @type IN_DELETE_SELF: int 352 @cvar IN_MOVE_SELF: Self (watched item itself) was moved. 353 @type IN_MOVE_SELF: int 354 @cvar IN_UNMOUNT: Backing fs was unmounted. 355 @type IN_UNMOUNT: int 356 @cvar IN_Q_OVERFLOW: Event queued overflowed. 357 @type IN_Q_OVERFLOW: int 358 @cvar IN_IGNORED: File was ignored. 359 @type IN_IGNORED: int 360 @cvar IN_ONLYDIR: only watch the path if it is a directory (new 361 in kernel 2.6.15). 362 @type IN_ONLYDIR: int 363 @cvar IN_DONT_FOLLOW: don't follow a symlink (new in kernel 2.6.15). 364 IN_ONLYDIR we can make sure that we don't watch 365 the target of symlinks. 366 @type IN_DONT_FOLLOW: int 367 @cvar IN_EXCL_UNLINK: Events are not generated for children after they 368 have been unlinked from the watched directory. 369 (new in kernel 2.6.36). 370 @type IN_EXCL_UNLINK: int 371 @cvar IN_MASK_ADD: add to the mask of an already existing watch (new 372 in kernel 2.6.14). 373 @type IN_MASK_ADD: int 374 @cvar IN_ISDIR: Event occurred against dir. 375 @type IN_ISDIR: int 376 @cvar IN_ONESHOT: Only send event once. 377 @type IN_ONESHOT: int 378 @cvar ALL_EVENTS: Alias for considering all of the events. 379 @type ALL_EVENTS: int 380 """ 381 382 # The idea here is 'configuration-as-code' - this way, we get our nice class 383 # constants, but we also get nice human-friendly text mappings to do lookups 384 # against as well, for free: 385 FLAG_COLLECTIONS = {'OP_FLAGS': { 386 'IN_ACCESS' : 0x00000001, # File was accessed 387 'IN_MODIFY' : 0x00000002, # File was modified 388 'IN_ATTRIB' : 0x00000004, # Metadata changed 389 'IN_CLOSE_WRITE' : 0x00000008, # Writable file was closed 390 'IN_CLOSE_NOWRITE' : 0x00000010, # Unwritable file closed 391 'IN_OPEN' : 0x00000020, # File was opened 392 'IN_MOVED_FROM' : 0x00000040, # File was moved from X 393 'IN_MOVED_TO' : 0x00000080, # File was moved to Y 394 'IN_CREATE' : 0x00000100, # Subfile was created 395 'IN_DELETE' : 0x00000200, # Subfile was deleted 396 'IN_DELETE_SELF' : 0x00000400, # Self (watched item itself) 397 # was deleted 398 'IN_MOVE_SELF' : 0x00000800, # Self (watched item itself) was moved 399 }, 400 'EVENT_FLAGS': { 401 'IN_UNMOUNT' : 0x00002000, # Backing fs was unmounted 402 'IN_Q_OVERFLOW' : 0x00004000, # Event queued overflowed 403 'IN_IGNORED' : 0x00008000, # File was ignored 404 }, 405 'SPECIAL_FLAGS': { 406 'IN_ONLYDIR' : 0x01000000, # only watch the path if it is a 407 # directory 408 'IN_DONT_FOLLOW' : 0x02000000, # don't follow a symlink 409 'IN_EXCL_UNLINK' : 0x04000000, # exclude events on unlinked objects 410 'IN_MASK_ADD' : 0x20000000, # add to the mask of an already 411 # existing watch 412 'IN_ISDIR' : 0x40000000, # event occurred against dir 413 'IN_ONESHOT' : 0x80000000, # only send event once 414 }, 415 } 416 417 def maskname(mask): 418 """ 419 Returns the event name associated to mask. IN_ISDIR is appended to 420 the result when appropriate. Note: only one event is returned, because 421 only one event can be raised at a given time. 422 423 @param mask: mask. 424 @type mask: int 425 @return: event name. 426 @rtype: str 427 """ 428 ms = mask 429 name = '%s' 430 if mask & IN_ISDIR: 431 ms = mask - IN_ISDIR 432 name = '%s|IN_ISDIR' 433 return name % EventsCodes.ALL_VALUES[ms] 434 435 maskname = staticmethod(maskname) 436 437 438# So let's now turn the configuration into code 439EventsCodes.ALL_FLAGS = {} 440EventsCodes.ALL_VALUES = {} 441for flagc, valc in EventsCodes.FLAG_COLLECTIONS.items(): 442 # Make the collections' members directly accessible through the 443 # class dictionary 444 setattr(EventsCodes, flagc, valc) 445 446 # Collect all the flags under a common umbrella 447 EventsCodes.ALL_FLAGS.update(valc) 448 449 # Make the individual masks accessible as 'constants' at globals() scope 450 # and masknames accessible by values. 451 for name, val in valc.items(): 452 globals()[name] = val 453 EventsCodes.ALL_VALUES[val] = name 454 455 456# all 'normal' events 457ALL_EVENTS = reduce(lambda x, y: x | y, EventsCodes.OP_FLAGS.values()) 458EventsCodes.ALL_FLAGS['ALL_EVENTS'] = ALL_EVENTS 459EventsCodes.ALL_VALUES[ALL_EVENTS] = 'ALL_EVENTS' 460 461 462class _Event: 463 """ 464 Event structure, represent events raised by the system. This 465 is the base class and should be subclassed. 466 467 """ 468 def __init__(self, dict_): 469 """ 470 Attach attributes (contained in dict_) to self. 471 472 @param dict_: Set of attributes. 473 @type dict_: dictionary 474 """ 475 for tpl in dict_.items(): 476 setattr(self, *tpl) 477 478 def __repr__(self): 479 """ 480 @return: Generic event string representation. 481 @rtype: str 482 """ 483 s = '' 484 for attr, value in sorted(self.__dict__.items(), key=lambda x: x[0]): 485 if attr.startswith('_'): 486 continue 487 if attr == 'mask': 488 value = hex(getattr(self, attr)) 489 elif isinstance(value, str) and not value: 490 value = "''" 491 s += ' %s%s%s' % (output_format.field_name(attr), 492 output_format.punctuation('='), 493 output_format.field_value(value)) 494 495 s = '%s%s%s %s' % (output_format.punctuation('<'), 496 output_format.class_name(self.__class__.__name__), 497 s, 498 output_format.punctuation('>')) 499 return s 500 501 def __str__(self): 502 return repr(self) 503 504 505class _RawEvent(_Event): 506 """ 507 Raw event, it contains only the informations provided by the system. 508 It doesn't infer anything. 509 """ 510 def __init__(self, wd, mask, cookie, name): 511 """ 512 @param wd: Watch Descriptor. 513 @type wd: int 514 @param mask: Bitmask of events. 515 @type mask: int 516 @param cookie: Cookie. 517 @type cookie: int 518 @param name: Basename of the file or directory against which the 519 event was raised in case where the watched directory 520 is the parent directory. None if the event was raised 521 on the watched item itself. 522 @type name: string or None 523 """ 524 # Use this variable to cache the result of str(self), this object 525 # is immutable. 526 self._str = None 527 # name: remove trailing '\0' 528 d = {'wd': wd, 529 'mask': mask, 530 'cookie': cookie, 531 'name': name.rstrip('\0')} 532 _Event.__init__(self, d) 533 log.debug(str(self)) 534 535 def __str__(self): 536 if self._str is None: 537 self._str = _Event.__str__(self) 538 return self._str 539 540 541class Event(_Event): 542 """ 543 This class contains all the useful informations about the observed 544 event. However, the presence of each field is not guaranteed and 545 depends on the type of event. In effect, some fields are irrelevant 546 for some kind of event (for example 'cookie' is meaningless for 547 IN_CREATE whereas it is mandatory for IN_MOVE_TO). 548 549 The possible fields are: 550 - wd (int): Watch Descriptor. 551 - mask (int): Mask. 552 - maskname (str): Readable event name. 553 - path (str): path of the file or directory being watched. 554 - name (str): Basename of the file or directory against which the 555 event was raised in case where the watched directory 556 is the parent directory. None if the event was raised 557 on the watched item itself. This field is always provided 558 even if the string is ''. 559 - pathname (str): Concatenation of 'path' and 'name'. 560 - src_pathname (str): Only present for IN_MOVED_TO events and only in 561 the case where IN_MOVED_FROM events are watched too. Holds the 562 source pathname from where pathname was moved from. 563 - cookie (int): Cookie. 564 - dir (bool): True if the event was raised against a directory. 565 566 """ 567 def __init__(self, raw): 568 """ 569 Concretely, this is the raw event plus inferred infos. 570 """ 571 _Event.__init__(self, raw) 572 self.maskname = EventsCodes.maskname(self.mask) 573 if COMPATIBILITY_MODE: 574 self.event_name = self.maskname 575 try: 576 if self.name: 577 self.pathname = os.path.abspath(os.path.join(self.path, 578 self.name)) 579 else: 580 self.pathname = os.path.abspath(self.path) 581 except AttributeError as err: 582 # Usually it is not an error some events are perfectly valids 583 # despite the lack of these attributes. 584 log.debug(err) 585 586 587class ProcessEventError(PyinotifyError): 588 """ 589 ProcessEventError Exception. Raised on ProcessEvent error. 590 """ 591 def __init__(self, err): 592 """ 593 @param err: Exception error description. 594 @type err: string 595 """ 596 PyinotifyError.__init__(self, err) 597 598 599class _ProcessEvent: 600 """ 601 Abstract processing event class. 602 """ 603 def __call__(self, event): 604 """ 605 To behave like a functor the object must be callable. 606 This method is a dispatch method. Its lookup order is: 607 1. process_MASKNAME method 608 2. process_FAMILY_NAME method 609 3. otherwise calls process_default 610 611 @param event: Event to be processed. 612 @type event: Event object 613 @return: By convention when used from the ProcessEvent class: 614 - Returning False or None (default value) means keep on 615 executing next chained functors (see chain.py example). 616 - Returning True instead means do not execute next 617 processing functions. 618 @rtype: bool 619 @raise ProcessEventError: Event object undispatchable, 620 unknown event. 621 """ 622 stripped_mask = event.mask - (event.mask & IN_ISDIR) 623 maskname = EventsCodes.ALL_VALUES.get(stripped_mask) 624 if maskname is None: 625 raise ProcessEventError("Unknown mask 0x%08x" % stripped_mask) 626 627 # 1- look for process_MASKNAME 628 meth = getattr(self, 'process_' + maskname, None) 629 if meth is not None: 630 return meth(event) 631 # 2- look for process_FAMILY_NAME 632 meth = getattr(self, 'process_IN_' + maskname.split('_')[1], None) 633 if meth is not None: 634 return meth(event) 635 # 3- default call method process_default 636 return self.process_default(event) 637 638 def __repr__(self): 639 return '<%s>' % self.__class__.__name__ 640 641 642class _SysProcessEvent(_ProcessEvent): 643 """ 644 There is three kind of processing according to each event: 645 646 1. special handling (deletion from internal container, bug, ...). 647 2. default treatment: which is applied to the majority of events. 648 3. IN_ISDIR is never sent alone, he is piggybacked with a standard 649 event, he is not processed as the others events, instead, its 650 value is captured and appropriately aggregated to dst event. 651 """ 652 def __init__(self, wm, notifier): 653 """ 654 655 @param wm: Watch Manager. 656 @type wm: WatchManager instance 657 @param notifier: Notifier. 658 @type notifier: Notifier instance 659 """ 660 self._watch_manager = wm # watch manager 661 self._notifier = notifier # notifier 662 self._mv_cookie = {} # {cookie(int): (src_path(str), date), ...} 663 self._mv = {} # {src_path(str): (dst_path(str), date), ...} 664 665 def cleanup(self): 666 """ 667 Cleanup (delete) old (>1mn) records contained in self._mv_cookie 668 and self._mv. 669 """ 670 date_cur_ = datetime.now() 671 for seq in (self._mv_cookie, self._mv): 672 for k in list(seq.keys()): 673 if (date_cur_ - seq[k][1]) > timedelta(minutes=1): 674 log.debug('Cleanup: deleting entry %s', seq[k][0]) 675 del seq[k] 676 677 def process_IN_CREATE(self, raw_event): 678 """ 679 If the event affects a directory and the auto_add flag of the 680 targetted watch is set to True, a new watch is added on this 681 new directory, with the same attribute values than those of 682 this watch. 683 """ 684 if raw_event.mask & IN_ISDIR: 685 watch_ = self._watch_manager.get_watch(raw_event.wd) 686 created_dir = os.path.join(watch_.path, raw_event.name) 687 if watch_.auto_add and not watch_.exclude_filter(created_dir): 688 addw = self._watch_manager.add_watch 689 # The newly monitored directory inherits attributes from its 690 # parent directory. 691 addw_ret = addw(created_dir, watch_.mask, 692 proc_fun=watch_.proc_fun, 693 rec=False, auto_add=watch_.auto_add, 694 exclude_filter=watch_.exclude_filter) 695 696 # Trick to handle mkdir -p /d1/d2/t3 where d1 is watched and 697 # d2 and t3 (directory or file) are created. 698 # Since the directory d2 is new, then everything inside it must 699 # also be new. 700 created_dir_wd = addw_ret.get(created_dir) 701 if ((created_dir_wd is not None) and (created_dir_wd > 0) and 702 os.path.isdir(created_dir)): 703 try: 704 for name in os.listdir(created_dir): 705 inner = os.path.join(created_dir, name) 706 if self._watch_manager.get_wd(inner) is not None: 707 continue 708 # Generate (simulate) creation events for sub- 709 # directories and files. 710 if os.path.isfile(inner): 711 # symlinks are handled as files. 712 flags = IN_CREATE 713 elif os.path.isdir(inner): 714 flags = IN_CREATE | IN_ISDIR 715 else: 716 # This path should not be taken. 717 continue 718 rawevent = _RawEvent(created_dir_wd, flags, 0, name) 719 self._notifier.append_event(rawevent) 720 except OSError as err: 721 msg = "process_IN_CREATE, invalid directory: %s" 722 log.debug(msg % str(err)) 723 return self.process_default(raw_event) 724 725 def process_IN_MOVED_FROM(self, raw_event): 726 """ 727 Map the cookie with the source path (+ date for cleaning). 728 """ 729 watch_ = self._watch_manager.get_watch(raw_event.wd) 730 path_ = watch_.path 731 src_path = os.path.normpath(os.path.join(path_, raw_event.name)) 732 self._mv_cookie[raw_event.cookie] = (src_path, datetime.now()) 733 return self.process_default(raw_event, {'cookie': raw_event.cookie}) 734 735 def process_IN_MOVED_TO(self, raw_event): 736 """ 737 Map the source path with the destination path (+ date for 738 cleaning). 739 """ 740 watch_ = self._watch_manager.get_watch(raw_event.wd) 741 path_ = watch_.path 742 dst_path = os.path.normpath(os.path.join(path_, raw_event.name)) 743 mv_ = self._mv_cookie.get(raw_event.cookie) 744 to_append = {'cookie': raw_event.cookie} 745 if mv_ is not None: 746 self._mv[mv_[0]] = (dst_path, datetime.now()) 747 # Let's assume that IN_MOVED_FROM event is always queued before 748 # that its associated (they share a common cookie) IN_MOVED_TO 749 # event is queued itself. It is then possible in that scenario 750 # to provide as additional information to the IN_MOVED_TO event 751 # the original pathname of the moved file/directory. 752 to_append['src_pathname'] = mv_[0] 753 elif (raw_event.mask & IN_ISDIR and watch_.auto_add and 754 not watch_.exclude_filter(dst_path)): 755 # We got a diretory that's "moved in" from an unknown source and 756 # auto_add is enabled. Manually add watches to the inner subtrees. 757 # The newly monitored directory inherits attributes from its 758 # parent directory. 759 self._watch_manager.add_watch(dst_path, watch_.mask, 760 proc_fun=watch_.proc_fun, 761 rec=True, auto_add=True, 762 exclude_filter=watch_.exclude_filter) 763 return self.process_default(raw_event, to_append) 764 765 def process_IN_MOVE_SELF(self, raw_event): 766 """ 767 STATUS: the following bug has been fixed in recent kernels (FIXME: 768 which version ?). Now it raises IN_DELETE_SELF instead. 769 770 Old kernels were bugged, this event raised when the watched item 771 were moved, so we had to update its path, but under some circumstances 772 it was impossible: if its parent directory and its destination 773 directory wasn't watched. The kernel (see include/linux/fsnotify.h) 774 doesn't bring us enough informations like the destination path of 775 moved items. 776 """ 777 watch_ = self._watch_manager.get_watch(raw_event.wd) 778 src_path = watch_.path 779 mv_ = self._mv.get(src_path) 780 if mv_: 781 dest_path = mv_[0] 782 watch_.path = dest_path 783 # add the separator to the source path to avoid overlapping 784 # path issue when testing with startswith() 785 src_path += os.path.sep 786 src_path_len = len(src_path) 787 # The next loop renames all watches with src_path as base path. 788 # It seems that IN_MOVE_SELF does not provide IN_ISDIR information 789 # therefore the next loop is iterated even if raw_event is a file. 790 for w in self._watch_manager.watches.values(): 791 if w.path.startswith(src_path): 792 # Note that dest_path is a normalized path. 793 w.path = os.path.join(dest_path, w.path[src_path_len:]) 794 else: 795 log.error("The pathname '%s' of this watch %s has probably changed " 796 "and couldn't be updated, so it cannot be trusted " 797 "anymore. To fix this error move directories/files only " 798 "between watched parents directories, in this case e.g. " 799 "put a watch on '%s'.", 800 watch_.path, watch_, 801 os.path.normpath(os.path.join(watch_.path, 802 os.path.pardir))) 803 if not watch_.path.endswith('-unknown-path'): 804 watch_.path += '-unknown-path' 805 return self.process_default(raw_event) 806 807 def process_IN_Q_OVERFLOW(self, raw_event): 808 """ 809 Only signal an overflow, most of the common flags are irrelevant 810 for this event (path, wd, name). 811 """ 812 return Event({'mask': raw_event.mask}) 813 814 def process_IN_IGNORED(self, raw_event): 815 """ 816 The watch descriptor raised by this event is now ignored (forever), 817 it can be safely deleted from the watch manager dictionary. 818 After this event we can be sure that neither the event queue nor 819 the system will raise an event associated to this wd again. 820 """ 821 event_ = self.process_default(raw_event) 822 self._watch_manager.del_watch(raw_event.wd) 823 return event_ 824 825 def process_default(self, raw_event, to_append=None): 826 """ 827 Commons handling for the followings events: 828 829 IN_ACCESS, IN_MODIFY, IN_ATTRIB, IN_CLOSE_WRITE, IN_CLOSE_NOWRITE, 830 IN_OPEN, IN_DELETE, IN_DELETE_SELF, IN_UNMOUNT. 831 """ 832 watch_ = self._watch_manager.get_watch(raw_event.wd) 833 if raw_event.mask & (IN_DELETE_SELF | IN_MOVE_SELF): 834 # Unfornulately this information is not provided by the kernel 835 dir_ = watch_.dir 836 else: 837 dir_ = bool(raw_event.mask & IN_ISDIR) 838 dict_ = {'wd': raw_event.wd, 839 'mask': raw_event.mask, 840 'path': watch_.path, 841 'name': raw_event.name, 842 'dir': dir_} 843 if COMPATIBILITY_MODE: 844 dict_['is_dir'] = dir_ 845 if to_append is not None: 846 dict_.update(to_append) 847 return Event(dict_) 848 849 850class ProcessEvent(_ProcessEvent): 851 """ 852 Process events objects, can be specialized via subclassing, thus its 853 behavior can be overriden: 854 855 Note: you should not override __init__ in your subclass instead define 856 a my_init() method, this method will be called automatically from the 857 constructor of this class with its optionals parameters. 858 859 1. Provide specialized individual methods, e.g. process_IN_DELETE for 860 processing a precise type of event (e.g. IN_DELETE in this case). 861 2. Or/and provide methods for processing events by 'family', e.g. 862 process_IN_CLOSE method will process both IN_CLOSE_WRITE and 863 IN_CLOSE_NOWRITE events (if process_IN_CLOSE_WRITE and 864 process_IN_CLOSE_NOWRITE aren't defined though). 865 3. Or/and override process_default for catching and processing all 866 the remaining types of events. 867 """ 868 pevent = None 869 870 def __init__(self, pevent=None, **kargs): 871 """ 872 Enable chaining of ProcessEvent instances. 873 874 @param pevent: Optional callable object, will be called on event 875 processing (before self). 876 @type pevent: callable 877 @param kargs: This constructor is implemented as a template method 878 delegating its optionals keyworded arguments to the 879 method my_init(). 880 @type kargs: dict 881 """ 882 self.pevent = pevent 883 self.my_init(**kargs) 884 885 def my_init(self, **kargs): 886 """ 887 This method is called from ProcessEvent.__init__(). This method is 888 empty here and must be redefined to be useful. In effect, if you 889 need to specifically initialize your subclass' instance then you 890 just have to override this method in your subclass. Then all the 891 keyworded arguments passed to ProcessEvent.__init__() will be 892 transmitted as parameters to this method. Beware you MUST pass 893 keyword arguments though. 894 895 @param kargs: optional delegated arguments from __init__(). 896 @type kargs: dict 897 """ 898 pass 899 900 def __call__(self, event): 901 stop_chaining = False 902 if self.pevent is not None: 903 # By default methods return None so we set as guideline 904 # that methods asking for stop chaining must explicitely 905 # return non None or non False values, otherwise the default 906 # behavior will be to accept chain call to the corresponding 907 # local method. 908 stop_chaining = self.pevent(event) 909 if not stop_chaining: 910 return _ProcessEvent.__call__(self, event) 911 912 def nested_pevent(self): 913 return self.pevent 914 915 def process_IN_Q_OVERFLOW(self, event): 916 """ 917 By default this method only reports warning messages, you can overredide 918 it by subclassing ProcessEvent and implement your own 919 process_IN_Q_OVERFLOW method. The actions you can take on receiving this 920 event is either to update the variable max_queued_events in order to 921 handle more simultaneous events or to modify your code in order to 922 accomplish a better filtering diminishing the number of raised events. 923 Because this method is defined, IN_Q_OVERFLOW will never get 924 transmitted as arguments to process_default calls. 925 926 @param event: IN_Q_OVERFLOW event. 927 @type event: dict 928 """ 929 log.warning('Event queue overflowed.') 930 931 def process_default(self, event): 932 """ 933 Default processing event method. By default does nothing. Subclass 934 ProcessEvent and redefine this method in order to modify its behavior. 935 936 @param event: Event to be processed. Can be of any type of events but 937 IN_Q_OVERFLOW events (see method process_IN_Q_OVERFLOW). 938 @type event: Event instance 939 """ 940 pass 941 942 943class PrintAllEvents(ProcessEvent): 944 """ 945 Dummy class used to print events strings representations. For instance this 946 class is used from command line to print all received events to stdout. 947 """ 948 def my_init(self, out=None): 949 """ 950 @param out: Where events will be written. 951 @type out: Object providing a valid file object interface. 952 """ 953 if out is None: 954 out = sys.stdout 955 self._out = out 956 957 def process_default(self, event): 958 """ 959 Writes event string representation to file object provided to 960 my_init(). 961 962 @param event: Event to be processed. Can be of any type of events but 963 IN_Q_OVERFLOW events (see method process_IN_Q_OVERFLOW). 964 @type event: Event instance 965 """ 966 self._out.write(str(event)) 967 self._out.write('\n') 968 self._out.flush() 969 970 971class ChainIfTrue(ProcessEvent): 972 """ 973 Makes conditional chaining depending on the result of the nested 974 processing instance. 975 """ 976 def my_init(self, func): 977 """ 978 Method automatically called from base class constructor. 979 """ 980 self._func = func 981 982 def process_default(self, event): 983 return not self._func(event) 984 985 986class Stats(ProcessEvent): 987 """ 988 Compute and display trivial statistics about processed events. 989 """ 990 def my_init(self): 991 """ 992 Method automatically called from base class constructor. 993 """ 994 self._start_time = time.time() 995 self._stats = {} 996 self._stats_lock = threading.Lock() 997 998 def process_default(self, event): 999 """ 1000 Processes |event|. 1001 """ 1002 self._stats_lock.acquire() 1003 try: 1004 events = event.maskname.split('|') 1005 for event_name in events: 1006 count = self._stats.get(event_name, 0) 1007 self._stats[event_name] = count + 1 1008 finally: 1009 self._stats_lock.release() 1010 1011 def _stats_copy(self): 1012 self._stats_lock.acquire() 1013 try: 1014 return self._stats.copy() 1015 finally: 1016 self._stats_lock.release() 1017 1018 def __repr__(self): 1019 stats = self._stats_copy() 1020 1021 elapsed = int(time.time() - self._start_time) 1022 elapsed_str = '' 1023 if elapsed < 60: 1024 elapsed_str = str(elapsed) + 'sec' 1025 elif 60 <= elapsed < 3600: 1026 elapsed_str = '%dmn%dsec' % (elapsed / 60, elapsed % 60) 1027 elif 3600 <= elapsed < 86400: 1028 elapsed_str = '%dh%dmn' % (elapsed / 3600, (elapsed % 3600) / 60) 1029 elif elapsed >= 86400: 1030 elapsed_str = '%dd%dh' % (elapsed / 86400, (elapsed % 86400) / 3600) 1031 stats['ElapsedTime'] = elapsed_str 1032 1033 l = [] 1034 for ev, value in sorted(stats.items(), key=lambda x: x[0]): 1035 l.append(' %s=%s' % (output_format.field_name(ev), 1036 output_format.field_value(value))) 1037 s = '<%s%s >' % (output_format.class_name(self.__class__.__name__), 1038 ''.join(l)) 1039 return s 1040 1041 def dump(self, filename): 1042 """ 1043 Dumps statistics. 1044 1045 @param filename: filename where stats will be dumped, filename is 1046 created and must not exist prior to this call. 1047 @type filename: string 1048 """ 1049 flags = os.O_WRONLY|os.O_CREAT|os.O_NOFOLLOW|os.O_EXCL 1050 fd = os.open(filename, flags, 0o0600) 1051 os.write(fd, bytes(self.__str__(), locale.getpreferredencoding())) 1052 os.close(fd) 1053 1054 def __str__(self, scale=45): 1055 stats = self._stats_copy() 1056 if not stats: 1057 return '' 1058 1059 m = max(stats.values()) 1060 unity = scale / m 1061 fmt = '%%-26s%%-%ds%%s' % (len(output_format.field_value('@' * scale)) 1062 + 1) 1063 def func(x): 1064 return fmt % (output_format.field_name(x[0]), 1065 output_format.field_value('@' * int(x[1] * unity)), 1066 output_format.simple('%d' % x[1], 'yellow')) 1067 s = '\n'.join(map(func, sorted(stats.items(), key=lambda x: x[0]))) 1068 return s 1069 1070 1071class NotifierError(PyinotifyError): 1072 """ 1073 Notifier Exception. Raised on Notifier error. 1074 1075 """ 1076 def __init__(self, err): 1077 """ 1078 @param err: Exception string's description. 1079 @type err: string 1080 """ 1081 PyinotifyError.__init__(self, err) 1082 1083 1084class Notifier: 1085 """ 1086 Read notifications, process events. 1087 1088 """ 1089 def __init__(self, watch_manager, default_proc_fun=None, read_freq=0, 1090 threshold=0, timeout=None): 1091 """ 1092 Initialization. read_freq, threshold and timeout parameters are used 1093 when looping. 1094 1095 @param watch_manager: Watch Manager. 1096 @type watch_manager: WatchManager instance 1097 @param default_proc_fun: Default processing method. If None, a new 1098 instance of PrintAllEvents will be assigned. 1099 @type default_proc_fun: instance of ProcessEvent 1100 @param read_freq: if read_freq == 0, events are read asap, 1101 if read_freq is > 0, this thread sleeps 1102 max(0, read_freq - (timeout / 1000)) seconds. But if 1103 timeout is None it may be different because 1104 poll is blocking waiting for something to read. 1105 @type read_freq: int 1106 @param threshold: File descriptor will be read only if the accumulated 1107 size to read becomes >= threshold. If != 0, you likely 1108 want to use it in combination with an appropriate 1109 value for read_freq because without that you would 1110 keep looping without really reading anything and that 1111 until the amount of events to read is >= threshold. 1112 At least with read_freq set you might sleep. 1113 @type threshold: int 1114 @param timeout: see read_freq above. If provided, it must be set in 1115 milliseconds. See 1116 https://docs.python.org/3/library/select.html#select.poll.poll 1117 @type timeout: int 1118 """ 1119 # Watch Manager instance 1120 self._watch_manager = watch_manager 1121 # File descriptor 1122 self._fd = self._watch_manager.get_fd() 1123 # Poll object and registration 1124 self._pollobj = select.poll() 1125 self._pollobj.register(self._fd, select.POLLIN) 1126 # This pipe is correctely initialized and used by ThreadedNotifier 1127 self._pipe = (-1, -1) 1128 # Event queue 1129 self._eventq = deque() 1130 # System processing functor, common to all events 1131 self._sys_proc_fun = _SysProcessEvent(self._watch_manager, self) 1132 # Default processing method 1133 self._default_proc_fun = default_proc_fun 1134 if default_proc_fun is None: 1135 self._default_proc_fun = PrintAllEvents() 1136 # Loop parameters 1137 self._read_freq = read_freq 1138 self._threshold = threshold 1139 self._timeout = timeout 1140 # Coalesce events option 1141 self._coalesce = False 1142 # set of str(raw_event), only used when coalesce option is True 1143 self._eventset = set() 1144 1145 def append_event(self, event): 1146 """ 1147 Append a raw event to the event queue. 1148 1149 @param event: An event. 1150 @type event: _RawEvent instance. 1151 """ 1152 self._eventq.append(event) 1153 1154 def proc_fun(self): 1155 return self._default_proc_fun 1156 1157 def coalesce_events(self, coalesce=True): 1158 """ 1159 Coalescing events. Events are usually processed by batchs, their size 1160 depend on various factors. Thus, before processing them, events received 1161 from inotify are aggregated in a fifo queue. If this coalescing 1162 option is enabled events are filtered based on their unicity, only 1163 unique events are enqueued, doublons are discarded. An event is unique 1164 when the combination of its fields (wd, mask, cookie, name) is unique 1165 among events of a same batch. After a batch of events is processed any 1166 events is accepted again. By default this option is disabled, you have 1167 to explictly call this function to turn it on. 1168 1169 @param coalesce: Optional new coalescing value. True by default. 1170 @type coalesce: Bool 1171 """ 1172 self._coalesce = coalesce 1173 if not coalesce: 1174 self._eventset.clear() 1175 1176 def check_events(self, timeout=None): 1177 """ 1178 Check for new events available to read, blocks up to timeout 1179 milliseconds. 1180 1181 @param timeout: If specified it overrides the corresponding instance 1182 attribute _timeout. timeout must be sepcified in 1183 milliseconds. 1184 @type timeout: int 1185 1186 @return: New events to read. 1187 @rtype: bool 1188 """ 1189 while True: 1190 try: 1191 # blocks up to 'timeout' milliseconds 1192 if timeout is None: 1193 timeout = self._timeout 1194 ret = self._pollobj.poll(timeout) 1195 except select.error as err: 1196 if err.args[0] == errno.EINTR: 1197 continue # interrupted, retry 1198 else: 1199 raise 1200 else: 1201 break 1202 1203 if not ret or (self._pipe[0] == ret[0][0]): 1204 return False 1205 # only one fd is polled 1206 return ret[0][1] & select.POLLIN 1207 1208 def read_events(self): 1209 """ 1210 Read events from device, build _RawEvents, and enqueue them. 1211 """ 1212 buf_ = array.array('i', [0]) 1213 # get event queue size 1214 if fcntl.ioctl(self._fd, termios.FIONREAD, buf_, 1) == -1: 1215 return 1216 queue_size = buf_[0] 1217 if queue_size < self._threshold: 1218 log.debug('(fd: %d) %d bytes available to read but threshold is ' 1219 'fixed to %d bytes', self._fd, queue_size, 1220 self._threshold) 1221 return 1222 1223 try: 1224 # Read content from file 1225 r = os.read(self._fd, queue_size) 1226 except Exception as msg: 1227 raise NotifierError(msg) 1228 log.debug('Event queue size: %d', queue_size) 1229 rsum = 0 # counter 1230 while rsum < queue_size: 1231 s_size = 16 1232 # Retrieve wd, mask, cookie and fname_len 1233 wd, mask, cookie, fname_len = struct.unpack('iIII', 1234 r[rsum:rsum+s_size]) 1235 # Retrieve name 1236 bname, = struct.unpack('%ds' % fname_len, 1237 r[rsum + s_size:rsum + s_size + fname_len]) 1238 # FIXME: should we explictly call sys.getdefaultencoding() here ?? 1239 uname = bname.decode() 1240 rawevent = _RawEvent(wd, mask, cookie, uname) 1241 if self._coalesce: 1242 # Only enqueue new (unique) events. 1243 raweventstr = str(rawevent) 1244 if raweventstr not in self._eventset: 1245 self._eventset.add(raweventstr) 1246 self._eventq.append(rawevent) 1247 else: 1248 self._eventq.append(rawevent) 1249 rsum += s_size + fname_len 1250 1251 def process_events(self): 1252 """ 1253 Routine for processing events from queue by calling their 1254 associated proccessing method (an instance of ProcessEvent). 1255 It also does internal processings, to keep the system updated. 1256 """ 1257 while self._eventq: 1258 raw_event = self._eventq.popleft() # pop next event 1259 if self._watch_manager.ignore_events: 1260 log.debug("Event ignored: %s" % repr(raw_event)) 1261 continue 1262 watch_ = self._watch_manager.get_watch(raw_event.wd) 1263 if (watch_ is None) and not (raw_event.mask & IN_Q_OVERFLOW): 1264 if not (raw_event.mask & IN_IGNORED): 1265 # Not really sure how we ended up here, nor how we should 1266 # handle these types of events and if it is appropriate to 1267 # completly skip them (like we are doing here). 1268 log.warning("Unable to retrieve Watch object associated to %s", 1269 repr(raw_event)) 1270 continue 1271 revent = self._sys_proc_fun(raw_event) # system processings 1272 if watch_ and watch_.proc_fun: 1273 watch_.proc_fun(revent) # user processings 1274 else: 1275 self._default_proc_fun(revent) 1276 self._sys_proc_fun.cleanup() # remove olds MOVED_* events records 1277 if self._coalesce: 1278 self._eventset.clear() 1279 1280 def __daemonize(self, pid_file=None, stdin=os.devnull, stdout=os.devnull, 1281 stderr=os.devnull): 1282 """ 1283 pid_file: file where the pid will be written. If pid_file=None the pid 1284 is written to /var/run/<sys.argv[0]|pyinotify>.pid, if 1285 pid_file=False no pid_file is written. 1286 stdin, stdout, stderr: files associated to common streams. 1287 """ 1288 if pid_file is None: 1289 dirname = '/var/run/' 1290 basename = os.path.basename(sys.argv[0]) or 'pyinotify' 1291 pid_file = os.path.join(dirname, basename + '.pid') 1292 1293 if pid_file != False and os.path.lexists(pid_file): 1294 err = 'Cannot daemonize: pid file %s already exists.' % pid_file 1295 raise NotifierError(err) 1296 1297 def fork_daemon(): 1298 # Adapted from Chad J. Schroeder's recipe 1299 # @see http://code.activestate.com/recipes/278731/ 1300 pid = os.fork() 1301 if (pid == 0): 1302 # parent 2 1303 os.setsid() 1304 pid = os.fork() 1305 if (pid == 0): 1306 # child 1307 os.chdir('/') 1308 os.umask(0o022) 1309 else: 1310 # parent 2 1311 os._exit(0) 1312 else: 1313 # parent 1 1314 os._exit(0) 1315 1316 fd_inp = os.open(stdin, os.O_RDONLY) 1317 os.dup2(fd_inp, 0) 1318 fd_out = os.open(stdout, os.O_WRONLY|os.O_CREAT, 0o0600) 1319 os.dup2(fd_out, 1) 1320 fd_err = os.open(stderr, os.O_WRONLY|os.O_CREAT, 0o0600) 1321 os.dup2(fd_err, 2) 1322 1323 # Detach task 1324 fork_daemon() 1325 1326 # Write pid 1327 if pid_file != False: 1328 flags = os.O_WRONLY|os.O_CREAT|os.O_NOFOLLOW|os.O_EXCL 1329 fd_pid = os.open(pid_file, flags, 0o0600) 1330 os.write(fd_pid, bytes(str(os.getpid()) + '\n', 1331 locale.getpreferredencoding())) 1332 os.close(fd_pid) 1333 # Register unlink function 1334 atexit.register(lambda : os.unlink(pid_file)) 1335 1336 def _sleep(self, ref_time): 1337 # Only consider sleeping if read_freq is > 0 1338 if self._read_freq > 0: 1339 cur_time = time.time() 1340 sleep_amount = self._read_freq - (cur_time - ref_time) 1341 if sleep_amount > 0: 1342 log.debug('Now sleeping %d seconds', sleep_amount) 1343 time.sleep(sleep_amount) 1344 1345 def loop(self, callback=None, daemonize=False, **args): 1346 """ 1347 Events are read only one time every min(read_freq, timeout) 1348 seconds at best and only if the size to read is >= threshold. 1349 After this method returns it must not be called again for the same 1350 instance. 1351 1352 @param callback: Functor called after each event processing iteration. 1353 Expects to receive the notifier object (self) as first 1354 parameter. If this function returns True the loop is 1355 immediately terminated otherwise the loop method keeps 1356 looping. 1357 @type callback: callable object or function 1358 @param daemonize: This thread is daemonized if set to True. 1359 @type daemonize: boolean 1360 @param args: Optional and relevant only if daemonize is True. Remaining 1361 keyworded arguments are directly passed to daemonize see 1362 __daemonize() method. If pid_file=None or is set to a 1363 pathname the caller must ensure the file does not exist 1364 before this method is called otherwise an exception 1365 pyinotify.NotifierError will be raised. If pid_file=False 1366 it is still daemonized but the pid is not written in any 1367 file. 1368 @type args: various 1369 """ 1370 if daemonize: 1371 self.__daemonize(**args) 1372 1373 # Read and process events forever 1374 while 1: 1375 try: 1376 self.process_events() 1377 if (callback is not None) and (callback(self) is True): 1378 break 1379 ref_time = time.time() 1380 # check_events is blocking 1381 if self.check_events(): 1382 self._sleep(ref_time) 1383 self.read_events() 1384 except KeyboardInterrupt: 1385 # Stop monitoring if sigint is caught (Control-C). 1386 log.debug('Pyinotify stops monitoring.') 1387 break 1388 # Close internals 1389 self.stop() 1390 1391 def stop(self): 1392 """ 1393 Close inotify's instance (close its file descriptor). 1394 It destroys all existing watches, pending events,... 1395 This method is automatically called at the end of loop(). 1396 Afterward it is invalid to access this instance. 1397 """ 1398 if self._fd is not None: 1399 self._pollobj.unregister(self._fd) 1400 os.close(self._fd) 1401 self._fd = None 1402 self._sys_proc_fun = None 1403 1404 1405class ThreadedNotifier(threading.Thread, Notifier): 1406 """ 1407 This notifier inherits from threading.Thread for instanciating a separate 1408 thread, and also inherits from Notifier, because it is a threaded notifier. 1409 1410 Note that every functionality provided by this class is also provided 1411 through Notifier class. Moreover Notifier should be considered first because 1412 it is not threaded and could be easily daemonized. 1413 """ 1414 def __init__(self, watch_manager, default_proc_fun=None, read_freq=0, 1415 threshold=0, timeout=None): 1416 """ 1417 Initialization, initialize base classes. read_freq, threshold and 1418 timeout parameters are used when looping. 1419 1420 @param watch_manager: Watch Manager. 1421 @type watch_manager: WatchManager instance 1422 @param default_proc_fun: Default processing method. See base class. 1423 @type default_proc_fun: instance of ProcessEvent 1424 @param read_freq: if read_freq == 0, events are read asap, 1425 if read_freq is > 0, this thread sleeps 1426 max(0, read_freq - (timeout / 1000)) seconds. 1427 @type read_freq: int 1428 @param threshold: File descriptor will be read only if the accumulated 1429 size to read becomes >= threshold. If != 0, you likely 1430 want to use it in combination with an appropriate 1431 value set for read_freq because without that you would 1432 keep looping without really reading anything and that 1433 until the amount of events to read is >= threshold. At 1434 least with read_freq you might sleep. 1435 @type threshold: int 1436 @param timeout: see read_freq above. If provided, it must be set in 1437 milliseconds. See 1438 https://docs.python.org/3/library/select.html#select.poll.poll 1439 @type timeout: int 1440 """ 1441 # Init threading base class 1442 threading.Thread.__init__(self) 1443 # Stop condition 1444 self._stop_event = threading.Event() 1445 # Init Notifier base class 1446 Notifier.__init__(self, watch_manager, default_proc_fun, read_freq, 1447 threshold, timeout) 1448 # Create a new pipe used for thread termination 1449 self._pipe = os.pipe() 1450 self._pollobj.register(self._pipe[0], select.POLLIN) 1451 1452 def stop(self): 1453 """ 1454 Stop notifier's loop. Stop notification. Join the thread. 1455 """ 1456 self._stop_event.set() 1457 os.write(self._pipe[1], b'stop') 1458 threading.Thread.join(self) 1459 Notifier.stop(self) 1460 self._pollobj.unregister(self._pipe[0]) 1461 os.close(self._pipe[0]) 1462 os.close(self._pipe[1]) 1463 1464 def loop(self): 1465 """ 1466 Thread's main loop. Don't meant to be called by user directly. 1467 Call inherited start() method instead. 1468 1469 Events are read only once time every min(read_freq, timeout) 1470 seconds at best and only if the size of events to read is >= threshold. 1471 """ 1472 # When the loop must be terminated .stop() is called, 'stop' 1473 # is written to pipe fd so poll() returns and .check_events() 1474 # returns False which make evaluate the While's stop condition 1475 # ._stop_event.isSet() wich put an end to the thread's execution. 1476 while not self._stop_event.isSet(): 1477 self.process_events() 1478 ref_time = time.time() 1479 if self.check_events(): 1480 self._sleep(ref_time) 1481 self.read_events() 1482 1483 def run(self): 1484 """ 1485 Start thread's loop: read and process events until the method 1486 stop() is called. 1487 Never call this method directly, instead call the start() method 1488 inherited from threading.Thread, which then will call run() in 1489 its turn. 1490 """ 1491 self.loop() 1492 1493 1494class AsyncNotifier(asyncore.file_dispatcher, Notifier): 1495 """ 1496 This notifier inherits from asyncore.file_dispatcher in order to be able to 1497 use pyinotify along with the asyncore framework. 1498 1499 """ 1500 def __init__(self, watch_manager, default_proc_fun=None, read_freq=0, 1501 threshold=0, timeout=None, channel_map=None): 1502 """ 1503 Initializes the async notifier. The only additional parameter is 1504 'channel_map' which is the optional asyncore private map. See 1505 Notifier class for the meaning of the others parameters. 1506 1507 """ 1508 Notifier.__init__(self, watch_manager, default_proc_fun, read_freq, 1509 threshold, timeout) 1510 asyncore.file_dispatcher.__init__(self, self._fd, channel_map) 1511 1512 def handle_read(self): 1513 """ 1514 When asyncore tells us we can read from the fd, we proceed processing 1515 events. This method can be overridden for handling a notification 1516 differently. 1517 1518 """ 1519 self.read_events() 1520 self.process_events() 1521 1522 1523class TornadoAsyncNotifier(Notifier): 1524 """ 1525 Tornado ioloop adapter. 1526 1527 """ 1528 def __init__(self, watch_manager, ioloop, callback=None, 1529 default_proc_fun=None, read_freq=0, threshold=0, timeout=None, 1530 channel_map=None): 1531 """ 1532 Note that if later you must call ioloop.close() be sure to let the 1533 default parameter to all_fds=False. 1534 1535 See example tornado_notifier.py for an example using this notifier. 1536 1537 @param ioloop: Tornado's IO loop. 1538 @type ioloop: tornado.ioloop.IOLoop instance. 1539 @param callback: Functor called at the end of each call to handle_read 1540 (IOLoop's read handler). Expects to receive the 1541 notifier object (self) as single parameter. 1542 @type callback: callable object or function 1543 """ 1544 self.io_loop = ioloop 1545 self.handle_read_callback = callback 1546 Notifier.__init__(self, watch_manager, default_proc_fun, read_freq, 1547 threshold, timeout) 1548 ioloop.add_handler(self._fd, self.handle_read, ioloop.READ) 1549 1550 def stop(self): 1551 self.io_loop.remove_handler(self._fd) 1552 Notifier.stop(self) 1553 1554 def handle_read(self, *args, **kwargs): 1555 """ 1556 See comment in AsyncNotifier. 1557 1558 """ 1559 self.read_events() 1560 self.process_events() 1561 if self.handle_read_callback is not None: 1562 self.handle_read_callback(self) 1563 1564 1565class AsyncioNotifier(Notifier): 1566 """ 1567 1568 asyncio/trollius event loop adapter. 1569 1570 """ 1571 def __init__(self, watch_manager, loop, callback=None, 1572 default_proc_fun=None, read_freq=0, threshold=0, timeout=None): 1573 """ 1574 1575 See examples/asyncio_notifier.py for an example usage. 1576 1577 @param loop: asyncio or trollius event loop instance. 1578 @type loop: asyncio.BaseEventLoop or trollius.BaseEventLoop instance. 1579 @param callback: Functor called at the end of each call to handle_read. 1580 Expects to receive the notifier object (self) as 1581 single parameter. 1582 @type callback: callable object or function 1583 1584 """ 1585 self.loop = loop 1586 self.handle_read_callback = callback 1587 Notifier.__init__(self, watch_manager, default_proc_fun, read_freq, 1588 threshold, timeout) 1589 loop.add_reader(self._fd, self.handle_read) 1590 1591 def stop(self): 1592 self.loop.remove_reader(self._fd) 1593 Notifier.stop(self) 1594 1595 def handle_read(self, *args, **kwargs): 1596 self.read_events() 1597 self.process_events() 1598 if self.handle_read_callback is not None: 1599 self.handle_read_callback(self) 1600 1601 1602class Watch: 1603 """ 1604 Represent a watch, i.e. a file or directory being watched. 1605 1606 """ 1607 __slots__ = ('wd', 'path', 'mask', 'proc_fun', 'auto_add', 1608 'exclude_filter', 'dir') 1609 1610 def __init__(self, wd, path, mask, proc_fun, auto_add, exclude_filter): 1611 """ 1612 Initializations. 1613 1614 @param wd: Watch descriptor. 1615 @type wd: int 1616 @param path: Path of the file or directory being watched. 1617 @type path: str 1618 @param mask: Mask. 1619 @type mask: int 1620 @param proc_fun: Processing callable object. 1621 @type proc_fun: 1622 @param auto_add: Automatically add watches on new directories. 1623 @type auto_add: bool 1624 @param exclude_filter: Boolean function, used to exclude new 1625 directories from being automatically watched. 1626 See WatchManager.__init__ 1627 @type exclude_filter: callable object 1628 """ 1629 self.wd = wd 1630 self.path = path 1631 self.mask = mask 1632 self.proc_fun = proc_fun 1633 self.auto_add = auto_add 1634 self.exclude_filter = exclude_filter 1635 self.dir = os.path.isdir(self.path) 1636 1637 def __repr__(self): 1638 """ 1639 @return: String representation. 1640 @rtype: str 1641 """ 1642 s = ' '.join(['%s%s%s' % (output_format.field_name(attr), 1643 output_format.punctuation('='), 1644 output_format.field_value(getattr(self, 1645 attr))) \ 1646 for attr in self.__slots__ if not attr.startswith('_')]) 1647 1648 s = '%s%s %s %s' % (output_format.punctuation('<'), 1649 output_format.class_name(self.__class__.__name__), 1650 s, 1651 output_format.punctuation('>')) 1652 return s 1653 1654 1655class ExcludeFilter: 1656 """ 1657 ExcludeFilter is an exclusion filter. 1658 """ 1659 def __init__(self, arg_lst): 1660 """ 1661 Examples: 1662 ef1 = ExcludeFilter(["/etc/rc.*", "/etc/hostname"]) 1663 ef2 = ExcludeFilter("/my/path/exclude.lst") 1664 Where exclude.lst contains: 1665 /etc/rc.* 1666 /etc/hostname 1667 1668 Note: it is not possible to exclude a file if its encapsulating 1669 directory is itself watched. See this issue for more details 1670 https://github.com/seb-m/pyinotify/issues/31 1671 1672 @param arg_lst: is either a list of patterns or a filename from which 1673 patterns will be loaded. 1674 @type arg_lst: list of str or str 1675 """ 1676 if isinstance(arg_lst, str): 1677 lst = self._load_patterns_from_file(arg_lst) 1678 elif isinstance(arg_lst, list): 1679 lst = arg_lst 1680 else: 1681 raise TypeError 1682 1683 self._lregex = [] 1684 for regex in lst: 1685 self._lregex.append(re.compile(regex, re.UNICODE)) 1686 1687 def _load_patterns_from_file(self, filename): 1688 lst = [] 1689 with open(filename, 'r') as file_obj: 1690 for line in file_obj.readlines(): 1691 # Trim leading an trailing whitespaces 1692 pattern = line.strip() 1693 if not pattern or pattern.startswith('#'): 1694 continue 1695 lst.append(pattern) 1696 return lst 1697 1698 def _match(self, regex, path): 1699 return regex.match(path) is not None 1700 1701 def __call__(self, path): 1702 """ 1703 @param path: Path to match against provided regexps. 1704 @type path: str 1705 @return: Return True if path has been matched and should 1706 be excluded, False otherwise. 1707 @rtype: bool 1708 """ 1709 for regex in self._lregex: 1710 if self._match(regex, path): 1711 return True 1712 return False 1713 1714 1715class WatchManagerError(Exception): 1716 """ 1717 WatchManager Exception. Raised on error encountered on watches 1718 operations. 1719 """ 1720 def __init__(self, msg, wmd): 1721 """ 1722 @param msg: Exception string's description. 1723 @type msg: string 1724 @param wmd: This dictionary contains the wd assigned to paths of the 1725 same call for which watches were successfully added. 1726 @type wmd: dict 1727 """ 1728 self.wmd = wmd 1729 Exception.__init__(self, msg) 1730 1731 1732class WatchManager: 1733 """ 1734 Provide operations for watching files and directories. Its internal 1735 dictionary is used to reference watched items. When used inside 1736 threaded code, one must instanciate as many WatchManager instances as 1737 there are ThreadedNotifier instances. 1738 1739 """ 1740 def __init__(self, exclude_filter=lambda path: False): 1741 """ 1742 Initialization: init inotify, init watch manager dictionary. 1743 Raise OSError if initialization fails, raise InotifyBindingNotFoundError 1744 if no inotify binding was found (through ctypes or from direct access to 1745 syscalls). 1746 1747 @param exclude_filter: boolean function, returns True if current 1748 path must be excluded from being watched. 1749 Convenient for providing a common exclusion 1750 filter for every call to add_watch. 1751 @type exclude_filter: callable object 1752 """ 1753 self._ignore_events = False 1754 self._exclude_filter = exclude_filter 1755 self._wmd = {} # watch dict key: watch descriptor, value: watch 1756 1757 self._inotify_wrapper = INotifyWrapper.create() 1758 if self._inotify_wrapper is None: 1759 raise InotifyBindingNotFoundError() 1760 1761 self._fd = self._inotify_wrapper.inotify_init() # file descriptor 1762 if self._fd < 0: 1763 err = 'Cannot initialize new instance of inotify, %s' 1764 raise OSError(err % self._inotify_wrapper.str_errno()) 1765 1766 def close(self): 1767 """ 1768 Close inotify's file descriptor, this action will also automatically 1769 remove (i.e. stop watching) all its associated watch descriptors. 1770 After a call to this method the WatchManager's instance become useless 1771 and cannot be reused, a new instance must then be instanciated. It 1772 makes sense to call this method in few situations for instance if 1773 several independant WatchManager must be instanciated or if all watches 1774 must be removed and no other watches need to be added. 1775 """ 1776 os.close(self._fd) 1777 1778 def get_fd(self): 1779 """ 1780 Return assigned inotify's file descriptor. 1781 1782 @return: File descriptor. 1783 @rtype: int 1784 """ 1785 return self._fd 1786 1787 def get_watch(self, wd): 1788 """ 1789 Get watch from provided watch descriptor wd. 1790 1791 @param wd: Watch descriptor. 1792 @type wd: int 1793 """ 1794 return self._wmd.get(wd) 1795 1796 def del_watch(self, wd): 1797 """ 1798 Remove watch entry associated to watch descriptor wd. 1799 1800 @param wd: Watch descriptor. 1801 @type wd: int 1802 """ 1803 try: 1804 del self._wmd[wd] 1805 except KeyError as err: 1806 log.error('Cannot delete unknown watch descriptor %s' % str(err)) 1807 1808 @property 1809 def watches(self): 1810 """ 1811 Get a reference on the internal watch manager dictionary. 1812 1813 @return: Internal watch manager dictionary. 1814 @rtype: dict 1815 """ 1816 return self._wmd 1817 1818 def __format_path(self, path): 1819 """ 1820 Format path to its internal (stored in watch manager) representation. 1821 """ 1822 # path must be a unicode string (str) and is just normalized. 1823 return os.path.normpath(path) 1824 1825 def __add_watch(self, path, mask, proc_fun, auto_add, exclude_filter): 1826 """ 1827 Add a watch on path, build a Watch object and insert it in the 1828 watch manager dictionary. Return the wd value. 1829 """ 1830 path = self.__format_path(path) 1831 if auto_add and not mask & IN_CREATE: 1832 mask |= IN_CREATE 1833 wd = self._inotify_wrapper.inotify_add_watch(self._fd, path, mask) 1834 if wd < 0: 1835 return wd 1836 watch = Watch(wd=wd, path=path, mask=mask, proc_fun=proc_fun, 1837 auto_add=auto_add, exclude_filter=exclude_filter) 1838 # wd are _always_ indexed with their original unicode paths in wmd. 1839 self._wmd[wd] = watch 1840 log.debug('New %s', watch) 1841 return wd 1842 1843 def __glob(self, path, do_glob): 1844 if do_glob: 1845 return glob.iglob(path) 1846 else: 1847 return [path] 1848 1849 def add_watch(self, path, mask, proc_fun=None, rec=False, 1850 auto_add=False, do_glob=False, quiet=True, 1851 exclude_filter=None): 1852 """ 1853 Add watch(s) on the provided |path|(s) with associated |mask| flag 1854 value and optionally with a processing |proc_fun| function and 1855 recursive flag |rec| set to True. 1856 All |path| components _must_ be str (i.e. unicode) objects. 1857 If |path| is already watched it is ignored, but if it is called with 1858 option rec=True a watch is put on each one of its not-watched 1859 subdirectory. 1860 1861 @param path: Path to watch, the path can either be a file or a 1862 directory. Also accepts a sequence (list) of paths. 1863 @type path: string or list of strings 1864 @param mask: Bitmask of events. 1865 @type mask: int 1866 @param proc_fun: Processing object. 1867 @type proc_fun: function or ProcessEvent instance or instance of 1868 one of its subclasses or callable object. 1869 @param rec: Recursively add watches from path on all its 1870 subdirectories, set to False by default (doesn't 1871 follows symlinks in any case). 1872 @type rec: bool 1873 @param auto_add: Automatically add watches on newly created 1874 directories in watched parent |path| directory. 1875 If |auto_add| is True, IN_CREATE is ored with |mask| 1876 when the watch is added. 1877 @type auto_add: bool 1878 @param do_glob: Do globbing on pathname (see standard globbing 1879 module for more informations). 1880 @type do_glob: bool 1881 @param quiet: if False raises a WatchManagerError exception on 1882 error. See example not_quiet.py. 1883 @type quiet: bool 1884 @param exclude_filter: predicate (boolean function), which returns 1885 True if the current path must be excluded 1886 from being watched. This argument has 1887 precedence over exclude_filter passed to 1888 the class' constructor. 1889 @type exclude_filter: callable object 1890 @return: dict of paths associated to watch descriptors. A wd value 1891 is positive if the watch was added sucessfully, otherwise 1892 the value is negative. If the path was invalid or was already 1893 watched it is not included into this returned dictionary. 1894 @rtype: dict of {str: int} 1895 """ 1896 ret_ = {} # return {path: wd, ...} 1897 1898 if exclude_filter is None: 1899 exclude_filter = self._exclude_filter 1900 1901 # normalize args as list elements 1902 for npath in self.__format_param(path): 1903 # Require that path be a unicode string 1904 if not isinstance(npath, str): 1905 ret_[path] = -3 1906 continue 1907 1908 # unix pathname pattern expansion 1909 for apath in self.__glob(npath, do_glob): 1910 # recursively list subdirs according to rec param 1911 for rpath in self.__walk_rec(apath, rec): 1912 if not exclude_filter(rpath): 1913 wd = ret_[rpath] = self.__add_watch(rpath, mask, 1914 proc_fun, 1915 auto_add, 1916 exclude_filter) 1917 if wd < 0: 1918 err = ('add_watch: cannot watch %s WD=%d, %s' % \ 1919 (rpath, wd, 1920 self._inotify_wrapper.str_errno())) 1921 if quiet: 1922 log.error(err) 1923 else: 1924 raise WatchManagerError(err, ret_) 1925 else: 1926 # Let's say -2 means 'explicitely excluded 1927 # from watching'. 1928 ret_[rpath] = -2 1929 return ret_ 1930 1931 def __get_sub_rec(self, lpath): 1932 """ 1933 Get every wd from self._wmd if its path is under the path of 1934 one (at least) of those in lpath. Doesn't follow symlinks. 1935 1936 @param lpath: list of watch descriptor 1937 @type lpath: list of int 1938 @return: list of watch descriptor 1939 @rtype: list of int 1940 """ 1941 for d in lpath: 1942 root = self.get_path(d) 1943 if root is not None: 1944 # always keep root 1945 yield d 1946 else: 1947 # if invalid 1948 continue 1949 1950 # nothing else to expect 1951 if not os.path.isdir(root): 1952 continue 1953 1954 # normalization 1955 root = os.path.normpath(root) 1956 # recursion 1957 lend = len(root) 1958 for iwd in self._wmd.items(): 1959 cur = iwd[1].path 1960 pref = os.path.commonprefix([root, cur]) 1961 if root == os.sep or (len(pref) == lend and \ 1962 len(cur) > lend and \ 1963 cur[lend] == os.sep): 1964 yield iwd[1].wd 1965 1966 def update_watch(self, wd, mask=None, proc_fun=None, rec=False, 1967 auto_add=False, quiet=True): 1968 """ 1969 Update existing watch descriptors |wd|. The |mask| value, the 1970 processing object |proc_fun|, the recursive param |rec| and the 1971 |auto_add| and |quiet| flags can all be updated. 1972 1973 @param wd: Watch Descriptor to update. Also accepts a list of 1974 watch descriptors. 1975 @type wd: int or list of int 1976 @param mask: Optional new bitmask of events. 1977 @type mask: int 1978 @param proc_fun: Optional new processing function. 1979 @type proc_fun: function or ProcessEvent instance or instance of 1980 one of its subclasses or callable object. 1981 @param rec: Optionally adds watches recursively on all 1982 subdirectories contained into |wd| directory. 1983 @type rec: bool 1984 @param auto_add: Automatically adds watches on newly created 1985 directories in the watch's path corresponding to |wd|. 1986 If |auto_add| is True, IN_CREATE is ored with |mask| 1987 when the watch is updated. 1988 @type auto_add: bool 1989 @param quiet: If False raises a WatchManagerError exception on 1990 error. See example not_quiet.py 1991 @type quiet: bool 1992 @return: dict of watch descriptors associated to booleans values. 1993 True if the corresponding wd has been successfully 1994 updated, False otherwise. 1995 @rtype: dict of {int: bool} 1996 """ 1997 lwd = self.__format_param(wd) 1998 if rec: 1999 lwd = self.__get_sub_rec(lwd) 2000 2001 ret_ = {} # return {wd: bool, ...} 2002 for awd in lwd: 2003 apath = self.get_path(awd) 2004 if not apath or awd < 0: 2005 err = 'update_watch: invalid WD=%d' % awd 2006 if quiet: 2007 log.error(err) 2008 continue 2009 raise WatchManagerError(err, ret_) 2010 2011 if mask: 2012 wd_ = self._inotify_wrapper.inotify_add_watch(self._fd, apath, 2013 mask) 2014 if wd_ < 0: 2015 ret_[awd] = False 2016 err = ('update_watch: cannot update %s WD=%d, %s' % \ 2017 (apath, wd_, self._inotify_wrapper.str_errno())) 2018 if quiet: 2019 log.error(err) 2020 continue 2021 raise WatchManagerError(err, ret_) 2022 2023 assert(awd == wd_) 2024 2025 if proc_fun or auto_add: 2026 watch_ = self._wmd[awd] 2027 2028 if proc_fun: 2029 watch_.proc_fun = proc_fun 2030 2031 if auto_add: 2032 watch_.auto_add = auto_add 2033 2034 ret_[awd] = True 2035 log.debug('Updated watch - %s', self._wmd[awd]) 2036 return ret_ 2037 2038 def __format_param(self, param): 2039 """ 2040 @param param: Parameter. 2041 @type param: string or int 2042 @return: wrap param. 2043 @rtype: list of type(param) 2044 """ 2045 if isinstance(param, list): 2046 for p_ in param: 2047 yield p_ 2048 else: 2049 yield param 2050 2051 def get_wd(self, path): 2052 """ 2053 Returns the watch descriptor associated to path. This method 2054 presents a prohibitive cost, always prefer to keep the WD 2055 returned by add_watch(). If the path is unknown it returns None. 2056 2057 @param path: Path. 2058 @type path: str 2059 @return: WD or None. 2060 @rtype: int or None 2061 """ 2062 path = self.__format_path(path) 2063 for iwd in self._wmd.items(): 2064 if iwd[1].path == path: 2065 return iwd[0] 2066 2067 def get_path(self, wd): 2068 """ 2069 Returns the path associated to WD, if WD is unknown it returns None. 2070 2071 @param wd: Watch descriptor. 2072 @type wd: int 2073 @return: Path or None. 2074 @rtype: string or None 2075 """ 2076 watch_ = self._wmd.get(wd) 2077 if watch_ is not None: 2078 return watch_.path 2079 2080 def __walk_rec(self, top, rec): 2081 """ 2082 Yields each subdirectories of top, doesn't follow symlinks. 2083 If rec is false, only yield top. 2084 2085 @param top: root directory. 2086 @type top: string 2087 @param rec: recursive flag. 2088 @type rec: bool 2089 @return: path of one subdirectory. 2090 @rtype: string 2091 """ 2092 if not rec or os.path.islink(top) or not os.path.isdir(top): 2093 yield top 2094 else: 2095 for root, dirs, files in os.walk(top): 2096 yield root 2097 2098 def rm_watch(self, wd, rec=False, quiet=True): 2099 """ 2100 Removes watch(s). 2101 2102 @param wd: Watch Descriptor of the file or directory to unwatch. 2103 Also accepts a list of WDs. 2104 @type wd: int or list of int. 2105 @param rec: Recursively removes watches on every already watched 2106 subdirectories and subfiles. 2107 @type rec: bool 2108 @param quiet: If False raises a WatchManagerError exception on 2109 error. See example not_quiet.py 2110 @type quiet: bool 2111 @return: dict of watch descriptors associated to booleans values. 2112 True if the corresponding wd has been successfully 2113 removed, False otherwise. 2114 @rtype: dict of {int: bool} 2115 """ 2116 lwd = self.__format_param(wd) 2117 if rec: 2118 lwd = self.__get_sub_rec(lwd) 2119 2120 ret_ = {} # return {wd: bool, ...} 2121 for awd in lwd: 2122 # remove watch 2123 wd_ = self._inotify_wrapper.inotify_rm_watch(self._fd, awd) 2124 if wd_ < 0: 2125 ret_[awd] = False 2126 err = ('rm_watch: cannot remove WD=%d, %s' % \ 2127 (awd, self._inotify_wrapper.str_errno())) 2128 if quiet: 2129 log.error(err) 2130 continue 2131 raise WatchManagerError(err, ret_) 2132 2133 # Remove watch from our dictionary 2134 if awd in self._wmd: 2135 del self._wmd[awd] 2136 ret_[awd] = True 2137 log.debug('Watch WD=%d (%s) removed', awd, self.get_path(awd)) 2138 return ret_ 2139 2140 2141 def watch_transient_file(self, filename, mask, proc_class): 2142 """ 2143 Watch a transient file, which will be created and deleted frequently 2144 over time (e.g. pid file). 2145 2146 @attention: Currently under the call to this function it is not 2147 possible to correctly watch the events triggered into the same 2148 base directory than the directory where is located this watched 2149 transient file. For instance it would be wrong to make these 2150 two successive calls: wm.watch_transient_file('/var/run/foo.pid', ...) 2151 and wm.add_watch('/var/run/', ...) 2152 2153 @param filename: Filename. 2154 @type filename: string 2155 @param mask: Bitmask of events, should contain IN_CREATE and IN_DELETE. 2156 @type mask: int 2157 @param proc_class: ProcessEvent (or of one of its subclass), beware of 2158 accepting a ProcessEvent's instance as argument into 2159 __init__, see transient_file.py example for more 2160 details. 2161 @type proc_class: ProcessEvent's instance or of one of its subclasses. 2162 @return: Same as add_watch(). 2163 @rtype: Same as add_watch(). 2164 """ 2165 dirname = os.path.dirname(filename) 2166 if dirname == '': 2167 return {} # Maintains coherence with add_watch() 2168 basename = os.path.basename(filename) 2169 # Assuming we are watching at least for IN_CREATE and IN_DELETE 2170 mask |= IN_CREATE | IN_DELETE 2171 2172 def cmp_name(event): 2173 if getattr(event, 'name') is None: 2174 return False 2175 return basename == event.name 2176 return self.add_watch(dirname, mask, 2177 proc_fun=proc_class(ChainIfTrue(func=cmp_name)), 2178 rec=False, 2179 auto_add=False, do_glob=False, 2180 exclude_filter=lambda path: False) 2181 2182 def get_ignore_events(self): 2183 return self._ignore_events 2184 2185 def set_ignore_events(self, nval): 2186 self._ignore_events = nval 2187 2188 ignore_events = property(get_ignore_events, set_ignore_events, 2189 "Make watch manager ignoring new events.") 2190 2191 2192class RawOutputFormat: 2193 """ 2194 Format string representations. 2195 """ 2196 def __init__(self, format=None): 2197 self.format = format or {} 2198 2199 def simple(self, s, attribute): 2200 if not isinstance(s, str): 2201 s = str(s) 2202 return (self.format.get(attribute, '') + s + 2203 self.format.get('normal', '')) 2204 2205 def punctuation(self, s): 2206 """Punctuation color.""" 2207 return self.simple(s, 'normal') 2208 2209 def field_value(self, s): 2210 """Field value color.""" 2211 return self.simple(s, 'purple') 2212 2213 def field_name(self, s): 2214 """Field name color.""" 2215 return self.simple(s, 'blue') 2216 2217 def class_name(self, s): 2218 """Class name color.""" 2219 return self.format.get('red', '') + self.simple(s, 'bold') 2220 2221output_format = RawOutputFormat() 2222 2223class ColoredOutputFormat(RawOutputFormat): 2224 """ 2225 Format colored string representations. 2226 """ 2227 def __init__(self): 2228 f = {'normal': '\033[0m', 2229 'black': '\033[30m', 2230 'red': '\033[31m', 2231 'green': '\033[32m', 2232 'yellow': '\033[33m', 2233 'blue': '\033[34m', 2234 'purple': '\033[35m', 2235 'cyan': '\033[36m', 2236 'bold': '\033[1m', 2237 'uline': '\033[4m', 2238 'blink': '\033[5m', 2239 'invert': '\033[7m'} 2240 RawOutputFormat.__init__(self, f) 2241 2242 2243def compatibility_mode(): 2244 """ 2245 Use this function to turn on the compatibility mode. The compatibility 2246 mode is used to improve compatibility with Pyinotify 0.7.1 (or older) 2247 programs. The compatibility mode provides additional variables 'is_dir', 2248 'event_name', 'EventsCodes.IN_*' and 'EventsCodes.ALL_EVENTS' as 2249 Pyinotify 0.7.1 provided. Do not call this function from new programs!! 2250 Especially if there are developped for Pyinotify >= 0.8.x. 2251 """ 2252 setattr(EventsCodes, 'ALL_EVENTS', ALL_EVENTS) 2253 for evname in globals(): 2254 if evname.startswith('IN_'): 2255 setattr(EventsCodes, evname, globals()[evname]) 2256 global COMPATIBILITY_MODE 2257 COMPATIBILITY_MODE = True 2258 2259 2260def command_line(): 2261 """ 2262 By default the watched path is '/tmp' and all types of events are 2263 monitored. Events monitoring serves forever, type c^c to stop it. 2264 """ 2265 from optparse import OptionParser 2266 2267 usage = "usage: %prog [options] [path1] [path2] [pathn]" 2268 2269 parser = OptionParser(usage=usage) 2270 parser.add_option("-v", "--verbose", action="store_true", 2271 dest="verbose", help="Verbose mode") 2272 parser.add_option("-r", "--recursive", action="store_true", 2273 dest="recursive", 2274 help="Add watches recursively on paths") 2275 parser.add_option("-a", "--auto_add", action="store_true", 2276 dest="auto_add", 2277 help="Automatically add watches on new directories") 2278 parser.add_option("-g", "--glob", action="store_true", 2279 dest="glob", 2280 help="Treat paths as globs") 2281 parser.add_option("-e", "--events-list", metavar="EVENT[,...]", 2282 dest="events_list", 2283 help=("A comma-separated list of events to watch for - " 2284 "see the documentation for valid options (defaults" 2285 " to everything)")) 2286 parser.add_option("-s", "--stats", action="store_true", 2287 dest="stats", 2288 help="Display dummy statistics") 2289 parser.add_option("-V", "--version", action="store_true", 2290 dest="version", help="Pyinotify version") 2291 parser.add_option("-f", "--raw-format", action="store_true", 2292 dest="raw_format", 2293 help="Disable enhanced output format.") 2294 parser.add_option("-c", "--command", action="store", 2295 dest="command", 2296 help="Shell command to run upon event") 2297 2298 (options, args) = parser.parse_args() 2299 2300 if options.verbose: 2301 log.setLevel(10) 2302 2303 if options.version: 2304 print(__version__) 2305 2306 if not options.raw_format: 2307 global output_format 2308 output_format = ColoredOutputFormat() 2309 2310 if len(args) < 1: 2311 path = '/tmp' # default watched path 2312 else: 2313 path = args 2314 2315 # watch manager instance 2316 wm = WatchManager() 2317 # notifier instance and init 2318 if options.stats: 2319 notifier = Notifier(wm, default_proc_fun=Stats(), read_freq=5) 2320 else: 2321 notifier = Notifier(wm, default_proc_fun=PrintAllEvents()) 2322 2323 # What mask to apply 2324 mask = 0 2325 if options.events_list: 2326 events_list = options.events_list.split(',') 2327 for ev in events_list: 2328 evcode = EventsCodes.ALL_FLAGS.get(ev, 0) 2329 if evcode: 2330 mask |= evcode 2331 else: 2332 parser.error("The event '%s' specified with option -e" 2333 " is not valid" % ev) 2334 else: 2335 mask = ALL_EVENTS 2336 2337 # stats 2338 cb_fun = None 2339 if options.stats: 2340 def cb(s): 2341 sys.stdout.write(repr(s.proc_fun())) 2342 sys.stdout.write('\n') 2343 sys.stdout.write(str(s.proc_fun())) 2344 sys.stdout.write('\n') 2345 sys.stdout.flush() 2346 cb_fun = cb 2347 2348 # External command 2349 if options.command: 2350 def cb(s): 2351 subprocess.Popen(options.command, shell=True) 2352 cb_fun = cb 2353 2354 log.debug('Start monitoring %s, (press c^c to halt pyinotify)' % path) 2355 2356 wm.add_watch(path, mask, rec=options.recursive, auto_add=options.auto_add, do_glob=options.glob) 2357 # Loop forever (until sigint signal get caught) 2358 notifier.loop(callback=cb_fun) 2359 2360 2361if __name__ == '__main__': 2362 command_line() 2363