comparison mercurial/sshpeer.py @ 43077:687b865b95ad

formatting: byteify all mercurial/ and hgext/ string literals Done with python3.7 contrib/byteify-strings.py -i $(hg files 'set:mercurial/**.py - mercurial/thirdparty/** + hgext/**.py - hgext/fsmonitor/pywatchman/** - mercurial/__init__.py') black -l 80 -t py33 -S $(hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**" - hgext/fsmonitor/pywatchman/**') # skip-blame mass-reformatting only Differential Revision: https://phab.mercurial-scm.org/D6972
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:48:39 -0400
parents 2372284d9457
children c59eb1560c44
comparison
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
28 28
29 def _serverquote(s): 29 def _serverquote(s):
30 """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"""
31 if not s: 31 if not s:
32 return s 32 return s
33 if re.match('[a-zA-Z0-9@%_+=:,./-]*$', s): 33 if re.match(b'[a-zA-Z0-9@%_+=:,./-]*$', s):
34 return s 34 return s
35 return "'%s'" % s.replace("'", "'\\''") 35 return b"'%s'" % s.replace(b"'", b"'\\''")
36 36
37 37
38 def _forwardoutput(ui, pipe): 38 def _forwardoutput(ui, pipe):
39 """display all data currently available on pipe as remote output. 39 """display all data currently available on pipe as remote output.
40 40
41 This is non blocking.""" 41 This is non blocking."""
42 if pipe: 42 if pipe:
43 s = procutil.readpipe(pipe) 43 s = procutil.readpipe(pipe)
44 if s: 44 if s:
45 for l in s.splitlines(): 45 for l in s.splitlines():
46 ui.status(_("remote: "), l, '\n') 46 ui.status(_(b"remote: "), l, b'\n')
47 47
48 48
49 class doublepipe(object): 49 class doublepipe(object):
50 """Operate a side-channel pipe in addition of a main one 50 """Operate a side-channel pipe in addition of a main one
51 51
89 # non supported yet case, assume all have data. 89 # non supported yet case, assume all have data.
90 act = fds 90 act = fds
91 return (self._main.fileno() in act, self._side.fileno() in act) 91 return (self._main.fileno() in act, self._side.fileno() in act)
92 92
93 def write(self, data): 93 def write(self, data):
94 return self._call('write', data) 94 return self._call(b'write', data)
95 95
96 def read(self, size): 96 def read(self, size):
97 r = self._call('read', size) 97 r = self._call(b'read', size)
98 if size != 0 and not r: 98 if size != 0 and not r:
99 # We've observed a condition that indicates the 99 # We've observed a condition that indicates the
100 # stdout closed unexpectedly. Check stderr one 100 # stdout closed unexpectedly. Check stderr one
101 # more time and snag anything that's there before 101 # more time and snag anything that's there before
102 # letting anyone know the main part of the pipe 102 # letting anyone know the main part of the pipe
103 # closed prematurely. 103 # closed prematurely.
104 _forwardoutput(self._ui, self._side) 104 _forwardoutput(self._ui, self._side)
105 return r 105 return r
106 106
107 def unbufferedread(self, size): 107 def unbufferedread(self, size):
108 r = self._call('unbufferedread', size) 108 r = self._call(b'unbufferedread', size)
109 if size != 0 and not r: 109 if size != 0 and not r:
110 # We've observed a condition that indicates the 110 # We've observed a condition that indicates the
111 # stdout closed unexpectedly. Check stderr one 111 # stdout closed unexpectedly. Check stderr one
112 # more time and snag anything that's there before 112 # more time and snag anything that's there before
113 # letting anyone know the main part of the pipe 113 # letting anyone know the main part of the pipe
114 # closed prematurely. 114 # closed prematurely.
115 _forwardoutput(self._ui, self._side) 115 _forwardoutput(self._ui, self._side)
116 return r 116 return r
117 117
118 def readline(self): 118 def readline(self):
119 return self._call('readline') 119 return self._call(b'readline')
120 120
121 def _call(self, methname, data=None): 121 def _call(self, methname, data=None):
122 """call <methname> on "main", forward output of "side" while blocking 122 """call <methname> on "main", forward output of "side" while blocking
123 """ 123 """
124 # data can be '' or 0 124 # data can be '' or 0
125 if (data is not None and not data) or self._main.closed: 125 if (data is not None and not data) or self._main.closed:
126 _forwardoutput(self._ui, self._side) 126 _forwardoutput(self._ui, self._side)
127 return '' 127 return b''
128 while True: 128 while True:
129 mainready, sideready = self._wait() 129 mainready, sideready = self._wait()
130 if sideready: 130 if sideready:
131 _forwardoutput(self._ui, self._side) 131 _forwardoutput(self._ui, self._side)
132 if mainready: 132 if mainready:
152 152
153 if pipee: 153 if pipee:
154 # Try to read from the err descriptor until EOF. 154 # Try to read from the err descriptor until EOF.
155 try: 155 try:
156 for l in pipee: 156 for l in pipee:
157 ui.status(_('remote: '), l) 157 ui.status(_(b'remote: '), l)
158 except (IOError, ValueError): 158 except (IOError, ValueError):
159 pass 159 pass
160 160
161 pipee.close() 161 pipee.close()
162 162
165 """Create an SSH connection to a server. 165 """Create an SSH connection to a server.
166 166
167 Returns a tuple of (process, stdin, stdout, stderr) for the 167 Returns a tuple of (process, stdin, stdout, stderr) for the
168 spawned process. 168 spawned process.
169 """ 169 """
170 cmd = '%s %s %s' % ( 170 cmd = b'%s %s %s' % (
171 sshcmd, 171 sshcmd,
172 args, 172 args,
173 procutil.shellquote( 173 procutil.shellquote(
174 '%s -R %s serve --stdio' 174 b'%s -R %s serve --stdio'
175 % (_serverquote(remotecmd), _serverquote(path)) 175 % (_serverquote(remotecmd), _serverquote(path))
176 ), 176 ),
177 ) 177 )
178 178
179 ui.debug('running %s\n' % cmd) 179 ui.debug(b'running %s\n' % cmd)
180 cmd = procutil.quotecommand(cmd) 180 cmd = procutil.quotecommand(cmd)
181 181
182 # no buffer allow the use of 'select' 182 # no buffer allow the use of 'select'
183 # feel free to remove buffering and select usage when we ultimately 183 # feel free to remove buffering and select usage when we ultimately
184 # move to threading. 184 # move to threading.
190 def _clientcapabilities(): 190 def _clientcapabilities():
191 """Return list of capabilities of this client. 191 """Return list of capabilities of this client.
192 192
193 Returns a list of capabilities that are supported by this client. 193 Returns a list of capabilities that are supported by this client.
194 """ 194 """
195 protoparams = {'partial-pull'} 195 protoparams = {b'partial-pull'}
196 comps = [ 196 comps = [
197 e.wireprotosupport().name 197 e.wireprotosupport().name
198 for e in util.compengines.supportedwireengines(util.CLIENTROLE) 198 for e in util.compengines.supportedwireengines(util.CLIENTROLE)
199 ] 199 ]
200 protoparams.add('comp=%s' % ','.join(comps)) 200 protoparams.add(b'comp=%s' % b','.join(comps))
201 return protoparams 201 return protoparams
202 202
203 203
204 def _performhandshake(ui, stdin, stdout, stderr): 204 def _performhandshake(ui, stdin, stdout, stderr):
205 def badresponse(): 205 def badresponse():
206 # Flush any output on stderr. 206 # Flush any output on stderr.
207 _forwardoutput(ui, stderr) 207 _forwardoutput(ui, stderr)
208 208
209 msg = _('no suitable response from remote hg') 209 msg = _(b'no suitable response from remote hg')
210 hint = ui.config('ui', 'ssherrorhint') 210 hint = ui.config(b'ui', b'ssherrorhint')
211 raise error.RepoError(msg, hint=hint) 211 raise error.RepoError(msg, hint=hint)
212 212
213 # The handshake consists of sending wire protocol commands in reverse 213 # The handshake consists of sending wire protocol commands in reverse
214 # order of protocol implementation and then sniffing for a response 214 # order of protocol implementation and then sniffing for a response
215 # to one of them. 215 # to one of them.
260 # print messages to stdout on login. Issuing commands on connection 260 # print messages to stdout on login. Issuing commands on connection
261 # allows us to flush this banner output from the server by scanning 261 # allows us to flush this banner output from the server by scanning
262 # for output to our well-known ``between`` command. Of course, if 262 # for output to our well-known ``between`` command. Of course, if
263 # the banner contains ``1\n\n``, this will throw off our detection. 263 # the banner contains ``1\n\n``, this will throw off our detection.
264 264
265 requestlog = ui.configbool('devel', 'debug.peer-request') 265 requestlog = ui.configbool(b'devel', b'debug.peer-request')
266 266
267 # Generate a random token to help identify responses to version 2 267 # Generate a random token to help identify responses to version 2
268 # upgrade request. 268 # upgrade request.
269 token = pycompat.sysbytes(str(uuid.uuid4())) 269 token = pycompat.sysbytes(str(uuid.uuid4()))
270 upgradecaps = [ 270 upgradecaps = [
271 ('proto', wireprotoserver.SSHV2), 271 (b'proto', wireprotoserver.SSHV2),
272 ] 272 ]
273 upgradecaps = util.urlreq.urlencode(upgradecaps) 273 upgradecaps = util.urlreq.urlencode(upgradecaps)
274 274
275 try: 275 try:
276 pairsarg = '%s-%s' % ('0' * 40, '0' * 40) 276 pairsarg = b'%s-%s' % (b'0' * 40, b'0' * 40)
277 handshake = [ 277 handshake = [
278 'hello\n', 278 b'hello\n',
279 'between\n', 279 b'between\n',
280 'pairs %d\n' % len(pairsarg), 280 b'pairs %d\n' % len(pairsarg),
281 pairsarg, 281 pairsarg,
282 ] 282 ]
283 283
284 # Request upgrade to version 2 if configured. 284 # Request upgrade to version 2 if configured.
285 if ui.configbool('experimental', 'sshpeer.advertise-v2'): 285 if ui.configbool(b'experimental', b'sshpeer.advertise-v2'):
286 ui.debug('sending upgrade request: %s %s\n' % (token, upgradecaps)) 286 ui.debug(b'sending upgrade request: %s %s\n' % (token, upgradecaps))
287 handshake.insert(0, 'upgrade %s %s\n' % (token, upgradecaps)) 287 handshake.insert(0, b'upgrade %s %s\n' % (token, upgradecaps))
288 288
289 if requestlog: 289 if requestlog:
290 ui.debug('devel-peer-request: hello+between\n') 290 ui.debug(b'devel-peer-request: hello+between\n')
291 ui.debug('devel-peer-request: pairs: %d bytes\n' % len(pairsarg)) 291 ui.debug(b'devel-peer-request: pairs: %d bytes\n' % len(pairsarg))
292 ui.debug('sending hello command\n') 292 ui.debug(b'sending hello command\n')
293 ui.debug('sending between command\n') 293 ui.debug(b'sending between command\n')
294 294
295 stdin.write(''.join(handshake)) 295 stdin.write(b''.join(handshake))
296 stdin.flush() 296 stdin.flush()
297 except IOError: 297 except IOError:
298 badresponse() 298 badresponse()
299 299
300 # Assume version 1 of wire protocol by default. 300 # Assume version 1 of wire protocol by default.
301 protoname = wireprototypes.SSHV1 301 protoname = wireprototypes.SSHV1
302 reupgraded = re.compile(b'^upgraded %s (.*)$' % stringutil.reescape(token)) 302 reupgraded = re.compile(b'^upgraded %s (.*)$' % stringutil.reescape(token))
303 303
304 lines = ['', 'dummy'] 304 lines = [b'', b'dummy']
305 max_noise = 500 305 max_noise = 500
306 while lines[-1] and max_noise: 306 while lines[-1] and max_noise:
307 try: 307 try:
308 l = stdout.readline() 308 l = stdout.readline()
309 _forwardoutput(ui, stderr) 309 _forwardoutput(ui, stderr)
311 # Look for reply to protocol upgrade request. It has a token 311 # Look for reply to protocol upgrade request. It has a token
312 # in it, so there should be no false positives. 312 # in it, so there should be no false positives.
313 m = reupgraded.match(l) 313 m = reupgraded.match(l)
314 if m: 314 if m:
315 protoname = m.group(1) 315 protoname = m.group(1)
316 ui.debug('protocol upgraded to %s\n' % protoname) 316 ui.debug(b'protocol upgraded to %s\n' % protoname)
317 # If an upgrade was handled, the ``hello`` and ``between`` 317 # If an upgrade was handled, the ``hello`` and ``between``
318 # requests are ignored. The next output belongs to the 318 # requests are ignored. The next output belongs to the
319 # protocol, so stop scanning lines. 319 # protocol, so stop scanning lines.
320 break 320 break
321 321
322 # Otherwise it could be a banner, ``0\n`` response if server 322 # Otherwise it could be a banner, ``0\n`` response if server
323 # doesn't support upgrade. 323 # doesn't support upgrade.
324 324
325 if lines[-1] == '1\n' and l == '\n': 325 if lines[-1] == b'1\n' and l == b'\n':
326 break 326 break
327 if l: 327 if l:
328 ui.debug('remote: ', l) 328 ui.debug(b'remote: ', l)
329 lines.append(l) 329 lines.append(l)
330 max_noise -= 1 330 max_noise -= 1
331 except IOError: 331 except IOError:
332 badresponse() 332 badresponse()
333 else: 333 else:
339 # ``hello`` command. 339 # ``hello`` command.
340 if protoname == wireprototypes.SSHV1: 340 if protoname == wireprototypes.SSHV1:
341 for l in reversed(lines): 341 for l in reversed(lines):
342 # Look for response to ``hello`` command. Scan from the back so 342 # Look for response to ``hello`` command. Scan from the back so
343 # we don't misinterpret banner output as the command reply. 343 # we don't misinterpret banner output as the command reply.
344 if l.startswith('capabilities:'): 344 if l.startswith(b'capabilities:'):
345 caps.update(l[:-1].split(':')[1].split()) 345 caps.update(l[:-1].split(b':')[1].split())
346 break 346 break
347 elif protoname == wireprotoserver.SSHV2: 347 elif protoname == wireprotoserver.SSHV2:
348 # We see a line with number of bytes to follow and then a value 348 # We see a line with number of bytes to follow and then a value
349 # looking like ``capabilities: *``. 349 # looking like ``capabilities: *``.
350 line = stdout.readline() 350 line = stdout.readline()
352 valuelen = int(line) 352 valuelen = int(line)
353 except ValueError: 353 except ValueError:
354 badresponse() 354 badresponse()
355 355
356 capsline = stdout.read(valuelen) 356 capsline = stdout.read(valuelen)
357 if not capsline.startswith('capabilities: '): 357 if not capsline.startswith(b'capabilities: '):
358 badresponse() 358 badresponse()
359 359
360 ui.debug('remote: %s\n' % capsline) 360 ui.debug(b'remote: %s\n' % capsline)
361 361
362 caps.update(capsline.split(':')[1].split()) 362 caps.update(capsline.split(b':')[1].split())
363 # Trailing newline. 363 # Trailing newline.
364 stdout.read(1) 364 stdout.read(1)
365 365
366 # Error if we couldn't find capabilities, this means: 366 # Error if we couldn't find capabilities, this means:
367 # 367 #
410 self._autoreadstderr = autoreadstderr 410 self._autoreadstderr = autoreadstderr
411 411
412 # Commands that have a "framed" response where the first line of the 412 # Commands that have a "framed" response where the first line of the
413 # response contains the length of that response. 413 # response contains the length of that response.
414 _FRAMED_COMMANDS = { 414 _FRAMED_COMMANDS = {
415 'batch', 415 b'batch',
416 } 416 }
417 417
418 # Begin of ipeerconnection interface. 418 # Begin of ipeerconnection interface.
419 419
420 def url(self): 420 def url(self):
453 453
454 __del__ = _cleanup 454 __del__ = _cleanup
455 455
456 def _sendrequest(self, cmd, args, framed=False): 456 def _sendrequest(self, cmd, args, framed=False):
457 if self.ui.debugflag and self.ui.configbool( 457 if self.ui.debugflag and self.ui.configbool(
458 'devel', 'debug.peer-request' 458 b'devel', b'debug.peer-request'
459 ): 459 ):
460 dbg = self.ui.debug 460 dbg = self.ui.debug
461 line = 'devel-peer-request: %s\n' 461 line = b'devel-peer-request: %s\n'
462 dbg(line % cmd) 462 dbg(line % cmd)
463 for key, value in sorted(args.items()): 463 for key, value in sorted(args.items()):
464 if not isinstance(value, dict): 464 if not isinstance(value, dict):
465 dbg(line % ' %s: %d bytes' % (key, len(value))) 465 dbg(line % b' %s: %d bytes' % (key, len(value)))
466 else: 466 else:
467 for dk, dv in sorted(value.items()): 467 for dk, dv in sorted(value.items()):
468 dbg(line % ' %s-%s: %d' % (key, dk, len(dv))) 468 dbg(line % b' %s-%s: %d' % (key, dk, len(dv)))
469 self.ui.debug("sending %s command\n" % cmd) 469 self.ui.debug(b"sending %s command\n" % cmd)
470 self._pipeo.write("%s\n" % cmd) 470 self._pipeo.write(b"%s\n" % cmd)
471 _func, names = wireprotov1server.commands[cmd] 471 _func, names = wireprotov1server.commands[cmd]
472 keys = names.split() 472 keys = names.split()
473 wireargs = {} 473 wireargs = {}
474 for k in keys: 474 for k in keys:
475 if k == '*': 475 if k == b'*':
476 wireargs['*'] = args 476 wireargs[b'*'] = args
477 break 477 break
478 else: 478 else:
479 wireargs[k] = args[k] 479 wireargs[k] = args[k]
480 del args[k] 480 del args[k]
481 for k, v in sorted(wireargs.iteritems()): 481 for k, v in sorted(wireargs.iteritems()):
482 self._pipeo.write("%s %d\n" % (k, len(v))) 482 self._pipeo.write(b"%s %d\n" % (k, len(v)))
483 if isinstance(v, dict): 483 if isinstance(v, dict):
484 for dk, dv in v.iteritems(): 484 for dk, dv in v.iteritems():
485 self._pipeo.write("%s %d\n" % (dk, len(dv))) 485 self._pipeo.write(b"%s %d\n" % (dk, len(dv)))
486 self._pipeo.write(dv) 486 self._pipeo.write(dv)
487 else: 487 else:
488 self._pipeo.write(v) 488 self._pipeo.write(v)
489 self._pipeo.flush() 489 self._pipeo.flush()
490 490
513 def _callpush(self, cmd, fp, **args): 513 def _callpush(self, cmd, fp, **args):
514 # The server responds with an empty frame if the client should 514 # The server responds with an empty frame if the client should
515 # continue submitting the payload. 515 # continue submitting the payload.
516 r = self._call(cmd, **args) 516 r = self._call(cmd, **args)
517 if r: 517 if r:
518 return '', r 518 return b'', r
519 519
520 # The payload consists of frames with content followed by an empty 520 # The payload consists of frames with content followed by an empty
521 # frame. 521 # frame.
522 for d in iter(lambda: fp.read(4096), ''): 522 for d in iter(lambda: fp.read(4096), b''):
523 self._writeframed(d) 523 self._writeframed(d)
524 self._writeframed("", flush=True) 524 self._writeframed(b"", flush=True)
525 525
526 # In case of success, there is an empty frame and a frame containing 526 # In case of success, there is an empty frame and a frame containing
527 # the integer result (as a string). 527 # the integer result (as a string).
528 # In case of error, there is a non-empty frame containing the error. 528 # In case of error, there is a non-empty frame containing the error.
529 r = self._readframed() 529 r = self._readframed()
530 if r: 530 if r:
531 return '', r 531 return b'', r
532 return self._readframed(), '' 532 return self._readframed(), b''
533 533
534 def _calltwowaystream(self, cmd, fp, **args): 534 def _calltwowaystream(self, cmd, fp, **args):
535 # The server responds with an empty frame if the client should 535 # The server responds with an empty frame if the client should
536 # continue submitting the payload. 536 # continue submitting the payload.
537 r = self._call(cmd, **args) 537 r = self._call(cmd, **args)
538 if r: 538 if r:
539 # XXX needs to be made better 539 # XXX needs to be made better
540 raise error.Abort(_('unexpected remote reply: %s') % r) 540 raise error.Abort(_(b'unexpected remote reply: %s') % r)
541 541
542 # The payload consists of frames with content followed by an empty 542 # The payload consists of frames with content followed by an empty
543 # frame. 543 # frame.
544 for d in iter(lambda: fp.read(4096), ''): 544 for d in iter(lambda: fp.read(4096), b''):
545 self._writeframed(d) 545 self._writeframed(d)
546 self._writeframed("", flush=True) 546 self._writeframed(b"", flush=True)
547 547
548 return self._pipei 548 return self._pipei
549 549
550 def _getamount(self): 550 def _getamount(self):
551 l = self._pipei.readline() 551 l = self._pipei.readline()
552 if l == '\n': 552 if l == b'\n':
553 if self._autoreadstderr: 553 if self._autoreadstderr:
554 self._readerr() 554 self._readerr()
555 msg = _('check previous remote output') 555 msg = _(b'check previous remote output')
556 self._abort(error.OutOfBandError(hint=msg)) 556 self._abort(error.OutOfBandError(hint=msg))
557 if self._autoreadstderr: 557 if self._autoreadstderr:
558 self._readerr() 558 self._readerr()
559 try: 559 try:
560 return int(l) 560 return int(l)
561 except ValueError: 561 except ValueError:
562 self._abort(error.ResponseError(_("unexpected response:"), l)) 562 self._abort(error.ResponseError(_(b"unexpected response:"), l))
563 563
564 def _readframed(self): 564 def _readframed(self):
565 size = self._getamount() 565 size = self._getamount()
566 if not size: 566 if not size:
567 return b'' 567 return b''
568 568
569 return self._pipei.read(size) 569 return self._pipei.read(size)
570 570
571 def _writeframed(self, data, flush=False): 571 def _writeframed(self, data, flush=False):
572 self._pipeo.write("%d\n" % len(data)) 572 self._pipeo.write(b"%d\n" % len(data))
573 if data: 573 if data:
574 self._pipeo.write(data) 574 self._pipeo.write(data)
575 if flush: 575 if flush:
576 self._pipeo.flush() 576 self._pipeo.flush()
577 if self._autoreadstderr: 577 if self._autoreadstderr:
629 autoreadstderr=autoreadstderr, 629 autoreadstderr=autoreadstderr,
630 ) 630 )
631 else: 631 else:
632 _cleanuppipes(ui, stdout, stdin, stderr) 632 _cleanuppipes(ui, stdout, stdin, stderr)
633 raise error.RepoError( 633 raise error.RepoError(
634 _('unknown version of SSH protocol: %s') % protoname 634 _(b'unknown version of SSH protocol: %s') % protoname
635 ) 635 )
636 636
637 637
638 def instance(ui, path, create, intents=None, createopts=None): 638 def instance(ui, path, create, intents=None, createopts=None):
639 """Create an SSH peer. 639 """Create an SSH peer.
640 640
641 The returned object conforms to the ``wireprotov1peer.wirepeer`` interface. 641 The returned object conforms to the ``wireprotov1peer.wirepeer`` interface.
642 """ 642 """
643 u = util.url(path, parsequery=False, parsefragment=False) 643 u = util.url(path, parsequery=False, parsefragment=False)
644 if u.scheme != 'ssh' or not u.host or u.path is None: 644 if u.scheme != b'ssh' or not u.host or u.path is None:
645 raise error.RepoError(_("couldn't parse location %s") % path) 645 raise error.RepoError(_(b"couldn't parse location %s") % path)
646 646
647 util.checksafessh(path) 647 util.checksafessh(path)
648 648
649 if u.passwd is not None: 649 if u.passwd is not None:
650 raise error.RepoError(_('password in URL not supported')) 650 raise error.RepoError(_(b'password in URL not supported'))
651 651
652 sshcmd = ui.config('ui', 'ssh') 652 sshcmd = ui.config(b'ui', b'ssh')
653 remotecmd = ui.config('ui', 'remotecmd') 653 remotecmd = ui.config(b'ui', b'remotecmd')
654 sshaddenv = dict(ui.configitems('sshenv')) 654 sshaddenv = dict(ui.configitems(b'sshenv'))
655 sshenv = procutil.shellenviron(sshaddenv) 655 sshenv = procutil.shellenviron(sshaddenv)
656 remotepath = u.path or '.' 656 remotepath = u.path or b'.'
657 657
658 args = procutil.sshargs(sshcmd, u.host, u.user, u.port) 658 args = procutil.sshargs(sshcmd, u.host, u.user, u.port)
659 659
660 if create: 660 if create:
661 # 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
662 # 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
663 # 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
664 # supports said requested feature. 664 # supports said requested feature.
665 if createopts: 665 if createopts:
666 raise error.RepoError( 666 raise error.RepoError(
667 _('cannot create remote SSH repositories ' 'with extra options') 667 _(
668 b'cannot create remote SSH repositories '
669 b'with extra options'
670 )
668 ) 671 )
669 672
670 cmd = '%s %s %s' % ( 673 cmd = b'%s %s %s' % (
671 sshcmd, 674 sshcmd,
672 args, 675 args,
673 procutil.shellquote( 676 procutil.shellquote(
674 '%s init %s' 677 b'%s init %s'
675 % (_serverquote(remotecmd), _serverquote(remotepath)) 678 % (_serverquote(remotecmd), _serverquote(remotepath))
676 ), 679 ),
677 ) 680 )
678 ui.debug('running %s\n' % cmd) 681 ui.debug(b'running %s\n' % cmd)
679 res = ui.system(cmd, blockedtag='sshpeer', environ=sshenv) 682 res = ui.system(cmd, blockedtag=b'sshpeer', environ=sshenv)
680 if res != 0: 683 if res != 0:
681 raise error.RepoError(_('could not create remote repo')) 684 raise error.RepoError(_(b'could not create remote repo'))
682 685
683 proc, stdin, stdout, stderr = _makeconnection( 686 proc, stdin, stdout, stderr = _makeconnection(
684 ui, sshcmd, args, remotecmd, remotepath, sshenv 687 ui, sshcmd, args, remotecmd, remotepath, sshenv
685 ) 688 )
686 689
687 peer = makepeer(ui, path, proc, stdin, stdout, stderr) 690 peer = makepeer(ui, path, proc, stdin, stdout, stderr)
688 691
689 # Finally, if supported by the server, notify it about our own 692 # Finally, if supported by the server, notify it about our own
690 # capabilities. 693 # capabilities.
691 if 'protocaps' in peer.capabilities(): 694 if b'protocaps' in peer.capabilities():
692 try: 695 try:
693 peer._call( 696 peer._call(
694 "protocaps", caps=' '.join(sorted(_clientcapabilities())) 697 b"protocaps", caps=b' '.join(sorted(_clientcapabilities()))
695 ) 698 )
696 except IOError: 699 except IOError:
697 peer._cleanup() 700 peer._cleanup()
698 raise error.RepoError(_('capability exchange failed')) 701 raise error.RepoError(_(b'capability exchange failed'))
699 702
700 return peer 703 return peer