~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/ssh.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2008-03-13 23:45:11 UTC
  • mfrom: (3272.1.1 ianc-integration)
  • Revision ID: pqm@pqm.ubuntu.com-20080313234511-fkj5oa8gm3nrfcro
(Neil Martinsen-Burrell) Explain version-info --custom in the User
        Guide

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2011 Robey Pointer <robey@lag.net>
 
1
# Copyright (C) 2005 Robey Pointer <robey@lag.net>
2
2
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
3
3
#
4
4
# This program is free software; you can redistribute it and/or modify
13
13
#
14
14
# You should have received a copy of the GNU General Public License
15
15
# along with this program; if not, write to the Free Software
16
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
17
 
18
18
"""Foundation SSH support for SFTP and smart server."""
19
19
 
20
20
import errno
21
21
import getpass
22
 
import logging
23
22
import os
24
23
import socket
25
24
import subprocess
94
93
            try:
95
94
                vendor = self._ssh_vendors[vendor_name]
96
95
            except KeyError:
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
 
96
                raise errors.UnknownSSH(vendor_name)
101
97
            return vendor
102
98
        return None
103
99
 
113
109
            stdout = stderr = ''
114
110
        return stdout + stderr
115
111
 
116
 
    def _get_vendor_by_version_string(self, version, progname):
 
112
    def _get_vendor_by_version_string(self, version, args):
117
113
        """Return the vendor or None based on output from the subprocess.
118
114
 
119
115
        :param version: The output of 'ssh -V' like command.
126
122
        elif 'SSH Secure Shell' in version:
127
123
            trace.mutter('ssh implementation is SSH Corp.')
128
124
            vendor = SSHCorpSubprocessVendor()
129
 
        elif 'lsh' in version:
130
 
            trace.mutter('ssh implementation is GNU lsh.')
131
 
            vendor = LSHSubprocessVendor()
132
 
        # As plink user prompts are not handled currently, don't auto-detect
133
 
        # it by inspection below, but keep this vendor detection for if a path
134
 
        # is given in BZR_SSH. See https://bugs.launchpad.net/bugs/414743
135
 
        elif 'plink' in version and progname == 'plink':
 
125
        elif 'plink' in version and args[0] == 'plink':
136
126
            # Checking if "plink" was the executed argument as Windows
137
 
            # sometimes reports 'ssh -V' incorrectly with 'plink' in its
 
127
            # sometimes reports 'ssh -V' incorrectly with 'plink' in it's
138
128
            # version.  See https://bugs.launchpad.net/bzr/+bug/107155
139
129
            trace.mutter("ssh implementation is Putty's plink.")
140
130
            vendor = PLinkSubprocessVendor()
142
132
 
143
133
    def _get_vendor_by_inspection(self):
144
134
        """Return the vendor or None by checking for known SSH implementations."""
145
 
        version = self._get_ssh_version_string(['ssh', '-V'])
146
 
        return self._get_vendor_by_version_string(version, "ssh")
147
 
 
148
 
    def _get_vendor_from_path(self, path):
149
 
        """Return the vendor or None using the program at the given path"""
150
 
        version = self._get_ssh_version_string([path, '-V'])
151
 
        return self._get_vendor_by_version_string(version, 
152
 
            os.path.splitext(os.path.basename(path))[0])
 
135
        for args in (['ssh', '-V'], ['plink', '-V']):
 
136
            version = self._get_ssh_version_string(args)
 
137
            vendor = self._get_vendor_by_version_string(version, args)
 
138
            if vendor is not None:
 
139
                return vendor
 
140
        return None
153
141
 
154
142
    def get_vendor(self, environment=None):
155
143
        """Find out what version of SSH is on the system.
176
164
register_ssh_vendor = _ssh_vendor_manager.register_vendor
177
165
 
178
166
 
179
 
def _ignore_signals():
 
167
def _ignore_sigint():
180
168
    # TODO: This should possibly ignore SIGHUP as well, but bzr currently
181
169
    # doesn't handle it itself.
182
170
    # <https://launchpad.net/products/bzr/+bug/41433/+index>
183
171
    import signal
184
172
    signal.signal(signal.SIGINT, signal.SIG_IGN)
185
 
    # GZ 2010-02-19: Perhaps make this check if breakin is installed instead
186
 
    if signal.getsignal(signal.SIGQUIT) != signal.SIG_DFL:
