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>