Fix the error message on one of the rename operations.
[rsync/rsync.git] / support / atomic-rsync
CommitLineData
fa170b2e 1#!/usr/bin/perl
716b46c5
WD
2#
3# This script lets you update a hierarchy of files in an atomic way by
e96d7972
WD
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!**
fa170b2e
WD
7
8use strict;
9use Cwd 'abs_path';
10
716b46c5
WD
11my $RSYNC_PROG = '/usr/bin/rsync';
12my $RM_PROG = '/bin/rm';
fa170b2e
WD
13
14my $dest_dir = $ARGV[-1];
15usage(1) if $dest_dir eq '' || $dest_dir =~ /^--/;
16
17if (!-d $dest_dir) {
18 print STDERR "$dest_dir is not a directory.\n\n";
19 usage(1);
20}
21
22if (@_ = grep(/^--(link|compare)-dest/, @ARGV)) {
23 $_ = join(' or ', @_);
24 print STDERR "You may not use $_ as an rsync option.\n\n";
25 usage(1);
26}
27
28$dest_dir = abs_path($dest_dir);
29if ($dest_dir eq '/') {
30 print STDERR 'You must not use "/" as the destination directory.', "\n\n";
31 usage(1);
32}
33
34my $old_dir = "$dest_dir~old~";
35my $new_dir = $ARGV[-1] = "$dest_dir~new~";
36
716b46c5 37system($RM_PROG, '-rf', $old_dir) if -d $old_dir;
fa170b2e 38
716b46c5 39if (system($RSYNC_PROG, "--link-dest=$dest_dir", @ARGV)) {
fa170b2e 40 if ($? == -1) {
716b46c5 41 print "failed to execute $RSYNC_PROG: $!\n";
fa170b2e
WD
42 } elsif ($? & 127) {
43 printf "child died with signal %d, %s coredump\n",
44 ($? & 127), ($? & 128) ? 'with' : 'without';
45 } else {
46 printf "child exited with value %d\n", $? >> 8;
47 }
48 exit $?;
49}
50
2c11e80e 51rename($dest_dir, $old_dir) or die "Unable to rename $dest_dir to $old_dir: $!";
fa170b2e
WD
52rename($new_dir, $dest_dir) or die "Unable to rename $new_dir to $dest_dir: $!";
53
54exit;
55
56
57sub usage
58{
59 my($ret) = @_;
60 my $fh = $ret ? *STDERR : *STDOUT;
61 print $fh <<EOT;
716b46c5
WD
62Usage: atomic-rsync [RSYNC-OPTIONS] HOST:/SOURCE/DIR/ /DEST/DIR/
63 atomic-rsync [RSYNC-OPTIONS] HOST::MOD/DIR/ /DEST/DIR/
64
65This script lets you update a hierarchy of files in an atomic way by first
66creating a new hierarchy (using hard-links to leverage the existing files),
67and then swapping the new hierarchy into place. You must be pulling files
68to a local directory, and that directory must already exist. For example:
69
70 atomic-rsync -av host:/remote/files/ /local/files/
71
72This would make the transfer to the directory /local/files~new~ and then
73swap out /local/files at the end of the transfer by renaming it to
74/local/files~old~ and putting the new directory into its place. The
75/local/files~old~ directory will be preserved until the next update, at
76which point it will be deleted.
77
78Do NOT specify this command:
79
80 atomic-rsync -av host:/remote/files /local/
fa170b2e 81
716b46c5 82... UNLESS you want the entire /local dir to be swapped out!
fa170b2e
WD
83
84See the "rsync" command for its list of options. You may not use the
85--link-dest or --compare-dest options (since this script uses --link-dest
716b46c5
WD
86to make the transfer efficient). Also, the destination directory cannot
87be "/".
fa170b2e
WD
88EOT
89 exit $ret;
90}