187
 
        signal.signal(signal.SIGQUIT, signal.SIG_IGN)
188
 
 
189
 
 
190
 
class SocketAsChannelAdapter(object):
 
173
 
 
174
 
 
175
class LoopbackSFTP(object):
191
176
    """Simple wrapper for a socket that pretends to be a paramiko Channel."""
192
177
 
193
178
    def __init__(self, sock):
194
179
        self.__socket = sock
195
180
 
196
 
    def get_name(self):
197
 
        return "bzr SocketAsChannelAdapter"
198
 
 
199
181
    def send(self, data):
200
182
        return self.__socket.send(data)
201
183
 
202
184
    def recv(self, n):
203
 
        try:
204
 
            return self.__socket.recv(n)
205
 
        except socket.error, e:
206
 
            if e.args[0] in (errno.EPIPE, errno.ECONNRESET, errno.ECONNABORTED,
207
 
                             errno.EBADF):
208
 
                # Connection has closed.  Paramiko expects an empty string in
209
 
                # this case, not an exception.
210
 
                return ''
211
 
            raise
 
185
        return self.__socket.recv(n)
212
186
 
213
187
    def recv_ready(self):
214
 
        # TODO: jam 20051215 this function is necessary to support the
215
 
        # pipelined() function. In reality, it probably should use
216
 
        # poll() or select() to actually return if there is data
217
 
        # available, otherwise we probably don't get any benefit
218
188
        return True
219
189
 
220
190
    def close(self):
226
196
 
227
197
    def connect_sftp(self, username, password, host, port):
228
198
        """Make an SSH connection, and return an SFTPClient.
229
 
 
 
199
        
230
200
        :param username: an ascii string
231
201
        :param password: an ascii string
232
202
        :param host: a host name as an ascii string
241
211
 
242
212
    def connect_ssh(self, username, password, host, port, command):
243
213
        """Make an SSH connection.
244
 
 
245
 
        :returns: an SSHConnection.
 
214
        
 
215
        :returns: something with a `close` method, and a `get_filelike_channels`
 
216
            method that returns a pair of (read, write) filelike objects.
