1
# Copyright (C) 2005 Robey Pointer <robey@lag.net>
2
# Copyright (C) 2005, 2006 Canonical Ltd
1
# Copyright (C) 2005 Robey Pointer <robey@lag.net>, Canonical Ltd
4
3
# This program is free software; you can redistribute it and/or modify
5
4
# it under the terms of the GNU General Public License as published by
36
35
TransportNotPossible, NoSuchFile, PathNotChild,
37
LockError, ParamikoNotPresent
42
39
from bzrlib.osutils import pathjoin, fancy_rename
43
40
from bzrlib.trace import mutter, warning, error
44
from bzrlib.transport import (
45
register_urlparse_netloc_protocol,
41
from bzrlib.transport import Transport, Server, urlescape
61
53
from paramiko.sftp_file import SFTPFile
62
54
from paramiko.sftp_client import SFTPClient
65
register_urlparse_netloc_protocol('sftp')
68
def os_specific_subprocess_params():
69
"""Get O/S specific subprocess parameters."""
70
if sys.platform == 'win32':
71
# setting the process group and closing fds is not supported on
75
# we close fds as the child process does not need them to be open.
76
# we set the process group so that signals from the keyboard like
77
# 'SIGINT' - KeyboardInterrupt - are not recieved in the child procecss
78
# if we do not do this, then the sftp/ssh subprocesses will terminate
79
# when a user hits CTRL-C, and we are unable to use them to unlock the
80
# remote branch/repository etc.
81
return {'preexec_fn': os.setpgrp,
56
if 'sftp' not in urlparse.uses_netloc:
57
urlparse.uses_netloc.append('sftp')
86
59
# don't use prefetch unless paramiko version >= 1.5.2 (there were bugs earlier)
87
60
_default_do_prefetch = False
88
if getattr(paramiko, '__version_info__', (0, 0, 0)) >= (1, 5, 5):
61
if getattr(paramiko, '__version_info__', (0, 0, 0)) >= (1, 5, 2):
89
62
_default_do_prefetch = True
66
if sys.platform == 'win32':
67
# close_fds not supported on win32
93
72
def _get_ssh_vendor():
94
73
"""Find out what version of SSH is on the system."""
108
87
p = subprocess.Popen(['ssh', '-V'],
109
89
stdin=subprocess.PIPE,
110
90
stdout=subprocess.PIPE,
111
stderr=subprocess.PIPE,
112
**os_specific_subprocess_params())
91
stderr=subprocess.PIPE)
113
92
returncode = p.returncode
114
93
stdout, stderr = p.communicate()
154
133
args.extend(['-l', user])
155
134
args.extend(['-s', 'sftp', hostname])
157
self.proc = subprocess.Popen(args,
136
self.proc = subprocess.Popen(args, close_fds=_close_fds,
158
137
stdin=subprocess.PIPE,
159
stdout=subprocess.PIPE,
160
**os_specific_subprocess_params())
138
stdout=subprocess.PIPE)
162
140
def send(self, data):
163
141
return os.write(self.proc.stdin.fileno(), data)
206
184
# X seconds. But that requires a lot more fanciness.
207
185
_connected_hosts = weakref.WeakValueDictionary()
209
def clear_connection_cache():
210
"""Remove all hosts from the SFTP connection cache.
212
Primarily useful for test cases wanting to force garbage collection.
214
_connected_hosts.clear()
217
188
def load_host_keys():
493
464
self._translate_io_exception(e, path, ': unable to mkdir',
494
465
failure_exc=FileExists)
496
def _translate_io_exception(self, e, path, more_info='',
497
failure_exc=PathError):
467
def _translate_io_exception(self, e, path, more_info='', failure_exc=NoSuchFile):
498
468
"""Translate a paramiko or IOError into a friendlier exception.
500
470
:param e: The original exception
504
474
:param failure_exc: Paramiko has the super fun ability to raise completely
505
475
opaque errors that just set "e.args = ('Failure',)" with
506
476
no more information.
507
If this parameter is set, it defines the exception
508
to raise in these cases.
477
This sometimes means FileExists, but it also sometimes
510
480
# paramiko seems to generate detailless errors.
511
481
self._translate_error(e, path, raise_generic=False)
523
493
mutter('Raising exception with errno %s', e.errno)
526
def append(self, relpath, f, mode=None):
496
def append(self, relpath, f):
528
498
Append the text in the file-like object into the final
643
611
netloc = '%s@%s' % (urllib.quote(self._username), netloc)
644
612
if self._port is not None:
645
613
netloc = '%s:%d' % (netloc, self._port)
646
615
return urlparse.urlunparse(('sftp', netloc, path, '', '', ''))
648
617
def _split_url(self, url):