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
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
35
36
TransportNotPossible, NoSuchFile, PathNotChild,
37
LockError, ParamikoNotPresent
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')
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>
73
signal.signal(signal.SIGINT, signal.SIG_IGN)
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
83
# We close fds other than the pipes as the child process does not need
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
92
# <https://launchpad.net/products/bzr/+bug/5987>
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,
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
72
if sys.platform == 'win32':
73
# close_fds not supported on win32
76
108
_ssh_vendor = None
78
109
def _get_ssh_vendor():
79
110
"""Find out what version of SSH is on the system."""
80
111
global _ssh_vendor
93
124
p = subprocess.Popen(['ssh', '-V'],
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()
139
170
args.extend(['-l', user])
140
171
args.extend(['-s', 'sftp', hostname])
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())
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()
225
def clear_connection_cache():
226
"""Remove all hosts from the SFTP connection cache.
228
Primarily useful for test cases wanting to force garbage collection.
230
_connected_hosts.clear()
194
233
def load_host_keys():
470
509
self._translate_io_exception(e, path, ': unable to mkdir',
471
510
failure_exc=FileExists)
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.
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
523
If this parameter is set, it defines the exception
524
to raise in these cases.
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)
502
def append(self, relpath, f):
542
def append(self, relpath, f, mode=None):
504
544
Append the text in the file-like object into the final
978
1020
def get_hexdump(self):
981
1025
server = paramiko.SFTPServer(FakeChannel(), 'sftp', StubServer(self), StubSFTPServer,
982
1026
root=self._root, home=self._server_homedir)