Annotation of embedaddon/rsync/support/atomic-rsync, revision 1.1.1.2
1.1.1.2 ! misho 1: #!/usr/bin/env perl
1.1 misho 2: #
3: # This script lets you update a hierarchy of files in an atomic way by
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!**
7:
8: use strict;
9: use warnings;
10: use Cwd 'abs_path';
11:
12: my $RSYNC_PROG = '/usr/bin/rsync';
13: my $RM_PROG = '/bin/rm';
14:
15: my $dest_dir = $ARGV[-1];
16: &usage if !defined $dest_dir || $dest_dir =~ /(^-|^$)/ || grep(/^--help/, @ARGV);
17: $dest_dir =~ s{(?<=.)/+$} {};
18:
19: if (!-d $dest_dir) {
20: die "$dest_dir is not a directory.\nUse --help for help.\n";
21: }
22:
23: if (@_ = grep(/^--[a-z]+-dest\b/, @ARGV)) {
24: $_ = join(' or ', @_);
25: die "You cannot use the $_ option with atomic-rsync.\nUse --help for help.\n";
26: }
27:
28: my $symlink_content = readlink $dest_dir; # undef when a real dir
29:
30: my $dest_arg = $dest_dir;
31: # This gives us the real destination dir, with all symlinks dereferenced.
32: $dest_dir = abs_path($dest_dir);
33: if ($dest_dir eq '/') {
34: die qq|You must not use "/" as the destination directory.\nUse --help for help.\n|;
35: }
36:
37: my($old_dir, $new_dir);
38: if (defined $symlink_content && $dest_dir =~ /-([12])$/) {
39: my $num = 3 - $1;
40: $old_dir = undef;
41: ($new_dir = $dest_dir) =~ s/-[12]$/-$num/;
42: $symlink_content =~ s/-[12]$/-$num/;
43: } else {
44: $old_dir = "$dest_dir~old~";
45: $new_dir = "$dest_dir~new~";
46: }
47:
48: $ARGV[-1] = "$new_dir/";
49:
50: system($RM_PROG, '-rf', $old_dir) if defined $old_dir && -d $old_dir;
51: system($RM_PROG, '-rf', $new_dir) if -d $new_dir;
52:
53: if (system($RSYNC_PROG, "--link-dest=$dest_dir", @ARGV)) {
54: if ($? == -1) {
55: print "failed to execute $RSYNC_PROG: $!\n";
56: } elsif ($? & 127) {
57: printf "child died with signal %d, %s coredump\n",
58: ($? & 127), ($? & 128) ? 'with' : 'without';
59: } else {
60: printf "child exited with value %d\n", $? >> 8;
61: }
1.1.1.2 ! misho 62: exit 1;
1.1 misho 63: }
64:
65: if (!defined $old_dir) {
66: atomic_symlink($symlink_content, $dest_arg);
67: exit;
68: }
69:
70: rename($dest_dir, $old_dir) or die "Unable to rename $dest_dir to $old_dir: $!";
71: rename($new_dir, $dest_dir) or die "Unable to rename $new_dir to $dest_dir: $!";
72:
73: exit;
74:
75: sub atomic_symlink
76: {
77: my($target, $link) = @_;
78: my $newlink = "$link~new~";
79:
80: unlink($newlink); # Just in case
81: symlink($target, $newlink) or die "Unable to symlink $newlink -> $target: $!\n";
82: rename($newlink, $link) or die "Unable to rename $newlink to $link: $!\n";
83: }
84:
85:
86: sub usage
87: {
88: die <<EOT;
89: Usage: atomic-rsync [RSYNC-OPTIONS] HOST:/SOURCE/DIR/ /DEST/DIR/
90: atomic-rsync [RSYNC-OPTIONS] HOST::MOD/DIR/ /DEST/DIR/
91:
92: This script lets you update a hierarchy of files in an atomic way by first
93: creating a new hierarchy (using hard-links to leverage the existing files),
94: and then swapping the new hierarchy into place. You must be pulling files
95: to a local directory, and that directory must already exist. For example:
96:
97: mkdir /local/files-1
98: ln -s files-1 /local/files
99: atomic-rsync -av host:/remote/files/ /local/files/
100:
101: If /local/files is a symlink to a directory that ends in -1 or -2, the
102: copy will go to the alternate suffix and the symlink will be changed to
103: point to the new dir. This is a fully atomic update. If the destination
104: is not a symlink (or not a symlink to a *-1 or a *-2 directory), this
105: will instead create a directory with "~new~" suffixed, move the current
106: directory to a name with "~old~" suffixed, and then move the ~new~
107: directory to the original destination name (this double rename is not
108: fully atomic, but is rapid). In both cases, the prior destintaion
109: directory will be preserved until the next update, at which point it
110: will be deleted.
111:
112: In all likelihood, you do NOT want to specify this command:
113:
114: atomic-rsync -av host:/remote/files /local/
115:
116: ... UNLESS you want the entire /local dir to be swapped out!
117:
118: See the "rsync" command for its list of options. You may not use the
119: --link-dest, --compare-dest, or --copy-dest options (since this script
120: uses --link-dest to make the transfer efficient).
121: EOT
122: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>