~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/ssh.py

  • Committer: Vincent Ladeuil
  • Date: 2010-01-25 15:55:48 UTC
  • mto: (4985.1.4 add-attr-cleanup)
  • mto: This revision was merged to the branch mainline in revision 4988.
  • Revision ID: v.ladeuil+lp@free.fr-20100125155548-0l352pujvt5bzl5e
Deploy addAttrCleanup on the whole test suite.

Several use case worth mentioning:

- setting a module or any other object attribute is the majority
by far. In some cases the setting itself is deferred but most of
the time we want to set at the same time we add the cleanup.

- there multiple occurrences of protecting hooks or ui factory
which are now useless (the test framework takes care of that now),

- there was some lambda uses that can now be avoided.

That first cleanup already simplifies things a lot.

Show diffs side-by-side

added added

removed removed

Lines of Context:
94
94
            try:
95
95
                vendor = self._ssh_vendors[vendor_name]
96
96
            except KeyError:
97
 
                raise errors.UnknownSSH(vendor_name)
 
97
                vendor = self._get_vendor_from_path(vendor_name)
 
98
                if vendor is None:
 
99
                    raise errors.UnknownSSH(vendor_name)
 
100
                vendor.executable_path = vendor_name
98
101
            return vendor
99
102
        return None
100
103
 
110
113
            stdout = stderr = ''
111
114
        return stdout + stderr
112
115
 
113
 
    def _get_vendor_by_version_string(self, version, args):
 
116
    def _get_vendor_by_version_string(self, version, progname):
114
117
        """Return the vendor or None based on output from the subprocess.
115
118
 
116
119
        :param version: The output of 'ssh -V' like command.
123
126
        elif 'SSH Secure Shell' in version:
124
127
            trace.mutter('ssh implementation is SSH Corp.')
125
128
            vendor = SSHCorpSubprocessVendor()
126
 
        elif 'plink' in version and args[0] == 'plink':
 
129
        # As plink user prompts are not handled currently, don't auto-detect
 
130
        # it by inspection below, but keep this vendor detection for if a path
 
131
        # is given in BZR_SSH. See https://bugs.launchpad.net/bugs/414743
 
132
        elif 'plink' in version and progname == 'plink':
127
133
            # Checking if "plink" was the executed argument as Windows
128
134
            # sometimes reports 'ssh -V' incorrectly with 'plink' in it's
129
135
            # version.  See https://bugs.launchpad.net/bzr/+bug/107155
133
139
 
134
140
    def _get_vendor_by_inspection(self):
135
141
        """Return the vendor or None by checking for known SSH implementations."""
136
 
        for args in (['ssh', '-V'], ['plink', '-V']):
137
 
            version = self._get_ssh_version_string(args)
138
 
            vendor = self._get_vendor_by_version_string(version, args)
139
 
            if vendor is not None:
140
 
                return vendor
141
 
        return None
 
142
        version = self._get_ssh_version_string(['ssh', '-V'])
 
143
        return self._get_vendor_by_version_string(version, "ssh")
 
144
 
 
145
    def _get_vendor_from_path(self, path):
 
146
        """Return the vendor or None using the program at the given path"""
 
147
        version = self._get_ssh_version_string([path, '-V'])
 
148
        return self._get_vendor_by_version_string(version, 
 
149
            os.path.splitext(os.path.basename(path))[0])
142
150
 
143
151
    def get_vendor(self, environment=None):
144
152
        """Find out what version of SSH is on the system.
401
409
class OpenSSHSubprocessVendor(SubprocessVendor):
402
410
    """SSH vendor that uses the 'ssh' executable from OpenSSH."""
403
411
 
 
412
    executable_path = 'ssh'
 
413
 
404
414
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
405
415
                                  command=None):
