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