]> git.neil.brown.name Git - susman.git/blob - dnotify.py
wakealarmd: cope with delta between system time and RTC time.
[susman.git] / dnotify.py
1 #!/usr/bin/env python
2
3 # class to allow watching multiple files and
4 # calling a callback when any change (size or mtime)
5 #
6 # We take exclusive use of SIGIO and maintain a global list of
7 # watched files.
8 # As we cannot get siginfo in python, we check every file
9 # every time we get a signal.
10 # we report change is size, mtime, or ino of the file (given by name)
11
12 # Copyright (C) 2011 Neil Brown <neilb@suse.de>
13 #
14 #    This program is free software; you can redistribute it and/or modify
15 #    it under the terms of the GNU General Public License as published by
16 #    the Free Software Foundation; either version 2 of the License, or
17 #    (at your option) any later version.
18 #
19 #    This program is distributed in the hope that it will be useful,
20 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
21 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 #    GNU General Public License for more details.
23 #
24 #    You should have received a copy of the GNU General Public License along
25 #    with this program; if not, write to the Free Software Foundation, Inc.,
26 #    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27
28
29 import os, fcntl, signal
30
31
32 dirlist = []
33 def notified(sig, stack):
34     for d in dirlist:
35         fcntl.fcntl(d.fd, fcntl.F_NOTIFY, (fcntl.DN_MODIFY|fcntl.DN_RENAME|
36                                            fcntl.DN_CREATE|fcntl.DN_DELETE))
37         d.check()
38
39 class dir():
40     def __init__(self, dname):
41         self.dname = dname
42         self.fd = os.open(dname, 0)
43         self.files = []
44         self.callbacks = []
45         fcntl.fcntl(self.fd, fcntl.F_NOTIFY, (fcntl.DN_MODIFY|fcntl.DN_RENAME|
46                                               fcntl.DN_CREATE|fcntl.DN_DELETE))
47         if not dirlist:
48             signal.signal(signal.SIGIO, notified)
49         dirlist.append(self)
50
51     def watch(self, fname, callback):
52         f = file(os.path.join(self.dname, fname), callback)
53         self.files.append(f)
54         return f
55
56     def watchall(self, callback):
57         self.callbacks.append(callback)
58
59     def check(self):
60         newlist = []
61         for c in self.callbacks:
62             if c():
63                 newlist.append(c)
64         self.callbacks = newlist
65
66         for f in self.files:
67             f.check()
68
69     def cancel(self, victim):
70         if victim in self.files:
71             self.files.remove(victim)
72
73 class file():
74     def __init__(self, fname, callback):
75         self.name = fname
76         try:
77             stat = os.stat(self.name)
78         except OSError:
79             self.ino = 0
80             self.size = 0
81             self.mtime = 0
82         else:
83             self.ino = stat.st_ino
84             self.size = stat.st_size
85             self.mtime = stat.st_mtime
86         self.callback = callback
87
88     def check(self):
89         try:
90             stat = os.stat(self.name)
91         except OSError:
92             if self.ino == 0:
93                 return False
94             self.size = 0
95             self.mtime = 0
96             self.ino = 0
97         else:
98             if stat.st_size == self.size and stat.st_mtime == self.mtime \
99                    and stat.st_ino == self.ino:
100                 return False
101             self.size = stat.st_size
102             self.mtime = stat.st_mtime
103             self.ino = stat.st_ino
104
105         self.callback(self)
106         return True
107
108     def cancel(self):
109         global dirlist
110         for d in dirlist:
111             d.cancel(self)
112
113
114 if __name__ == "__main__" :
115     import signal
116
117
118     ##
119     def ping(f): print "got ", f.name
120
121     d = dir("/tmp/n")
122     a = d.watch("a", ping)
123     b = d.watch("b", ping)
124     c = d.watch("c", ping)
125
126     while True:
127         signal.pause()