406
 
        args = ['ssh',
 
416
        args = [self.executable_path,
407
417
                '-oForwardX11=no', '-oForwardAgent=no',
408
418
                '-oClearAllForwardings=yes', '-oProtocol=2',
409
419
                '-oNoHostAuthenticationForLocalhost=yes']
423
433
class SSHCorpSubprocessVendor(SubprocessVendor):
424
434
    """SSH vendor that uses the 'ssh' executable from SSH Corporation."""
425
435
 
 
436
    executable_path = 'ssh'
 
437
 
426
438
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
427
439
                                  command=None):
428
 
        args = ['ssh', '-x']
 
440
        args = [self.executable_path, '-x']
429
441
        if port is not None:
430
442
            args.extend(['-p', str(port)])
431
443
        if username is not None:
436
448
            args.extend([host] + command)
437
449
        return args
438
450
 
439
 
register_ssh_vendor('ssh', SSHCorpSubprocessVendor())
 
451
register_ssh_vendor('sshcorp', SSHCorpSubprocessVendor())
440
452
 
441
453
 
442
454
class PLinkSubprocessVendor(SubprocessVendor):
443
455
    """SSH vendor that uses the 'plink' executable from Putty."""
444
456
 
 
457
    executable_path = 'plink'
 
458
 
445
459
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
446
460
                                  command=None):
447
 
        args = ['plink', '-x', '-a', '-ssh', '-2', '-batch']
 
461
        args = [self.executable_path, '-x', '-a', '-ssh', '-2', '-batch']
448
462
        if port is not None:
449
463
            args.extend(['-P', str(port)])
450
464
        if username is not None:
501
515
    except paramiko.SSHException, e:
502
516
        # Don't know what happened, but just ignore it
503
517
        pass
504
 
    if 'password' not in supported_auth_types:
 
518
    # We treat 'keyboard-interactive' and 'password' auth methods identically,
 
519
    # because Paramiko's auth_password method will automatically try
 
520
    # 'keyboard-interactive' auth (using the password as the response) if
 
521
    # 'password' auth is not available.  Apparently some Debian and Gentoo
 
522
    # OpenSSH servers require this.
 
523
    # XXX: It's possible for a server to require keyboard-interactive auth that
 
524
    # requires something other than a single password, but we currently don't
 
525
    # support that.
 
526
    if ('password' not in supported_auth_types and
 
527
        'keyboard-interactive' not in supported_auth_types):
505
528
        raise errors.ConnectionError('Unable to authenticate to SSH host as'
506
529
            '\n  %s@%s\nsupported auth types: %s'
507
530
            % (username, host, supported_auth_types))
615
638
                'close_fds': True,
616
639
                }
617
640
 
 
641
import weakref
 
642
_subproc_weakrefs = set()
 
643
 
 
644
def _close_ssh_proc(proc):
 
645
    for func in [proc.stdin.close, proc.stdout.close, proc.wait]:
 
646
        try:
 
647
            func()
 
648
        except OSError:
 
649
            pass
 
650
 
618
651
 
619
652
class SSHSubprocess(object):
620
653
    """A socket-like object that talks to an ssh subprocess via pipes."""
621
654
 
622
655
    def __init__(self, proc):
623
656
        self.proc = proc
 
657
        # Add a weakref to proc that will attempt to do the same as self.close
 
658
        # to avoid leaving processes lingering indefinitely.
 
659
        def terminate(ref):
 
660
            _subproc_weakrefs.remove(ref)
 
661
            _close_ssh_proc(proc)
 
662
        _subproc_weakrefs.add(weakref.ref(self, terminate))
624
663
 
625
664
    def send(self, data):
626
665
        return os.write(self.proc.stdin.fileno(), data)
629
668
        return os.read(self.proc.stdout.fileno(), count)
630
669
 
631
670
    def close(self):
632
 
        self.proc.stdin.close()
633
 
        self.proc.stdout.close()
634
 
        self.proc.wait()
 
671
        _close_ssh_proc(self.proc)
635
672
 
636
673
    def get_filelike_channels(self):
637
674
        return (self.proc.stdout, self.proc.stdin)