Commit | Line | Data |
---|---|---|
fa170b2e WD |
1 | #!/usr/bin/perl |
2 | ||
3 | use strict; | |
4 | use Cwd 'abs_path'; | |
5 | ||
6 | my $RSYNC = '/usr/bin/rsync'; | |
7 | ||
8 | my $dest_dir = $ARGV[-1]; | |
9 | usage(1) if $dest_dir eq '' || $dest_dir =~ /^--/; | |
10 | ||
11 | if (!-d $dest_dir) { | |
12 | print STDERR "$dest_dir is not a directory.\n\n"; | |
13 | usage(1); | |
14 | } | |
15 | ||
16 | if (@_ = 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); | |
23 | if ($dest_dir eq '/') { | |
24 | print STDERR 'You must not use "/" as the destination directory.', "\n\n"; | |
25 | usage(1); | |
26 | } | |
27 | ||
28 | my $old_dir = "$dest_dir~old~"; | |
29 | my $new_dir = $ARGV[-1] = "$dest_dir~new~"; | |
30 | ||
31 | if (-d $old_dir) { | |
32 | rename($old_dir, $new_dir) or die "Unable to rename $old_dir to $new_dir: $!"; | |
33 | } | |
34 | ||
35 | if (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 | ||
47 | rename($dest_dir, $old_dir) or die "Unable to rename $new_dir to $old_dir: $!"; | |
48 | rename($new_dir, $dest_dir) or die "Unable to rename $new_dir to $dest_dir: $!"; | |
49 | ||
50 | exit; | |
51 | ||
52 | ||
53 | sub usage | |
54 | { | |
55 | my($ret) = @_; | |
56 | my $fh = $ret ? *STDERR : *STDOUT; | |
57 | print $fh <<EOT; | |
58 | Usage: atomic-rsync [RSYNC-OPTIONS] HOST:SOURCE DEST | |
59 | ||
60 | This 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 | |
62 | files to DEST~new~ (using hard-links to unchanged files in order to keep | |
63 | the space requirements down), and then, at the end of the transfer, it | |
64 | renames DEST to DEST~old~ and renames DEST~new~ to DEST to effect the | |
65 | atomic update. The DEST~old~ hierarchy will be preserved until the next | |
66 | run of this script, at which point it will be renamed to DEST~new~ and | |
67 | used in the copy. | |
68 | ||
69 | See 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 | |
71 | to effect the atomic transfer). Also, DEST cannot be "/". | |
72 | EOT | |
73 | exit $ret; | |
74 | } |