1 # client.py - inotify status client
3 # Copyright 2006, 2007, 2008 Bryan O'Sullivan <bos@serpentine.com>
4 # Copyright 2007, 2008 Brendan Cully <brendan@kublai.com>
5 # Copyright 2009 Nicolas Dumazet <nicdumz@gmail.com>
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2, incorporated herein by reference.
10 from mercurial.i18n import _
12 import errno, os, socket, struct
14 class QueryFailed(Exception): pass
16 def start_server(function):
19 Tries to call function, if it fails, try to (re)start inotify server.
20 Raise QueryFailed if something went wrong
22 def decorated_function(self, *args):
25 return function(self, *args)
26 except (OSError, socket.error), err:
27 autostart = self.ui.configbool('inotify', 'autostart', True)
29 if err[0] == errno.ECONNREFUSED:
30 self.ui.warn(_('(found dead inotify server socket; '
32 os.unlink(os.path.join(self.root, '.hg', 'inotify.sock'))
33 if err[0] in (errno.ECONNREFUSED, errno.ENOENT) and autostart:
34 self.ui.debug(_('(starting inotify server)\n'))
37 server.start(self.ui, self.dirstate, self.root)
38 except server.AlreadyStartedException, inst:
39 # another process may have started its own
40 # inotify server while this one was starting.
41 self.ui.debug(str(inst))
42 except Exception, inst:
43 self.ui.warn(_('could not start inotify server: '
47 return function(self, *args)
48 except socket.error, err:
49 self.ui.warn(_('could not talk to new inotify '
50 'server: %s\n') % err[-1])
51 elif err[0] in (errno.ECONNREFUSED, errno.ENOENT):
52 # silently ignore normal errors if autostart is False
53 self.ui.debug(_('(inotify server not running)\n'))
55 self.ui.warn(_('failed to contact inotify server: %s\n')
59 raise QueryFailed('inotify query failed')
61 return decorated_function
65 def __init__(self, ui, repo):
67 self.dirstate = repo.dirstate
69 self.sock = socket.socket(socket.AF_UNIX)
72 sockpath = os.path.join(self.root, '.hg', 'inotify.sock')
74 self.sock.connect(sockpath)
75 except socket.error, err:
76 if err[0] == "AF_UNIX path too long":
77 sockpath = os.readlink(sockpath)
78 self.sock.connect(sockpath)
82 def _send(self, type, data):
83 """Sends protocol version number, and the data"""
84 self.sock.sendall(chr(common.version) + type + data)
86 self.sock.shutdown(socket.SHUT_WR)
88 def _receive(self, type):
90 Read data, check version number, extract headers,
91 and returns a tuple (data descriptor, header)
92 Raises QueryFailed on error
94 cs = common.recvcs(self.sock)
96 version = ord(cs.read(1))
98 # empty answer, assume the server crashed
99 self.ui.warn(_('received empty answer from inotify server'))
100 raise QueryFailed('server crashed')
102 if version != common.version:
103 self.ui.warn(_('(inotify: received response from incompatible '
104 'server version %d)\n') % version)
105 raise QueryFailed('incompatible server version')
107 readtype = cs.read(4)
109 self.ui.warn(_('(inotify: received \'%s\' response when expecting'
110 ' \'%s\')\n') % (readtype, type))
111 raise QueryFailed('wrong response type')
113 hdrfmt = common.resphdrfmts[type]
114 hdrsize = common.resphdrsizes[type]
116 resphdr = struct.unpack(hdrfmt, cs.read(hdrsize))
118 raise QueryFailed('unable to retrieve query response headers')
122 def query(self, type, req):
125 self._send(type, req)
127 return self._receive(type)
130 def statusquery(self, names, match, ignored, clean, unknown=True):
137 raise ValueError('this is insanity')
138 if clean: states += 'c'
139 if unknown: states += '?'
142 req = '\0'.join(genquery())
144 cs, resphdr = self.query('STAT', req)
146 def readnames(nbytes):
148 names = cs.read(nbytes)
150 return filter(match, names.split('\0'))
152 return map(readnames, resphdr)
155 def debugquery(self):
156 cs, resphdr = self.query('DBUG', '')
159 names = cs.read(nbytes)
160 return names.split('\0')