246
217
        """
247
218
        raise NotImplementedError(self.connect_ssh)
248
219
 
266
237
            sock.connect((host, port))
267
238
        except socket.error, e:
268
239
            self._raise_connection_error(host, port=port, orig_error=e)
269
 
        return SFTPClient(SocketAsChannelAdapter(sock))
 
240
        return SFTPClient(LoopbackSFTP(sock))
270
241
 
271
242
register_ssh_vendor('loopback', LoopbackVendor())
272
243
 
273
244
 
 
245
class _ParamikoSSHConnection(object):
 
246
    def __init__(self, channel):
 
247
        self.channel = channel
 
248
 
 
249
    def get_filelike_channels(self):
 
250
        return self.channel.makefile('rb'), self.channel.makefile('wb')
 
251
 
 
252
    def close(self):
 
253
        return self.channel.close()
 
254
 
 
255
 
274
256
class ParamikoVendor(SSHVendor):
275
257
    """Vendor that uses paramiko."""
276
258
 
339
321
            self._raise_connection_error(host, port=port, orig_error=e,
340
322
                                         msg='Unable to invoke remote bzr')
341
323
 
342
 
_ssh_connection_errors = (EOFError, OSError, IOError, socket.error)
343
324
if paramiko is not None:
344
325
    vendor = ParamikoVendor()
345
326
    register_ssh_vendor('paramiko', vendor)
346
327
    register_ssh_vendor('none', vendor)
347
328
    register_default_ssh_vendor(vendor)
348
 
    _ssh_connection_errors += (paramiko.SSHException,)
 
329
    _sftp_connection_errors = (EOFError, paramiko.SSHException)
349
330
    del vendor
 
331
else:
 
332
    _sftp_connection_errors = (EOFError,)
350
333
 
351
334
 
352
335
class SubprocessVendor(SSHVendor):
353
336
    """Abstract base class for vendors that use pipes to a subprocess."""
354
337
 
355
338
    def _connect(self, argv):
356
 
        # Attempt to make a socketpair to use as stdin/stdout for the SSH
357
 
        # subprocess.  We prefer sockets to pipes because they support
358
 
        # non-blocking short reads, allowing us to optimistically read 64k (or
359
 
        # whatever) chunks.
360
 
        try:
361
 
            my_sock, subproc_sock = socket.socketpair()
362
 
            osutils.set_fd_cloexec(my_sock)
363
 
        except (AttributeError, socket.error):
364
 
            # This platform doesn't support socketpair(), so just use ordinary
365
 
            # pipes instead.
366
 
            stdin = stdout = subprocess.PIPE
367
 
            my_sock, subproc_sock = None, None
368
 
        else:
369
 
            stdin = stdout = subproc_sock
370
 
        proc = subprocess.Popen(argv, stdin=stdin, stdout=stdout,
 
339
        proc = subprocess.Popen(argv,
 
340
                                stdin=subprocess.PIPE,
 
341
                                stdout=subprocess.PIPE,
371
342
                                **os_specific_subprocess_params())
372
 
        if subproc_sock is not None:
373
 
            subproc_sock.close()
374
 
        return SSHSubprocessConnection(proc, sock=my_sock)
 
343
        return SSHSubprocess(proc)
375
344
 
376
345
    def connect_sftp(self, username, password, host, port):
377
346
        try:
378
347
            argv = self._get_vendor_specific_argv(username, host, port,
379
348
                                                  subsystem='sftp')
380
349
            sock = self._connect(argv)
381
 
            return SFTPClient(SocketAsChannelAdapter(sock))
382
 
        except _ssh_connection_errors, e:
 
350
            return SFTPClient(sock)
 
351
        except _sftp_connection_errors, e:
 
352
            self._raise_connection_error(host, port=port, orig_error=e)
 
353
        except (OSError, IOError), e:
 
354
            # If the machine is fast enough, ssh can actually exit
 
355
            # before we try and send it the sftp request, which
 
356
            # raises a Broken Pipe
 
357
            if e.errno not in (errno.EPIPE,):
 
358
                raise
383
359
            self._raise_connection_error(host, port=port, orig_error=e)
384
360
 
385
361
    def connect_ssh(self, username, password, host, port, command):
387
363
            argv = self._get_vendor_specific_argv(username, host, port,
388
364
                                                  command=command)
389
365
            return self._connect(argv)
390
 
        except _ssh_connection_errors, e:
 
366
        except (EOFError), e:
 
367
            self._raise_connection_error(host, port=port, orig_error=e)
 
368
        except (OSError, IOError), e:
 
369
            # If the machine is fast enough, ssh can actually exit
 
370
            # before we try and send it the sftp request, which
 
371
            # raises a Broken Pipe
 
372
            if e.errno not in (errno.EPIPE,):
 
373
                raise
391
374
            self._raise_connection_error(host, port=port, orig_error=e)
392
375
 
393
376
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
394
377
                                  command=None):
395
378
        """Returns the argument list to run the subprocess with.
396
 
 
 
379
        
397
380
        Exactly one of 'subsystem' and 'command' must be specified.
