A perl script to effect an atomic transfer of a set of files.
[rsync/rsync.git] / support / atomic-rsync
CommitLineData
fa170b2e
WD
1#!/usr/bin/perl
2
3use strict;
4use Cwd 'abs_path';
5
6my $RSYNC = '/usr/bin/rsync';
7
8my $dest_dir = $ARGV[-1];
9usage(1) if $dest_dir eq '' || $dest_dir =~ /^--/;
10
11if (!-d $dest_dir) {
12 print STDERR "$dest_dir is not a directory.\n\n";
13 usage(1);
14}
15
16if (@_ = grep(/^--(link|compare)-dest/, @ARGV)) {
17 $_ = join(' or ', @_);
18 print STDERR "You may not use $_ as an rsync option.\n\n";
19 usage(1);
20}
21
22$dest_dir = abs_path($dest_dir);
23if ($dest_dir eq '/') {
24 print STDERR 'You must not use "/" as the destination directory.', "\n\n";
25 usage(1);
26}
27
28my $old_dir = "$dest_dir~old~";
29my $new_dir = $ARGV[-1] = "$dest_dir~new~";
30
31if (-d $old_dir) {
32 rename($old_dir, $new_dir) or die "Unable to rename $old_dir to $new_dir: $!";
33}
34
35if (system($RSYNC, "--link-dest=$dest_dir", @ARGV)) {
36 if ($? == -1) {
37 print "failed to execute $RSYNC: $!\n";
38 } elsif ($? & 127) {
39 printf "child died with signal %d, %s coredump\n",
40 ($? & 127), ($? & 128) ? 'with' : 'without';
41 } else {
42 printf "child exited with value %d\n", $? >> 8;
43 }
44 exit $?;
45}
46
47rename($dest_dir, $old_dir) or die "Unable to rename $new_dir to $old_dir: $!";
48rename($new_dir, $dest_dir) or die "Unable to rename $new_dir to $dest_dir: $!";
49
50exit;
51
52
53sub usage
54{
55 my($ret) = @_;
56 my $fh = $ret ? *STDERR : *STDOUT;
57 print $fh <<EOT;
58Usage: atomic-rsync [RSYNC-OPTIONS] HOST:SOURCE DEST
59
60This script allows you to pull some files into DEST on the local system
61(which must exist) in an atomic manner. It does this by first pulling
62files to DEST~new~ (using hard-links to unchanged files in order to keep
63the space requirements down), and then, at the end of the transfer, it
64renames DEST to DEST~old~ and renames DEST~new~ to DEST to effect the
65atomic update. The DEST~old~ hierarchy will be preserved until the next
66run of this script, at which point it will be renamed to DEST~new~ and
67used in the copy.
68
69See the "rsync" command for its list of options. You may not use the
70--link-dest or --compare-dest options (since this script uses --link-dest
71to effect the atomic transfer). Also, DEST cannot be "/".
72EOT
73 exit $ret;
74}