##// END OF EJS Templates
inotify: make inotifydirstate.status() returns a tuple of lists....
Greg Ward -
r11628:68a30dae stable
parent child Browse files
Show More
@@ -1,174 +1,174
1 # client.py - inotify status client
1 # client.py - inotify status client
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 # Copyright 2009 Nicolas Dumazet <nicdumz@gmail.com>
5 # Copyright 2009 Nicolas Dumazet <nicdumz@gmail.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 from mercurial.i18n import _
10 from mercurial.i18n import _
11 import common, server
11 import common, server
12 import errno, os, socket, struct
12 import errno, os, socket, struct
13
13
14 class QueryFailed(Exception):
14 class QueryFailed(Exception):
15 pass
15 pass
16
16
17 def start_server(function):
17 def start_server(function):
18 """
18 """
19 Decorator.
19 Decorator.
20 Tries to call function, if it fails, try to (re)start inotify server.
20 Tries to call function, if it fails, try to (re)start inotify server.
21 Raise QueryFailed if something went wrong
21 Raise QueryFailed if something went wrong
22 """
22 """
23 def decorated_function(self, *args):
23 def decorated_function(self, *args):
24 result = None
24 result = None
25 try:
25 try:
26 return function(self, *args)
26 return function(self, *args)
27 except (OSError, socket.error), err:
27 except (OSError, socket.error), err:
28 autostart = self.ui.configbool('inotify', 'autostart', True)
28 autostart = self.ui.configbool('inotify', 'autostart', True)
29
29
30 if err[0] == errno.ECONNREFUSED:
30 if err[0] == errno.ECONNREFUSED:
31 self.ui.warn(_('inotify-client: found dead inotify server '
31 self.ui.warn(_('inotify-client: found dead inotify server '
32 'socket; removing it\n'))
32 'socket; removing it\n'))
33 os.unlink(os.path.join(self.root, '.hg', 'inotify.sock'))
33 os.unlink(os.path.join(self.root, '.hg', 'inotify.sock'))
34 if err[0] in (errno.ECONNREFUSED, errno.ENOENT) and autostart:
34 if err[0] in (errno.ECONNREFUSED, errno.ENOENT) and autostart:
35 self.ui.debug('(starting inotify server)\n')
35 self.ui.debug('(starting inotify server)\n')
36 try:
36 try:
37 try:
37 try:
38 server.start(self.ui, self.dirstate, self.root,
38 server.start(self.ui, self.dirstate, self.root,
39 dict(daemon=True, daemon_pipefds=''))
39 dict(daemon=True, daemon_pipefds=''))
40 except server.AlreadyStartedException, inst:
40 except server.AlreadyStartedException, inst:
41 # another process may have started its own
41 # another process may have started its own
42 # inotify server while this one was starting.
42 # inotify server while this one was starting.
43 self.ui.debug(str(inst))
43 self.ui.debug(str(inst))
44 except Exception, inst:
44 except Exception, inst:
45 self.ui.warn(_('inotify-client: could not start inotify '
45 self.ui.warn(_('inotify-client: could not start inotify '
46 'server: %s\n') % inst)
46 'server: %s\n') % inst)
47 else:
47 else:
48 try:
48 try:
49 return function(self, *args)
49 return function(self, *args)
50 except socket.error, err:
50 except socket.error, err:
51 self.ui.warn(_('inotify-client: could not talk to new '
51 self.ui.warn(_('inotify-client: could not talk to new '
52 'inotify server: %s\n') % err[-1])
52 'inotify server: %s\n') % err[-1])
53 elif err[0] in (errno.ECONNREFUSED, errno.ENOENT):
53 elif err[0] in (errno.ECONNREFUSED, errno.ENOENT):
54 # silently ignore normal errors if autostart is False
54 # silently ignore normal errors if autostart is False
55 self.ui.debug('(inotify server not running)\n')
55 self.ui.debug('(inotify server not running)\n')
56 else:
56 else:
57 self.ui.warn(_('inotify-client: failed to contact inotify '
57 self.ui.warn(_('inotify-client: failed to contact inotify '
58 'server: %s\n') % err[-1])
58 'server: %s\n') % err[-1])
59
59
60 self.ui.traceback()
60 self.ui.traceback()
61 raise QueryFailed('inotify query failed')
61 raise QueryFailed('inotify query failed')
62
62
63 return decorated_function
63 return decorated_function
64
64
65
65
66 class client(object):
66 class client(object):
67 def __init__(self, ui, repo):
67 def __init__(self, ui, repo):
68 self.ui = ui
68 self.ui = ui
69 self.dirstate = repo.dirstate
69 self.dirstate = repo.dirstate
70 self.root = repo.root
70 self.root = repo.root
71 self.sock = socket.socket(socket.AF_UNIX)
71 self.sock = socket.socket(socket.AF_UNIX)
72
72
73 def _connect(self):
73 def _connect(self):
74 sockpath = os.path.join(self.root, '.hg', 'inotify.sock')
74 sockpath = os.path.join(self.root, '.hg', 'inotify.sock')
75 try:
75 try:
76 self.sock.connect(sockpath)
76 self.sock.connect(sockpath)
77 except socket.error, err:
77 except socket.error, err:
78 if err[0] == "AF_UNIX path too long":
78 if err[0] == "AF_UNIX path too long":
79 sockpath = os.readlink(sockpath)
79 sockpath = os.readlink(sockpath)
80 self.sock.connect(sockpath)
80 self.sock.connect(sockpath)
81 else:
81 else:
82 raise
82 raise
83
83
84 def _send(self, type, data):
84 def _send(self, type, data):
85 """Sends protocol version number, and the data"""
85 """Sends protocol version number, and the data"""
86 self.sock.sendall(chr(common.version) + type + data)
86 self.sock.sendall(chr(common.version) + type + data)
87
87
88 self.sock.shutdown(socket.SHUT_WR)
88 self.sock.shutdown(socket.SHUT_WR)
89
89
90 def _receive(self, type):
90 def _receive(self, type):
91 """
91 """
92 Read data, check version number, extract headers,
92 Read data, check version number, extract headers,
93 and returns a tuple (data descriptor, header)
93 and returns a tuple (data descriptor, header)
94 Raises QueryFailed on error
94 Raises QueryFailed on error
95 """
95 """
96 cs = common.recvcs(self.sock)
96 cs = common.recvcs(self.sock)
97 try:
97 try:
98 version = ord(cs.read(1))
98 version = ord(cs.read(1))
99 except TypeError:
99 except TypeError:
100 # empty answer, assume the server crashed
100 # empty answer, assume the server crashed
101 self.ui.warn(_('inotify-client: received empty answer from inotify '
101 self.ui.warn(_('inotify-client: received empty answer from inotify '
102 'server'))
102 'server'))
103 raise QueryFailed('server crashed')
103 raise QueryFailed('server crashed')
104
104
105 if version != common.version:
105 if version != common.version:
106 self.ui.warn(_('(inotify: received response from incompatible '
106 self.ui.warn(_('(inotify: received response from incompatible '
107 'server version %d)\n') % version)
107 'server version %d)\n') % version)
108 raise QueryFailed('incompatible server version')
108 raise QueryFailed('incompatible server version')
109
109
110 readtype = cs.read(4)
110 readtype = cs.read(4)
111 if readtype != type:
111 if readtype != type:
112 self.ui.warn(_('(inotify: received \'%s\' response when expecting'
112 self.ui.warn(_('(inotify: received \'%s\' response when expecting'
113 ' \'%s\')\n') % (readtype, type))
113 ' \'%s\')\n') % (readtype, type))
114 raise QueryFailed('wrong response type')
114 raise QueryFailed('wrong response type')
115
115
116 hdrfmt = common.resphdrfmts[type]
116 hdrfmt = common.resphdrfmts[type]
117 hdrsize = common.resphdrsizes[type]
117 hdrsize = common.resphdrsizes[type]
118 try:
118 try:
119 resphdr = struct.unpack(hdrfmt, cs.read(hdrsize))
119 resphdr = struct.unpack(hdrfmt, cs.read(hdrsize))
120 except struct.error:
120 except struct.error:
121 raise QueryFailed('unable to retrieve query response headers')
121 raise QueryFailed('unable to retrieve query response headers')
122
122
123 return cs, resphdr
123 return cs, resphdr
124
124
125 def query(self, type, req):
125 def query(self, type, req):
126 self._connect()
126 self._connect()
127
127
128 self._send(type, req)
128 self._send(type, req)
129
129
130 return self._receive(type)
130 return self._receive(type)
131
131
132 @start_server
132 @start_server
133 def statusquery(self, names, match, ignored, clean, unknown=True):
133 def statusquery(self, names, match, ignored, clean, unknown=True):
134
134
135 def genquery():
135 def genquery():
136 for n in names:
136 for n in names:
137 yield n
137 yield n
138 states = 'almrx!'
138 states = 'almrx!'
139 if ignored:
139 if ignored:
140 raise ValueError('this is insanity')
140 raise ValueError('this is insanity')
141 if clean:
141 if clean:
142 states += 'c'
142 states += 'c'
143 if unknown:
143 if unknown:
144 states += '?'
144 states += '?'
145 yield states
145 yield states
146
146
147 req = '\0'.join(genquery())
147 req = '\0'.join(genquery())
148
148
149 cs, resphdr = self.query('STAT', req)
149 cs, resphdr = self.query('STAT', req)
150
150
151 def readnames(nbytes):
151 def readnames(nbytes):
152 if nbytes:
152 if nbytes:
153 names = cs.read(nbytes)
153 names = cs.read(nbytes)
154 if names:
154 if names:
155 return filter(match, names.split('\0'))
155 return filter(match, names.split('\0'))
156 return []
156 return []
157 results = map(readnames, resphdr[:-1])
157 results = tuple(map(readnames, resphdr[:-1]))
158
158
159 if names:
159 if names:
160 nbytes = resphdr[-1]
160 nbytes = resphdr[-1]
161 vdirs = cs.read(nbytes)
161 vdirs = cs.read(nbytes)
162 if vdirs:
162 if vdirs:
163 for vdir in vdirs.split('\0'):
163 for vdir in vdirs.split('\0'):
164 match.dir(vdir)
164 match.dir(vdir)
165
165
166 return results
166 return results
167
167
168 @start_server
168 @start_server
169 def debugquery(self):
169 def debugquery(self):
170 cs, resphdr = self.query('DBUG', '')
170 cs, resphdr = self.query('DBUG', '')
171
171
172 nbytes = resphdr[0]
172 nbytes = resphdr[0]
173 names = cs.read(nbytes)
173 names = cs.read(nbytes)
174 return names.split('\0')
174 return names.split('\0')
General Comments 0
You need to be logged in to leave comments. Login now