~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/ssh.py

Show diffs side-by-side

added added

removed removed

Lines of Context:
126
126
        elif 'SSH Secure Shell' in version:
127
127
            trace.mutter('ssh implementation is SSH Corp.')
128
128
            vendor = SSHCorpSubprocessVendor()
129
 
        elif 'lsh' in version:
130
 
            trace.mutter('ssh implementation is GNU lsh.')
131
 
            vendor = LSHSubprocessVendor()
132
129
        # As plink user prompts are not handled currently, don't auto-detect
133
130
        # it by inspection below, but keep this vendor detection for if a path
134
131
        # is given in BZR_SSH. See https://bugs.launchpad.net/bugs/414743
135
132
        elif 'plink' in version and progname == 'plink':
136
133
            # Checking if "plink" was the executed argument as Windows
137
 
            # sometimes reports 'ssh -V' incorrectly with 'plink' in its
 
134
            # sometimes reports 'ssh -V' incorrectly with 'plink' in it's
138
135
            # version.  See https://bugs.launchpad.net/bzr/+bug/107155
139
136
            trace.mutter("ssh implementation is Putty's plink.")
140
137
            vendor = PLinkSubprocessVendor()
339
336
            self._raise_connection_error(host, port=port, orig_error=e,
340
337
                                         msg='Unable to invoke remote bzr')
341
338
 
342
 
_ssh_connection_errors = (EOFError, OSError, IOError, socket.error)
343
339
if paramiko is not None:
344
340
    vendor = ParamikoVendor()
345
341
    register_ssh_vendor('paramiko', vendor)
346
342
    register_ssh_vendor('none', vendor)
347
343
    register_default_ssh_vendor(vendor)
348
 
    _ssh_connection_errors += (paramiko.SSHException,)
 
344
    _sftp_connection_errors = (EOFError, paramiko.SSHException)
349
345
    del vendor
 
346
else:
 
347
    _sftp_connection_errors = (EOFError,)
350
348
 
351
349
 
352
350
class SubprocessVendor(SSHVendor):
363
361
            # This platform doesn't support socketpair(), so just use ordinary
364
362
            # pipes instead.
365
363
            stdin = stdout = subprocess.PIPE
366
 
            my_sock, subproc_sock = None, None
 
364
            sock = None
367
365
        else:
368
366
            stdin = stdout = subproc_sock
 
367
            sock = my_sock
369
368
        proc = subprocess.Popen(argv, stdin=stdin, stdout=stdout,
370
369
                                **os_specific_subprocess_params())
371
 
        if subproc_sock is not None:
372
 
            subproc_sock.close()
373
 
        return SSHSubprocessConnection(proc, sock=my_sock)
 
370
        return SSHSubprocessConnection(proc, sock=sock)
374
371
 
375
372
    def connect_sftp(self, username, password, host, port):
376
373
        try:
378
375
                                                  subsystem='sftp')
379
376
            sock = self._connect(argv)
380
377
            return SFTPClient(SocketAsChannelAdapter(sock))
381
 
        except _ssh_connection_errors, e:
 
378
        except _sftp_connection_errors, e:
 
379
            self._raise_connection_error(host, port=port, orig_error=e)
 
380
        except (OSError, IOError), e:
 
381
            # If the machine is fast enough, ssh can actually exit
 
382
            # before we try and send it the sftp request, which
 
383
            # raises a Broken Pipe
 
384
            if e.errno not in (errno.EPIPE,):
 
385
                raise
382
386
            self._raise_connection_error(host, port=port, orig_error=e)
383
387
 
384
388
    def connect_ssh(self, username, password, host, port, command):
386
390
            argv = self._get_vendor_specific_argv(username, host, port,
387
391
                                                  command=command)
388
392
            return self._connect(argv)
389
 
        except _ssh_connection_errors, e:
 
393
        except (EOFError), e:
 
394
            self._raise_connection_error(host, port=port, orig_error=e)
 
395
        except (OSError, IOError), e:
 
396
            # If the machine is fast enough, ssh can actually exit
 
