##// END OF EJS Templates
inotify: completely ignore events on the repository root...
Nicolas Dumazet -
r10091:0ce645cc default
parent child Browse files
Show More
@@ -1,437 +1,441
1 # linuxserver.py - inotify status server for linux
1 # linuxserver.py - inotify status server for linux
2 #
2 #
3 # Copyright 2006, 2007, 2008 Bryan O'Sullivan <bos@serpentine.com>
3 # Copyright 2006, 2007, 2008 Bryan O'Sullivan <bos@serpentine.com>
4 # Copyright 2007, 2008 Brendan Cully <brendan@kublai.com>
4 # Copyright 2007, 2008 Brendan Cully <brendan@kublai.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2, incorporated herein by reference.
7 # GNU General Public License version 2, incorporated herein by reference.
8
8
9 from mercurial.i18n import _
9 from mercurial.i18n import _
10 from mercurial import osutil, util
10 from mercurial import osutil, util
11 import common
11 import common
12 import server
12 import server
13 import errno, os, select, stat, sys, time
13 import errno, os, select, stat, sys, time
14
14
15 try:
15 try:
16 import linux as inotify
16 import linux as inotify
17 from linux import watcher
17 from linux import watcher
18 except ImportError:
18 except ImportError:
19 raise
19 raise
20
20
21 def walkrepodirs(dirstate, absroot):
21 def walkrepodirs(dirstate, absroot):
22 '''Iterate over all subdirectories of this repo.
22 '''Iterate over all subdirectories of this repo.
23 Exclude the .hg directory, any nested repos, and ignored dirs.'''
23 Exclude the .hg directory, any nested repos, and ignored dirs.'''
24 def walkit(dirname, top):
24 def walkit(dirname, top):
25 fullpath = server.join(absroot, dirname)
25 fullpath = server.join(absroot, dirname)
26 try:
26 try:
27 for name, kind in osutil.listdir(fullpath):
27 for name, kind in osutil.listdir(fullpath):
28 if kind == stat.S_IFDIR:
28 if kind == stat.S_IFDIR:
29 if name == '.hg':
29 if name == '.hg':
30 if not top:
30 if not top:
31 return
31 return
32 else:
32 else:
33 d = server.join(dirname, name)
33 d = server.join(dirname, name)
34 if dirstate._ignore(d):
34 if dirstate._ignore(d):
35 continue
35 continue
36 for subdir in walkit(d, False):
36 for subdir in walkit(d, False):
37 yield subdir
37 yield subdir
38 except OSError, err:
38 except OSError, err:
39 if err.errno not in server.walk_ignored_errors:
39 if err.errno not in server.walk_ignored_errors:
40 raise
40 raise
41 yield fullpath
41 yield fullpath
42
42
43 return walkit('', True)
43 return walkit('', True)
44
44
45 def _explain_watch_limit(ui, dirstate, rootabs):
45 def _explain_watch_limit(ui, dirstate, rootabs):
46 path = '/proc/sys/fs/inotify/max_user_watches'
46 path = '/proc/sys/fs/inotify/max_user_watches'
47 try:
47 try:
48 limit = int(file(path).read())
48 limit = int(file(path).read())
49 except IOError, err:
49 except IOError, err:
50 if err.errno != errno.ENOENT:
50 if err.errno != errno.ENOENT:
51 raise
51 raise
52 raise util.Abort(_('this system does not seem to '
52 raise util.Abort(_('this system does not seem to '
53 'support inotify'))
53 'support inotify'))
54 ui.warn(_('*** the current per-user limit on the number '
54 ui.warn(_('*** the current per-user limit on the number '
55 'of inotify watches is %s\n') % limit)
55 'of inotify watches is %s\n') % limit)
56 ui.warn(_('*** this limit is too low to watch every '
56 ui.warn(_('*** this limit is too low to watch every '
57 'directory in this repository\n'))
57 'directory in this repository\n'))
58 ui.warn(_('*** counting directories: '))
58 ui.warn(_('*** counting directories: '))
59 ndirs = len(list(walkrepodirs(dirstate, rootabs)))
59 ndirs = len(list(walkrepodirs(dirstate, rootabs)))
60 ui.warn(_('found %d\n') % ndirs)
60 ui.warn(_('found %d\n') % ndirs)
61 newlimit = min(limit, 1024)
61 newlimit = min(limit, 1024)
62 while newlimit < ((limit + ndirs) * 1.1):
62 while newlimit < ((limit + ndirs) * 1.1):
63 newlimit *= 2
63 newlimit *= 2
64 ui.warn(_('*** to raise the limit from %d to %d (run as root):\n') %
64 ui.warn(_('*** to raise the limit from %d to %d (run as root):\n') %
65 (limit, newlimit))
65 (limit, newlimit))
66 ui.warn(_('*** echo %d > %s\n') % (newlimit, path))
66 ui.warn(_('*** echo %d > %s\n') % (newlimit, path))
67 raise util.Abort(_('cannot watch %s until inotify watch limit is raised')
67 raise util.Abort(_('cannot watch %s until inotify watch limit is raised')
68 % rootabs)
68 % rootabs)
69
69
70 class pollable(object):
70 class pollable(object):
71 """
71 """
72 Interface to support polling.
72 Interface to support polling.
73 The file descriptor returned by fileno() is registered to a polling
73 The file descriptor returned by fileno() is registered to a polling
74 object.
74 object.
75 Usage:
75 Usage:
76 Every tick, check if an event has happened since the last tick:
76 Every tick, check if an event has happened since the last tick:
77 * If yes, call handle_events
77 * If yes, call handle_events
78 * If no, call handle_timeout
78 * If no, call handle_timeout
79 """
79 """
80 poll_events = select.POLLIN
80 poll_events = select.POLLIN
81 instances = {}
81 instances = {}
82 poll = select.poll()
82 poll = select.poll()
83
83
84 def fileno(self):
84 def fileno(self):
85 raise NotImplementedError
85 raise NotImplementedError
86
86
87 def handle_events(self, events):
87 def handle_events(self, events):
88 raise NotImplementedError
88 raise NotImplementedError
89
89
90 def handle_timeout(self):
90 def handle_timeout(self):
91 raise NotImplementedError
91 raise NotImplementedError
92
92
93 def shutdown(self):
93 def shutdown(self):
94 raise NotImplementedError
94 raise NotImplementedError
95
95
96 def register(self, timeout):
96 def register(self, timeout):
97 fd = self.fileno()
97 fd = self.fileno()
98
98
99 pollable.poll.register(fd, pollable.poll_events)
99 pollable.poll.register(fd, pollable.poll_events)
100 pollable.instances[fd] = self
100 pollable.instances[fd] = self
101
101
102 self.registered = True
102 self.registered = True
103 self.timeout = timeout
103 self.timeout = timeout
104
104
105 def unregister(self):
105 def unregister(self):
106 pollable.poll.unregister(self)
106 pollable.poll.unregister(self)
107 self.registered = False
107 self.registered = False
108
108
109 @classmethod
109 @classmethod
110 def run(cls):
110 def run(cls):
111 while True:
111 while True:
112 timeout = None
112 timeout = None
113 timeobj = None
113 timeobj = None
114 for obj in cls.instances.itervalues():
114 for obj in cls.instances.itervalues():
115 if obj.timeout is not None and (timeout is None or obj.timeout < timeout):
115 if obj.timeout is not None and (timeout is None or obj.timeout < timeout):
116 timeout, timeobj = obj.timeout, obj
116 timeout, timeobj = obj.timeout, obj
117 try:
117 try:
118 events = cls.poll.poll(timeout)
118 events = cls.poll.poll(timeout)
119 except select.error, err:
119 except select.error, err:
120 if err[0] == errno.EINTR:
120 if err[0] == errno.EINTR:
121 continue
121 continue
122 raise
122 raise
123 if events:
123 if events:
124 by_fd = {}
124 by_fd = {}
125 for fd, event in events:
125 for fd, event in events:
126 by_fd.setdefault(fd, []).append(event)
126 by_fd.setdefault(fd, []).append(event)
127
127
128 for fd, events in by_fd.iteritems():
128 for fd, events in by_fd.iteritems():
129 cls.instances[fd].handle_pollevents(events)
129 cls.instances[fd].handle_pollevents(events)
130
130
131 elif timeobj:
131 elif timeobj:
132 timeobj.handle_timeout()
132 timeobj.handle_timeout()
133
133
134 def eventaction(code):
134 def eventaction(code):
135 """
135 """
136 Decorator to help handle events in repowatcher
136 Decorator to help handle events in repowatcher
137 """
137 """
138 def decorator(f):
138 def decorator(f):
139 def wrapper(self, wpath):
139 def wrapper(self, wpath):
140 if code == 'm' and wpath in self.lastevent and \
140 if code == 'm' and wpath in self.lastevent and \
141 self.lastevent[wpath] in 'cm':
141 self.lastevent[wpath] in 'cm':
142 return
142 return
143 self.lastevent[wpath] = code
143 self.lastevent[wpath] = code
144 self.timeout = 250
144 self.timeout = 250
145
145
146 f(self, wpath)
146 f(self, wpath)
147
147
148 wrapper.func_name = f.func_name
148 wrapper.func_name = f.func_name
149 return wrapper
149 return wrapper
150 return decorator
150 return decorator
151
151
152 class repowatcher(server.repowatcher, pollable):
152 class repowatcher(server.repowatcher, pollable):
153 """
153 """
154 Watches inotify events
154 Watches inotify events
155 """
155 """
156 mask = (
156 mask = (
157 inotify.IN_ATTRIB |
157 inotify.IN_ATTRIB |
158 inotify.IN_CREATE |
158 inotify.IN_CREATE |
159 inotify.IN_DELETE |
159 inotify.IN_DELETE |
160 inotify.IN_DELETE_SELF |
160 inotify.IN_DELETE_SELF |
161 inotify.IN_MODIFY |
161 inotify.IN_MODIFY |
162 inotify.IN_MOVED_FROM |
162 inotify.IN_MOVED_FROM |
163 inotify.IN_MOVED_TO |
163 inotify.IN_MOVED_TO |
164 inotify.IN_MOVE_SELF |
164 inotify.IN_MOVE_SELF |
165 inotify.IN_ONLYDIR |
165 inotify.IN_ONLYDIR |
166 inotify.IN_UNMOUNT |
166 inotify.IN_UNMOUNT |
167 0)
167 0)
168
168
169 def __init__(self, ui, dirstate, root):
169 def __init__(self, ui, dirstate, root):
170 server.repowatcher.__init__(self, ui, dirstate, root)
170 server.repowatcher.__init__(self, ui, dirstate, root)
171
171
172 self.lastevent = {}
172 self.lastevent = {}
173 self.dirty = False
173 self.dirty = False
174 try:
174 try:
175 self.watcher = watcher.watcher()
175 self.watcher = watcher.watcher()
176 except OSError, err:
176 except OSError, err:
177 raise util.Abort(_('inotify service not available: %s') %
177 raise util.Abort(_('inotify service not available: %s') %
178 err.strerror)
178 err.strerror)
179 self.threshold = watcher.threshold(self.watcher)
179 self.threshold = watcher.threshold(self.watcher)
180 self.fileno = self.watcher.fileno
180 self.fileno = self.watcher.fileno
181 self.register(timeout=None)
181 self.register(timeout=None)
182
182
183 self.handle_timeout()
183 self.handle_timeout()
184 self.scan()
184 self.scan()
185
185
186 def event_time(self):
186 def event_time(self):
187 last = self.last_event
187 last = self.last_event
188 now = time.time()
188 now = time.time()
189 self.last_event = now
189 self.last_event = now
190
190
191 if last is None:
191 if last is None:
192 return 'start'
192 return 'start'
193 delta = now - last
193 delta = now - last
194 if delta < 5:
194 if delta < 5:
195 return '+%.3f' % delta
195 return '+%.3f' % delta
196 if delta < 50:
196 if delta < 50:
197 return '+%.2f' % delta
197 return '+%.2f' % delta
198 return '+%.1f' % delta
198 return '+%.1f' % delta
199
199
200 def add_watch(self, path, mask):
200 def add_watch(self, path, mask):
201 if not path:
201 if not path:
202 return
202 return
203 if self.watcher.path(path) is None:
203 if self.watcher.path(path) is None:
204 if self.ui.debugflag:
204 if self.ui.debugflag:
205 self.ui.note(_('watching %r\n') % path[self.prefixlen:])
205 self.ui.note(_('watching %r\n') % path[self.prefixlen:])
206 try:
206 try:
207 self.watcher.add(path, mask)
207 self.watcher.add(path, mask)
208 except OSError, err:
208 except OSError, err:
209 if err.errno in (errno.ENOENT, errno.ENOTDIR):
209 if err.errno in (errno.ENOENT, errno.ENOTDIR):
210 return
210 return
211 if err.errno != errno.ENOSPC:
211 if err.errno != errno.ENOSPC:
212 raise
212 raise
213 _explain_watch_limit(self.ui, self.dirstate, self.wprefix)
213 _explain_watch_limit(self.ui, self.dirstate, self.wprefix)
214
214
215 def setup(self):
215 def setup(self):
216 self.ui.note(_('watching directories under %r\n') % self.wprefix)
216 self.ui.note(_('watching directories under %r\n') % self.wprefix)
217 self.add_watch(self.wprefix + '.hg', inotify.IN_DELETE)
217 self.add_watch(self.wprefix + '.hg', inotify.IN_DELETE)
218
218
219 def scan(self, topdir=''):
219 def scan(self, topdir=''):
220 ds = self.dirstate._map.copy()
220 ds = self.dirstate._map.copy()
221 self.add_watch(server.join(self.wprefix, topdir), self.mask)
221 self.add_watch(server.join(self.wprefix, topdir), self.mask)
222 for root, dirs, files in server.walk(self.dirstate, self.wprefix,
222 for root, dirs, files in server.walk(self.dirstate, self.wprefix,
223 topdir):
223 topdir):
224 for d in dirs:
224 for d in dirs:
225 self.add_watch(server.join(root, d), self.mask)
225 self.add_watch(server.join(root, d), self.mask)
226 wroot = root[self.prefixlen:]
226 wroot = root[self.prefixlen:]
227 for fn in files:
227 for fn in files:
228 wfn = server.join(wroot, fn)
228 wfn = server.join(wroot, fn)
229 self.updatefile(wfn, self.getstat(wfn))
229 self.updatefile(wfn, self.getstat(wfn))
230 ds.pop(wfn, None)
230 ds.pop(wfn, None)
231 wtopdir = topdir
231 wtopdir = topdir
232 if wtopdir and wtopdir[-1] != '/':
232 if wtopdir and wtopdir[-1] != '/':
233 wtopdir += '/'
233 wtopdir += '/'
234 for wfn, state in ds.iteritems():
234 for wfn, state in ds.iteritems():
235 if not wfn.startswith(wtopdir):
235 if not wfn.startswith(wtopdir):
236 continue
236 continue
237 try:
237 try:
238 st = self.stat(wfn)
238 st = self.stat(wfn)
239 except OSError:
239 except OSError:
240 status = state[0]
240 status = state[0]
241 self.deletefile(wfn, status)
241 self.deletefile(wfn, status)
242 else:
242 else:
243 self.updatefile(wfn, st)
243 self.updatefile(wfn, st)
244 self.check_deleted('!')
244 self.check_deleted('!')
245 self.check_deleted('r')
245 self.check_deleted('r')
246
246
247 @eventaction('c')
247 @eventaction('c')
248 def created(self, wpath):
248 def created(self, wpath):
249 if wpath == '.hgignore':
249 if wpath == '.hgignore':
250 self.update_hgignore()
250 self.update_hgignore()
251 try:
251 try:
252 st = self.stat(wpath)
252 st = self.stat(wpath)
253 if stat.S_ISREG(st[0]) or stat.S_ISLNK(st[0]):
253 if stat.S_ISREG(st[0]) or stat.S_ISLNK(st[0]):
254 self.updatefile(wpath, st)
254 self.updatefile(wpath, st)
255 except OSError:
255 except OSError:
256 pass
256 pass
257
257
258 @eventaction('m')
258 @eventaction('m')
259 def modified(self, wpath):
259 def modified(self, wpath):
260 if wpath == '.hgignore':
260 if wpath == '.hgignore':
261 self.update_hgignore()
261 self.update_hgignore()
262 try:
262 try:
263 st = self.stat(wpath)
263 st = self.stat(wpath)
264 if stat.S_ISREG(st[0]):
264 if stat.S_ISREG(st[0]):
265 if self.dirstate[wpath] in 'lmn':
265 if self.dirstate[wpath] in 'lmn':
266 self.updatefile(wpath, st)
266 self.updatefile(wpath, st)
267 except OSError:
267 except OSError:
268 pass
268 pass
269
269
270 @eventaction('d')
270 @eventaction('d')
271 def deleted(self, wpath):
271 def deleted(self, wpath):
272 if wpath == '.hgignore':
272 if wpath == '.hgignore':
273 self.update_hgignore()
273 self.update_hgignore()
274 elif wpath.startswith('.hg/'):
274 elif wpath.startswith('.hg/'):
275 return
275 return
276
276
277 self.deletefile(wpath, self.dirstate[wpath])
277 self.deletefile(wpath, self.dirstate[wpath])
278
278
279 def process_create(self, wpath, evt):
279 def process_create(self, wpath, evt):
280 if self.ui.debugflag:
280 if self.ui.debugflag:
281 self.ui.note(_('%s event: created %s\n') %
281 self.ui.note(_('%s event: created %s\n') %
282 (self.event_time(), wpath))
282 (self.event_time(), wpath))
283
283
284 if evt.mask & inotify.IN_ISDIR:
284 if evt.mask & inotify.IN_ISDIR:
285 self.scan(wpath)
285 self.scan(wpath)
286 else:
286 else:
287 self.created(wpath)
287 self.created(wpath)
288
288
289 def process_delete(self, wpath, evt):
289 def process_delete(self, wpath, evt):
290 if self.ui.debugflag:
290 if self.ui.debugflag:
291 self.ui.note(_('%s event: deleted %s\n') %
291 self.ui.note(_('%s event: deleted %s\n') %
292 (self.event_time(), wpath))
292 (self.event_time(), wpath))
293
293
294 if evt.mask & inotify.IN_ISDIR:
294 if evt.mask & inotify.IN_ISDIR:
295 tree = self.tree.dir(wpath)
295 tree = self.tree.dir(wpath)
296 todelete = [wfn for wfn, ignore in tree.walk('?')]
296 todelete = [wfn for wfn, ignore in tree.walk('?')]
297 for fn in todelete:
297 for fn in todelete:
298 self.deletefile(fn, '?')
298 self.deletefile(fn, '?')
299 self.scan(wpath)
299 self.scan(wpath)
300 else:
300 else:
301 self.deleted(wpath)
301 self.deleted(wpath)
302
302
303 def process_modify(self, wpath, evt):
303 def process_modify(self, wpath, evt):
304 if self.ui.debugflag:
304 if self.ui.debugflag:
305 self.ui.note(_('%s event: modified %s\n') %
305 self.ui.note(_('%s event: modified %s\n') %
306 (self.event_time(), wpath))
306 (self.event_time(), wpath))
307
307
308 if not (evt.mask & inotify.IN_ISDIR):
308 if not (evt.mask & inotify.IN_ISDIR):
309 self.modified(wpath)
309 self.modified(wpath)
310
310
311 def process_unmount(self, evt):
311 def process_unmount(self, evt):
312 self.ui.warn(_('filesystem containing %s was unmounted\n') %
312 self.ui.warn(_('filesystem containing %s was unmounted\n') %
313 evt.fullpath)
313 evt.fullpath)
314 sys.exit(0)
314 sys.exit(0)
315
315
316 def handle_pollevents(self, events):
316 def handle_pollevents(self, events):
317 if self.ui.debugflag:
317 if self.ui.debugflag:
318 self.ui.note(_('%s readable: %d bytes\n') %
318 self.ui.note(_('%s readable: %d bytes\n') %
319 (self.event_time(), self.threshold.readable()))
319 (self.event_time(), self.threshold.readable()))
320 if not self.threshold():
320 if not self.threshold():
321 if self.registered:
321 if self.registered:
322 if self.ui.debugflag:
322 if self.ui.debugflag:
323 self.ui.note(_('%s below threshold - unhooking\n') %
323 self.ui.note(_('%s below threshold - unhooking\n') %
324 (self.event_time()))
324 (self.event_time()))
325 self.unregister()
325 self.unregister()
326 self.timeout = 250
326 self.timeout = 250
327 else:
327 else:
328 self.read_events()
328 self.read_events()
329
329
330 def read_events(self, bufsize=None):
330 def read_events(self, bufsize=None):
331 events = self.watcher.read(bufsize)
331 events = self.watcher.read(bufsize)
332 if self.ui.debugflag:
332 if self.ui.debugflag:
333 self.ui.note(_('%s reading %d events\n') %
333 self.ui.note(_('%s reading %d events\n') %
334 (self.event_time(), len(events)))
334 (self.event_time(), len(events)))
335 for evt in events:
335 for evt in events:
336 if evt.fullpath == self.wprefix[:-1]:
337 # events on the root of the repository
338 # itself, e.g. permission changes or repository move
339 continue
336 assert evt.fullpath.startswith(self.wprefix)
340 assert evt.fullpath.startswith(self.wprefix)
337 wpath = evt.fullpath[self.prefixlen:]
341 wpath = evt.fullpath[self.prefixlen:]
338
342
339 # paths have been normalized, wpath never ends with a '/'
343 # paths have been normalized, wpath never ends with a '/'
340
344
341 if wpath.startswith('.hg/') and evt.mask & inotify.IN_ISDIR:
345 if wpath.startswith('.hg/') and evt.mask & inotify.IN_ISDIR:
342 # ignore subdirectories of .hg/ (merge, patches...)
346 # ignore subdirectories of .hg/ (merge, patches...)
343 continue
347 continue
344 if wpath == ".hg/wlock":
348 if wpath == ".hg/wlock":
345 if evt.mask & inotify.IN_DELETE:
349 if evt.mask & inotify.IN_DELETE:
346 self.dirstate.invalidate()
350 self.dirstate.invalidate()
347 self.dirty = False
351 self.dirty = False
348 self.scan()
352 self.scan()
349 elif evt.mask & inotify.IN_CREATE:
353 elif evt.mask & inotify.IN_CREATE:
350 self.dirty = True
354 self.dirty = True
351 else:
355 else:
352 if self.dirty:
356 if self.dirty:
353 continue
357 continue
354
358
355 if evt.mask & inotify.IN_UNMOUNT:
359 if evt.mask & inotify.IN_UNMOUNT:
356 self.process_unmount(wpath, evt)
360 self.process_unmount(wpath, evt)
357 elif evt.mask & (inotify.IN_MODIFY | inotify.IN_ATTRIB):
361 elif evt.mask & (inotify.IN_MODIFY | inotify.IN_ATTRIB):
358 self.process_modify(wpath, evt)
362 self.process_modify(wpath, evt)
359 elif evt.mask & (inotify.IN_DELETE | inotify.IN_DELETE_SELF |
363 elif evt.mask & (inotify.IN_DELETE | inotify.IN_DELETE_SELF |
360 inotify.IN_MOVED_FROM):
364 inotify.IN_MOVED_FROM):
361 self.process_delete(wpath, evt)
365 self.process_delete(wpath, evt)
362 elif evt.mask & (inotify.IN_CREATE | inotify.IN_MOVED_TO):
366 elif evt.mask & (inotify.IN_CREATE | inotify.IN_MOVED_TO):
363 self.process_create(wpath, evt)
367 self.process_create(wpath, evt)
364
368
365 self.lastevent.clear()
369 self.lastevent.clear()
366
370
367 def handle_timeout(self):
371 def handle_timeout(self):
368 if not self.registered:
372 if not self.registered:
369 if self.ui.debugflag:
373 if self.ui.debugflag:
370 self.ui.note(_('%s hooking back up with %d bytes readable\n') %
374 self.ui.note(_('%s hooking back up with %d bytes readable\n') %
371 (self.event_time(), self.threshold.readable()))
375 (self.event_time(), self.threshold.readable()))
372 self.read_events(0)
376 self.read_events(0)
373 self.register(timeout=None)
377 self.register(timeout=None)
374
378
375 self.timeout = None
379 self.timeout = None
376
380
377 def shutdown(self):
381 def shutdown(self):
378 self.watcher.close()
382 self.watcher.close()
379
383
380 def debug(self):
384 def debug(self):
381 """
385 """
382 Returns a sorted list of relatives paths currently watched,
386 Returns a sorted list of relatives paths currently watched,
383 for debugging purposes.
387 for debugging purposes.
384 """
388 """
385 return sorted(tuple[0][self.prefixlen:] for tuple in self.watcher)
389 return sorted(tuple[0][self.prefixlen:] for tuple in self.watcher)
386
390
387 class socketlistener(server.socketlistener, pollable):
391 class socketlistener(server.socketlistener, pollable):
388 """
392 """
389 Listens for client queries on unix socket inotify.sock
393 Listens for client queries on unix socket inotify.sock
390 """
394 """
391 def __init__(self, ui, root, repowatcher, timeout):
395 def __init__(self, ui, root, repowatcher, timeout):
392 server.socketlistener.__init__(self, ui, root, repowatcher, timeout)
396 server.socketlistener.__init__(self, ui, root, repowatcher, timeout)
393 self.register(timeout=timeout)
397 self.register(timeout=timeout)
394
398
395 def handle_timeout(self):
399 def handle_timeout(self):
396 pass
400 pass
397
401
398 def handle_pollevents(self, events):
402 def handle_pollevents(self, events):
399 for e in events:
403 for e in events:
400 self.accept_connection()
404 self.accept_connection()
401
405
402 def shutdown(self):
406 def shutdown(self):
403 self.sock.close()
407 self.sock.close()
404 try:
408 try:
405 os.unlink(self.sockpath)
409 os.unlink(self.sockpath)
406 if self.realsockpath:
410 if self.realsockpath:
407 os.unlink(self.realsockpath)
411 os.unlink(self.realsockpath)
408 os.rmdir(os.path.dirname(self.realsockpath))
412 os.rmdir(os.path.dirname(self.realsockpath))
409 except OSError, err:
413 except OSError, err:
410 if err.errno != errno.ENOENT:
414 if err.errno != errno.ENOENT:
411 raise
415 raise
412
416
413 def answer_stat_query(self, cs):
417 def answer_stat_query(self, cs):
414 if self.repowatcher.timeout:
418 if self.repowatcher.timeout:
415 # We got a query while a rescan is pending. Make sure we
419 # We got a query while a rescan is pending. Make sure we
416 # rescan before responding, or we could give back a wrong
420 # rescan before responding, or we could give back a wrong
417 # answer.
421 # answer.
418 self.repowatcher.handle_timeout()
422 self.repowatcher.handle_timeout()
419 return server.socketlistener.answer_stat_query(self, cs)
423 return server.socketlistener.answer_stat_query(self, cs)
420
424
421 class master(object):
425 class master(object):
422 def __init__(self, ui, dirstate, root, timeout=None):
426 def __init__(self, ui, dirstate, root, timeout=None):
423 self.ui = ui
427 self.ui = ui
424 self.repowatcher = repowatcher(ui, dirstate, root)
428 self.repowatcher = repowatcher(ui, dirstate, root)
425 self.socketlistener = socketlistener(ui, root, self.repowatcher,
429 self.socketlistener = socketlistener(ui, root, self.repowatcher,
426 timeout)
430 timeout)
427
431
428 def shutdown(self):
432 def shutdown(self):
429 for obj in pollable.instances.itervalues():
433 for obj in pollable.instances.itervalues():
430 obj.shutdown()
434 obj.shutdown()
431
435
432 def run(self):
436 def run(self):
433 self.repowatcher.setup()
437 self.repowatcher.setup()
434 self.ui.note(_('finished setup\n'))
438 self.ui.note(_('finished setup\n'))
435 if os.getenv('TIME_STARTUP'):
439 if os.getenv('TIME_STARTUP'):
436 sys.exit(0)
440 sys.exit(0)
437 pollable.run()
441 pollable.run()
General Comments 0
You need to be logged in to leave comments. Login now