398
381
        """
399
382
        raise NotImplementedError(self._get_vendor_specific_argv)
402
385
class OpenSSHSubprocessVendor(SubprocessVendor):
403
386
    """SSH vendor that uses the 'ssh' executable from OpenSSH."""
404
387
 
405
 
    executable_path = 'ssh'
406
 
 
407
388
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
408
389
                                  command=None):
409
 
        args = [self.executable_path,
 
390
        assert subsystem is not None or command is not None, (
 
391
            'Must specify a command or subsystem')
 
392
        if subsystem is not None:
 
393
            assert command is None, (
 
394
                'subsystem and command are mutually exclusive')
 
395
        args = ['ssh',
410
396
                '-oForwardX11=no', '-oForwardAgent=no',
411
 
                '-oClearAllForwardings=yes',
 
397
                '-oClearAllForwardings=yes', '-oProtocol=2',
412
398
                '-oNoHostAuthenticationForLocalhost=yes']
413
399
        if port is not None:
414
400
            args.extend(['-p', str(port)])
426
412
class SSHCorpSubprocessVendor(SubprocessVendor):
427
413
    """SSH vendor that uses the 'ssh' executable from SSH Corporation."""
428
414
 
429
 
    executable_path = 'ssh'
430
 
 
431
415
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
432
416
                                  command=None):
433
 
        args = [self.executable_path, '-x']
 
417
        assert subsystem is not None or command is not None, (
 
418
            'Must specify a command or subsystem')
 
419
        if subsystem is not None:
 
420
            assert command is None, (
 
421
                'subsystem and command are mutually exclusive')
 
422
        args = ['ssh', '-x']
434
423
        if port is not None:
435
424
            args.extend(['-p', str(port)])
436
425
        if username is not None:
441
430
            args.extend([host] + command)
442
431
        return args
443
432
 
444
 
register_ssh_vendor('sshcorp', SSHCorpSubprocessVendor())
445
 
 
446
 
 
447
 
class LSHSubprocessVendor(SubprocessVendor):
448
 
    """SSH vendor that uses the 'lsh' executable from GNU"""
449
 
 
450
 
    executable_path = 'lsh'
451
 
 
452
 
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
453
 
                                  command=None):
454
 
        args = [self.executable_path]
455
 
        if port is not None:
456
 
            args.extend(['-p', str(port)])
457
 
        if username is not None:
458
 
            args.extend(['-l', username])
459
 
        if subsystem is not None:
460
 
            args.extend(['--subsystem', subsystem, host])
461
 
        else:
462
 
            args.extend([host] + command)
463
 
        return args
464
 
 
465
 
register_ssh_vendor('lsh', LSHSubprocessVendor())
 
433
register_ssh_vendor('ssh', SSHCorpSubprocessVendor())
466
434
 
467
435
 
468
436
class PLinkSubprocessVendor(SubprocessVendor):
469
437
    """SSH vendor that uses the 'plink' executable from Putty."""
470
438
 
471
 
    executable_path = 'plink'
472
 
 
473
439
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
474
440
                                  command=None):
475
 
        args = [self.executable_path, '-x', '-a', '-ssh', '-2', '-batch']
 
441
        assert subsystem is not None or command is not None, (
 
442
            'Must specify a command or subsystem')
 
443
        if subsystem is not None:
 
444
            assert command is None, (
 
445
                'subsystem and command are mutually exclusive')
 
446
        args = ['plink', '-x', '-a', '-ssh', '-2', '-batch']
476
447
        if port is not None:
477
448
            args.extend(['-P', str(port)])
478
449
        if username is not None:
487
458
 
488
459
 
489
460
def _paramiko_auth(username, password, host, port, paramiko_transport):
 
461
    # paramiko requires a username, but it might be none if nothing was supplied
 
462
    # use the local username, just in case.
 
463
    # We don't override username, because if we aren't using paramiko,
 
464
    # the username might be specified in ~/.ssh/config and we don't want to
 
465
    # force it to something else
 
466
    # Also, it would mess up the self.relpath() functionality
490
467
    auth = config.AuthenticationConfig()
491
 
    # paramiko requires a username, but it might be none if nothing was
492
 
    # supplied.  If so, use the local username.
493
468
    if username is None:
494
 
        username = auth.get_user('ssh', host, port=port,
495
 
                                 default=getpass.getuser())
 
469
        username = auth.get_user('ssh', host, port=port)
 
470
        if username is None:
 
471
            # Default to local user
 
472
            username = getpass.getuser()
 
473
 
496
474
    if _use_ssh_agent:
497
475
        agent = paramiko.Agent()
498
476
        for key in agent.get_keys():
510
488
    if _try_pkey_auth(paramiko_transport, paramiko.DSSKey, username, 'id_dsa'):
511
489
        return
512
490
 
513
 
    # If we have gotten this far, we are about to try for passwords, do an
514
 
    # auth_none check to see if it is even supported.
515
 
    supported_auth_types = []
516
 
    try:
517
 
        # Note that with paramiko <1.7.5 this logs an INFO message:
518
 
        #    Authentication type (none) not permitted.
519
 
        # So we explicitly disable the logging level for this action
520
 
        old_level = paramiko_transport.logger.level
521
 
        paramiko_transport.logger.setLevel(logging.WARNING)
522
 
        try:
523
 
            paramiko_transport.auth_none(username)
524
 
        finally:
525
 
            paramiko_transport.logger.setLevel(old_level)
526
 
    except paramiko.BadAuthenticationType, e:
527
 
        # Supported methods are in the exception
528
 
        supported_auth_types = e.allowed_types
529
 
    except paramiko.SSHException, e:
530
 
        # Don't know what happened, but just ignore it
531
 
        pass
532
 
    # We treat 'keyboard-interactive' and 'password' auth methods identically,
533
 
    # because Paramiko's auth_password method will automatically try
534
 
    # 'keyboard-interactive' auth (using the password as the response) if
535
 
    # 'password' auth is not available.  Apparently some Debian and Gentoo
536
 
    # OpenSSH servers require this.
537
 
    # XXX: It's possible for a server to require keyboard-interactive auth that
538
 
    # requires something other than a single password, but we currently don't
539
 
    # support that.
540
 
    if ('password' not in supported_auth_types and
541
 
        'keyboard-interactive' not in supported_auth_types):
542
 
        raise errors.ConnectionError('Unable to authenticate to SSH host as'
543
 
            '\n  %s@%s\nsupported auth types: %s'
544
 
            % (username, host, supported_auth_types))
545
 
 
546
491
    if password:
547
492
        try:
548
493
            paramiko_transport.auth_password(username, password)
552
497
 
553
498
    # give up and ask for a password
554
499
    password = auth.get_password('ssh', host, username, port=port)
555
 
    # get_password can still return None, which means we should not prompt
556
 
    if password is not None:
557
 
        try:
558
 
            paramiko_transport.auth_password(username, password)
559
 
        except paramiko.SSHException, e:
560
 
            raise errors.ConnectionError(
561
 
                'Unable to authenticate to SSH host as'
562
 
                '\n  %s@%s\n' % (username, host), e)
563
 
    else:
564
 
        raise errors.ConnectionError('Unable to authenticate to SSH host as'
565
 
                                     '  %s@%s' % (username, host))
 
500
    try:
 
501
        paramiko_transport.auth_password(username, password)
 
502
    except paramiko.SSHException, e:
 
503
        raise errors.ConnectionError(
 
504
            'Unable to authenticate to SSH host as %s@%s' % (username, host), e)
566
505
 
567
506
 
568
507
def _try_pkey_auth(paramiko_transport, pkey_class, username, filename):
573
512
        return True
574
513
    except paramiko.PasswordRequiredException:
575
514
        password = ui.ui_factory.get_password(
576
 
            prompt=u'SSH %(filename)s password',
577
 
            filename=filename.decode(osutils._fs_enc))
 
515
            prompt='SSH %(filename)s password', filename=filename)
578
516
        try:
579
517
            key = pkey_class.from_private_key_file(filename, password)
580
518
            paramiko_transport.auth_publickey(username, key)
631
569
def os_specific_subprocess_params():
632
570
    """Get O/S specific subprocess parameters."""
633
571
    if sys.platform == 'win32':
634
 
        # setting the process group and closing fds is not supported on
 
572
        # setting the process group and closing fds is not supported on 
635
573
        # win32
636
574
        return {}
637
575
    else:
638
 
        # We close fds other than the pipes as the child process does not need
 
576
        # We close fds other than the pipes as the child process does not need 
639
577
        # them to be open.
640
578
        #
641
579
        # We also set the child process to ignore SIGINT.  Normally the signal
643
581
        # this causes it to be seen only by bzr and not by ssh.  Python will
644
582
        # generate a KeyboardInterrupt in bzr, and we will then have a chance
645
583
        # to release locks or do other cleanup over ssh before the connection
646
 
        # goes away.
 
584
        # goes away.  
647
585
        # <https://launchpad.net/products/bzr/+bug/5987>
648
586
        #
649
587
        # Running it in a separate process group is not good because then it
650
588
        # can't get non-echoed input of a password or passphrase.
651
589
        # <https://launchpad.net/products/bzr/+bug/40508>
652
 
        return {'preexec_fn': _ignore_signals,
 
590
        return {'preexec_fn': _ignore_sigint,
653
591
                'close_fds': True,
654
592
                }
655
593
 
656
 
import weakref
657
 
_subproc_weakrefs = set()
658
 
 
659
 
def _close_ssh_proc(proc, sock):
660
 
    """Carefully close stdin/stdout and reap the SSH process.