397
            # before we try and send it the sftp request, which
 
398
            # raises a Broken Pipe
 
399
            if e.errno not in (errno.EPIPE,):
 
400
                raise
390
401
            self._raise_connection_error(host, port=port, orig_error=e)
391
402
 
392
403
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
407
418
                                  command=None):
408
419
        args = [self.executable_path,
409
420
                '-oForwardX11=no', '-oForwardAgent=no',
410
 
                '-oClearAllForwardings=yes',
 
421
                '-oClearAllForwardings=yes', '-oProtocol=2',
411
422
                '-oNoHostAuthenticationForLocalhost=yes']
412
423
        if port is not None:
413
424
            args.extend(['-p', str(port)])
443
454
register_ssh_vendor('sshcorp', SSHCorpSubprocessVendor())
444
455
 
445
456
 
446
 
class LSHSubprocessVendor(SubprocessVendor):
447
 
    """SSH vendor that uses the 'lsh' executable from GNU"""
448
 
 
449
 
    executable_path = 'lsh'
450
 
 
451
 
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
452
 
                                  command=None):
453
 
        args = [self.executable_path]
454
 
        if port is not None:
455
 
            args.extend(['-p', str(port)])
456
 
        if username is not None:
457
 
            args.extend(['-l', username])
458
 
        if subsystem is not None:
459
 
            args.extend(['--subsystem', subsystem, host])
460
 
        else:
461
 
            args.extend([host] + command)
462
 
        return args
463
 
 
464
 
register_ssh_vendor('lsh', LSHSubprocessVendor())
465
 
 
466
 
 
467
457
class PLinkSubprocessVendor(SubprocessVendor):
468
458
    """SSH vendor that uses the 'plink' executable from Putty."""
469
459
 
654
644
import weakref
655
645
_subproc_weakrefs = set()
656
646
 
657
 
def _close_ssh_proc(proc, sock):
 
647
def _close_ssh_proc(proc):
658
648
    """Carefully close stdin/stdout and reap the SSH process.
659
649
 
660
650
    If the pipes are already closed and/or the process has already been
661
651
    wait()ed on, that's ok, and no error is raised.  The goal is to do our best
662
652
    to clean up (whether or not a clean up was already tried).
663
653
    """
664
 
    funcs = []
665
 
    for closeable in (proc.stdin, proc.stdout, sock):
666
 
        # We expect that either proc (a subprocess.Popen) will have stdin and
667
 
        # stdout streams to close, or that we will have been passed a socket to
668
 
        # close, with the option not in use being None.
669
 
        if closeable is not None:
670
 
            funcs.append(closeable.close)
671
 
    funcs.append(proc.wait)
672
 
    for func in funcs:
673
 
        try:
674
 
            func()
 
654
    dotted_names = ['stdin.close', 'stdout.close', 'wait']
 
655
    for dotted_name in dotted_names:
 
656
        attrs = dotted_name.split('.')
 
657
        try:
 
658
            obj = proc
 
659
            for attr in attrs:
 
660
                obj = getattr(obj, attr)
 
661
        except AttributeError:
 
662
            # It's ok for proc.stdin or proc.stdout to be None.
 
663
            continue
 
664
        try:
 
665
            obj()
675
666
        except OSError:
676
667
            # It's ok for the pipe to already be closed, or the process to
677
668
            # already be finished.
716
707
        # to avoid leaving processes lingering indefinitely.
717
708
        def terminate(ref):
718
709
            _subproc_weakrefs.remove(ref)
719
 
            _close_ssh_proc(proc, sock)
 
710
            _close_ssh_proc(proc)
720
711
        _subproc_weakrefs.add(weakref.ref(self, terminate))
721
712
 
722
713
    def send(self, data):
732
723
            return os.read(self.proc.stdout.fileno(), count)
733
724
 
734
725
    def close(self):
735
 
        _close_ssh_proc(self.proc, self._sock)
 
726
        _close_ssh_proc(self.proc)
736
727
 
737
728
    def get_sock_or_pipes(self):
738
729
        if self._sock is not None: