#!/usr/bin/perl # # 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 Cwd 'abs_path'; my $RSYNC_PROG = '/usr/bin/rsync'; my $RM_PROG = '/bin/rm'; 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~"; system($RM_PROG, '-rf', $old_dir) if -d $old_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 $?; } 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 usage { my($ret) = @_; my $fh = $ret ? *STDERR : *STDOUT; print $fh <