diff options
Diffstat (limited to 'bin/byteback-restore')
-rwxr-xr-x | bin/byteback-restore | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/bin/byteback-restore b/bin/byteback-restore new file mode 100755 index 0000000..48eea9a --- /dev/null +++ b/bin/byteback-restore @@ -0,0 +1,125 @@ +#!/usr/bin/ruby +# +# Restore a file from the most recent backup, from the remote host. +# + +$LOAD_PATH.unshift('/usr/lib/byteback') +$LOAD_PATH.unshift('./lib/') + +require 'trollop' + +# +# Show an error message and abort. +# +def fatal(str) + STDERR.puts(str) + exit(1) +end + +# +# Run a remote command. +# +def ssh(*ssh_args) + args = ['ssh', + '-o', 'BatchMode=yes', + '-o', 'ConnectionAttempts=5', + '-o', 'ConnectTimeout=30', + '-o', 'ServerAliveInterval=60', + '-o', 'TCPKeepAlive=yes', + '-x', '-a', + '-i', @ssh_key, + '-l', @destination_user, + @destination_host + ] + + ssh_args + .map { |a| a ? a : '' } + + system(*args) +end + +def list_files(pattern) + ssh('byteback-receive', '--list', pattern) +end + +# +# We cannot use plain 'rsync' here because the receiver command will +# see that, and rewrite our arguments. +# +# To cater to this we have to wrap the rsync for the restore and we +# do that by setting "rsync-path" to point to the receiver program. +# +# +def restore_file(path, revision) + cmd = %w( rsync ) + cmd += ['--rsh', 'ssh -o BatchMode=yes -x -a -i /etc/byteback/key -l byteback'] + cmd += ['--rsync-path', 'restore --fake-super'] + cmd += ['-aApzrX', '--numeric-ids'] + cmd += ["#{@destination_host}:/#{revision}/#{path}", '.'] + system(*cmd) +end + +# +# Parse our command-line arguments +# +opts = Trollop.options do + banner "byteback-restore: Restore a file\n " + + opt :file, 'The file to restore', + type: :string + + opt :revision, "The version of the file to restore - default is 'latest'", + type: :string + + opt :destination, 'Backup destination (i.e. user@host:/path)', + type: :string + + opt :ssh_key, 'SSH key filename', + type: :string, + default: '/etc/byteback/key', + short: 'k' +end + +# +# Setup default destination and key. +# +@destination = File.read('/etc/byteback/destination').chomp if + File.exist?('/etc/byteback/destination') +@ssh_key = '/etc/byteback/key' if File.exist?('/etc/byteback/key') + +# +# Allow the command-line to override them. +# +@ssh_key = opts[:ssh_key] unless opts[:ssh_key].nil? +@destination = opts[:destination] unless opts[:destination].nil? + +# +# Check our destination is well-formed +# +if @destination =~ /^(?:(.+)@)?([^@:]+):(.+)?$/ + @destination_user, @destination_host, @destination_path = [Regexp.last_match(1), Regexp.last_match(2), Regexp.last_match(3)] +else + fatal('Destination must be a remote path, e.g. ssh@host.com:/store/backups') +end + +# +# If the user didn't specify a file then we're not restoring anything, +# and we should abort. +# +if opts[:file].nil? + fatal('You must specify a file to restore') +end + +# +# If the user specified a file, but not a revision, then we list +# the available revisions. +# +if opts[:revision].nil? + list_files(opts[:file]) + exit(0) +end + +# +# Restore a file +# +restore_file(opts[:file], opts[:revision]) +exit(0) |