comparison mercurial/sshpeer.py @ 43076:2372284d9457

formatting: blacken the codebase This is using my patch to black (https://github.com/psf/black/pull/826) so we don't un-wrap collection literals. Done with: hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S # skip-blame mass-reformatting only # no-check-commit reformats foo_bar functions Differential Revision: https://phab.mercurial-scm.org/D6971
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:45:02 -0400
parents 089fc0db0954
children 687b865b95ad
comparison
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
23 from .utils import ( 23 from .utils import (
24 procutil, 24 procutil,
25 stringutil, 25 stringutil,
26 ) 26 )
27 27
28
28 def _serverquote(s): 29 def _serverquote(s):
29 """quote a string for the remote shell ... which we assume is sh""" 30 """quote a string for the remote shell ... which we assume is sh"""
30 if not s: 31 if not s:
31 return s 32 return s
32 if re.match('[a-zA-Z0-9@%_+=:,./-]*$', s): 33 if re.match('[a-zA-Z0-9@%_+=:,./-]*$', s):
33 return s 34 return s
34 return "'%s'" % s.replace("'", "'\\''") 35 return "'%s'" % s.replace("'", "'\\''")
35 36
37
36 def _forwardoutput(ui, pipe): 38 def _forwardoutput(ui, pipe):
37 """display all data currently available on pipe as remote output. 39 """display all data currently available on pipe as remote output.
38 40
39 This is non blocking.""" 41 This is non blocking."""
40 if pipe: 42 if pipe:
41 s = procutil.readpipe(pipe) 43 s = procutil.readpipe(pipe)
42 if s: 44 if s:
43 for l in s.splitlines(): 45 for l in s.splitlines():
44 ui.status(_("remote: "), l, '\n') 46 ui.status(_("remote: "), l, '\n')
45 47
48
46 class doublepipe(object): 49 class doublepipe(object):
47 """Operate a side-channel pipe in addition of a main one 50 """Operate a side-channel pipe in addition of a main one
48 51
49 The side-channel pipe contains server output to be forwarded to the user 52 The side-channel pipe contains server output to be forwarded to the user
50 input. The double pipe will behave as the "main" pipe, but will ensure the 53 input. The double pipe will behave as the "main" pipe, but will ensure the
70 73
71 return a pair of boolean (ismainready, issideready) 74 return a pair of boolean (ismainready, issideready)
72 75
73 (This will only wait for data if the setup is supported by `util.poll`) 76 (This will only wait for data if the setup is supported by `util.poll`)
74 """ 77 """
75 if (isinstance(self._main, util.bufferedinputpipe) and 78 if (
76 self._main.hasbuffer): 79 isinstance(self._main, util.bufferedinputpipe)
80 and self._main.hasbuffer
81 ):
77 # Main has data. Assume side is worth poking at. 82 # Main has data. Assume side is worth poking at.
78 return True, True 83 return True, True
79 84
80 fds = [self._main.fileno(), self._side.fileno()] 85 fds = [self._main.fileno(), self._side.fileno()]
81 try: 86 try:
135 return self._main.close() 140 return self._main.close()
136 141
137 def flush(self): 142 def flush(self):
138 return self._main.flush() 143 return self._main.flush()
139 144
145
140 def _cleanuppipes(ui, pipei, pipeo, pipee): 146 def _cleanuppipes(ui, pipei, pipeo, pipee):
141 """Clean up pipes used by an SSH connection.""" 147 """Clean up pipes used by an SSH connection."""
142 if pipeo: 148 if pipeo:
143 pipeo.close() 149 pipeo.close()
144 if pipei: 150 if pipei:
152 except (IOError, ValueError): 158 except (IOError, ValueError):
153 pass 159 pass
154 160
155 pipee.close() 161 pipee.close()
156 162
163
157 def _makeconnection(ui, sshcmd, args, remotecmd, path, sshenv=None): 164 def _makeconnection(ui, sshcmd, args, remotecmd, path, sshenv=None):
158 """Create an SSH connection to a server. 165 """Create an SSH connection to a server.
159 166
160 Returns a tuple of (process, stdin, stdout, stderr) for the 167 Returns a tuple of (process, stdin, stdout, stderr) for the
161 spawned process. 168 spawned process.
162 """ 169 """
163 cmd = '%s %s %s' % ( 170 cmd = '%s %s %s' % (
164 sshcmd, 171 sshcmd,
165 args, 172 args,
166 procutil.shellquote('%s -R %s serve --stdio' % ( 173 procutil.shellquote(
167 _serverquote(remotecmd), _serverquote(path)))) 174 '%s -R %s serve --stdio'
175 % (_serverquote(remotecmd), _serverquote(path))
176 ),
177 )
168 178
169 ui.debug('running %s\n' % cmd) 179 ui.debug('running %s\n' % cmd)
170 cmd = procutil.quotecommand(cmd) 180 cmd = procutil.quotecommand(cmd)
171 181
172 # no buffer allow the use of 'select' 182 # no buffer allow the use of 'select'
174 # move to threading. 184 # move to threading.
175 stdin, stdout, stderr, proc = procutil.popen4(cmd, bufsize=0, env=sshenv) 185 stdin, stdout, stderr, proc = procutil.popen4(cmd, bufsize=0, env=sshenv)
176 186
177 return proc, stdin, stdout, stderr 187 return proc, stdin, stdout, stderr
178 188
189
179 def _clientcapabilities(): 190 def _clientcapabilities():
180 """Return list of capabilities of this client. 191 """Return list of capabilities of this client.
181 192
182 Returns a list of capabilities that are supported by this client. 193 Returns a list of capabilities that are supported by this client.
183 """ 194 """
184 protoparams = {'partial-pull'} 195 protoparams = {'partial-pull'}
185 comps = [e.wireprotosupport().name for e in 196 comps = [
186 util.compengines.supportedwireengines(util.CLIENTROLE)] 197 e.wireprotosupport().name
198 for e in util.compengines.supportedwireengines(util.CLIENTROLE)
199 ]
187 protoparams.add('comp=%s' % ','.join(comps)) 200 protoparams.add('comp=%s' % ','.join(comps))
188 return protoparams 201 return protoparams
202
189 203
190 def _performhandshake(ui, stdin, stdout, stderr): 204 def _performhandshake(ui, stdin, stdout, stderr):
191 def badresponse(): 205 def badresponse():
192 # Flush any output on stderr. 206 # Flush any output on stderr.
193 _forwardoutput(ui, stderr) 207 _forwardoutput(ui, stderr)
361 # Flush any output on stderr before proceeding. 375 # Flush any output on stderr before proceeding.
362 _forwardoutput(ui, stderr) 376 _forwardoutput(ui, stderr)
363 377
364 return protoname, caps 378 return protoname, caps
365 379
380
366 class sshv1peer(wireprotov1peer.wirepeer): 381 class sshv1peer(wireprotov1peer.wirepeer):
367 def __init__(self, ui, url, proc, stdin, stdout, stderr, caps, 382 def __init__(
368 autoreadstderr=True): 383 self, ui, url, proc, stdin, stdout, stderr, caps, autoreadstderr=True
384 ):
369 """Create a peer from an existing SSH connection. 385 """Create a peer from an existing SSH connection.
370 386
371 ``proc`` is a handle on the underlying SSH process. 387 ``proc`` is a handle on the underlying SSH process.
372 ``stdin``, ``stdout``, and ``stderr`` are handles on the stdio 388 ``stdin``, ``stdout``, and ``stderr`` are handles on the stdio
373 pipes for that process. 389 pipes for that process.
436 _cleanuppipes(self.ui, self._pipei, self._pipeo, self._pipee) 452 _cleanuppipes(self.ui, self._pipei, self._pipeo, self._pipee)
437 453
438 __del__ = _cleanup 454 __del__ = _cleanup
439 455
440 def _sendrequest(self, cmd, args, framed=False): 456 def _sendrequest(self, cmd, args, framed=False):
441 if (self.ui.debugflag 457 if self.ui.debugflag and self.ui.configbool(
442 and self.ui.configbool('devel', 'debug.peer-request')): 458 'devel', 'debug.peer-request'
459 ):
443 dbg = self.ui.debug 460 dbg = self.ui.debug
444 line = 'devel-peer-request: %s\n' 461 line = 'devel-peer-request: %s\n'
445 dbg(line % cmd) 462 dbg(line % cmd)
446 for key, value in sorted(args.items()): 463 for key, value in sorted(args.items()):
447 if not isinstance(value, dict): 464 if not isinstance(value, dict):
558 if flush: 575 if flush:
559 self._pipeo.flush() 576 self._pipeo.flush()
560 if self._autoreadstderr: 577 if self._autoreadstderr:
561 self._readerr() 578 self._readerr()
562 579
580
563 class sshv2peer(sshv1peer): 581 class sshv2peer(sshv1peer):
564 """A peer that speakers version 2 of the transport protocol.""" 582 """A peer that speakers version 2 of the transport protocol."""
583
565 # Currently version 2 is identical to version 1 post handshake. 584 # Currently version 2 is identical to version 1 post handshake.
566 # And handshake is performed before the peer is instantiated. So 585 # And handshake is performed before the peer is instantiated. So
567 # we need no custom code. 586 # we need no custom code.
587
568 588
569 def makepeer(ui, path, proc, stdin, stdout, stderr, autoreadstderr=True): 589 def makepeer(ui, path, proc, stdin, stdout, stderr, autoreadstderr=True):
570 """Make a peer instance from existing pipes. 590 """Make a peer instance from existing pipes.
571 591
572 ``path`` and ``proc`` are stored on the eventual peer instance and may 592 ``path`` and ``proc`` are stored on the eventual peer instance and may
585 except Exception: 605 except Exception:
586 _cleanuppipes(ui, stdout, stdin, stderr) 606 _cleanuppipes(ui, stdout, stdin, stderr)
587 raise 607 raise
588 608
589 if protoname == wireprototypes.SSHV1: 609 if protoname == wireprototypes.SSHV1:
590 return sshv1peer(ui, path, proc, stdin, stdout, stderr, caps, 610 return sshv1peer(
591 autoreadstderr=autoreadstderr) 611 ui,
612 path,
613 proc,
614 stdin,
615 stdout,
616 stderr,
617 caps,
618 autoreadstderr=autoreadstderr,
619 )
592 elif protoname == wireprototypes.SSHV2: 620 elif protoname == wireprototypes.SSHV2:
593 return sshv2peer(ui, path, proc, stdin, stdout, stderr, caps, 621 return sshv2peer(
594 autoreadstderr=autoreadstderr) 622 ui,
623 path,
624 proc,
625 stdin,
626 stdout,
627 stderr,
628 caps,
629 autoreadstderr=autoreadstderr,
630 )
595 else: 631 else:
596 _cleanuppipes(ui, stdout, stdin, stderr) 632 _cleanuppipes(ui, stdout, stdin, stderr)
597 raise error.RepoError(_('unknown version of SSH protocol: %s') % 633 raise error.RepoError(
598 protoname) 634 _('unknown version of SSH protocol: %s') % protoname
635 )
636
599 637
600 def instance(ui, path, create, intents=None, createopts=None): 638 def instance(ui, path, create, intents=None, createopts=None):
601 """Create an SSH peer. 639 """Create an SSH peer.
602 640
603 The returned object conforms to the ``wireprotov1peer.wirepeer`` interface. 641 The returned object conforms to the ``wireprotov1peer.wirepeer`` interface.
623 # We /could/ do this, but only if the remote init command knows how to 661 # We /could/ do this, but only if the remote init command knows how to
624 # handle them. We don't yet make any assumptions about that. And without 662 # handle them. We don't yet make any assumptions about that. And without
625 # querying the remote, there's no way of knowing if the remote even 663 # querying the remote, there's no way of knowing if the remote even
626 # supports said requested feature. 664 # supports said requested feature.
627 if createopts: 665 if createopts:
628 raise error.RepoError(_('cannot create remote SSH repositories ' 666 raise error.RepoError(
629 'with extra options')) 667 _('cannot create remote SSH repositories ' 'with extra options')
630 668 )
631 cmd = '%s %s %s' % (sshcmd, args, 669
632 procutil.shellquote('%s init %s' % 670 cmd = '%s %s %s' % (
633 (_serverquote(remotecmd), _serverquote(remotepath)))) 671 sshcmd,
672 args,
673 procutil.shellquote(
674 '%s init %s'
675 % (_serverquote(remotecmd), _serverquote(remotepath))
676 ),
677 )
634 ui.debug('running %s\n' % cmd) 678 ui.debug('running %s\n' % cmd)
635 res = ui.system(cmd, blockedtag='sshpeer', environ=sshenv) 679 res = ui.system(cmd, blockedtag='sshpeer', environ=sshenv)
636 if res != 0: 680 if res != 0:
637 raise error.RepoError(_('could not create remote repo')) 681 raise error.RepoError(_('could not create remote repo'))
638 682
639 proc, stdin, stdout, stderr = _makeconnection(ui, sshcmd, args, remotecmd, 683 proc, stdin, stdout, stderr = _makeconnection(
640 remotepath, sshenv) 684 ui, sshcmd, args, remotecmd, remotepath, sshenv
685 )
641 686
642 peer = makepeer(ui, path, proc, stdin, stdout, stderr) 687 peer = makepeer(ui, path, proc, stdin, stdout, stderr)
643 688
644 # Finally, if supported by the server, notify it about our own 689 # Finally, if supported by the server, notify it about our own
645 # capabilities. 690 # capabilities.
646 if 'protocaps' in peer.capabilities(): 691 if 'protocaps' in peer.capabilities():
647 try: 692 try:
648 peer._call("protocaps", 693 peer._call(
649 caps=' '.join(sorted(_clientcapabilities()))) 694 "protocaps", caps=' '.join(sorted(_clientcapabilities()))
695 )
650 except IOError: 696 except IOError:
651 peer._cleanup() 697 peer._cleanup()
652 raise error.RepoError(_('capability exchange failed')) 698 raise error.RepoError(_('capability exchange failed'))
653 699
654 return peer 700 return peer