661
 
 
662
 
    If the pipes are already closed and/or the process has already been
663
 
    wait()ed on, that's ok, and no error is raised.  The goal is to do our best
664
 
    to clean up (whether or not a clean up was already tried).
665
 
    """
666
 
    funcs = []
667
 
    for closeable in (proc.stdin, proc.stdout, sock):
668
 
        # We expect that either proc (a subprocess.Popen) will have stdin and
669
 
        # stdout streams to close, or that we will have been passed a socket to
670
 
        # close, with the option not in use being None.
671
 
        if closeable is not None:
672
 
            funcs.append(closeable.close)
673
 
    funcs.append(proc.wait)
674
 
    for func in funcs:
675
 
        try:
676
 
            func()
677
 
        except OSError:
678
 
            # It's ok for the pipe to already be closed, or the process to
679
 
            # already be finished.
680
 
            continue
681
 
 
682
 
 
683
 
class SSHConnection(object):
684
 
    """Abstract base class for SSH connections."""
685
 
 
686
 
    def get_sock_or_pipes(self):
687
 
        """Returns a (kind, io_object) pair.
688
 
 
689
 
        If kind == 'socket', then io_object is a socket.
690
 
 
691
 
        If kind == 'pipes', then io_object is a pair of file-like objects
692
 
        (read_from, write_to).
