cmdserver: forcibly use L channel to read password input (issue3161) stable
authorYuya Nishihara <yuya@tcha.org>
Sat, 26 Apr 2014 18:13:06 +0900
branchstable
changeset 21195 9336bc7dca8e
parent 21194 476069509e72
child 21196 5c0d5b95b824
cmdserver: forcibly use L channel to read password input (issue3161) Command server is designed to use the channel protocol even if the server process is accessible to tty, whereas vanilla hg should be able to read password from tty in that case. So it isn't enough to swap sys.stdin: # works only if the server process is detached from the console sys.stdin = self.fin getpass.getpass('') sys.stdin = oldin or test isatty: # vanilla hg can't talk to tty if stdin is redirected if self._isatty(self.fin): return getpass.getpass('') else: ... Since ui.nontty flag is undocumented and command-server channels don't provide isatty(), this change won't affect the other uses of ui._isatty(). issue3161 also suggests to provide some context of messages. I think it can be implemented by using the generic templating function.
mercurial/commandserver.py
mercurial/ui.py
tests/test-commandserver.py
tests/test-commandserver.py.out
--- a/mercurial/commandserver.py	Tue Apr 29 12:37:36 2014 +0900
+++ b/mercurial/commandserver.py	Sat Apr 26 18:13:06 2014 +0900
@@ -187,14 +187,20 @@
         # copy the uis so changes (e.g. --config or --verbose) don't
         # persist between requests
         copiedui = self.ui.copy()
+        uis = [copiedui]
         if self.repo:
             self.repo.baseui = copiedui
             # clone ui without using ui.copy because this is protected
             repoui = self.repoui.__class__(self.repoui)
             repoui.copy = copiedui.copy # redo copy protection
+            uis.append(repoui)
             self.repo.ui = self.repo.dirstate._ui = repoui
             self.repo.invalidateall()
 
+        for ui in uis:
+            # any kind of interaction must use server channels
+            ui.setconfig('ui', 'nontty', 'true', 'commandserver')
+
         req = dispatch.request(args[:], copiedui, self.repo, self.cin,
                                self.cout, self.cerr)
 
--- a/mercurial/ui.py	Tue Apr 29 12:37:36 2014 +0900
+++ b/mercurial/ui.py	Sat Apr 26 18:13:06 2014 +0900
@@ -689,7 +689,12 @@
             return default
         try:
             self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
-            return getpass.getpass('')
+            # disable getpass() only if explicitly specified. it's still valid
+            # to interact with tty even if fin is not a tty.
+            if self.configbool('ui', 'nontty'):
+                return self.fin.readline().rstrip('\n')
+            else:
+                return getpass.getpass('')
         except EOFError:
             raise util.Abort(_('response expected'))
     def status(self, *msg, **opts):
--- a/tests/test-commandserver.py	Tue Apr 29 12:37:36 2014 +0900
+++ b/tests/test-commandserver.py	Sat Apr 26 18:13:06 2014 +0900
@@ -294,6 +294,11 @@
     # repo.mq should be recreated to point to new queue
     runcommand(server, ['qqueue', '--active'])
 
+def getpass(server):
+    readchannel(server)
+    runcommand(server, ['debuggetpass', '--config', 'ui.interactive=True'],
+               input=cStringIO.StringIO('1234\n'))
+
 def startwithoutrepo(server):
     readchannel(server)
     runcommand(server, ['init', 'repo2'])
@@ -334,6 +339,19 @@
     hgrc.write('[extensions]\nmq=\n')
     hgrc.close()
     check(mqoutsidechanges)
+    dbg = open('dbgui.py', 'w')
+    dbg.write('from mercurial import cmdutil, commands\n'
+              'commands.norepo += " debuggetpass"\n'
+              'cmdtable = {}\n'
+              'command = cmdutil.command(cmdtable)\n'
+              '@command("debuggetpass")\n'
+              'def debuggetpass(ui):\n'
+              '    ui.write("%s\\n" % ui.getpass())\n')
+    dbg.close()
+    hgrc = open('.hg/hgrc', 'a')
+    hgrc.write('[extensions]\ndbgui=dbgui.py\n')
+    hgrc.close()
+    check(getpass)
 
     os.chdir('..')
     check(hellomessage)
--- a/tests/test-commandserver.py.out	Tue Apr 29 12:37:36 2014 +0900
+++ b/tests/test-commandserver.py.out	Sat Apr 26 18:13:06 2014 +0900
@@ -81,6 +81,7 @@
 ui.slash=True
 ui.interactive=False
 ui.foo=bar
+ui.nontty=true
  runcommand init foo
  runcommand -R foo showconfig ui defaults
 defaults.backout=-d "0 0"
@@ -89,6 +90,7 @@
 defaults.tag=-d "0 0"
 ui.slash=True
 ui.interactive=False
+ui.nontty=true
 
 testing hookoutput:
 
@@ -238,6 +240,11 @@
  runcommand qqueue --active
 foo
 
+testing getpass:
+
+ runcommand debuggetpass --config ui.interactive=True
+password: 1234
+
 testing hellomessage:
 
 o, 'capabilities: getencoding runcommand\nencoding: ***'