1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 import re
28 import string
29 import time
30 import os
31
32 import duplicity.backend
33 from duplicity import globals
34 from duplicity import log
35 from duplicity import pexpect
36 from duplicity.errors import *
37
39 """This backend copies files using scp. List not supported"""
73
75 """ Run an scp command, responding to password prompts """
76 for n in range(1, globals.num_retries+1):
77 if n > 1:
78
79 time.sleep(30)
80 log.Info("Running '%s' (attempt #%d)" % (commandline, n))
81 child = pexpect.spawn(commandline, timeout = None)
82 if globals.ssh_askpass:
83 state = "authorizing"
84 else:
85 state = "copying"
86 while 1:
87 if state == "authorizing":
88 match = child.expect([pexpect.EOF,
89 "(?i)timeout, server not responding",
90 "(?i)pass(word|phrase .*):",
91 "(?i)permission denied",
92 "authenticity"])
93 log.Debug("State = %s, Before = '%s'" % (state, child.before.strip()))
94 if match == 0:
95 log.Warn("Failed to authenticate")
96 break
97 elif match == 1:
98 log.Warn("Timeout waiting to authenticate")
99 break
100 elif match == 2:
101 child.sendline(self.password)
102 state = "copying"
103 elif match == 3:
104 log.Warn("Invalid SSH password")
105 break
106 elif match == 4:
107 log.Warn("Remote host authentication failed (missing known_hosts entry?)")
108 break
109 elif state == "copying":
110 match = child.expect([pexpect.EOF,
111 "(?i)timeout, server not responding",
112 "stalled",
113 "authenticity",
114 "ETA"])
115 log.Debug("State = %s, Before = '%s'" % (state, child.before.strip()))
116 if match == 0:
117 break
118 elif match == 1:
119 log.Warn("Timeout waiting for response")
120 break
121 elif match == 2:
122 state = "stalled"
123 elif match == 3:
124 log.Warn("Remote host authentication failed (missing known_hosts entry?)")
125 break
126 elif state == "stalled":
127 match = child.expect([pexpect.EOF,
128 "(?i)timeout, server not responding",
129 "ETA"])
130 log.Debug("State = %s, Before = '%s'" % (state, child.before.strip()))
131 if match == 0:
132 break
133 elif match == 1:
134 log.Warn("Stalled for too long, aborted copy")
135 break
136 elif match == 2:
137 state = "copying"
138 child.close(force = True)
139 if child.exitstatus == 0:
140 return
141 log.Warn("Running '%s' failed (attempt #%d)" % (commandline, n))
142 log.Warn("Giving up trying to execute '%s' after %d attempts" % (commandline, globals.num_retries))
143 raise BackendException("Error running '%s'" % commandline)
144
146 """ Run an sftp command, responding to password prompts, passing commands from list """
147 maxread = 2000
148 responses = [pexpect.EOF,
149 "(?i)timeout, server not responding",
150 "sftp>",
151 "(?i)pass(word|phrase .*):",
152 "(?i)permission denied",
153 "authenticity",
154 "(?i)no such file or directory",
155 "Couldn't delete file: No such file or directory",
156 "Couldn't delete file",
157 "open(.*): Failure"]
158 max_response_len = max([len(p) for p in responses[1:]])
159 for n in range(1, globals.num_retries+1):
160 if n > 1:
161
162 time.sleep(30)
163 log.Info("Running '%s' (attempt #%d)" % (commandline, n))
164 child = pexpect.spawn(commandline, timeout = None, maxread=maxread)
165 cmdloc = 0
166 while 1:
167 match = child.expect(responses,
168 searchwindowsize=maxread+max_response_len)
169 log.Debug("State = sftp, Before = '%s'" % (child.before.strip()))
170 if match == 0:
171 break
172 elif match == 1:
173 log.Info("Timeout waiting for response")
174 break
175 if match == 2:
176 if cmdloc < len(commands):
177 command = commands[cmdloc]
178 log.Info("sftp command: '%s'" % (command,))
179 child.sendline(command)
180 cmdloc += 1
181 else:
182 command = 'quit'
183 child.sendline(command)
184 res = child.before
185 elif match == 3:
186 child.sendline(self.password)
187 elif match == 4:
188 if not child.before.strip().startswith("mkdir"):
189 log.Warn("Invalid SSH password")
190 break
191 elif match == 5:
192 log.Warn("Host key authenticity could not be verified (missing known_hosts entry?)")
193 break
194 elif match == 6:
195 if not child.before.strip().startswith("rm"):
196 log.Warn("Remote file or directory does not exist in command='%s'" % (commandline,))
197 break
198 elif match == 7:
199 if not child.before.strip().startswith("Removing"):
200 log.Warn("Could not delete file in command='%s'" % (commandline,))
201 break;
202 elif match == 8:
203 log.Warn("Could not delete file in command='%s'" % (commandline,))
204 break
205 elif match == 9:
206 log.Warn("Could not open file in command='%s'" % (commandline,))
207 break
208 child.close(force = True)
209 if child.exitstatus == 0:
210 return res
211 log.Warn("Running '%s' failed (attempt #%d)" % (commandline, n))
212 log.Warn("Giving up trying to execute '%s' after %d attempts" % (commandline, globals.num_retries))
213 raise BackendException("Error running '%s'" % commandline)
214
215 - def put(self, source_path, remote_filename = None):
216 if globals.use_scp:
217 self.put_scp(source_path, remote_filename = remote_filename)
218 else:
219 self.put_sftp(source_path, remote_filename = remote_filename)
220
221 - def put_sftp(self, source_path, remote_filename = None):
233
234 - def put_scp(self, source_path, remote_filename = None):
242
243 - def get(self, remote_filename, local_path):
248
249 - def get_sftp(self, remote_filename, local_path):
261
262 - def get_scp(self, remote_filename, local_path):
272
274 """
275 List files available for scp
276
277 Note that this command can get confused when dealing with
278 files with newlines in them, as the embedded newlines cannot
279 be distinguished from the file boundaries.
280 """
281 dirs = self.remote_dir.split(os.sep)
282 if len(dirs) > 0:
283 if not dirs[0] :
284 dirs = dirs[1:]
285 dirs[0]= '/' + dirs[0]
286 mkdir_commands = [];
287 for d in dirs:
288 mkdir_commands += ["mkdir \"%s\"" % (d)] + ["cd \"%s\"" % (d)]
289
290 commands = mkdir_commands + ["ls -1"]
291 commandline = ("%s %s %s" % (globals.sftp_command,
292 globals.ssh_options,
293 self.host_string))
294
295 l = self.run_sftp_command(commandline, commands).split('\n')[1:]
296
297 return filter(lambda x: x, map(string.strip, l))
298
299 - def delete(self, filename_list):
308
309 duplicity.backend.register_backend("ssh", SSHBackend)
310 duplicity.backend.register_backend("scp", SSHBackend)
311 duplicity.backend.register_backend("sftp", SSHBackend)
312