The branch, master has been updated via 73ceea6a Convert atomic-rsync to python. from 39c3ae0e Convert munge-symlinks to python.
https://git.samba.org/?p=rsync.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit 73ceea6ad2af00f251a5e79a0a258f9fee97d531 Author: Wayne Davison <wa...@opencoder.net> Date: Mon Dec 20 17:28:18 2021 -0800 Convert atomic-rsync to python. ----------------------------------------------------------------------- Summary of changes: support/atomic-rsync | 179 ++++++++++++++++++++++++++------------------------- 1 file changed, 91 insertions(+), 88 deletions(-) Changeset truncated at 500 lines: diff --git a/support/atomic-rsync b/support/atomic-rsync index 0346fb49..37363e43 100755 --- a/support/atomic-rsync +++ b/support/atomic-rsync @@ -1,92 +1,83 @@ -#!/usr/bin/env perl -# +#!/usr/bin/env python3 # This script lets you update a hierarchy of files in an atomic way by # first creating a new hierarchy using rsync's --link-dest option, and # then swapping the hierarchy into place. **See the usage message for # more details and some important caveats!** -use strict; -use warnings; -use Cwd 'abs_path'; - -my $RSYNC_PROG = '/usr/bin/rsync'; -my $RM_PROG = '/bin/rm'; - -my $dest_dir = $ARGV[-1]; -&usage if !defined $dest_dir || $dest_dir =~ /(^-|^$)/ || grep(/^--help/, @ARGV); -$dest_dir =~ s{(?<=.)/+$} {}; - -if (!-d $dest_dir) { - die "$dest_dir is not a directory.\nUse --help for help.\n"; -} - -if (@_ = grep(/^--[a-z]+-dest\b/, @ARGV)) { - $_ = join(' or ', @_); - die "You cannot use the $_ option with atomic-rsync.\nUse --help for help.\n"; -} - -my $symlink_content = readlink $dest_dir; # undef when a real dir - -my $dest_arg = $dest_dir; -# This gives us the real destination dir, with all symlinks dereferenced. -$dest_dir = abs_path($dest_dir); -if ($dest_dir eq '/') { - die qq|You must not use "/" as the destination directory.\nUse --help for help.\n|; -} - -my($old_dir, $new_dir); -if (defined $symlink_content && $dest_dir =~ /-([12])$/) { - my $num = 3 - $1; - $old_dir = undef; - ($new_dir = $dest_dir) =~ s/-[12]$/-$num/; - $symlink_content =~ s/-[12]$/-$num/; -} else { - $old_dir = "$dest_dir~old~"; - $new_dir = "$dest_dir~new~"; -} - -$ARGV[-1] = "$new_dir/"; - -system($RM_PROG, '-rf', $old_dir) if defined $old_dir && -d $old_dir; -system($RM_PROG, '-rf', $new_dir) if -d $new_dir; - -if (system($RSYNC_PROG, "--link-dest=$dest_dir", @ARGV)) { - if ($? == -1) { - print "failed to execute $RSYNC_PROG: $!\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 1; -} - -if (!defined $old_dir) { - atomic_symlink($symlink_content, $dest_arg); - exit; -} - -rename($dest_dir, $old_dir) or die "Unable to rename $dest_dir to $old_dir: $!"; -rename($new_dir, $dest_dir) or die "Unable to rename $new_dir to $dest_dir: $!"; - -exit; - -sub atomic_symlink -{ - my($target, $link) = @_; - my $newlink = "$link~new~"; - - unlink($newlink); # Just in case - symlink($target, $newlink) or die "Unable to symlink $newlink -> $target: $!\n"; - rename($newlink, $link) or die "Unable to rename $newlink to $link: $!\n"; -} - - -sub usage -{ - die <<EOT; -Usage: atomic-rsync [RSYNC-OPTIONS] HOST:/SOURCE/DIR/ /DEST/DIR/ +import os, sys, re, subprocess, shutil + +ALT_DEST_ARG_RE = re.compile('^--[a-z][^ =]+-dest(=|$)') + +RSYNC_PROG = '/usr/bin/rsync' + +def main(): + cmd_args = sys.argv[1:] + if '--help' in cmd_args: + usage_and_exit() + + if len(cmd_args) < 2: + usage_and_exit(True) + + dest_dir = cmd_args[-1].rstrip('/') + if dest_dir == '' or dest_dir.startswith('-'): + usage_and_exit(True) + + if not os.path.isdir(dest_dir): + die(dest_dir, "is not a directory or a symlink to a dir.\nUse --help for help.") + + bad_args = [ arg for arg in cmd_args if ALT_DEST_ARG_RE.match(arg) ] + if bad_args: + die("You cannot use the", ' or '.join(bad_args), "option with atomic-rsync.\nUse --help for help.") + + symlink_content = os.readlink(dest_dir) if os.path.islink(dest_dir) else None + + dest_arg = dest_dir + dest_dir = os.path.realpath(dest_dir) # The real destination dir with all symlinks dereferenced + if dest_dir == '/': + die('You must not use "/" as the destination directory.\nUse --help for help.') + + old_dir = new_dir = None + if symlink_content is not None and dest_dir.endswith(('-1','-2')): + if not symlink_content.endswith(dest_dir[-2:]): + die("Symlink suffix out of sync with dest_dir name:", symlink_content, 'vs', dest_dir) + num = 3 - int(dest_dir[-1]); + old_dir = None + new_dir = dest_dir[:-1] + str(num) + symlink_content = symlink_content[:-1] + str(num) + else: + old_dir = dest_dir + '~old~' + new_dir = dest_dir + '~new~' + + cmd_args[-1] = new_dir + '/' + + if old_dir is not None and os.path.isdir(old_dir): + shutil.rmtree(old_dir) + if os.path.isdir(new_dir): + shutil.rmtree(new_dir) + + subprocess.run([RSYNC_PROG, '--link-dest=' + dest_dir, *cmd_args], check=True) + + if old_dir is None: + atomic_symlink(symlink_content, dest_arg) + return + + os.rename(dest_dir, old_dir) + os.rename(new_dir, dest_dir) + + +def atomic_symlink(target, link): + newlink = link + "~new~" + try: + os.unlink(newlink); # Just in case + except OSError: + pass + os.symlink(target, newlink) + os.rename(newlink, link) + + +def usage_and_exit(use_stderr=False): + usage_msg = """\ +Usage: atomic-rsync [RSYNC-OPTIONS] [HOST:]/SOURCE/DIR/ /DEST/DIR/ atomic-rsync [RSYNC-OPTIONS] HOST::MOD/DIR/ /DEST/DIR/ This script lets you update a hierarchy of files in an atomic way by first @@ -96,7 +87,7 @@ to a local directory, and that directory must already exist. For example: mkdir /local/files-1 ln -s files-1 /local/files - atomic-rsync -av host:/remote/files/ /local/files/ + atomic-rsync -aiv host:/remote/files/ /local/files/ If /local/files is a symlink to a directory that ends in -1 or -2, the copy will go to the alternate suffix and the symlink will be changed to @@ -111,12 +102,24 @@ will be deleted. In all likelihood, you do NOT want to specify this command: - atomic-rsync -av host:/remote/files /local/ + atomic-rsync -aiv host:/remote/files /local/ ... UNLESS you want the entire /local dir to be swapped out! See the "rsync" command for its list of options. You may not use the --link-dest, --compare-dest, or --copy-dest options (since this script uses --link-dest to make the transfer efficient). -EOT -} +""" + print(usage_msg, file=sys.stderr if use_stderr else sys.stdout) + sys.exit(1 if use_stderr else 0) + + +def die(*args): + print(*args, file=sys.stderr) + sys.exit(1) + + +if __name__ == '__main__': + main() + +# vim: sw=4 et -- The rsync repository. _______________________________________________ rsync-cvs mailing list rsync-cvs@lists.samba.org https://lists.samba.org/mailman/listinfo/rsync-cvs