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.
This commit is contained in:
Yuya Nishihara 2014-04-26 18:13:06 +09:00
parent 63bc6388d0
commit 96a8ea56df
4 changed files with 37 additions and 1 deletions

View File

@ -187,14 +187,20 @@ class server(object):
# 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)

View File

@ -689,7 +689,12 @@ class ui(object):
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):

View File

@ -294,6 +294,11 @@ def mqoutsidechanges(server):
# 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 @@ if __name__ == '__main__':
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)

View File

@ -81,6 +81,7 @@ defaults.tag=-d "0 0"
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.shelve=--date "0 0"
defaults.tag=-d "0 0"
ui.slash=True
ui.interactive=False
ui.nontty=true
testing hookoutput:
@ -238,6 +240,11 @@ patch queue now empty
runcommand qqueue --active
foo
testing getpass:
runcommand debuggetpass --config ui.interactive=True
password: 1234
testing hellomessage:
o, 'capabilities: getencoding runcommand\nencoding: ***'