##// END OF EJS Templates
inotify: on Python < 2.6, socket.error lacks errno
Bryan O'Sullivan -
r18107:c60d09dc default
parent child Browse files
Show More
@@ -1,464 +1,464 b''
1 # server.py - common entry point for inotify status server
1 # server.py - common entry point for inotify status server
2 #
2 #
3 # Copyright 2009 Nicolas Dumazet <nicdumz@gmail.com>
3 # Copyright 2009 Nicolas Dumazet <nicdumz@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from mercurial.i18n import _
8 from mercurial.i18n import _
9 from mercurial import cmdutil, posix, osutil, util
9 from mercurial import cmdutil, posix, osutil, util
10 import common
10 import common
11
11
12 import errno
12 import errno
13 import os
13 import os
14 import socket
14 import socket
15 import stat
15 import stat
16 import struct
16 import struct
17 import sys
17 import sys
18
18
19 class AlreadyStartedException(Exception):
19 class AlreadyStartedException(Exception):
20 pass
20 pass
21 class TimeoutException(Exception):
21 class TimeoutException(Exception):
22 pass
22 pass
23
23
24 def join(a, b):
24 def join(a, b):
25 if a:
25 if a:
26 if a[-1] == '/':
26 if a[-1] == '/':
27 return a + b
27 return a + b
28 return a + '/' + b
28 return a + '/' + b
29 return b
29 return b
30
30
31 def split(path):
31 def split(path):
32 c = path.rfind('/')
32 c = path.rfind('/')
33 if c == -1:
33 if c == -1:
34 return '', path
34 return '', path
35 return path[:c], path[c + 1:]
35 return path[:c], path[c + 1:]
36
36
37 walk_ignored_errors = (errno.ENOENT, errno.ENAMETOOLONG)
37 walk_ignored_errors = (errno.ENOENT, errno.ENAMETOOLONG)
38
38
39 def walk(dirstate, absroot, root):
39 def walk(dirstate, absroot, root):
40 '''Like os.walk, but only yields regular files.'''
40 '''Like os.walk, but only yields regular files.'''
41
41
42 # This function is critical to performance during startup.
42 # This function is critical to performance during startup.
43
43
44 def walkit(root, reporoot):
44 def walkit(root, reporoot):
45 files, dirs = [], []
45 files, dirs = [], []
46
46
47 try:
47 try:
48 fullpath = join(absroot, root)
48 fullpath = join(absroot, root)
49 for name, kind in osutil.listdir(fullpath):
49 for name, kind in osutil.listdir(fullpath):
50 if kind == stat.S_IFDIR:
50 if kind == stat.S_IFDIR:
51 if name == '.hg':
51 if name == '.hg':
52 if not reporoot:
52 if not reporoot:
53 return
53 return
54 else:
54 else:
55 dirs.append(name)
55 dirs.append(name)
56 path = join(root, name)
56 path = join(root, name)
57 if dirstate._ignore(path):
57 if dirstate._ignore(path):
58 continue
58 continue
59 for result in walkit(path, False):
59 for result in walkit(path, False):
60 yield result
60 yield result
61 elif kind in (stat.S_IFREG, stat.S_IFLNK):
61 elif kind in (stat.S_IFREG, stat.S_IFLNK):
62 files.append(name)
62 files.append(name)
63 yield fullpath, dirs, files
63 yield fullpath, dirs, files
64
64
65 except OSError, err:
65 except OSError, err:
66 if err.errno == errno.ENOTDIR:
66 if err.errno == errno.ENOTDIR:
67 # fullpath was a directory, but has since been replaced
67 # fullpath was a directory, but has since been replaced
68 # by a file.
68 # by a file.
69 yield fullpath, dirs, files
69 yield fullpath, dirs, files
70 elif err.errno not in walk_ignored_errors:
70 elif err.errno not in walk_ignored_errors:
71 raise
71 raise
72
72
73 return walkit(root, root == '')
73 return walkit(root, root == '')
74
74
75 class directory(object):
75 class directory(object):
76 """
76 """
77 Representing a directory
77 Representing a directory
78
78
79 * path is the relative path from repo root to this directory
79 * path is the relative path from repo root to this directory
80 * files is a dict listing the files in this directory
80 * files is a dict listing the files in this directory
81 - keys are file names
81 - keys are file names
82 - values are file status
82 - values are file status
83 * dirs is a dict listing the subdirectories
83 * dirs is a dict listing the subdirectories
84 - key are subdirectories names
84 - key are subdirectories names
85 - values are directory objects
85 - values are directory objects
86 """
86 """
87 def __init__(self, relpath=''):
87 def __init__(self, relpath=''):
88 self.path = relpath
88 self.path = relpath
89 self.files = {}
89 self.files = {}
90 self.dirs = {}
90 self.dirs = {}
91
91
92 def dir(self, relpath):
92 def dir(self, relpath):
93 """
93 """
94 Returns the directory contained at the relative path relpath.
94 Returns the directory contained at the relative path relpath.
95 Creates the intermediate directories if necessary.
95 Creates the intermediate directories if necessary.
96 """
96 """
97 if not relpath:
97 if not relpath:
98 return self
98 return self
99 l = relpath.split('/')
99 l = relpath.split('/')
100 ret = self
100 ret = self
101 while l:
101 while l:
102 next = l.pop(0)
102 next = l.pop(0)
103 try:
103 try:
104 ret = ret.dirs[next]
104 ret = ret.dirs[next]
105 except KeyError:
105 except KeyError:
106 d = directory(join(ret.path, next))
106 d = directory(join(ret.path, next))
107 ret.dirs[next] = d
107 ret.dirs[next] = d
108 ret = d
108 ret = d
109 return ret
109 return ret
110
110
111 def walk(self, states, visited=None):
111 def walk(self, states, visited=None):
112 """
112 """
113 yield (filename, status) pairs for items in the trees
113 yield (filename, status) pairs for items in the trees
114 that have status in states.
114 that have status in states.
115 filenames are relative to the repo root
115 filenames are relative to the repo root
116 """
116 """
117 for file, st in self.files.iteritems():
117 for file, st in self.files.iteritems():
118 if st in states:
118 if st in states:
119 yield join(self.path, file), st
119 yield join(self.path, file), st
120 for dir in self.dirs.itervalues():
120 for dir in self.dirs.itervalues():
121 if visited is not None:
121 if visited is not None:
122 visited.add(dir.path)
122 visited.add(dir.path)
123 for e in dir.walk(states):
123 for e in dir.walk(states):
124 yield e
124 yield e
125
125
126 def lookup(self, states, path, visited):
126 def lookup(self, states, path, visited):
127 """
127 """
128 yield root-relative filenames that match path, and whose
128 yield root-relative filenames that match path, and whose
129 status are in states:
129 status are in states:
130 * if path is a file, yield path
130 * if path is a file, yield path
131 * if path is a directory, yield directory files
131 * if path is a directory, yield directory files
132 * if path is not tracked, yield nothing
132 * if path is not tracked, yield nothing
133 """
133 """
134 if path[-1] == '/':
134 if path[-1] == '/':
135 path = path[:-1]
135 path = path[:-1]
136
136
137 paths = path.split('/')
137 paths = path.split('/')
138
138
139 # we need to check separately for last node
139 # we need to check separately for last node
140 last = paths.pop()
140 last = paths.pop()
141
141
142 tree = self
142 tree = self
143 try:
143 try:
144 for dir in paths:
144 for dir in paths:
145 tree = tree.dirs[dir]
145 tree = tree.dirs[dir]
146 except KeyError:
146 except KeyError:
147 # path is not tracked
147 # path is not tracked
148 visited.add(tree.path)
148 visited.add(tree.path)
149 return
149 return
150
150
151 try:
151 try:
152 # if path is a directory, walk it
152 # if path is a directory, walk it
153 target = tree.dirs[last]
153 target = tree.dirs[last]
154 visited.add(target.path)
154 visited.add(target.path)
155 for file, st in target.walk(states, visited):
155 for file, st in target.walk(states, visited):
156 yield file
156 yield file
157 except KeyError:
157 except KeyError:
158 try:
158 try:
159 if tree.files[last] in states:
159 if tree.files[last] in states:
160 # path is a file
160 # path is a file
161 visited.add(tree.path)
161 visited.add(tree.path)
162 yield path
162 yield path
163 except KeyError:
163 except KeyError:
164 # path is not tracked
164 # path is not tracked
165 pass
165 pass
166
166
167 class repowatcher(object):
167 class repowatcher(object):
168 """
168 """
169 Watches inotify events
169 Watches inotify events
170 """
170 """
171 statuskeys = 'almr!?'
171 statuskeys = 'almr!?'
172
172
173 def __init__(self, ui, dirstate, root):
173 def __init__(self, ui, dirstate, root):
174 self.ui = ui
174 self.ui = ui
175 self.dirstate = dirstate
175 self.dirstate = dirstate
176
176
177 self.wprefix = join(root, '')
177 self.wprefix = join(root, '')
178 self.prefixlen = len(self.wprefix)
178 self.prefixlen = len(self.wprefix)
179
179
180 self.tree = directory()
180 self.tree = directory()
181 self.statcache = {}
181 self.statcache = {}
182 self.statustrees = dict([(s, directory()) for s in self.statuskeys])
182 self.statustrees = dict([(s, directory()) for s in self.statuskeys])
183
183
184 self.ds_info = self.dirstate_info()
184 self.ds_info = self.dirstate_info()
185
185
186 self.last_event = None
186 self.last_event = None
187
187
188
188
189 def handle_timeout(self):
189 def handle_timeout(self):
190 pass
190 pass
191
191
192 def dirstate_info(self):
192 def dirstate_info(self):
193 try:
193 try:
194 st = os.lstat(self.wprefix + '.hg/dirstate')
194 st = os.lstat(self.wprefix + '.hg/dirstate')
195 return st.st_mtime, st.st_ino
195 return st.st_mtime, st.st_ino
196 except OSError, err:
196 except OSError, err:
197 if err.errno != errno.ENOENT:
197 if err.errno != errno.ENOENT:
198 raise
198 raise
199 return 0, 0
199 return 0, 0
200
200
201 def filestatus(self, fn, st):
201 def filestatus(self, fn, st):
202 try:
202 try:
203 type_, mode, size, time = self.dirstate._map[fn][:4]
203 type_, mode, size, time = self.dirstate._map[fn][:4]
204 except KeyError:
204 except KeyError:
205 type_ = '?'
205 type_ = '?'
206 if type_ == 'n':
206 if type_ == 'n':
207 st_mode, st_size, st_mtime = st
207 st_mode, st_size, st_mtime = st
208 if size == -1:
208 if size == -1:
209 return 'l'
209 return 'l'
210 if size and (size != st_size or (mode ^ st_mode) & 0100):
210 if size and (size != st_size or (mode ^ st_mode) & 0100):
211 return 'm'
211 return 'm'
212 if time != int(st_mtime):
212 if time != int(st_mtime):
213 return 'l'
213 return 'l'
214 return 'n'
214 return 'n'
215 if type_ == '?' and self.dirstate._dirignore(fn):
215 if type_ == '?' and self.dirstate._dirignore(fn):
216 # we must check not only if the file is ignored, but if any part
216 # we must check not only if the file is ignored, but if any part
217 # of its path match an ignore pattern
217 # of its path match an ignore pattern
218 return 'i'
218 return 'i'
219 return type_
219 return type_
220
220
221 def updatefile(self, wfn, osstat):
221 def updatefile(self, wfn, osstat):
222 '''
222 '''
223 update the file entry of an existing file.
223 update the file entry of an existing file.
224
224
225 osstat: (mode, size, time) tuple, as returned by os.lstat(wfn)
225 osstat: (mode, size, time) tuple, as returned by os.lstat(wfn)
226 '''
226 '''
227
227
228 self._updatestatus(wfn, self.filestatus(wfn, osstat))
228 self._updatestatus(wfn, self.filestatus(wfn, osstat))
229
229
230 def deletefile(self, wfn, oldstatus):
230 def deletefile(self, wfn, oldstatus):
231 '''
231 '''
232 update the entry of a file which has been deleted.
232 update the entry of a file which has been deleted.
233
233
234 oldstatus: char in statuskeys, status of the file before deletion
234 oldstatus: char in statuskeys, status of the file before deletion
235 '''
235 '''
236 if oldstatus == 'r':
236 if oldstatus == 'r':
237 newstatus = 'r'
237 newstatus = 'r'
238 elif oldstatus in 'almn':
238 elif oldstatus in 'almn':
239 newstatus = '!'
239 newstatus = '!'
240 else:
240 else:
241 newstatus = None
241 newstatus = None
242
242
243 self.statcache.pop(wfn, None)
243 self.statcache.pop(wfn, None)
244 self._updatestatus(wfn, newstatus)
244 self._updatestatus(wfn, newstatus)
245
245
246 def _updatestatus(self, wfn, newstatus):
246 def _updatestatus(self, wfn, newstatus):
247 '''
247 '''
248 Update the stored status of a file.
248 Update the stored status of a file.
249
249
250 newstatus: - char in (statuskeys + 'ni'), new status to apply.
250 newstatus: - char in (statuskeys + 'ni'), new status to apply.
251 - or None, to stop tracking wfn
251 - or None, to stop tracking wfn
252 '''
252 '''
253 root, fn = split(wfn)
253 root, fn = split(wfn)
254 d = self.tree.dir(root)
254 d = self.tree.dir(root)
255
255
256 oldstatus = d.files.get(fn)
256 oldstatus = d.files.get(fn)
257 # oldstatus can be either:
257 # oldstatus can be either:
258 # - None : fn is new
258 # - None : fn is new
259 # - a char in statuskeys: fn is a (tracked) file
259 # - a char in statuskeys: fn is a (tracked) file
260
260
261 if self.ui.debugflag and oldstatus != newstatus:
261 if self.ui.debugflag and oldstatus != newstatus:
262 self.ui.note(_('status: %r %s -> %s\n') %
262 self.ui.note(_('status: %r %s -> %s\n') %
263 (wfn, oldstatus, newstatus))
263 (wfn, oldstatus, newstatus))
264
264
265 if oldstatus and oldstatus in self.statuskeys \
265 if oldstatus and oldstatus in self.statuskeys \
266 and oldstatus != newstatus:
266 and oldstatus != newstatus:
267 del self.statustrees[oldstatus].dir(root).files[fn]
267 del self.statustrees[oldstatus].dir(root).files[fn]
268
268
269 if newstatus in (None, 'i'):
269 if newstatus in (None, 'i'):
270 d.files.pop(fn, None)
270 d.files.pop(fn, None)
271 elif oldstatus != newstatus:
271 elif oldstatus != newstatus:
272 d.files[fn] = newstatus
272 d.files[fn] = newstatus
273 if newstatus != 'n':
273 if newstatus != 'n':
274 self.statustrees[newstatus].dir(root).files[fn] = newstatus
274 self.statustrees[newstatus].dir(root).files[fn] = newstatus
275
275
276 def check_deleted(self, key):
276 def check_deleted(self, key):
277 # Files that had been deleted but were present in the dirstate
277 # Files that had been deleted but were present in the dirstate
278 # may have vanished from the dirstate; we must clean them up.
278 # may have vanished from the dirstate; we must clean them up.
279 nuke = []
279 nuke = []
280 for wfn, ignore in self.statustrees[key].walk(key):
280 for wfn, ignore in self.statustrees[key].walk(key):
281 if wfn not in self.dirstate:
281 if wfn not in self.dirstate:
282 nuke.append(wfn)
282 nuke.append(wfn)
283 for wfn in nuke:
283 for wfn in nuke:
284 root, fn = split(wfn)
284 root, fn = split(wfn)
285 del self.statustrees[key].dir(root).files[fn]
285 del self.statustrees[key].dir(root).files[fn]
286 del self.tree.dir(root).files[fn]
286 del self.tree.dir(root).files[fn]
287
287
288 def update_hgignore(self):
288 def update_hgignore(self):
289 # An update of the ignore file can potentially change the
289 # An update of the ignore file can potentially change the
290 # states of all unknown and ignored files.
290 # states of all unknown and ignored files.
291
291
292 # XXX If the user has other ignore files outside the repo, or
292 # XXX If the user has other ignore files outside the repo, or
293 # changes their list of ignore files at run time, we'll
293 # changes their list of ignore files at run time, we'll
294 # potentially never see changes to them. We could get the
294 # potentially never see changes to them. We could get the
295 # client to report to us what ignore data they're using.
295 # client to report to us what ignore data they're using.
296 # But it's easier to do nothing than to open that can of
296 # But it's easier to do nothing than to open that can of
297 # worms.
297 # worms.
298
298
299 if '_ignore' in self.dirstate.__dict__:
299 if '_ignore' in self.dirstate.__dict__:
300 delattr(self.dirstate, '_ignore')
300 delattr(self.dirstate, '_ignore')
301 self.ui.note(_('rescanning due to .hgignore change\n'))
301 self.ui.note(_('rescanning due to .hgignore change\n'))
302 self.handle_timeout()
302 self.handle_timeout()
303 self.scan()
303 self.scan()
304
304
305 def getstat(self, wpath):
305 def getstat(self, wpath):
306 try:
306 try:
307 return self.statcache[wpath]
307 return self.statcache[wpath]
308 except KeyError:
308 except KeyError:
309 try:
309 try:
310 return self.stat(wpath)
310 return self.stat(wpath)
311 except OSError, err:
311 except OSError, err:
312 if err.errno != errno.ENOENT:
312 if err.errno != errno.ENOENT:
313 raise
313 raise
314
314
315 def stat(self, wpath):
315 def stat(self, wpath):
316 try:
316 try:
317 st = os.lstat(join(self.wprefix, wpath))
317 st = os.lstat(join(self.wprefix, wpath))
318 ret = st.st_mode, st.st_size, st.st_mtime
318 ret = st.st_mode, st.st_size, st.st_mtime
319 self.statcache[wpath] = ret
319 self.statcache[wpath] = ret
320 return ret
320 return ret
321 except OSError:
321 except OSError:
322 self.statcache.pop(wpath, None)
322 self.statcache.pop(wpath, None)
323 raise
323 raise
324
324
325 class socketlistener(object):
325 class socketlistener(object):
326 """
326 """
327 Listens for client queries on unix socket inotify.sock
327 Listens for client queries on unix socket inotify.sock
328 """
328 """
329 def __init__(self, ui, root, repowatcher, timeout):
329 def __init__(self, ui, root, repowatcher, timeout):
330 self.ui = ui
330 self.ui = ui
331 self.repowatcher = repowatcher
331 self.repowatcher = repowatcher
332 try:
332 try:
333 self.sock = posix.unixdomainserver(
333 self.sock = posix.unixdomainserver(
334 lambda p: os.path.join(root, '.hg', p),
334 lambda p: os.path.join(root, '.hg', p),
335 'inotify')
335 'inotify')
336 except (OSError, socket.error), err:
336 except (OSError, socket.error), err:
337 if err.errno == errno.EADDRINUSE:
337 if err.args[0] == errno.EADDRINUSE:
338 raise AlreadyStartedException(_('cannot start: '
338 raise AlreadyStartedException(_('cannot start: '
339 'socket is already bound'))
339 'socket is already bound'))
340 raise
340 raise
341 self.fileno = self.sock.fileno
341 self.fileno = self.sock.fileno
342
342
343 def answer_stat_query(self, cs):
343 def answer_stat_query(self, cs):
344 names = cs.read().split('\0')
344 names = cs.read().split('\0')
345
345
346 states = names.pop()
346 states = names.pop()
347
347
348 self.ui.note(_('answering query for %r\n') % states)
348 self.ui.note(_('answering query for %r\n') % states)
349
349
350 visited = set()
350 visited = set()
351 if not names:
351 if not names:
352 def genresult(states, tree):
352 def genresult(states, tree):
353 for fn, state in tree.walk(states):
353 for fn, state in tree.walk(states):
354 yield fn
354 yield fn
355 else:
355 else:
356 def genresult(states, tree):
356 def genresult(states, tree):
357 for fn in names:
357 for fn in names:
358 for f in tree.lookup(states, fn, visited):
358 for f in tree.lookup(states, fn, visited):
359 yield f
359 yield f
360
360
361 return ['\0'.join(r) for r in [
361 return ['\0'.join(r) for r in [
362 genresult('l', self.repowatcher.statustrees['l']),
362 genresult('l', self.repowatcher.statustrees['l']),
363 genresult('m', self.repowatcher.statustrees['m']),
363 genresult('m', self.repowatcher.statustrees['m']),
364 genresult('a', self.repowatcher.statustrees['a']),
364 genresult('a', self.repowatcher.statustrees['a']),
365 genresult('r', self.repowatcher.statustrees['r']),
365 genresult('r', self.repowatcher.statustrees['r']),
366 genresult('!', self.repowatcher.statustrees['!']),
366 genresult('!', self.repowatcher.statustrees['!']),
367 '?' in states
367 '?' in states
368 and genresult('?', self.repowatcher.statustrees['?'])
368 and genresult('?', self.repowatcher.statustrees['?'])
369 or [],
369 or [],
370 [],
370 [],
371 'c' in states and genresult('n', self.repowatcher.tree) or [],
371 'c' in states and genresult('n', self.repowatcher.tree) or [],
372 visited
372 visited
373 ]]
373 ]]
374
374
375 def answer_dbug_query(self):
375 def answer_dbug_query(self):
376 return ['\0'.join(self.repowatcher.debug())]
376 return ['\0'.join(self.repowatcher.debug())]
377
377
378 def accept_connection(self):
378 def accept_connection(self):
379 sock, addr = self.sock.accept()
379 sock, addr = self.sock.accept()
380
380
381 cs = common.recvcs(sock)
381 cs = common.recvcs(sock)
382 version = ord(cs.read(1))
382 version = ord(cs.read(1))
383
383
384 if version != common.version:
384 if version != common.version:
385 self.ui.warn(_('received query from incompatible client '
385 self.ui.warn(_('received query from incompatible client '
386 'version %d\n') % version)
386 'version %d\n') % version)
387 try:
387 try:
388 # try to send back our version to the client
388 # try to send back our version to the client
389 # this way, the client too is informed of the mismatch
389 # this way, the client too is informed of the mismatch
390 sock.sendall(chr(common.version))
390 sock.sendall(chr(common.version))
391 except socket.error:
391 except socket.error:
392 pass
392 pass
393 return
393 return
394
394
395 type = cs.read(4)
395 type = cs.read(4)
396
396
397 if type == 'STAT':
397 if type == 'STAT':
398 results = self.answer_stat_query(cs)
398 results = self.answer_stat_query(cs)
399 elif type == 'DBUG':
399 elif type == 'DBUG':
400 results = self.answer_dbug_query()
400 results = self.answer_dbug_query()
401 else:
401 else:
402 self.ui.warn(_('unrecognized query type: %s\n') % type)
402 self.ui.warn(_('unrecognized query type: %s\n') % type)
403 return
403 return
404
404
405 try:
405 try:
406 try:
406 try:
407 v = chr(common.version)
407 v = chr(common.version)
408
408
409 sock.sendall(v + type + struct.pack(common.resphdrfmts[type],
409 sock.sendall(v + type + struct.pack(common.resphdrfmts[type],
410 *map(len, results)))
410 *map(len, results)))
411 sock.sendall(''.join(results))
411 sock.sendall(''.join(results))
412 finally:
412 finally:
413 sock.shutdown(socket.SHUT_WR)
413 sock.shutdown(socket.SHUT_WR)
414 except socket.error, err:
414 except socket.error, err:
415 if err.args[0] != errno.EPIPE:
415 if err.args[0] != errno.EPIPE:
416 raise
416 raise
417
417
418 if sys.platform.startswith('linux'):
418 if sys.platform.startswith('linux'):
419 import linuxserver as _server
419 import linuxserver as _server
420 else:
420 else:
421 raise ImportError
421 raise ImportError
422
422
423 master = _server.master
423 master = _server.master
424
424
425 def start(ui, dirstate, root, opts):
425 def start(ui, dirstate, root, opts):
426 timeout = opts.get('idle_timeout')
426 timeout = opts.get('idle_timeout')
427 if timeout:
427 if timeout:
428 timeout = float(timeout) * 60000
428 timeout = float(timeout) * 60000
429 else:
429 else:
430 timeout = None
430 timeout = None
431
431
432 class service(object):
432 class service(object):
433 def init(self):
433 def init(self):
434 try:
434 try:
435 self.master = master(ui, dirstate, root, timeout)
435 self.master = master(ui, dirstate, root, timeout)
436 except AlreadyStartedException, inst:
436 except AlreadyStartedException, inst:
437 raise util.Abort("inotify-server: %s" % inst)
437 raise util.Abort("inotify-server: %s" % inst)
438
438
439 def run(self):
439 def run(self):
440 try:
440 try:
441 try:
441 try:
442 self.master.run()
442 self.master.run()
443 except TimeoutException:
443 except TimeoutException:
444 pass
444 pass
445 finally:
445 finally:
446 self.master.shutdown()
446 self.master.shutdown()
447
447
448 if 'inserve' not in sys.argv:
448 if 'inserve' not in sys.argv:
449 runargs = util.hgcmd() + ['inserve', '-R', root]
449 runargs = util.hgcmd() + ['inserve', '-R', root]
450 else:
450 else:
451 runargs = util.hgcmd() + sys.argv[1:]
451 runargs = util.hgcmd() + sys.argv[1:]
452
452
453 pidfile = ui.config('inotify', 'pidfile')
453 pidfile = ui.config('inotify', 'pidfile')
454 if opts['daemon'] and pidfile is not None and 'pid-file' not in runargs:
454 if opts['daemon'] and pidfile is not None and 'pid-file' not in runargs:
455 runargs.append("--pid-file=%s" % pidfile)
455 runargs.append("--pid-file=%s" % pidfile)
456
456
457 service = service()
457 service = service()
458 logfile = ui.config('inotify', 'log')
458 logfile = ui.config('inotify', 'log')
459
459
460 appendpid = ui.configbool('inotify', 'appendpid', False)
460 appendpid = ui.configbool('inotify', 'appendpid', False)
461
461
462 ui.debug('starting inotify server: %s\n' % ' '.join(runargs))
462 ui.debug('starting inotify server: %s\n' % ' '.join(runargs))
463 cmdutil.service(opts, initfn=service.init, runfn=service.run,
463 cmdutil.service(opts, initfn=service.init, runfn=service.run,
464 logfile=logfile, runargs=runargs, appendpid=appendpid)
464 logfile=logfile, runargs=runargs, appendpid=appendpid)
General Comments 0
You need to be logged in to leave comments. Login now