##// END OF EJS Templates
chgserver: use _readlist and _readstr...
Jun Wu -
r28158:7cc57a53 default
parent child Browse files
Show More
@@ -1,400 +1,392 b''
1 # chgserver.py - command server extension for cHg
1 # chgserver.py - command server extension for cHg
2 #
2 #
3 # Copyright 2011 Yuya Nishihara <yuya@tcha.org>
3 # Copyright 2011 Yuya Nishihara <yuya@tcha.org>
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 """command server extension for cHg (EXPERIMENTAL)
8 """command server extension for cHg (EXPERIMENTAL)
9
9
10 'S' channel (read/write)
10 'S' channel (read/write)
11 propagate ui.system() request to client
11 propagate ui.system() request to client
12
12
13 'attachio' command
13 'attachio' command
14 attach client's stdio passed by sendmsg()
14 attach client's stdio passed by sendmsg()
15
15
16 'chdir' command
16 'chdir' command
17 change current directory
17 change current directory
18
18
19 'getpager' command
19 'getpager' command
20 checks if pager is enabled and which pager should be executed
20 checks if pager is enabled and which pager should be executed
21
21
22 'setenv' command
22 'setenv' command
23 replace os.environ completely
23 replace os.environ completely
24
24
25 'SIGHUP' signal
25 'SIGHUP' signal
26 reload configuration files
26 reload configuration files
27 """
27 """
28
28
29 from __future__ import absolute_import
29 from __future__ import absolute_import
30
30
31 import SocketServer
31 import SocketServer
32 import errno
32 import errno
33 import os
33 import os
34 import re
34 import re
35 import signal
35 import signal
36 import struct
36 import struct
37 import traceback
37 import traceback
38
38
39 from mercurial.i18n import _
39 from mercurial.i18n import _
40
40
41 from mercurial import (
41 from mercurial import (
42 cmdutil,
42 cmdutil,
43 commands,
43 commands,
44 commandserver,
44 commandserver,
45 dispatch,
45 dispatch,
46 error,
46 error,
47 osutil,
47 osutil,
48 util,
48 util,
49 )
49 )
50
50
51 # Note for extension authors: ONLY specify testedwith = 'internal' for
51 # Note for extension authors: ONLY specify testedwith = 'internal' for
52 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
52 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
53 # be specifying the version(s) of Mercurial they are tested with, or
53 # be specifying the version(s) of Mercurial they are tested with, or
54 # leave the attribute unspecified.
54 # leave the attribute unspecified.
55 testedwith = 'internal'
55 testedwith = 'internal'
56
56
57 _log = commandserver.log
57 _log = commandserver.log
58
58
59 # copied from hgext/pager.py:uisetup()
59 # copied from hgext/pager.py:uisetup()
60 def _setuppagercmd(ui, options, cmd):
60 def _setuppagercmd(ui, options, cmd):
61 if not ui.formatted():
61 if not ui.formatted():
62 return
62 return
63
63
64 p = ui.config("pager", "pager", os.environ.get("PAGER"))
64 p = ui.config("pager", "pager", os.environ.get("PAGER"))
65 usepager = False
65 usepager = False
66 always = util.parsebool(options['pager'])
66 always = util.parsebool(options['pager'])
67 auto = options['pager'] == 'auto'
67 auto = options['pager'] == 'auto'
68
68
69 if not p:
69 if not p:
70 pass
70 pass
71 elif always:
71 elif always:
72 usepager = True
72 usepager = True
73 elif not auto:
73 elif not auto:
74 usepager = False
74 usepager = False
75 else:
75 else:
76 attended = ['annotate', 'cat', 'diff', 'export', 'glog', 'log', 'qdiff']
76 attended = ['annotate', 'cat', 'diff', 'export', 'glog', 'log', 'qdiff']
77 attend = ui.configlist('pager', 'attend', attended)
77 attend = ui.configlist('pager', 'attend', attended)
78 ignore = ui.configlist('pager', 'ignore')
78 ignore = ui.configlist('pager', 'ignore')
79 cmds, _ = cmdutil.findcmd(cmd, commands.table)
79 cmds, _ = cmdutil.findcmd(cmd, commands.table)
80
80
81 for cmd in cmds:
81 for cmd in cmds:
82 var = 'attend-%s' % cmd
82 var = 'attend-%s' % cmd
83 if ui.config('pager', var):
83 if ui.config('pager', var):
84 usepager = ui.configbool('pager', var)
84 usepager = ui.configbool('pager', var)
85 break
85 break
86 if (cmd in attend or
86 if (cmd in attend or
87 (cmd not in ignore and not attend)):
87 (cmd not in ignore and not attend)):
88 usepager = True
88 usepager = True
89 break
89 break
90
90
91 if usepager:
91 if usepager:
92 ui.setconfig('ui', 'formatted', ui.formatted(), 'pager')
92 ui.setconfig('ui', 'formatted', ui.formatted(), 'pager')
93 ui.setconfig('ui', 'interactive', False, 'pager')
93 ui.setconfig('ui', 'interactive', False, 'pager')
94 return p
94 return p
95
95
96 _envvarre = re.compile(r'\$[a-zA-Z_]+')
96 _envvarre = re.compile(r'\$[a-zA-Z_]+')
97
97
98 def _clearenvaliases(cmdtable):
98 def _clearenvaliases(cmdtable):
99 """Remove stale command aliases referencing env vars; variable expansion
99 """Remove stale command aliases referencing env vars; variable expansion
100 is done at dispatch.addaliases()"""
100 is done at dispatch.addaliases()"""
101 for name, tab in cmdtable.items():
101 for name, tab in cmdtable.items():
102 cmddef = tab[0]
102 cmddef = tab[0]
103 if (isinstance(cmddef, dispatch.cmdalias) and
103 if (isinstance(cmddef, dispatch.cmdalias) and
104 not cmddef.definition.startswith('!') and # shell alias
104 not cmddef.definition.startswith('!') and # shell alias
105 _envvarre.search(cmddef.definition)):
105 _envvarre.search(cmddef.definition)):
106 del cmdtable[name]
106 del cmdtable[name]
107
107
108 def _newchgui(srcui, csystem):
108 def _newchgui(srcui, csystem):
109 class chgui(srcui.__class__):
109 class chgui(srcui.__class__):
110 def __init__(self, src=None):
110 def __init__(self, src=None):
111 super(chgui, self).__init__(src)
111 super(chgui, self).__init__(src)
112 if src:
112 if src:
113 self._csystem = getattr(src, '_csystem', csystem)
113 self._csystem = getattr(src, '_csystem', csystem)
114 else:
114 else:
115 self._csystem = csystem
115 self._csystem = csystem
116
116
117 def system(self, cmd, environ=None, cwd=None, onerr=None,
117 def system(self, cmd, environ=None, cwd=None, onerr=None,
118 errprefix=None):
118 errprefix=None):
119 # copied from mercurial/util.py:system()
119 # copied from mercurial/util.py:system()
120 self.flush()
120 self.flush()
121 def py2shell(val):
121 def py2shell(val):
122 if val is None or val is False:
122 if val is None or val is False:
123 return '0'
123 return '0'
124 if val is True:
124 if val is True:
125 return '1'
125 return '1'
126 return str(val)
126 return str(val)
127 env = os.environ.copy()
127 env = os.environ.copy()
128 if environ:
128 if environ:
129 env.update((k, py2shell(v)) for k, v in environ.iteritems())
129 env.update((k, py2shell(v)) for k, v in environ.iteritems())
130 env['HG'] = util.hgexecutable()
130 env['HG'] = util.hgexecutable()
131 rc = self._csystem(cmd, env, cwd)
131 rc = self._csystem(cmd, env, cwd)
132 if rc and onerr:
132 if rc and onerr:
133 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
133 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
134 util.explainexit(rc)[0])
134 util.explainexit(rc)[0])
135 if errprefix:
135 if errprefix:
136 errmsg = '%s: %s' % (errprefix, errmsg)
136 errmsg = '%s: %s' % (errprefix, errmsg)
137 raise onerr(errmsg)
137 raise onerr(errmsg)
138 return rc
138 return rc
139
139
140 return chgui(srcui)
140 return chgui(srcui)
141
141
142 def _renewui(srcui):
142 def _renewui(srcui):
143 newui = srcui.__class__()
143 newui = srcui.__class__()
144 for a in ['fin', 'fout', 'ferr', 'environ']:
144 for a in ['fin', 'fout', 'ferr', 'environ']:
145 setattr(newui, a, getattr(srcui, a))
145 setattr(newui, a, getattr(srcui, a))
146 if util.safehasattr(srcui, '_csystem'):
146 if util.safehasattr(srcui, '_csystem'):
147 newui._csystem = srcui._csystem
147 newui._csystem = srcui._csystem
148 # stolen from tortoisehg.util.copydynamicconfig()
148 # stolen from tortoisehg.util.copydynamicconfig()
149 for section, name, value in srcui.walkconfig():
149 for section, name, value in srcui.walkconfig():
150 source = srcui.configsource(section, name)
150 source = srcui.configsource(section, name)
151 if ':' in source:
151 if ':' in source:
152 # path:line
152 # path:line
153 continue
153 continue
154 if source == 'none':
154 if source == 'none':
155 # ui.configsource returns 'none' by default
155 # ui.configsource returns 'none' by default
156 source = ''
156 source = ''
157 newui.setconfig(section, name, value, source)
157 newui.setconfig(section, name, value, source)
158 return newui
158 return newui
159
159
160 class channeledsystem(object):
160 class channeledsystem(object):
161 """Propagate ui.system() request in the following format:
161 """Propagate ui.system() request in the following format:
162
162
163 payload length (unsigned int),
163 payload length (unsigned int),
164 cmd, '\0',
164 cmd, '\0',
165 cwd, '\0',
165 cwd, '\0',
166 envkey, '=', val, '\0',
166 envkey, '=', val, '\0',
167 ...
167 ...
168 envkey, '=', val
168 envkey, '=', val
169
169
170 and waits:
170 and waits:
171
171
172 exitcode length (unsigned int),
172 exitcode length (unsigned int),
173 exitcode (int)
173 exitcode (int)
174 """
174 """
175 def __init__(self, in_, out, channel):
175 def __init__(self, in_, out, channel):
176 self.in_ = in_
176 self.in_ = in_
177 self.out = out
177 self.out = out
178 self.channel = channel
178 self.channel = channel
179
179
180 def __call__(self, cmd, environ, cwd):
180 def __call__(self, cmd, environ, cwd):
181 args = [util.quotecommand(cmd), cwd or '.']
181 args = [util.quotecommand(cmd), cwd or '.']
182 args.extend('%s=%s' % (k, v) for k, v in environ.iteritems())
182 args.extend('%s=%s' % (k, v) for k, v in environ.iteritems())
183 data = '\0'.join(args)
183 data = '\0'.join(args)
184 self.out.write(struct.pack('>cI', self.channel, len(data)))
184 self.out.write(struct.pack('>cI', self.channel, len(data)))
185 self.out.write(data)
185 self.out.write(data)
186 self.out.flush()
186 self.out.flush()
187
187
188 length = self.in_.read(4)
188 length = self.in_.read(4)
189 length, = struct.unpack('>I', length)
189 length, = struct.unpack('>I', length)
190 if length != 4:
190 if length != 4:
191 raise error.Abort(_('invalid response'))
191 raise error.Abort(_('invalid response'))
192 rc, = struct.unpack('>i', self.in_.read(4))
192 rc, = struct.unpack('>i', self.in_.read(4))
193 return rc
193 return rc
194
194
195 _iochannels = [
195 _iochannels = [
196 # server.ch, ui.fp, mode
196 # server.ch, ui.fp, mode
197 ('cin', 'fin', 'rb'),
197 ('cin', 'fin', 'rb'),
198 ('cout', 'fout', 'wb'),
198 ('cout', 'fout', 'wb'),
199 ('cerr', 'ferr', 'wb'),
199 ('cerr', 'ferr', 'wb'),
200 ]
200 ]
201
201
202 class chgcmdserver(commandserver.server):
202 class chgcmdserver(commandserver.server):
203 def __init__(self, ui, repo, fin, fout, sock):
203 def __init__(self, ui, repo, fin, fout, sock):
204 super(chgcmdserver, self).__init__(
204 super(chgcmdserver, self).__init__(
205 _newchgui(ui, channeledsystem(fin, fout, 'S')), repo, fin, fout)
205 _newchgui(ui, channeledsystem(fin, fout, 'S')), repo, fin, fout)
206 self.clientsock = sock
206 self.clientsock = sock
207 self._oldios = [] # original (self.ch, ui.fp, fd) before "attachio"
207 self._oldios = [] # original (self.ch, ui.fp, fd) before "attachio"
208
208
209 def cleanup(self):
209 def cleanup(self):
210 # dispatch._runcatch() does not flush outputs if exception is not
210 # dispatch._runcatch() does not flush outputs if exception is not
211 # handled by dispatch._dispatch()
211 # handled by dispatch._dispatch()
212 self.ui.flush()
212 self.ui.flush()
213 self._restoreio()
213 self._restoreio()
214
214
215 def attachio(self):
215 def attachio(self):
216 """Attach to client's stdio passed via unix domain socket; all
216 """Attach to client's stdio passed via unix domain socket; all
217 channels except cresult will no longer be used
217 channels except cresult will no longer be used
218 """
218 """
219 # tell client to sendmsg() with 1-byte payload, which makes it
219 # tell client to sendmsg() with 1-byte payload, which makes it
220 # distinctive from "attachio\n" command consumed by client.read()
220 # distinctive from "attachio\n" command consumed by client.read()
221 self.clientsock.sendall(struct.pack('>cI', 'I', 1))
221 self.clientsock.sendall(struct.pack('>cI', 'I', 1))
222 clientfds = osutil.recvfds(self.clientsock.fileno())
222 clientfds = osutil.recvfds(self.clientsock.fileno())
223 _log('received fds: %r\n' % clientfds)
223 _log('received fds: %r\n' % clientfds)
224
224
225 ui = self.ui
225 ui = self.ui
226 ui.flush()
226 ui.flush()
227 first = self._saveio()
227 first = self._saveio()
228 for fd, (cn, fn, mode) in zip(clientfds, _iochannels):
228 for fd, (cn, fn, mode) in zip(clientfds, _iochannels):
229 assert fd > 0
229 assert fd > 0
230 fp = getattr(ui, fn)
230 fp = getattr(ui, fn)
231 os.dup2(fd, fp.fileno())
231 os.dup2(fd, fp.fileno())
232 os.close(fd)
232 os.close(fd)
233 if not first:
233 if not first:
234 continue
234 continue
235 # reset buffering mode when client is first attached. as we want
235 # reset buffering mode when client is first attached. as we want
236 # to see output immediately on pager, the mode stays unchanged
236 # to see output immediately on pager, the mode stays unchanged
237 # when client re-attached. ferr is unchanged because it should
237 # when client re-attached. ferr is unchanged because it should
238 # be unbuffered no matter if it is a tty or not.
238 # be unbuffered no matter if it is a tty or not.
239 if fn == 'ferr':
239 if fn == 'ferr':
240 newfp = fp
240 newfp = fp
241 else:
241 else:
242 # make it line buffered explicitly because the default is
242 # make it line buffered explicitly because the default is
243 # decided on first write(), where fout could be a pager.
243 # decided on first write(), where fout could be a pager.
244 if fp.isatty():
244 if fp.isatty():
245 bufsize = 1 # line buffered
245 bufsize = 1 # line buffered
246 else:
246 else:
247 bufsize = -1 # system default
247 bufsize = -1 # system default
248 newfp = os.fdopen(fp.fileno(), mode, bufsize)
248 newfp = os.fdopen(fp.fileno(), mode, bufsize)
249 setattr(ui, fn, newfp)
249 setattr(ui, fn, newfp)
250 setattr(self, cn, newfp)
250 setattr(self, cn, newfp)
251
251
252 self.cresult.write(struct.pack('>i', len(clientfds)))
252 self.cresult.write(struct.pack('>i', len(clientfds)))
253
253
254 def _saveio(self):
254 def _saveio(self):
255 if self._oldios:
255 if self._oldios:
256 return False
256 return False
257 ui = self.ui
257 ui = self.ui
258 for cn, fn, _mode in _iochannels:
258 for cn, fn, _mode in _iochannels:
259 ch = getattr(self, cn)
259 ch = getattr(self, cn)
260 fp = getattr(ui, fn)
260 fp = getattr(ui, fn)
261 fd = os.dup(fp.fileno())
261 fd = os.dup(fp.fileno())
262 self._oldios.append((ch, fp, fd))
262 self._oldios.append((ch, fp, fd))
263 return True
263 return True
264
264
265 def _restoreio(self):
265 def _restoreio(self):
266 ui = self.ui
266 ui = self.ui
267 for (ch, fp, fd), (cn, fn, _mode) in zip(self._oldios, _iochannels):
267 for (ch, fp, fd), (cn, fn, _mode) in zip(self._oldios, _iochannels):
268 newfp = getattr(ui, fn)
268 newfp = getattr(ui, fn)
269 # close newfp while it's associated with client; otherwise it
269 # close newfp while it's associated with client; otherwise it
270 # would be closed when newfp is deleted
270 # would be closed when newfp is deleted
271 if newfp is not fp:
271 if newfp is not fp:
272 newfp.close()
272 newfp.close()
273 # restore original fd: fp is open again
273 # restore original fd: fp is open again
274 os.dup2(fd, fp.fileno())
274 os.dup2(fd, fp.fileno())
275 os.close(fd)
275 os.close(fd)
276 setattr(self, cn, ch)
276 setattr(self, cn, ch)
277 setattr(ui, fn, fp)
277 setattr(ui, fn, fp)
278 del self._oldios[:]
278 del self._oldios[:]
279
279
280 def chdir(self):
280 def chdir(self):
281 """Change current directory
281 """Change current directory
282
282
283 Note that the behavior of --cwd option is bit different from this.
283 Note that the behavior of --cwd option is bit different from this.
284 It does not affect --config parameter.
284 It does not affect --config parameter.
285 """
285 """
286 length = struct.unpack('>I', self._read(4))[0]
286 path = self._readstr()
287 if not length:
287 if not path:
288 return
288 return
289 path = self._read(length)
290 _log('chdir to %r\n' % path)
289 _log('chdir to %r\n' % path)
291 os.chdir(path)
290 os.chdir(path)
292
291
293 def getpager(self):
292 def getpager(self):
294 """Read cmdargs and write pager command to r-channel if enabled
293 """Read cmdargs and write pager command to r-channel if enabled
295
294
296 If pager isn't enabled, this writes '\0' because channeledoutput
295 If pager isn't enabled, this writes '\0' because channeledoutput
297 does not allow to write empty data.
296 does not allow to write empty data.
298 """
297 """
299 length = struct.unpack('>I', self._read(4))[0]
298 args = self._readlist()
300 if not length:
301 args = []
302 else:
303 args = self._read(length).split('\0')
304 try:
299 try:
305 cmd, _func, args, options, _cmdoptions = dispatch._parse(self.ui,
300 cmd, _func, args, options, _cmdoptions = dispatch._parse(self.ui,
306 args)
301 args)
307 except (error.Abort, error.AmbiguousCommand, error.CommandError,
302 except (error.Abort, error.AmbiguousCommand, error.CommandError,
308 error.UnknownCommand):
303 error.UnknownCommand):
309 cmd = None
304 cmd = None
310 options = {}
305 options = {}
311 if not cmd or 'pager' not in options:
306 if not cmd or 'pager' not in options:
312 self.cresult.write('\0')
307 self.cresult.write('\0')
313 return
308 return
314
309
315 pagercmd = _setuppagercmd(self.ui, options, cmd)
310 pagercmd = _setuppagercmd(self.ui, options, cmd)
316 if pagercmd:
311 if pagercmd:
317 self.cresult.write(pagercmd)
312 self.cresult.write(pagercmd)
318 else:
313 else:
319 self.cresult.write('\0')
314 self.cresult.write('\0')
320
315
321 def setenv(self):
316 def setenv(self):
322 """Clear and update os.environ
317 """Clear and update os.environ
323
318
324 Note that not all variables can make an effect on the running process.
319 Note that not all variables can make an effect on the running process.
325 """
320 """
326 length = struct.unpack('>I', self._read(4))[0]
321 l = self._readlist()
327 if not length:
328 return
329 s = self._read(length)
330 try:
322 try:
331 newenv = dict(l.split('=', 1) for l in s.split('\0'))
323 newenv = dict(s.split('=', 1) for s in l)
332 except ValueError:
324 except ValueError:
333 raise ValueError('unexpected value in setenv request')
325 raise ValueError('unexpected value in setenv request')
334
326
335 diffkeys = set(k for k in set(os.environ.keys() + newenv.keys())
327 diffkeys = set(k for k in set(os.environ.keys() + newenv.keys())
336 if os.environ.get(k) != newenv.get(k))
328 if os.environ.get(k) != newenv.get(k))
337 _log('change env: %r\n' % sorted(diffkeys))
329 _log('change env: %r\n' % sorted(diffkeys))
338
330
339 os.environ.clear()
331 os.environ.clear()
340 os.environ.update(newenv)
332 os.environ.update(newenv)
341
333
342 if set(['HGPLAIN', 'HGPLAINEXCEPT']) & diffkeys:
334 if set(['HGPLAIN', 'HGPLAINEXCEPT']) & diffkeys:
343 # reload config so that ui.plain() takes effect
335 # reload config so that ui.plain() takes effect
344 self.ui = _renewui(self.ui)
336 self.ui = _renewui(self.ui)
345
337
346 _clearenvaliases(commands.table)
338 _clearenvaliases(commands.table)
347
339
348 capabilities = commandserver.server.capabilities.copy()
340 capabilities = commandserver.server.capabilities.copy()
349 capabilities.update({'attachio': attachio,
341 capabilities.update({'attachio': attachio,
350 'chdir': chdir,
342 'chdir': chdir,
351 'getpager': getpager,
343 'getpager': getpager,
352 'setenv': setenv})
344 'setenv': setenv})
353
345
354 # copied from mercurial/commandserver.py
346 # copied from mercurial/commandserver.py
355 class _requesthandler(SocketServer.StreamRequestHandler):
347 class _requesthandler(SocketServer.StreamRequestHandler):
356 def handle(self):
348 def handle(self):
357 # use a different process group from the master process, making this
349 # use a different process group from the master process, making this
358 # process pass kernel "is_current_pgrp_orphaned" check so signals like
350 # process pass kernel "is_current_pgrp_orphaned" check so signals like
359 # SIGTSTP, SIGTTIN, SIGTTOU are not ignored.
351 # SIGTSTP, SIGTTIN, SIGTTOU are not ignored.
360 os.setpgid(0, 0)
352 os.setpgid(0, 0)
361 ui = self.server.ui
353 ui = self.server.ui
362 repo = self.server.repo
354 repo = self.server.repo
363 sv = chgcmdserver(ui, repo, self.rfile, self.wfile, self.connection)
355 sv = chgcmdserver(ui, repo, self.rfile, self.wfile, self.connection)
364 try:
356 try:
365 try:
357 try:
366 sv.serve()
358 sv.serve()
367 # handle exceptions that may be raised by command server. most of
359 # handle exceptions that may be raised by command server. most of
368 # known exceptions are caught by dispatch.
360 # known exceptions are caught by dispatch.
369 except error.Abort as inst:
361 except error.Abort as inst:
370 ui.warn(_('abort: %s\n') % inst)
362 ui.warn(_('abort: %s\n') % inst)
371 except IOError as inst:
363 except IOError as inst:
372 if inst.errno != errno.EPIPE:
364 if inst.errno != errno.EPIPE:
373 raise
365 raise
374 except KeyboardInterrupt:
366 except KeyboardInterrupt:
375 pass
367 pass
376 finally:
368 finally:
377 sv.cleanup()
369 sv.cleanup()
378 except: # re-raises
370 except: # re-raises
379 # also write traceback to error channel. otherwise client cannot
371 # also write traceback to error channel. otherwise client cannot
380 # see it because it is written to server's stderr by default.
372 # see it because it is written to server's stderr by default.
381 traceback.print_exc(file=sv.cerr)
373 traceback.print_exc(file=sv.cerr)
382 raise
374 raise
383
375
384 class chgunixservice(commandserver.unixservice):
376 class chgunixservice(commandserver.unixservice):
385 def init(self):
377 def init(self):
386 # drop options set for "hg serve --cmdserver" command
378 # drop options set for "hg serve --cmdserver" command
387 self.ui.setconfig('progress', 'assume-tty', None)
379 self.ui.setconfig('progress', 'assume-tty', None)
388 signal.signal(signal.SIGHUP, self._reloadconfig)
380 signal.signal(signal.SIGHUP, self._reloadconfig)
389 class cls(SocketServer.ForkingMixIn, SocketServer.UnixStreamServer):
381 class cls(SocketServer.ForkingMixIn, SocketServer.UnixStreamServer):
390 ui = self.ui
382 ui = self.ui
391 repo = self.repo
383 repo = self.repo
392 self.server = cls(self.address, _requesthandler)
384 self.server = cls(self.address, _requesthandler)
393 # avoid writing "listening at" message to stdout before attachio
385 # avoid writing "listening at" message to stdout before attachio
394 # request, which calls setvbuf()
386 # request, which calls setvbuf()
395
387
396 def _reloadconfig(self, signum, frame):
388 def _reloadconfig(self, signum, frame):
397 self.ui = self.server.ui = _renewui(self.ui)
389 self.ui = self.server.ui = _renewui(self.ui)
398
390
399 def uisetup(ui):
391 def uisetup(ui):
400 commandserver._servicemap['chgunix'] = chgunixservice
392 commandserver._servicemap['chgunix'] = chgunixservice
General Comments 0
You need to be logged in to leave comments. Login now