693
 
        """
694
 
        raise NotImplementedError(self.get_sock_or_pipes)
695
 
 
696
 
    def close(self):
697
 
        raise NotImplementedError(self.close)
698
 
 
699
 
 
700
 
class SSHSubprocessConnection(SSHConnection):
701
 
    """A connection to an ssh subprocess via pipes or a socket.
702
 
 
703
 
    This class is also socket-like enough to be used with
704
 
    SocketAsChannelAdapter (it has 'send' and 'recv' methods).
705
 
    """
706
 
 
707
 
    def __init__(self, proc, sock=None):
708
 
        """Constructor.
709
 
 
710
 
        :param proc: a subprocess.Popen
711
 
        :param sock: if proc.stdin/out is a socket from a socketpair, then sock
712
 
            should bzrlib's half of that socketpair.  If not passed, proc's
713
 
            stdin/out is assumed to be ordinary pipes.
714
 
        """
 
594
 
 
595
class SSHSubprocess(object):
 
596
    """A socket-like object that talks to an ssh subprocess via pipes."""
 
597
 
 
598
    def __init__(self, proc):
715
599
        self.proc = proc
716
 
        self._sock = sock
717
 
        # Add a weakref to proc that will attempt to do the same as self.close
718
 
        # to avoid leaving processes lingering indefinitely.
719
 
        def terminate(ref):
720
 
            _subproc_weakrefs.remove(ref)
721
 
            _close_ssh_proc(proc, sock)
722
 
        _subproc_weakrefs.add(weakref.ref(self, terminate))
723
600
 
724
601
    def send(self, data):
725
 
        if self._sock is not None:
726
 
            return self._sock.send(data)
727
 
        else:
728
 
            return os.write(self.proc.stdin.fileno(), data)
 
602
        return os.write(self.proc.stdin.fileno(), data)
 
603
 
 
604
    def recv_ready(self):
 
605
        # TODO: jam 20051215 this function is necessary to support the
 
606
        # pipelined() function. In reality, it probably should use
 
607
        # poll() or select() to actually return if there is data
 
608
        # available, otherwise we probably don't get any benefit
 
609
        return True
729
610
 
730
611
    def recv(self, count):
731
 
        if self._sock is not None:
732
 
            return self._sock.recv(count)
733
 
        else:
734
 
            return os.read(self.proc.stdout.fileno(), count)
735
 
 
736
 
    def close(self):
737
 
        _close_ssh_proc(self.proc, self._sock)
738
 
 
739
 
    def get_sock_or_pipes(self):
740
 
        if self._sock is not None:
741
 
            return 'socket', self._sock
742
 
        else:
743
 
            return 'pipes', (self.proc.stdout, self.proc.stdin)
744
 
 
745
 
 
746
 
class _ParamikoSSHConnection(SSHConnection):
747
 
    """An SSH connection via paramiko."""
748
 
 
749
 
    def __init__(self, channel):
750
 
        self.channel = channel
751
 
 
752
 
    def get_sock_or_pipes(self):
753
 
        return ('socket', self.channel)
754
 
 
755
 
    def close(self):
756
 
        return self.channel.close()
757
 
 
 
612
        return os.read(self.proc.stdout.fileno(), count)
 
613
 
 
614
    def close(self):
 
615
        self.proc.stdin.close()
 
616
        self.proc.stdout.close()
 
617
        self.proc.wait()
 
618
 
 
619
    def get_filelike_channels(self):
 
620
        return (self.proc.stdout, self.proc.stdin)
758
621