Annotation of embedaddon/rsync/support/atomic-rsync, revision 1.1
1.1 ! misho 1: #!/usr/bin/perl
! 2: #
! 3: # This script lets you update a hierarchy of files in an atomic way by
! 4: # first creating a new hierarchy using rsync's --link-dest option, and
! 5: # then swapping the hierarchy into place. **See the usage message for
! 6: # more details and some important caveats!**
! 7:
! 8: use strict;
! 9: use warnings;
! 10: use Cwd 'abs_path';
! 11:
! 12: my $RSYNC_PROG = '/usr/bin/rsync';
! 13: my $RM_PROG = '/bin/rm';
! 14:
! 15: my $dest_dir = $ARGV[-1];
! 16: &usage if !defined $dest_dir || $dest_dir =~ /(^-|^$)/ || grep(/^--help/, @ARGV);
! 17: $dest_dir =~ s{(?<=.)/+$} {};
! 18:
! 19: if (!-d $dest_dir) {
! 20: die "$dest_dir is not a directory.\nUse --help for help.\n";
! 21: }
! 22:
! 23: if (@_ = grep(/^--[a-z]+-dest\b/, @ARGV)) {
! 24: $_ = join(' or ', @_);
! 25: die "You cannot use the $_ option with atomic-rsync.\nUse --help for help.\n";
! 26: }
! 27:
! 28: my $symlink_content = readlink $dest_dir; # undef when a real dir
! 29:
! 30: my $dest_arg = $dest_dir;
! 31: # This gives us the real destination dir, with all symlinks dereferenced.
! 32: $dest_dir = abs_path($dest_dir);
! 33: if ($dest_dir eq '/') {
! 34: die qq|You must not use "/" as the destination directory.\nUse --help for help.\n|;
! 35: }
! 36:
! 37: my($old_dir, $new_dir);
! 38: if (defined $symlink_content && $dest_dir =~ /-([12])$/) {
! 39: my $num = 3 - $1;
! 40: $old_dir = undef;
! 41: ($new_dir = $dest_dir) =~ s/-[12]$/-$num/;
! 42: $symlink_content =~ s/-[12]$/-$num/;
! 43: } else {
! 44: $old_dir = "$dest_dir~old~";
! 45: $new_dir = "$dest_dir~new~";
! 46: }
! 47:
! 48: $ARGV[-1] = "$new_dir/";
! 49:
! 50: system($RM_PROG, '-rf', $old_dir) if defined $old_dir && -d $old_dir;
! 51: system($RM_PROG, '-rf', $new_dir) if -d $new_dir;
! 52:
! 53: if (system($RSYNC_PROG, "--link-dest=$dest_dir", @ARGV)) {
! 54: if ($? == -1) {
! 55: print "failed to execute $RSYNC_PROG: $!\n";
! 56: } elsif ($? & 127) {
! 57: printf "child died with signal %d, %s coredump\n",
! 58: ($? & 127), ($? & 128) ? 'with' : 'without';
! 59: } else {
! 60: printf "child exited with value %d\n", $? >> 8;
! 61: }
! 62: exit $?;
! 63: }
! 64:
! 65: if (!defined $old_dir) {
! 66: atomic_symlink($symlink_content, $dest_arg);
! 67: exit;
! 68: }
! 69:
! 70: rename($dest_dir, $old_dir) or die "Unable to rename $dest_dir to $old_dir: $!";
! 71: rename($new_dir, $dest_dir) or die "Unable to rename $new_dir to $dest_dir: $!";
! 72:
! 73: exit;
! 74:
! 75: sub atomic_symlink
! 76: {
! 77: my($target, $link) = @_;
! 78: my $newlink = "$link~new~";
! 79:
! 80: unlink($newlink); # Just in case
! 81: symlink($target, $newlink) or die "Unable to symlink $newlink -> $target: $!\n";
! 82: rename($newlink, $link) or die "Unable to rename $newlink to $link: $!\n";
! 83: }
! 84:
! 85:
! 86: sub usage
! 87: {
! 88: die <<EOT;
! 89: Usage: atomic-rsync [RSYNC-OPTIONS] HOST:/SOURCE/DIR/ /DEST/DIR/
! 90: atomic-rsync [RSYNC-OPTIONS] HOST::MOD/DIR/ /DEST/DIR/
! 91:
! 92: This script lets you update a hierarchy of files in an atomic way by first
! 93: creating a new hierarchy (using hard-links to leverage the existing files),
! 94: and then swapping the new hierarchy into place. You must be pulling files
! 95: to a local directory, and that directory must already exist. For example:
! 96:
! 97: mkdir /local/files-1
! 98: ln -s files-1 /local/files
! 99: atomic-rsync -av host:/remote/files/ /local/files/
! 100:
! 101: If /local/files is a symlink to a directory that ends in -1 or -2, the
! 102: copy will go to the alternate suffix and the symlink will be changed to
! 103: point to the new dir. This is a fully atomic update. If the destination
! 104: is not a symlink (or not a symlink to a *-1 or a *-2 directory), this
! 105: will instead create a directory with "~new~" suffixed, move the current
! 106: directory to a name with "~old~" suffixed, and then move the ~new~
! 107: directory to the original destination name (this double rename is not
! 108: fully atomic, but is rapid). In both cases, the prior destintaion
! 109: directory will be preserved until the next update, at which point it
! 110: will be deleted.
! 111:
! 112: In all likelihood, you do NOT want to specify this command:
! 113:
! 114: atomic-rsync -av host:/remote/files /local/
! 115:
! 116: ... UNLESS you want the entire /local dir to be swapped out!
! 117:
! 118: See the "rsync" command for its list of options. You may not use the
! 119: --link-dest, --compare-dest, or --copy-dest options (since this script
! 120: uses --link-dest to make the transfer efficient).
! 121: EOT
! 122: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>