3 # Copyright (c) 2010 Peter Palfrader
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be
14 # included in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 # copy a file from the backup server, where the server runs debbackup-ssh-wrap
36 backuphost="debbackup@storace.debian.org"
39 syslog.openlog(sys.argv[0], syslog.LOG_PID, syslog.LOG_DAEMON)
42 syslog.syslog(syslog.LOG_INFO, m)
44 syslog.syslog(syslog.LOG_WARNING, m)
45 print >> sys.stderr, m
50 parser = optparse.OptionParser()
51 parser.set_usage("%prog [<options>] <from_host> <filename> <target>")
52 (options, args) = parser.parse_args()
58 from_host = args.pop(0)
59 filename = args.pop(0)
62 hostname = os.uname()[1]
64 # open pipe to backup server
65 p = subprocess.Popen(["ssh", "-C", backuphost, hostname, "retrieve-file", "pg", from_host, filename], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
70 line = f.readline().rstrip('\n')
71 if line != "Format: 1":
72 croak("Uknown format in line 1 (%s) when getting %s:%s from backup host"%(line, from_host, filename))
75 line = f.readline().rstrip('\n')
76 s = line.split(':', 1)
78 croak("Protocol violation when getting %s:%s from backup host (line 2)"%(from_host, filename))
81 croak("Protocol violation when getting %s:%s from backup host"%(from_host, filename))
82 first = s[1].split()[0]
86 croak("Invalid code '%s'"%(first))
89 info("Wanted to get %s:%s from backup host, but file does not exist."%(from_host, filename))
92 info("Unknown code %d when trying to get %s:%s from backup host"%(code, from_host, filename))
94 # get rest of the headers
97 line = f.readline().rstrip('\n')
100 s = line.split(':', 1)
102 croak("Protocol violation when getting %s:%s from backup host"%(from_host, filename))
104 value = s[1].lstrip()
105 metadata[key] = value
107 # verify we like them
108 for k in ['Size', 'SHA-512']:
109 if not k in metadata:
110 croak("Key %s not found in metadata when getting %s:%s from backup host"%(k, from_host, filename))
112 size = int(metadata['Size'])
114 croak("Invalid size '%s'"%(size))
115 checksum = metadata['SHA-512']
117 info("Getting %s:%s from backup host (size %d)."%(from_host, filename, size))
119 target=os.path.abspath(target)
120 targetdir=os.path.dirname(target)
121 tmp = tempfile.NamedTemporaryFile(dir=targetdir, prefix=".tmp.pg-receive-file-from-backup-%s:%s."%(from_host, filename))
125 digest = hashlib.sha512()
127 buf = f.read(block_size)
132 running_size += len(buf)
133 if running_size > size:
134 croak("Size mismatch")
139 file_size = os.stat(tmp.name)[stat.ST_SIZE]
141 # check size and checksum
142 if file_size != size:
143 croak("Size mismatch")
144 if file_size != running_size:
145 croak("Size mismatch. WTF.")
146 if checksum != digest.hexdigest():
147 croak("Checksum mismatch. WTF.")
151 os.link(tmp.name, target)
153 croak("Failed at linking to target: %s"%(e))
156 info("Successfully stored %s."%(target))
161 # vim:set shiftwidth=4: