summaryrefslogtreecommitdiff
path: root/bin/byteback-restore
diff options
context:
space:
mode:
Diffstat (limited to 'bin/byteback-restore')
-rwxr-xr-xbin/byteback-restore125
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)