From d25a72d5932341c7fe89040e077e980022107719 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Sat, 21 Apr 2012 20:17:10 +1000 Subject: [PATCH] Add dnotify.py This is a library to make it easy to python scripts to notice when files changes. Signed-off-by: NeilBrown --- lib/dnotify.py | 135 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 lib/dnotify.py diff --git a/lib/dnotify.py b/lib/dnotify.py new file mode 100644 index 0000000..8ec6cb8 --- /dev/null +++ b/lib/dnotify.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python + +# class to allow watching multiple files and +# calling a callback when any change (size or mtime) +# +# We take exclusive use of SIGIO and maintain a global list of +# watched files. +# As we cannot get siginfo in python, we check every file +# every time we get a signal. +# We report change if size, mtime, or ino of the file (given by name) + +# Copyright (C) 2011 Neil Brown +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import os, fcntl, signal + +dirlist = [] +def notified(sig, stack): + for d in dirlist: + fcntl.fcntl(d.fd, fcntl.F_NOTIFY, (fcntl.DN_MODIFY|fcntl.DN_RENAME| + fcntl.DN_CREATE|fcntl.DN_DELETE)) + d.check() + +class dir(): + def __init__(self, dname): + self.dname = dname + self.fd = os.open(dname, 0) + self.files = [] + self.callbacks = [] + fcntl.fcntl(self.fd, fcntl.F_NOTIFY, (fcntl.DN_MODIFY|fcntl.DN_RENAME| + fcntl.DN_CREATE|fcntl.DN_DELETE)) + if not dirlist: + signal.signal(signal.SIGIO, notified) + dirlist.append(self) + + def watch(self, fname, callback): + f = file(os.path.join(self.dname, fname), callback) + self.files.append(f) + return f + + def watchall(self, callback): + self.callbacks.append(callback) + + def check(self): + newlist = [] + for c in self.callbacks: + if c(): + newlist.append(c) + self.callbacks = newlist + + for f in self.files: + f.check() + + def cancel(self, victim): + if victim in self.files: + self.files.remove(victim) + +class file(): + def __init__(self, fname, callback): + self.name = fname + try: + stat = os.stat(self.name) + except OSError: + self.ino = 0 + self.size = 0 + self.mtime = 0 + else: + self.ino = stat.st_ino + self.size = stat.st_size + self.mtime = stat.st_mtime + self.callback = callback + + def check(self): + try: + stat = os.stat(self.name) + except OSError: + if self.ino == 0: + return False + self.size = 0 + self.mtime = 0 + self.ino = 0 + else: + if stat.st_size == self.size and stat.st_mtime == self.mtime \ + and stat.st_ino == self.ino: + return False + self.size = stat.st_size + self.mtime = stat.st_mtime + self.ino = stat.st_ino + + self.callback(self) + return True + + def cancel(self): + global dirlist + for d in dirlist: + d.cancel(self) + + +if __name__ == "__main__" : + import os + + def ping(f): print "got ", f.name + def something(): + print "some change happened" + return True + try: + os.mkdir("/tmp/n") + except OSError: + pass + d = dir("/tmp/n") + d.watchall(something) + a = d.watch("a", ping) + b = d.watch("b", ping) + c = d.watch("c", ping) + + print "Please modify files 'a', 'b', and 'c' in '/tmp/n'" + print "Ctrl-C when done" + try: + while True: + signal.pause() + except KeyboardInterrupt: + print "Goodbye" -- 2.39.5