62
63
def __init__(self, parsed_url):
63
64
duplicity.backend.Backend.__init__(self, parsed_url)
65
# host string could be [user@]hostname
66
if parsed_url.username:
67
username=parsed_url.username
69
username=getpass.getuser()
71
66
if parsed_url.path:
72
67
# remove first leading '/'
73
68
self.remote_dir = re.sub(r'^/', r'', parsed_url.path, 1)
75
70
self.remote_dir = '.'
79
if globals.ssh_askpass:
80
password = self.get_password()
82
if parsed_url.password:
83
password = parsed_url.password
86
72
self.client = paramiko.SSHClient()
87
73
# load known_hosts files
88
74
# paramiko is very picky wrt format and bails out on any problem...
96
82
except Exception, e:
97
83
raise BackendException("could not load ~/.ssh/known_hosts, maybe corrupt?")
99
# alternative ssh private key?
85
""" the next block reorganizes all host parameters into a
86
dictionary like SSHConfig does. this dictionary 'self.config'
87
becomes the authorative source for these values from here on.
88
rationale is that it is easiest to deal wrt overwriting multiple
89
values from ssh_config file. (ede 03/2012)
91
self.config={'hostname':parsed_url.hostname}
92
# get system host config entries
93
self.config.update(self.gethostconfig('/etc/ssh/ssh_config',parsed_url.hostname))
94
# update with user's config file
95
self.config.update(self.gethostconfig('~/.ssh/config',parsed_url.hostname))
96
# update with url values
98
if parsed_url.username:
99
self.config.update({'user':parsed_url.username})
100
## username from input
101
if not 'user' in self.config:
102
self.config.update({'user':getpass.getuser()})
105
self.config.update({'port':parsed_url.port})
106
## ensure there is deafult 22 or an int value
107
if 'port' in self.config:
108
self.config.update({'port':int(self.config['port'])})
110
self.config.update({'port':22})
111
## alternative ssh private key, identity file
101
112
m=re.search("-oidentityfile=(\S+)",globals.ssh_options,re.I)
103
114
keyfilename=m.group(1)
106
portnumber=parsed_url.port
115
self.config['identityfile'] = keyfilename
116
## ensure ~ is expanded and identity exists in dictionary
117
if 'identityfile' in self.config:
118
self.config['identityfile'] = os.path.expanduser(
119
self.config['identityfile'])
121
self.config['identityfile'] = None
123
# get password, enable prompt if askpass is set
124
self.use_getpass = globals.ssh_askpass
125
## set url values for beautiful login prompt
126
parsed_url.username = self.config['user']
127
parsed_url.hostname = self.config['hostname']
128
password = self.get_password()
110
self.client.connect(hostname=parsed_url.hostname, port=portnumber,
111
username=username, password=password,
112
allow_agent=True, look_for_keys=True,
113
key_filename=keyfilename)
131
self.client.connect(hostname=self.config['hostname'],
132
port=self.config['port'],
133
username=self.config['user'],
137
key_filename=self.config['identityfile'])
114
138
except Exception, e:
115
raise BackendException("ssh connection to %s:%d failed: %s" % (parsed_url.hostname,portnumber,e))
139
raise BackendException("ssh connection to %s@%s:%d failed: %s" % (
141
self.config['hostname'],
142
self.config['port'],e))
116
143
self.client.get_transport().set_keepalive((int)(globals.timeout / 2))
279
306
raise BackendException("%sfailed(%d): %s" % (errorprefix,res,chan.recv_stderr(4096)))
309
def gethostconfig(self, file, host):
310
file = os.path.expanduser(file)
311
if not os.path.isfile(file):
314
sshconfig = paramiko.SSHConfig()
316
sshconfig.parse(open(file))
318
raise BackendException("could not load '%s', maybe corrupt?" % (file))
320
return sshconfig.lookup(host)
282
324
duplicity.backend.register_backend("sftp", SftpBackend)
283
325
duplicity.backend.register_backend("scp", SftpBackend)
284
326
duplicity.backend.register_backend("ssh", SftpBackend)