From 7bbd6dab1c317314161567a6088076282100f736 Mon Sep 17 00:00:00 2001 From: Sebastien Martini Date: Sun, 3 Jan 2010 15:04:28 +0100 Subject: [PATCH] - Removed custom glob functions, uses standard glob module directly. - Modified exclusion filter mechanism, patterns still can be loaded from files but the syntax has changed see exclude.lst for more details. This new method is safer since the external file is not executed anymore, it is only parsed looking for patterns loaded as strings. --- python2/examples/exclude.lst | 12 ++++ python2/examples/exclude.patterns | 16 ----- python2/examples/exclude.py | 35 +++++----- python2/pyinotify.py | 111 +++++++----------------------- python3/pyinotify.py | 108 +++++++---------------------- 5 files changed, 80 insertions(+), 202 deletions(-) create mode 100644 python2/examples/exclude.lst delete mode 100644 python2/examples/exclude.patterns diff --git a/python2/examples/exclude.lst b/python2/examples/exclude.lst new file mode 100644 index 0000000..7e81272 --- /dev/null +++ b/python2/examples/exclude.lst @@ -0,0 +1,12 @@ +# File associated to exclude.py +# +# List of patterns using regexps as defined into re standard module. +# These regexps are matched against submitted paths with re.match(). + +# Put only one pattern by line. +^/etc/apache[2]?/ +^/etc/rc.* +^/etc/hostname +^/etc/hosts +^/etc/(fs|m)tab +^/etc/cron\..* diff --git a/python2/examples/exclude.patterns b/python2/examples/exclude.patterns deleted file mode 100644 index f519b15..0000000 --- a/python2/examples/exclude.patterns +++ /dev/null @@ -1,16 +0,0 @@ -# -*- mode: python; -*- - -# Patterns can use unrestricted regexps (see re module) -# regexps are matched against submitted paths through match() - -excl_lst1 = ['^/etc/apache[2]?/', - '^/etc/rc.*', - '^/etc/hostname', - '^/etc/hosts', - '^/etc/(fs|m)tab', - '^/etc/cron\..*'] - -excl_lst2 = ['^/var/log/.*', - '^/var/www/.*', - '^/var/cache/.*', - '^/var/spool/.*'] diff --git a/python2/examples/exclude.py b/python2/examples/exclude.py index 850dbfe..4f12960 100644 --- a/python2/examples/exclude.py +++ b/python2/examples/exclude.py @@ -1,27 +1,30 @@ -# Example: excludes items from being monitored. +# Example: exclude items from being monitored. # -import pyinotify import os - -excl_file = os.path.join(os.getcwd(), 'exclude.patterns') +import pyinotify wm = pyinotify.WatchManager() notifier = pyinotify.Notifier(wm) - ### Method 1: -# Exclude filter object -excl = pyinotify.ExcludeFilter({excl_file: ('excl_lst1', 'excl_lst2')}) +# Exclude patterns from file +excl_file = os.path.join(os.getcwd(), 'exclude.lst') +excl = pyinotify.ExcludeFilter(excl_file) # Add watches -wm.add_watch(['/etc/*', '/var'], pyinotify.ALL_EVENTS, - rec=True, do_glob=True, exclude_filter=excl) - +res = wm.add_watch(['/etc/hostname', '/etc/cups', '/etc/rc0.d'], + pyinotify.ALL_EVENTS, rec=True, exclude_filter=excl) ### Method 2 (Equivalent) -wm.add_watch('/etc/*', pyinotify.ALL_EVENTS, rec=True, do_glob=True, - exclude_filter=pyinotify.ExcludeFilter({excl_file:('excl_lst1',)})) -wm.add_watch('/var', pyinotify.ALL_EVENTS, rec=True, - exclude_filter=pyinotify.ExcludeFilter({excl_file:('excl_lst2',)})) - +# Exclude patterns from list +excl_lst = ['^/etc/apache[2]?/', + '^/etc/rc.*', + '^/etc/hostname', + '^/etc/hosts', + '^/etc/(fs|m)tab', + '^/etc/cron\..*'] +excl = pyinotify.ExcludeFilter(excl_lst) +# Add watches +res = wm.add_watch(['/etc/hostname', '/etc/cups', '/etc/rc0.d'], + pyinotify.ALL_EVENTS, rec=True, exclude_filter=excl) -notifier.loop() +#notifier.loop() diff --git a/python2/pyinotify.py b/python2/pyinotify.py index 8a6111b..8cfe2ec 100755 --- a/python2/pyinotify.py +++ b/python2/pyinotify.py @@ -85,6 +85,7 @@ def __init__(self, version): import ctypes import ctypes.util import asyncore +import glob try: from functools import reduce @@ -183,10 +184,7 @@ def logger_init(): log = logger_init() - -### inotify's variables ### - - +# inotify's variables class SysCtlINotify: """ Access (read, write) inotify's variables through sysctl. Usually it @@ -253,74 +251,6 @@ def __repr__(self): globals()[attrname] = SysCtlINotify(attrname) - -### iglob ### - - -# Code taken from standart Python Lib, slightly modified in order to work -# with pyinotify (don't exclude dotted files/dirs like .foo). -# Original version: -# @see: http://svn.python.org/projects/python/trunk/Lib/glob.py - -def iglob(pathname): - if not has_magic(pathname): - if hasattr(os.path, 'lexists'): - if os.path.lexists(pathname): - yield pathname - else: - if os.path.islink(pathname) or os.path.exists(pathname): - yield pathname - return - dirname, basename = os.path.split(pathname) - # relative pathname - if not dirname: - return - # absolute pathname - if has_magic(dirname): - dirs = iglob(dirname) - else: - dirs = [dirname] - if has_magic(basename): - glob_in_dir = glob1 - else: - glob_in_dir = glob0 - for dirname in dirs: - for name in glob_in_dir(dirname, basename): - yield os.path.join(dirname, name) - -def glob1(dirname, pattern): - if not dirname: - dirname = os.curdir - try: - names = os.listdir(dirname) - except os.error: - return [] - return fnmatch.filter(names, pattern) - -def glob0(dirname, basename): - if basename == '' and os.path.isdir(dirname): - # `os.path.split()` returns an empty basename for paths ending with a - # directory separator. 'q*x/' should match only directories. - return [basename] - if hasattr(os.path, 'lexists'): - if os.path.lexists(os.path.join(dirname, basename)): - return [basename] - else: - if (os.path.islink(os.path.join(dirname, basename)) or - os.path.exists(os.path.join(dirname, basename))): - return [basename] - return [] - -MAGIC_CHECK = re.compile('[*?[]') - -def has_magic(s): - return MAGIC_CHECK.search(s) is not None - - - -### Core ### - - class EventsCodes: """ Set of codes corresponding to each kind of events. @@ -1501,14 +1431,20 @@ class ExcludeFilter: """ def __init__(self, arg_lst): """ + Examples: + ef1 = ExcludeFilter(["^/etc/rc.*", "^/etc/hostname"]) + ef2 = ExcludeFilter("/my/path/exclude.lst") + Where exclude.lst contains: + ^/etc/rc.* + ^/etc/hostname + @param arg_lst: is either a list or dict of patterns: - [pattern1, ..., patternn] - {'filename1': (list1, listn), ...} where list1 is - a list of patterns - @type arg_lst: list or dict + [pattern1, ..., patternn] or a filename from which + patterns will be loaded. + @type arg_lst: list(str) or str """ - if isinstance(arg_lst, dict): - lst = self._load_patterns(arg_lst) + if isinstance(arg_lst, str): + lst = self._load_patterns_from_file(arg_lst) elif isinstance(arg_lst, list): lst = arg_lst else: @@ -1518,13 +1454,18 @@ def __init__(self, arg_lst): for regex in lst: self._lregex.append(re.compile(regex, re.UNICODE)) - def _load_patterns(self, dct): + def _load_patterns_from_file(self, filename): lst = [] - for path, varnames in dct.items(): - loc = {} - execfile(path, {}, loc) - for varname in varnames: - lst.extend(loc.get(varname, [])) + file_obj = file(filename, 'r') + try: + for line in file_obj.readlines(): + # Trim leading an trailing whitespaces + pattern = line.strip() + if not pattern or pattern.startswith('#'): + continue + lst.append(pattern) + finally: + file_obj.close() return lst def _match(self, regex, path): @@ -1656,7 +1597,7 @@ def __add_watch(self, path, mask, proc_fun, auto_add, exclude_filter): def __glob(self, path, do_glob): if do_glob: - return iglob(path) + return glob.iglob(path) else: return [path] diff --git a/python3/pyinotify.py b/python3/pyinotify.py index 2a5af1c..2596d43 100755 --- a/python3/pyinotify.py +++ b/python3/pyinotify.py @@ -86,6 +86,7 @@ def __init__(self, version): import ctypes import ctypes.util import asyncore +import glob try: from functools import reduce @@ -148,10 +149,7 @@ def logger_init(): log = logger_init() - -### inotify's variables ### - - +# inotify's variables class SysCtlINotify: """ Access (read, write) inotify's variables through sysctl. Usually it @@ -218,74 +216,6 @@ def __repr__(self): globals()[attrname] = SysCtlINotify(attrname) - -### iglob ### - - -# Code taken from standart Python Lib, slightly modified in order to work -# with pyinotify (don't exclude dotted files/dirs like .foo). -# Original version: -# @see: http://svn.python.org/projects/python/trunk/Lib/glob.py - -def iglob(pathname): - if not has_magic(pathname): - if hasattr(os.path, 'lexists'): - if os.path.lexists(pathname): - yield pathname - else: - if os.path.islink(pathname) or os.path.exists(pathname): - yield pathname - return - dirname, basename = os.path.split(pathname) - # relative pathname - if not dirname: - return - # absolute pathname - if has_magic(dirname): - dirs = iglob(dirname) - else: - dirs = [dirname] - if has_magic(basename): - glob_in_dir = glob1 - else: - glob_in_dir = glob0 - for dirname in dirs: - for name in glob_in_dir(dirname, basename): - yield os.path.join(dirname, name) - -def glob1(dirname, pattern): - if not dirname: - dirname = os.curdir - try: - names = os.listdir(dirname) - except os.error: - return [] - return fnmatch.filter(names, pattern) - -def glob0(dirname, basename): - if basename == '' and os.path.isdir(dirname): - # `os.path.split()` returns an empty basename for paths ending with a - # directory separator. 'q*x/' should match only directories. - return [basename] - if hasattr(os.path, 'lexists'): - if os.path.lexists(os.path.join(dirname, basename)): - return [basename] - else: - if (os.path.islink(os.path.join(dirname, basename)) or - os.path.exists(os.path.join(dirname, basename))): - return [basename] - return [] - -MAGIC_CHECK = re.compile('[*?[]') - -def has_magic(s): - return MAGIC_CHECK.search(s) is not None - - - -### Core ### - - class EventsCodes: """ Set of codes corresponding to each kind of events. @@ -1459,14 +1389,20 @@ class ExcludeFilter: """ def __init__(self, arg_lst): """ + Examples: + ef1 = ExcludeFilter(["^/etc/rc.*", "^/etc/hostname"]) + ef2 = ExcludeFilter("/my/path/exclude.lst") + Where exclude.lst contains: + ^/etc/rc.* + ^/etc/hostname + @param arg_lst: is either a list or dict of patterns: - [pattern1, ..., patternn] - {'filename1': (list1, listn), ...} where list1 is - a list of patterns - @type arg_lst: list or dict + [pattern1, ..., patternn] or a filename from which + patterns will be loaded. + @type arg_lst: list(str) or str """ - if isinstance(arg_lst, dict): - lst = self._load_patterns(arg_lst) + if isinstance(arg_lst, str): + lst = self._load_patterns_from_file(arg_lst) elif isinstance(arg_lst, list): lst = arg_lst else: @@ -1476,13 +1412,15 @@ def __init__(self, arg_lst): for regex in lst: self._lregex.append(re.compile(regex, re.UNICODE)) - def _load_patterns(self, dct): + def _load_patterns_from_file(self, filename): lst = [] - for path, varnames in dct.items(): - loc = {} - execfile(path, {}, loc) - for varname in varnames: - lst.extend(loc.get(varname, [])) + with open(filename, 'r') as file_obj: + for line in file_obj.readlines(): + # Trim leading an trailing whitespaces + pattern = line.strip() + if not pattern or pattern.startswith('#'): + continue + lst.append(pattern) return lst def _match(self, regex, path): @@ -1613,7 +1551,7 @@ def __add_watch(self, path, mask, proc_fun, auto_add, exclude_filter): def __glob(self, path, do_glob): if do_glob: - return iglob(path) + return glob.iglob(path) else: return [path]