A perl script to effect an atomic transfer of a set of files.
authorWayne Davison <wayned@samba.org>
Thu, 6 Jan 2005 17:13:31 +0000 (17:13 +0000)
committerWayne Davison <wayned@samba.org>
Thu, 6 Jan 2005 17:13:31 +0000 (17:13 +0000)
support/atomic-rsync [new file with mode: 0755]

diff --git a/support/atomic-rsync b/support/atomic-rsync
new file mode 100755 (executable)
index 0000000..4173abe
--- /dev/null
@@ -0,0 +1,74 @@
+#!/usr/bin/perl
+
+use strict;
+use Cwd 'abs_path';
+
+my $RSYNC = '/usr/bin/rsync';
+
+my $dest_dir = $ARGV[-1];
+usage(1) if $dest_dir eq '' || $dest_dir =~ /^--/;
+
+if (!-d $dest_dir) {
+    print STDERR "$dest_dir is not a directory.\n\n";
+    usage(1);
+}
+
+if (@_ = grep(/^--(link|compare)-dest/, @ARGV)) {
+    $_ = join(' or ', @_);
+    print STDERR "You may not use $_ as an rsync option.\n\n";
+    usage(1);
+}
+
+$dest_dir = abs_path($dest_dir);
+if ($dest_dir eq '/') {
+    print STDERR 'You must not use "/" as the destination directory.', "\n\n";
+    usage(1);
+}
+
+my $old_dir = "$dest_dir~old~";
+my $new_dir = $ARGV[-1] = "$dest_dir~new~";
+
+if (-d $old_dir) {
+    rename($old_dir, $new_dir) or die "Unable to rename $old_dir to $new_dir: $!";
+}
+
+if (system($RSYNC, "--link-dest=$dest_dir", @ARGV)) {
+    if ($? == -1) {
+       print "failed to execute $RSYNC: $!\n";
+    } elsif ($? & 127) {
+       printf "child died with signal %d, %s coredump\n",
+           ($? & 127),  ($? & 128) ? 'with' : 'without';
+    } else {
+       printf "child exited with value %d\n", $? >> 8;
+    }
+    exit $?;
+}
+
+rename($dest_dir, $old_dir) or die "Unable to rename $new_dir to $old_dir: $!";
+rename($new_dir, $dest_dir) or die "Unable to rename $new_dir to $dest_dir: $!";
+
+exit;
+
+
+sub usage
+{
+    my($ret) = @_;
+    my $fh = $ret ? *STDERR : *STDOUT;
+    print $fh <<EOT;
+Usage: atomic-rsync [RSYNC-OPTIONS] HOST:SOURCE DEST
+
+This script allows you to pull some files into DEST on the local system
+(which must exist) in an atomic manner.  It does this by first pulling
+files to DEST~new~ (using hard-links to unchanged files in order to keep
+the space requirements down), and then, at the end of the transfer, it
+renames DEST to DEST~old~ and renames DEST~new~ to DEST to effect the
+atomic update.  The DEST~old~ hierarchy will be preserved until the next
+run of this script, at which point it will be renamed to DEST~new~ and
+used in the copy.
+
+See the "rsync" command for its list of options.  You may not use the
+--link-dest or --compare-dest options (since this script uses --link-dest
+to effect the atomic transfer).  Also, DEST cannot be "/".
+EOT
+    exit $ret;
+}