##// END OF EJS Templates
cmdserver: protect pipe server streams against corruption caused by direct io...
Yuya Nishihara -
r23324:69f86b93 default
parent child Browse files
Show More
@@ -7,7 +7,7 b''
7 7
8 8 from i18n import _
9 9 import struct
10 import os, errno, traceback, SocketServer
10 import sys, os, errno, traceback, SocketServer
11 11 import dispatch, encoding, util
12 12
13 13 logfile = None
@@ -248,6 +248,29 b' class server(object):'
248 248
249 249 return 0
250 250
251 def _protectio(ui):
252 """ duplicates streams and redirect original to null if ui uses stdio """
253 ui.flush()
254 newfiles = []
255 nullfd = os.open(os.devnull, os.O_RDWR)
256 for f, sysf, mode in [(ui.fin, sys.stdin, 'rb'),
257 (ui.fout, sys.stdout, 'wb')]:
258 if f is sysf:
259 newfd = os.dup(f.fileno())
260 os.dup2(nullfd, f.fileno())
261 f = os.fdopen(newfd, mode)
262 newfiles.append(f)
263 os.close(nullfd)
264 return tuple(newfiles)
265
266 def _restoreio(ui, fin, fout):
267 """ restores streams from duplicated ones """
268 ui.flush()
269 for f, uif in [(fin, ui.fin), (fout, ui.fout)]:
270 if f is not uif:
271 os.dup2(f.fileno(), uif.fileno())
272 f.close()
273
251 274 class pipeservice(object):
252 275 def __init__(self, ui, repo, opts):
253 276 self.ui = ui
@@ -258,8 +281,14 b' class pipeservice(object):'
258 281
259 282 def run(self):
260 283 ui = self.ui
261 sv = server(ui, self.repo, ui.fin, ui.fout)
262 return sv.serve()
284 # redirect stdio to null device so that broken extensions or in-process
285 # hooks will never cause corruption of channel protocol.
286 fin, fout = _protectio(ui)
287 try:
288 sv = server(ui, self.repo, fin, fout)
289 return sv.serve()
290 finally:
291 _restoreio(ui, fin, fout)
263 292
264 293 class _requesthandler(SocketServer.StreamRequestHandler):
265 294 def handle(self):
@@ -492,6 +492,7 b' check that local configs for the cached '
492 492 foo
493 493
494 494 $ cat <<EOF > dbgui.py
495 > import os, sys
495 496 > from mercurial import cmdutil, commands
496 497 > cmdtable = {}
497 498 > command = cmdutil.command(cmdtable)
@@ -501,6 +502,14 b' check that local configs for the cached '
501 502 > @command("debugprompt", norepo=True)
502 503 > def debugprompt(ui):
503 504 > ui.write("%s\\n" % ui.prompt("prompt:"))
505 > @command("debugreadstdin", norepo=True)
506 > def debugreadstdin(ui):
507 > ui.write("read: %r\n" % sys.stdin.read(1))
508 > @command("debugwritestdout", norepo=True)
509 > def debugwritestdout(ui):
510 > os.write(1, "low-level stdout fd and\n")
511 > sys.stdout.write("stdout should be redirected to /dev/null\n")
512 > sys.stdout.flush()
504 513 > EOF
505 514 $ cat <<EOF >> .hg/hgrc
506 515 > [extensions]
@@ -518,10 +527,15 b' check that local configs for the cached '
518 527 ... runcommand(server, ['debugprompt', '--config',
519 528 ... 'ui.interactive=True'],
520 529 ... input=cStringIO.StringIO('5678\n'))
530 ... runcommand(server, ['debugreadstdin'])
531 ... runcommand(server, ['debugwritestdout'])
521 532 *** runcommand debuggetpass --config ui.interactive=True
522 533 password: 1234
523 534 *** runcommand debugprompt --config ui.interactive=True
524 535 prompt: 5678
536 *** runcommand debugreadstdin
537 read: ''
538 *** runcommand debugwritestdout
525 539
526 540
527 541 run commandserver in commandserver, which is silly but should work:
General Comments 0
You need to be logged in to leave comments. Login now