/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/transport/sftp.py

  • Committer: Aaron Bentley
  • Date: 2006-05-20 17:51:13 UTC
  • mfrom: (1718 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1727.
  • Revision ID: aaron.bentley@utoronto.ca-20060520175113-4549e0023f9210bf
Merge from bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 Robey Pointer <robey@lag.net>, Canonical Ltd
 
1
# Copyright (C) 2005 Robey Pointer <robey@lag.net>
 
2
# Copyright (C) 2005, 2006 Canonical Ltd
2
3
 
3
4
# This program is free software; you can redistribute it and/or modify
4
5
# it under the terms of the GNU General Public License as published by
34
35
                           FileExists, 
35
36
                           TransportNotPossible, NoSuchFile, PathNotChild,
36
37
                           TransportError,
37
 
                           LockError, ParamikoNotPresent
 
38
                           LockError, 
 
39
                           PathError,
 
40
                           ParamikoNotPresent,
38
41
                           )
39
42
from bzrlib.osutils import pathjoin, fancy_rename
40
43
from bzrlib.trace import mutter, warning, error
62
65
register_urlparse_netloc_protocol('sftp')
63
66
 
64
67
 
 
68
def _ignore_sigint():
 
69
    # TODO: This should possibly ignore SIGHUP as well, but bzr currently
 
70
    # doesn't handle it itself.
 
71
    # <https://launchpad.net/products/bzr/+bug/41433/+index>
 
72
    import signal
 
73
    signal.signal(signal.SIGINT, signal.SIG_IGN)
 
74
    
 
75
 
 
76
def os_specific_subprocess_params():
 
77
    """Get O/S specific subprocess parameters."""
 
78
    if sys.platform == 'win32':
 
79
        # setting the process group and closing fds is not supported on 
 
80
        # win32
 
81
        return {}
 
82
    else:
 
83
        # We close fds other than the pipes as the child process does not need 
 
84
        # them to be open.
 
85
        #
 
86
        # We also set the child process to ignore SIGINT.  Normally the signal
 
87
        # would be sent to every process in the foreground process group, but
 
88
        # this causes it to be seen only by bzr and not by ssh.  Python will
 
89
        # generate a KeyboardInterrupt in bzr, and we will then have a chance
 
90
        # to release locks or do other cleanup over ssh before the connection
 
91
        # goes away.  
 
92
        # <https://launchpad.net/products/bzr/+bug/5987>
 
93
        #
 
94
        # Running it in a separate process group is not good because then it
 
95
        # can't get non-echoed input of a password or passphrase.
 
96
        # <https://launchpad.net/products/bzr/+bug/40508>
 
97
        return {'preexec_fn': _ignore_sigint,
 
98
                'close_fds': True,
 
99
                }
 
100
 
 
101
 
65
102
# don't use prefetch unless paramiko version >= 1.5.2 (there were bugs earlier)
66
103
_default_do_prefetch = False
67
 
if getattr(paramiko, '__version_info__', (0, 0, 0)) >= (1, 5, 2):
 
104
if getattr(paramiko, '__version_info__', (0, 0, 0)) >= (1, 5, 5):
68
105
    _default_do_prefetch = True
69
106
 
70
107
 
71
 
_close_fds = True
72
 
if sys.platform == 'win32':
73
 
    # close_fds not supported on win32
74
 
    _close_fds = False
75
 
 
76
108
_ssh_vendor = None
77
 
 
78
109
def _get_ssh_vendor():
79
110
    """Find out what version of SSH is on the system."""
80
111
    global _ssh_vendor
91
122
 
92
123
    try:
93
124
        p = subprocess.Popen(['ssh', '-V'],
94
 
                             close_fds=_close_fds,
95
125
                             stdin=subprocess.PIPE,
96
126
                             stdout=subprocess.PIPE,
97
 
                             stderr=subprocess.PIPE)
 
127
                             stderr=subprocess.PIPE,
 
128
                             **os_specific_subprocess_params())
98
129
        returncode = p.returncode
99
130
        stdout, stderr = p.communicate()
100
131
    except OSError:
139
170
                args.extend(['-l', user])
140
171
            args.extend(['-s', 'sftp', hostname])
141
172
 
142
 
        self.proc = subprocess.Popen(args, close_fds=_close_fds,
 
173
        self.proc = subprocess.Popen(args,
143
174
                                     stdin=subprocess.PIPE,
144
 
                                     stdout=subprocess.PIPE)
 
175
                                     stdout=subprocess.PIPE,
 
176
                                     **os_specific_subprocess_params())
145
177
 
146
178
    def send(self, data):
147
179
        return os.write(self.proc.stdin.fileno(), data)
190
222
# X seconds. But that requires a lot more fanciness.
191
223
_connected_hosts = weakref.WeakValueDictionary()
192
224
 
 
225
def clear_connection_cache():
 
226
    """Remove all hosts from the SFTP connection cache.
 
227
 
 
228
    Primarily useful for test cases wanting to force garbage collection.
 
229
    """
 
230
    _connected_hosts.clear()
 
231
 
193
232
 
194
233
def load_host_keys():
195
234
    """
470
509
            self._translate_io_exception(e, path, ': unable to mkdir',
471
510
                failure_exc=FileExists)
472
511
 
473
 
    def _translate_io_exception(self, e, path, more_info='', failure_exc=NoSuchFile):
 
512
    def _translate_io_exception(self, e, path, more_info='', 
 
513
                                failure_exc=PathError):
474
514
        """Translate a paramiko or IOError into a friendlier exception.
475
515
 
476
516
        :param e: The original exception
480
520
        :param failure_exc: Paramiko has the super fun ability to raise completely
481
521
                           opaque errors that just set "e.args = ('Failure',)" with
482
522
                           no more information.
483
 
                           This sometimes means FileExists, but it also sometimes
484
 
                           means NoSuchFile
 
523
                           If this parameter is set, it defines the exception 
 
524
                           to raise in these cases.
485
525
        """
486
526
        # paramiko seems to generate detailless errors.
487
527
        self._translate_error(e, path, raise_generic=False)
499
539
            mutter('Raising exception with errno %s', e.errno)
500
540
        raise e
501
541
 
502
 
    def append(self, relpath, f):
 
542
    def append(self, relpath, f, mode=None):
503
543
        """
504
544
        Append the text in the file-like object into the final
505
545
        location.
507
547
        try:
508
548
            path = self._remote_path(relpath)
509
549
            fout = self._sftp.file(path, 'ab')
 
550
            if mode is not None:
 
551
                self._sftp.chmod(path, mode)
510
552
            result = fout.tell()
511
553
            self._pump(f, fout)
512
554
            return result
977
1019
                return '1'
978
1020
            def get_hexdump(self):
979
1021
                return False
 
1022
            def close(self):
 
1023
                pass
980
1024
 
981
1025
        server = paramiko.SFTPServer(FakeChannel(), 'sftp', StubServer(self), StubSFTPServer,
982
1026
                                     root=self._root, home=self._server_homedir)