File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / rsync / support / atomic-rsync
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Mar 17 00:32:36 2021 UTC (4 years, 1 month ago) by misho
Branches: rsync, MAIN
CVS tags: v3_2_3, HEAD
rsync 3.2.3

    1: #!/usr/bin/env 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 1;
   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>