| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package File::Rsync::Mirror::Recentfile; | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | # use warnings; | 
| 4 | 8 |  |  | 8 |  | 84049 | use strict; | 
|  | 8 |  |  |  |  | 34 |  | 
|  | 8 |  |  |  |  | 744 |  | 
| 5 |  |  |  |  |  |  |  | 
| 6 |  |  |  |  |  |  | =encoding utf-8 | 
| 7 |  |  |  |  |  |  |  | 
| 8 |  |  |  |  |  |  | =head1 NAME | 
| 9 |  |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | File::Rsync::Mirror::Recentfile - mirroring via rsync made efficient | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | =cut | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | my $HAVE = {}; | 
| 15 |  |  |  |  |  |  | for my $package ( | 
| 16 |  |  |  |  |  |  | "Data::Serializer", | 
| 17 |  |  |  |  |  |  | "File::Rsync" | 
| 18 |  |  |  |  |  |  | ) { | 
| 19 |  |  |  |  |  |  | $HAVE->{$package} = eval qq{ require $package; }; | 
| 20 |  |  |  |  |  |  | } | 
| 21 | 8 |  |  | 8 |  | 58 | use Config; | 
|  | 8 |  |  |  |  | 15 |  | 
|  | 8 |  |  |  |  | 307 |  | 
| 22 | 8 |  |  | 8 |  | 43 | use File::Basename qw(basename dirname fileparse); | 
|  | 8 |  |  |  |  | 11 |  | 
|  | 8 |  |  |  |  | 657 |  | 
| 23 | 8 |  |  | 8 |  | 1144 | use File::Copy qw(cp); | 
|  | 8 |  |  |  |  | 9272 |  | 
|  | 8 |  |  |  |  | 423 |  | 
| 24 | 8 |  |  | 8 |  | 51 | use File::Path qw(mkpath); | 
|  | 8 |  |  |  |  | 17 |  | 
|  | 8 |  |  |  |  | 399 |  | 
| 25 | 8 |  |  | 8 |  | 3444 | use File::Rsync::Mirror::Recentfile::FakeBigFloat qw(:all); | 
|  | 8 |  |  |  |  | 18 |  | 
|  | 8 |  |  |  |  | 1423 |  | 
| 26 | 8 |  |  | 8 |  | 6289 | use File::Temp; | 
|  | 8 |  |  |  |  | 151333 |  | 
|  | 8 |  |  |  |  | 674 |  | 
| 27 | 8 |  |  | 8 |  | 71 | use List::Util qw(first max min); | 
|  | 8 |  |  |  |  | 21 |  | 
|  | 8 |  |  |  |  | 944 |  | 
| 28 | 8 |  |  | 8 |  | 60 | use Scalar::Util qw(blessed reftype); | 
|  | 8 |  |  |  |  | 21 |  | 
|  | 8 |  |  |  |  | 388 |  | 
| 29 | 8 |  |  | 8 |  | 5048 | use Storable; | 
|  | 8 |  |  |  |  | 26499 |  | 
|  | 8 |  |  |  |  | 457 |  | 
| 30 | 8 |  |  | 8 |  | 4329 | use Time::HiRes qw(); | 
|  | 8 |  |  |  |  | 11095 |  | 
|  | 8 |  |  |  |  | 215 |  | 
| 31 | 8 |  |  | 8 |  | 3727 | use YAML::Syck; | 
|  | 8 |  |  |  |  | 15070 |  | 
|  | 8 |  |  |  |  | 500 |  | 
| 32 |  |  |  |  |  |  |  | 
| 33 | 8 |  |  | 8 |  | 69 | use version; our $VERSION = qv('0.0.9'); | 
|  | 8 |  |  |  |  | 15 |  | 
|  | 8 |  |  |  |  | 48 |  | 
| 34 |  |  |  |  |  |  |  | 
| 35 | 8 |  |  | 8 |  | 969 | use constant MAX_INT => ~0>>1; # anything better? | 
|  | 8 |  |  |  |  | 23 |  | 
|  | 8 |  |  |  |  | 577 |  | 
| 36 | 8 |  |  | 8 |  | 58 | use constant DEFAULT_PROTOCOL => 1; | 
|  | 8 |  |  |  |  | 56 |  | 
|  | 8 |  |  |  |  | 6877 |  | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | # cf. interval_secs | 
| 39 |  |  |  |  |  |  | my %seconds; | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | # maybe subclass if this mapping is bad? | 
| 42 |  |  |  |  |  |  | my %serializers; | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | Writer (of a single file): | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | use File::Rsync::Mirror::Recentfile; | 
| 49 |  |  |  |  |  |  | my $fr = File::Rsync::Mirror::Recentfile->new | 
| 50 |  |  |  |  |  |  | ( | 
| 51 |  |  |  |  |  |  | interval => q(6h), | 
| 52 |  |  |  |  |  |  | filenameroot => "RECENT", | 
| 53 |  |  |  |  |  |  | comment => "These 'RECENT' files are part of a test of a new CPAN mirroring concept. Please ignore them for now.", | 
| 54 |  |  |  |  |  |  | localroot => "/home/ftp/pub/PAUSE/authors/", | 
| 55 |  |  |  |  |  |  | aggregator => [qw(1d 1W 1M 1Q 1Y Z)], | 
| 56 |  |  |  |  |  |  | ); | 
| 57 |  |  |  |  |  |  | $rf->update("/home/ftp/pub/PAUSE/authors/id/A/AN/ANDK/CPAN-1.92_63.tar.gz","new"); | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | Reader/mirrorer: | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | my $rf = File::Rsync::Mirror::Recentfile->new | 
| 62 |  |  |  |  |  |  | ( | 
| 63 |  |  |  |  |  |  | filenameroot => "RECENT", | 
| 64 |  |  |  |  |  |  | interval => q(6h), | 
| 65 |  |  |  |  |  |  | localroot => "/home/ftp/pub/PAUSE/authors", | 
| 66 |  |  |  |  |  |  | remote_dir => "", | 
| 67 |  |  |  |  |  |  | remote_host => "pause.perl.org", | 
| 68 |  |  |  |  |  |  | remote_module => "authors", | 
| 69 |  |  |  |  |  |  | rsync_options => { | 
| 70 |  |  |  |  |  |  | compress => 1, | 
| 71 |  |  |  |  |  |  | 'rsync-path' => '/usr/bin/rsync', | 
| 72 |  |  |  |  |  |  | links => 1, | 
| 73 |  |  |  |  |  |  | times => 1, | 
| 74 |  |  |  |  |  |  | 'omit-dir-times' => 1, | 
| 75 |  |  |  |  |  |  | checksum => 1, | 
| 76 |  |  |  |  |  |  | }, | 
| 77 |  |  |  |  |  |  | verbose => 1, | 
| 78 |  |  |  |  |  |  | ); | 
| 79 |  |  |  |  |  |  | $rf->mirror; | 
| 80 |  |  |  |  |  |  |  | 
| 81 |  |  |  |  |  |  | Aggregator (usually the writer): | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  | my $rf = File::Rsync::Mirror::Recentfile->new_from_file ( $file ); | 
| 84 |  |  |  |  |  |  | $rf->aggregate; | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 87 |  |  |  |  |  |  |  | 
| 88 |  |  |  |  |  |  | Lower level than F:R:M:Recent, handles one recentfile. Whereas a tree | 
| 89 |  |  |  |  |  |  | is always composed of several recentfiles, controlled by the | 
| 90 |  |  |  |  |  |  | F:R:M:Recent object. The Recentfile object has to do the bookkeeping | 
| 91 |  |  |  |  |  |  | for a single timeslice. | 
| 92 |  |  |  |  |  |  |  | 
| 93 |  |  |  |  |  |  | =head1 EXPORT | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  | No exports. | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | =head1 CONSTRUCTORS / DESTRUCTOR | 
| 98 |  |  |  |  |  |  |  | 
| 99 |  |  |  |  |  |  | =head2 my $obj = CLASS->new(%hash) | 
| 100 |  |  |  |  |  |  |  | 
| 101 |  |  |  |  |  |  | Constructor. On every argument pair the key is a method name and the | 
| 102 |  |  |  |  |  |  | value is an argument to that method name. | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | If a recentfile for this resource already exists, metadata that are | 
| 105 |  |  |  |  |  |  | not defined by the constructor will be fetched from there as soon as | 
| 106 |  |  |  |  |  |  | it is being read by recent_events(). | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  | =cut | 
| 109 |  |  |  |  |  |  |  | 
| 110 |  |  |  |  |  |  | sub new { | 
| 111 | 542 |  |  | 542 | 1 | 550396 | my($class, @args) = @_; | 
| 112 | 542 |  |  |  |  | 2796 | my $self = bless {}, $class; | 
| 113 | 542 |  |  |  |  | 3482 | while (@args) { | 
| 114 | 1700 |  |  |  |  | 8160 | my($method,$arg) = splice @args, 0, 2; | 
| 115 | 1700 |  |  |  |  | 9780 | $self->$method($arg); | 
| 116 |  |  |  |  |  |  | } | 
| 117 | 542 | 50 |  |  |  | 5515 | unless (defined $self->protocol) { | 
| 118 | 542 |  |  |  |  | 4212 | $self->protocol(DEFAULT_PROTOCOL); | 
| 119 |  |  |  |  |  |  | } | 
| 120 | 542 | 100 |  |  |  | 4083 | unless (defined $self->filenameroot) { | 
| 121 | 517 |  |  |  |  | 3751 | $self->filenameroot("RECENT"); | 
| 122 |  |  |  |  |  |  | } | 
| 123 | 542 | 100 |  |  |  | 3682 | unless (defined $self->serializer_suffix) { | 
| 124 | 522 |  |  |  |  | 4506 | $self->serializer_suffix(".yaml"); | 
| 125 |  |  |  |  |  |  | } | 
| 126 | 542 |  |  |  |  | 4234 | return $self; | 
| 127 |  |  |  |  |  |  | } | 
| 128 |  |  |  |  |  |  |  | 
| 129 |  |  |  |  |  |  | =head2 my $obj = CLASS->new_from_file($file) | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | Constructor. $file is a I. | 
| 132 |  |  |  |  |  |  |  | 
| 133 |  |  |  |  |  |  | =cut | 
| 134 |  |  |  |  |  |  |  | 
| 135 |  |  |  |  |  |  | sub new_from_file { | 
| 136 | 1109 |  |  | 1109 | 1 | 455595 | my($class, $file) = @_; | 
| 137 | 1109 |  |  |  |  | 3711 | my $self = bless {}, $class; | 
| 138 | 1109 |  |  |  |  | 4848 | $self->_rfile($file); | 
| 139 |  |  |  |  |  |  | #?# $self->lock; | 
| 140 | 1109 | 50 |  |  |  | 6191 | my $serialized = do { open my $fh, $file or die "Could not open '$file': $!"; | 
|  | 1109 |  |  |  |  | 54394 |  | 
| 141 | 1109 |  |  |  |  | 8690 | local $/; | 
| 142 | 1109 |  |  |  |  | 58756 | <$fh>; | 
| 143 |  |  |  |  |  |  | }; | 
| 144 |  |  |  |  |  |  | # XXX: we can skip this step when the metadata are sufficient, but | 
| 145 |  |  |  |  |  |  | # we cannot parse the file without some magic stuff about | 
| 146 |  |  |  |  |  |  | # serialized formats | 
| 147 | 1109 |  |  |  |  | 20607 | while (-l $file) { | 
| 148 | 31 |  |  |  |  | 1507 | my($name,$path) = fileparse $file; | 
| 149 | 31 |  |  |  |  | 451 | my $symlink = readlink $file; | 
| 150 | 31 | 50 |  |  |  | 173 | if ($symlink =~ m|/|) { | 
| 151 | 0 |  |  |  |  | 0 | die "FIXME: filenames containing '/' not supported, got $symlink"; | 
| 152 |  |  |  |  |  |  | } | 
| 153 | 31 |  |  |  |  | 1014 | $file = File::Spec->catfile ( $path, $symlink ); | 
| 154 |  |  |  |  |  |  | } | 
| 155 | 1109 |  |  |  |  | 113646 | my($name,$path,$suffix) = fileparse $file, keys %serializers; | 
| 156 | 1109 |  |  |  |  | 8407 | $self->serializer_suffix($suffix); | 
| 157 | 1109 |  |  |  |  | 9037 | $self->localroot($path); | 
| 158 | 1109 | 50 |  |  |  | 7067 | die "Could not determine file format from suffix" unless $suffix; | 
| 159 | 1109 |  |  |  |  | 2158 | my $deserialized; | 
| 160 | 1109 | 50 |  |  |  | 3770 | if ($suffix eq ".yaml") { | 
|  |  | 0 |  |  |  |  |  | 
| 161 | 1109 |  |  |  |  | 7810 | require YAML::Syck; | 
| 162 | 1109 |  |  |  |  | 5581 | $deserialized = YAML::Syck::LoadFile($file); | 
| 163 |  |  |  |  |  |  | } elsif ($HAVE->{"Data::Serializer"}) { | 
| 164 |  |  |  |  |  |  | my $serializer = Data::Serializer->new | 
| 165 | 0 |  |  |  |  | 0 | ( serializer => $serializers{$suffix} ); | 
| 166 | 0 |  |  |  |  | 0 | $deserialized = $serializer->raw_deserialize($serialized); | 
| 167 |  |  |  |  |  |  | } else { | 
| 168 | 0 |  |  |  |  | 0 | die "Data::Serializer not installed, cannot proceed with suffix '$suffix'"; | 
| 169 |  |  |  |  |  |  | } | 
| 170 | 1109 |  |  |  |  | 916142 | while (my($k,$v) = each %{$deserialized->{meta}}) { | 
|  | 13119 |  |  |  |  | 68223 |  | 
| 171 | 12010 | 100 |  |  |  | 24665 | next if $k ne lc $k; # "Producers" | 
| 172 | 10901 |  |  |  |  | 27808 | $self->$k($v); | 
| 173 |  |  |  |  |  |  | } | 
| 174 | 1109 | 50 |  |  |  | 3334 | unless (defined $self->protocol) { | 
| 175 | 0 |  |  |  |  | 0 | $self->protocol(DEFAULT_PROTOCOL); | 
| 176 |  |  |  |  |  |  | } | 
| 177 | 1109 |  |  |  |  | 33306 | return $self; | 
| 178 |  |  |  |  |  |  | } | 
| 179 |  |  |  |  |  |  |  | 
| 180 |  |  |  |  |  |  | =head2 DESTROY | 
| 181 |  |  |  |  |  |  |  | 
| 182 |  |  |  |  |  |  | A simple unlock. | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | =cut | 
| 185 |  |  |  |  |  |  | sub DESTROY { | 
| 186 | 5198 |  |  | 5198 |  | 100779517 | my $self = shift; | 
| 187 | 5198 |  |  |  |  | 16346 | $self->unlock; | 
| 188 | 5198 | 100 |  |  |  | 34110 | unless ($self->_current_tempfile_fh) { | 
| 189 | 5194 | 100 |  |  |  | 23896 | if (my $tempfile = $self->_current_tempfile) { | 
| 190 | 119 | 100 |  |  |  | 16929 | if (-e $tempfile) { | 
| 191 |  |  |  |  |  |  | # unlink $tempfile; # may fail in global destruction | 
| 192 |  |  |  |  |  |  | } | 
| 193 |  |  |  |  |  |  | } | 
| 194 |  |  |  |  |  |  | } | 
| 195 |  |  |  |  |  |  | } | 
| 196 |  |  |  |  |  |  |  | 
| 197 |  |  |  |  |  |  | =head1 ACCESSORS | 
| 198 |  |  |  |  |  |  |  | 
| 199 |  |  |  |  |  |  | =cut | 
| 200 |  |  |  |  |  |  |  | 
| 201 |  |  |  |  |  |  | my @accessors; | 
| 202 |  |  |  |  |  |  |  | 
| 203 |  |  |  |  |  |  | BEGIN { | 
| 204 | 8 |  |  | 8 |  | 75 | @accessors = ( | 
| 205 |  |  |  |  |  |  | "_current_tempfile", | 
| 206 |  |  |  |  |  |  | "_current_tempfile_fh", | 
| 207 |  |  |  |  |  |  | "_delayed_operations", | 
| 208 |  |  |  |  |  |  | "_done", | 
| 209 |  |  |  |  |  |  | "_interval", | 
| 210 |  |  |  |  |  |  | "_is_locked", | 
| 211 |  |  |  |  |  |  | "_localroot", | 
| 212 |  |  |  |  |  |  | "_merged", | 
| 213 |  |  |  |  |  |  | "_pathdb", | 
| 214 |  |  |  |  |  |  | "_remember_last_uptodate_call", | 
| 215 |  |  |  |  |  |  | "_remote_dir", | 
| 216 |  |  |  |  |  |  | "_remoteroot", | 
| 217 |  |  |  |  |  |  | "_requires_fsck", | 
| 218 |  |  |  |  |  |  | "_rfile", | 
| 219 |  |  |  |  |  |  | "_rsync", | 
| 220 |  |  |  |  |  |  | "__verified_tempdir", | 
| 221 |  |  |  |  |  |  | "_seeded", | 
| 222 |  |  |  |  |  |  | "_uptodateness_ever_reached", | 
| 223 |  |  |  |  |  |  | "_use_tempfile", | 
| 224 |  |  |  |  |  |  | ); | 
| 225 |  |  |  |  |  |  |  | 
| 226 | 8 |  |  |  |  | 602 | my @pod_lines = | 
| 227 | 8 |  |  |  |  | 33 | split /\n/, <<'=cut'; push @accessors, grep {s/^=item\s+//} @pod_lines; } | 
|  | 1152 |  |  |  |  | 2287 |  | 
| 228 |  |  |  |  |  |  |  | 
| 229 |  |  |  |  |  |  | =over 4 | 
| 230 |  |  |  |  |  |  |  | 
| 231 |  |  |  |  |  |  | =item aggregator | 
| 232 |  |  |  |  |  |  |  | 
| 233 |  |  |  |  |  |  | A list of interval specs that tell the aggregator which Is | 
| 234 |  |  |  |  |  |  | are to be produced. | 
| 235 |  |  |  |  |  |  |  | 
| 236 |  |  |  |  |  |  | =item canonize | 
| 237 |  |  |  |  |  |  |  | 
| 238 |  |  |  |  |  |  | The name of a method to canonize the path before rsyncing. Only | 
| 239 |  |  |  |  |  |  | supported value is C. Defaults to that. | 
| 240 |  |  |  |  |  |  |  | 
| 241 |  |  |  |  |  |  | =item comment | 
| 242 |  |  |  |  |  |  |  | 
| 243 |  |  |  |  |  |  | A comment about this tree and setup. | 
| 244 |  |  |  |  |  |  |  | 
| 245 |  |  |  |  |  |  | =item dirtymark | 
| 246 |  |  |  |  |  |  |  | 
| 247 |  |  |  |  |  |  | A timestamp. The dirtymark is updated whenever an out of band change | 
| 248 |  |  |  |  |  |  | on the origin server is performed that violates the protocol. Say, | 
| 249 |  |  |  |  |  |  | they add or remove files in the middle somewhere. Slaves must react | 
| 250 |  |  |  |  |  |  | with a devaluation of their C structure which then leads to a | 
| 251 |  |  |  |  |  |  | full re-sync of all files. Implementation note: dirtymark may increase | 
| 252 |  |  |  |  |  |  | or decrease. | 
| 253 |  |  |  |  |  |  |  | 
| 254 |  |  |  |  |  |  | =item filenameroot | 
| 255 |  |  |  |  |  |  |  | 
| 256 |  |  |  |  |  |  | The (prefix of the) filename we use for this I. Defaults to | 
| 257 |  |  |  |  |  |  | C. The string must not contain a directory separator. | 
| 258 |  |  |  |  |  |  |  | 
| 259 |  |  |  |  |  |  | =item have_mirrored | 
| 260 |  |  |  |  |  |  |  | 
| 261 |  |  |  |  |  |  | Timestamp remembering when we mirrored this recentfile the last time. | 
| 262 |  |  |  |  |  |  | Only relevant for slaves. | 
| 263 |  |  |  |  |  |  |  | 
| 264 |  |  |  |  |  |  | =item ignore_link_stat_errors | 
| 265 |  |  |  |  |  |  |  | 
| 266 |  |  |  |  |  |  | If set to true, rsync errors are ignored that complain about link stat | 
| 267 |  |  |  |  |  |  | errors. These seem to happen only when there are files missing at the | 
| 268 |  |  |  |  |  |  | origin. In race conditions this can always happen, so it defaults to | 
| 269 |  |  |  |  |  |  | true. | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | =item is_slave | 
| 272 |  |  |  |  |  |  |  | 
| 273 |  |  |  |  |  |  | If set to true, this object will fetch a new recentfile from remote | 
| 274 |  |  |  |  |  |  | when the timespan between the last mirror (see have_mirrored) and now | 
| 275 |  |  |  |  |  |  | is too large (see C). | 
| 276 |  |  |  |  |  |  |  | 
| 277 |  |  |  |  |  |  | =item keep_delete_objects_forever | 
| 278 |  |  |  |  |  |  |  | 
| 279 |  |  |  |  |  |  | The default for delete events is that they are passed through the | 
| 280 |  |  |  |  |  |  | collection of recentfile objects until they reach the Z file. There | 
| 281 |  |  |  |  |  |  | they get dropped so that the associated file object ceases to exist at | 
| 282 |  |  |  |  |  |  | all. By setting C the delete objects are | 
| 283 |  |  |  |  |  |  | kept forever. This makes the Z file larger but has the advantage that | 
| 284 |  |  |  |  |  |  | slaves that have interrupted mirroring for a long time still can clean | 
| 285 |  |  |  |  |  |  | up their copy. | 
| 286 |  |  |  |  |  |  |  | 
| 287 |  |  |  |  |  |  | =item locktimeout | 
| 288 |  |  |  |  |  |  |  | 
| 289 |  |  |  |  |  |  | After how many seconds shall we die if we cannot lock a I? | 
| 290 |  |  |  |  |  |  | Defaults to 600 seconds. | 
| 291 |  |  |  |  |  |  |  | 
| 292 |  |  |  |  |  |  | =item loopinterval | 
| 293 |  |  |  |  |  |  |  | 
| 294 |  |  |  |  |  |  | When mirror_loop is called, this accessor can specify how much time | 
| 295 |  |  |  |  |  |  | every loop shall at least take. If the work of a loop is done before | 
| 296 |  |  |  |  |  |  | that time has gone, sleeps for the rest of the time. Defaults to | 
| 297 |  |  |  |  |  |  | arbitrary 42 seconds. | 
| 298 |  |  |  |  |  |  |  | 
| 299 |  |  |  |  |  |  | =item max_files_per_connection | 
| 300 |  |  |  |  |  |  |  | 
| 301 |  |  |  |  |  |  | Maximum number of files that are transferred on a single rsync call. | 
| 302 |  |  |  |  |  |  | Setting it higher means higher performance at the price of holding | 
| 303 |  |  |  |  |  |  | connections longer and potentially disturbing other users in the pool. | 
| 304 |  |  |  |  |  |  | Defaults to the arbitrary value 42. | 
| 305 |  |  |  |  |  |  |  | 
| 306 |  |  |  |  |  |  | =item max_rsync_errors | 
| 307 |  |  |  |  |  |  |  | 
| 308 |  |  |  |  |  |  | When rsync operations encounter that many errors without any resetting | 
| 309 |  |  |  |  |  |  | success in between, then we die. Defaults to unlimited. A value of | 
| 310 |  |  |  |  |  |  | -1 means we run forever ignoring all rsync errors. | 
| 311 |  |  |  |  |  |  |  | 
| 312 |  |  |  |  |  |  | =item minmax | 
| 313 |  |  |  |  |  |  |  | 
| 314 |  |  |  |  |  |  | Hashref remembering when we read the recent_events from this file the | 
| 315 |  |  |  |  |  |  | last time and what the timespan was. | 
| 316 |  |  |  |  |  |  |  | 
| 317 |  |  |  |  |  |  | =item protocol | 
| 318 |  |  |  |  |  |  |  | 
| 319 |  |  |  |  |  |  | When the RECENT file format changes, we increment the protocol. We try | 
| 320 |  |  |  |  |  |  | to support older protocols in later releases. | 
| 321 |  |  |  |  |  |  |  | 
| 322 |  |  |  |  |  |  | =item remote_host | 
| 323 |  |  |  |  |  |  |  | 
| 324 |  |  |  |  |  |  | The host we are mirroring from. Leave empty for the local filesystem. | 
| 325 |  |  |  |  |  |  |  | 
| 326 |  |  |  |  |  |  | =item remote_module | 
| 327 |  |  |  |  |  |  |  | 
| 328 |  |  |  |  |  |  | Rsync servers have so called modules to separate directory trees from | 
| 329 |  |  |  |  |  |  | each other. Put here the name of the module under which we are | 
| 330 |  |  |  |  |  |  | mirroring. Leave empty for local filesystem. | 
| 331 |  |  |  |  |  |  |  | 
| 332 |  |  |  |  |  |  | =item rsync_options | 
| 333 |  |  |  |  |  |  |  | 
| 334 |  |  |  |  |  |  | Things like compress, links, times or checksums. Passed in to the | 
| 335 |  |  |  |  |  |  | File::Rsync object used to run the mirror. | 
| 336 |  |  |  |  |  |  |  | 
| 337 |  |  |  |  |  |  | =item serializer_suffix | 
| 338 |  |  |  |  |  |  |  | 
| 339 |  |  |  |  |  |  | Mostly untested accessor. The only well tested format for | 
| 340 |  |  |  |  |  |  | Is at the moment is YAML. It is used with YAML::Syck via | 
| 341 |  |  |  |  |  |  | Data::Serializer. But in principle other formats are supported as | 
| 342 |  |  |  |  |  |  | well. See section SERIALIZERS below. | 
| 343 |  |  |  |  |  |  |  | 
| 344 |  |  |  |  |  |  | =item sleep_per_connection | 
| 345 |  |  |  |  |  |  |  | 
| 346 |  |  |  |  |  |  | Sleep that many seconds (floating point OK) after every chunk of rsyncing | 
| 347 |  |  |  |  |  |  | has finished. Defaults to arbitrary 0.42. | 
| 348 |  |  |  |  |  |  |  | 
| 349 |  |  |  |  |  |  | =item tempdir | 
| 350 |  |  |  |  |  |  |  | 
| 351 |  |  |  |  |  |  | Directory to write temporary files to. Must allow rename operations | 
| 352 |  |  |  |  |  |  | into the tree which usually means it must live on the same partition | 
| 353 |  |  |  |  |  |  | as the target directory. Defaults to C<< $self->localroot >>. | 
| 354 |  |  |  |  |  |  |  | 
| 355 |  |  |  |  |  |  | =item ttl | 
| 356 |  |  |  |  |  |  |  | 
| 357 |  |  |  |  |  |  | Time to live. Number of seconds after which this recentfile must be | 
| 358 |  |  |  |  |  |  | fetched again from the origin server. Only relevant for slaves. | 
| 359 |  |  |  |  |  |  | Defaults to arbitrary 24.2 seconds. | 
| 360 |  |  |  |  |  |  |  | 
| 361 |  |  |  |  |  |  | =item verbose | 
| 362 |  |  |  |  |  |  |  | 
| 363 |  |  |  |  |  |  | Boolean to turn on a bit verbosity. | 
| 364 |  |  |  |  |  |  |  | 
| 365 |  |  |  |  |  |  | =item verboselog | 
| 366 |  |  |  |  |  |  |  | 
| 367 |  |  |  |  |  |  | Path to the logfile to write verbose progress information to. This is | 
| 368 |  |  |  |  |  |  | a primitive stop gap solution to get simple verbose logging working. | 
| 369 |  |  |  |  |  |  | Switching to Log4perl or similar is probably the way to go. | 
| 370 |  |  |  |  |  |  |  | 
| 371 |  |  |  |  |  |  | =back | 
| 372 |  |  |  |  |  |  |  | 
| 373 |  |  |  |  |  |  | =cut | 
| 374 |  |  |  |  |  |  |  | 
| 375 | 8 |  |  | 8 |  | 3674 | use accessors @accessors; | 
|  | 8 |  |  |  |  | 7540 |  | 
|  | 8 |  |  |  |  | 44 |  | 
| 376 |  |  |  |  |  |  |  | 
| 377 |  |  |  |  |  |  | =head1 METHODS | 
| 378 |  |  |  |  |  |  |  | 
| 379 |  |  |  |  |  |  | =head2 (void) $obj->aggregate( %options ) | 
| 380 |  |  |  |  |  |  |  | 
| 381 |  |  |  |  |  |  | Takes all intervals that are collected in the accessor called | 
| 382 |  |  |  |  |  |  | aggregator. Sorts them by actual length of the interval. | 
| 383 |  |  |  |  |  |  | Removes those that are shorter than our own interval. Then merges this | 
| 384 |  |  |  |  |  |  | object into the next larger object. The merging continues upwards | 
| 385 |  |  |  |  |  |  | as long as the next I is old enough to warrant a merge. | 
| 386 |  |  |  |  |  |  |  | 
| 387 |  |  |  |  |  |  | If a merge is warranted is decided according to the interval of the | 
| 388 |  |  |  |  |  |  | previous interval so that larger files are not so often updated as | 
| 389 |  |  |  |  |  |  | smaller ones. If $options{force} is true, all files get updated. | 
| 390 |  |  |  |  |  |  |  | 
| 391 |  |  |  |  |  |  | Here is an example to illustrate the behaviour. Given aggregators | 
| 392 |  |  |  |  |  |  |  | 
| 393 |  |  |  |  |  |  | 1h 1d 1W 1M 1Q 1Y Z | 
| 394 |  |  |  |  |  |  |  | 
| 395 |  |  |  |  |  |  | then | 
| 396 |  |  |  |  |  |  |  | 
| 397 |  |  |  |  |  |  | 1h updates 1d on every call to aggregate() | 
| 398 |  |  |  |  |  |  | 1d updates 1W earliest after 1h | 
| 399 |  |  |  |  |  |  | 1W updates 1M earliest after 1d | 
| 400 |  |  |  |  |  |  | 1M updates 1Q earliest after 1W | 
| 401 |  |  |  |  |  |  | 1Q updates 1Y earliest after 1M | 
| 402 |  |  |  |  |  |  | 1Y updates  Z earliest after 1Q | 
| 403 |  |  |  |  |  |  |  | 
| 404 |  |  |  |  |  |  | Note that all but the smallest recentfile get updated at an arbitrary | 
| 405 |  |  |  |  |  |  | rate and as such are quite useless on their own. | 
| 406 |  |  |  |  |  |  |  | 
| 407 |  |  |  |  |  |  | =cut | 
| 408 |  |  |  |  |  |  |  | 
| 409 |  |  |  |  |  |  | sub aggregate { | 
| 410 | 354 |  |  | 354 | 1 | 18088338 | my($self, %option) = @_; | 
| 411 | 354 |  |  |  |  | 1162 | my %seen_interval; | 
| 412 | 2932 |  |  |  |  | 5235 | my @aggs = sort { $a->{secs} <=> $b->{secs} } | 
| 413 | 1870 | 50 |  |  |  | 8093 | grep { !$seen_interval{$_->{interval}}++ && $_->{secs} >= $self->interval_secs } | 
| 414 | 1870 |  |  |  |  | 5970 | map { { interval => $_, secs => $self->interval_secs($_)} } | 
| 415 | 354 | 50 |  |  |  | 1401 | $self->interval, @{$self->aggregator || []}; | 
|  | 354 |  |  |  |  | 1480 |  | 
| 416 | 354 |  |  |  |  | 1330 | $self->update; | 
| 417 | 354 |  |  |  |  | 10770 | $aggs[0]{object} = $self; | 
| 418 | 354 |  |  |  |  | 1574 | AGGREGATOR: for my $i (0..$#aggs-1) { | 
| 419 | 986 |  |  |  |  | 2055 | my $this = $aggs[$i]{object}; | 
| 420 | 986 |  |  |  |  | 2878 | my $next = $this->_sparse_clone; | 
| 421 | 986 |  |  |  |  | 4016 | $next->interval($aggs[$i+1]{interval}); | 
| 422 | 986 |  |  |  |  | 1633 | my $want_merge = 0; | 
| 423 | 986 | 100 | 100 |  |  | 4632 | if ($option{force} || $i == 0) { | 
| 424 | 606 |  |  |  |  | 991 | $want_merge = 1; | 
| 425 |  |  |  |  |  |  | } else { | 
| 426 | 380 |  |  |  |  | 1325 | my $next_rfile = $next->rfile; | 
| 427 | 380 | 100 |  |  |  | 8055 | if (-e $next_rfile) { | 
| 428 | 320 |  |  |  |  | 1650 | my $prev = $aggs[$i-1]{object}; | 
| 429 | 320 |  |  |  |  | 2305 | local $^T = time; | 
| 430 | 320 |  |  |  |  | 4310 | my $next_age = 86400 * -M $next_rfile; | 
| 431 | 320 | 100 |  |  |  | 1530 | if ($next_age > $prev->interval_secs) { | 
| 432 | 55 |  |  |  |  | 195 | $want_merge = 1; | 
| 433 |  |  |  |  |  |  | } | 
| 434 |  |  |  |  |  |  | } else { | 
| 435 | 60 |  |  |  |  | 175 | $want_merge = 1; | 
| 436 |  |  |  |  |  |  | } | 
| 437 |  |  |  |  |  |  | } | 
| 438 | 986 | 100 |  |  |  | 2407 | if ($want_merge) { | 
| 439 | 721 |  |  |  |  | 2959 | $next->merge($this); | 
| 440 | 721 |  |  |  |  | 20279 | $aggs[$i+1]{object} = $next; | 
| 441 |  |  |  |  |  |  | } else { | 
| 442 | 265 |  |  |  |  | 1515 | last AGGREGATOR; | 
| 443 |  |  |  |  |  |  | } | 
| 444 |  |  |  |  |  |  | } | 
| 445 |  |  |  |  |  |  | } | 
| 446 |  |  |  |  |  |  |  | 
| 447 |  |  |  |  |  |  | # collect file size and mtime for all files of this aggregate | 
| 448 |  |  |  |  |  |  | sub _debug_aggregate { | 
| 449 | 30 |  |  | 30 |  | 25530 | my($self) = @_; | 
| 450 | 270 |  |  |  |  | 430 | my @aggs = sort { $a->{secs} <=> $b->{secs} } | 
| 451 | 180 |  |  |  |  | 685 | map { { interval => $_, secs => $self->interval_secs($_)} } | 
| 452 | 30 | 50 |  |  |  | 120 | $self->interval, @{$self->aggregator || []}; | 
|  | 30 |  |  |  |  | 95 |  | 
| 453 | 30 |  |  |  |  | 85 | my $report = []; | 
| 454 | 30 |  |  |  |  | 115 | for my $i (0..$#aggs) { | 
| 455 | 180 |  |  |  |  | 10945 | my $this = Storable::dclone $self; | 
| 456 | 180 |  |  |  |  | 705 | $this->interval($aggs[$i]{interval}); | 
| 457 | 180 |  |  |  |  | 345 | my $rfile = $this->rfile; | 
| 458 | 180 |  |  |  |  | 3195 | my @stat = stat $rfile; | 
| 459 | 180 |  |  |  |  | 1410 | push @$report, {rfile => $rfile, size => $stat[7], mtime => $stat[9]}; | 
| 460 |  |  |  |  |  |  | } | 
| 461 | 30 |  |  |  |  | 425 | $report; | 
| 462 |  |  |  |  |  |  | } | 
| 463 |  |  |  |  |  |  |  | 
| 464 |  |  |  |  |  |  | # (void) $self->_assert_symlink() | 
| 465 |  |  |  |  |  |  | sub _assert_symlink { | 
| 466 | 1646 |  |  | 1646 |  | 3809 | my($self) = @_; | 
| 467 | 1646 |  |  |  |  | 4023 | my $recentrecentfile = File::Spec->catfile | 
| 468 |  |  |  |  |  |  | ( | 
| 469 |  |  |  |  |  |  | $self->localroot, | 
| 470 |  |  |  |  |  |  | sprintf | 
| 471 |  |  |  |  |  |  | ( | 
| 472 |  |  |  |  |  |  | "%s.recent", | 
| 473 |  |  |  |  |  |  | $self->filenameroot | 
| 474 |  |  |  |  |  |  | ) | 
| 475 |  |  |  |  |  |  | ); | 
| 476 | 1646 | 50 |  |  |  | 57766 | if ($Config{d_symlink} eq "define") { | 
| 477 | 1646 |  |  |  |  | 3413 | my $howto_create_symlink; # 0=no need; 1=straight symlink; 2=rename symlink | 
| 478 | 1646 | 100 |  |  |  | 31890 | if (-l $recentrecentfile) { | 
| 479 | 1625 |  |  |  |  | 18584 | my $found_symlink = readlink $recentrecentfile; | 
| 480 | 1625 | 100 |  |  |  | 6553 | if ($found_symlink eq $self->rfilename) { | 
| 481 | 1610 |  |  |  |  | 3675 | return; | 
| 482 |  |  |  |  |  |  | } else { | 
| 483 | 15 |  |  |  |  | 35 | $howto_create_symlink = 2; | 
| 484 |  |  |  |  |  |  | } | 
| 485 |  |  |  |  |  |  | } else { | 
| 486 | 21 |  |  |  |  | 63 | $howto_create_symlink = 1; | 
| 487 |  |  |  |  |  |  | } | 
| 488 | 36 | 100 |  |  |  | 100 | if (1 == $howto_create_symlink) { | 
| 489 | 21 | 50 |  |  |  | 54 | symlink $self->rfilename, $recentrecentfile or die "Could not create symlink '$recentrecentfile': $!" | 
| 490 |  |  |  |  |  |  | } else { | 
| 491 | 15 |  |  |  |  | 270 | unlink "$recentrecentfile.$$"; # may fail | 
| 492 | 15 | 50 |  |  |  | 60 | symlink $self->rfilename, "$recentrecentfile.$$" or die "Could not create symlink '$recentrecentfile.$$': $!"; | 
| 493 | 15 | 50 |  |  |  | 620 | rename "$recentrecentfile.$$", $recentrecentfile or die "Could not rename '$recentrecentfile.$$' to $recentrecentfile: $!"; | 
| 494 |  |  |  |  |  |  | } | 
| 495 |  |  |  |  |  |  | } else { | 
| 496 | 0 |  |  |  |  | 0 | warn "Warning: symlinks not supported on this system, doing a copy instead\n"; | 
| 497 | 0 |  |  |  |  | 0 | unlink "$recentrecentfile.$$"; # may fail | 
| 498 | 0 | 0 |  |  |  | 0 | cp $self->rfilename, "$recentrecentfile.$$" or die "Could not copy to '$recentrecentfile.$$': $!"; | 
| 499 | 0 | 0 |  |  |  | 0 | rename "$recentrecentfile.$$", $recentrecentfile or die "Could not rename '$recentrecentfile.$$' to $recentrecentfile: $!"; | 
| 500 |  |  |  |  |  |  | } | 
| 501 |  |  |  |  |  |  | } | 
| 502 |  |  |  |  |  |  |  | 
| 503 |  |  |  |  |  |  | =head2 $hashref = $obj->delayed_operations | 
| 504 |  |  |  |  |  |  |  | 
| 505 |  |  |  |  |  |  | A hash of hashes containing unlink and rmdir operations which had to | 
| 506 |  |  |  |  |  |  | wait until the recentfile got unhidden in order to not confuse | 
| 507 |  |  |  |  |  |  | downstream mirrors (in case we have some). | 
| 508 |  |  |  |  |  |  |  | 
| 509 |  |  |  |  |  |  | =cut | 
| 510 |  |  |  |  |  |  |  | 
| 511 |  |  |  |  |  |  | sub delayed_operations { | 
| 512 | 41 |  |  | 41 | 1 | 219 | my($self) = @_; | 
| 513 | 41 |  |  |  |  | 558 | my $x = $self->_delayed_operations; | 
| 514 | 41 | 100 |  |  |  | 599 | unless (defined $x) { | 
| 515 | 15 |  |  |  |  | 360 | $x = { | 
| 516 |  |  |  |  |  |  | unlink => {}, | 
| 517 |  |  |  |  |  |  | rmdir => {}, | 
| 518 |  |  |  |  |  |  | }; | 
| 519 | 15 |  |  |  |  | 181 | $self->_delayed_operations ($x); | 
| 520 |  |  |  |  |  |  | } | 
| 521 | 41 |  |  |  |  | 688 | return $x; | 
| 522 |  |  |  |  |  |  | } | 
| 523 |  |  |  |  |  |  |  | 
| 524 |  |  |  |  |  |  | =head2 $done = $obj->done | 
| 525 |  |  |  |  |  |  |  | 
| 526 |  |  |  |  |  |  | C<$done> is a reference to a L | 
| 527 |  |  |  |  |  |  | object that keeps track of rsync activities. Only needed and used when | 
| 528 |  |  |  |  |  |  | we are a mirroring slave. | 
| 529 |  |  |  |  |  |  |  | 
| 530 |  |  |  |  |  |  | =cut | 
| 531 |  |  |  |  |  |  |  | 
| 532 |  |  |  |  |  |  | sub done { | 
| 533 | 119 |  |  | 119 | 1 | 1110 | my($self) = @_; | 
| 534 | 119 |  |  |  |  | 2112 | require File::Rsync::Mirror::Recentfile::Done; | 
| 535 | 119 |  |  |  |  | 1700 | my $done = $self->_done; | 
| 536 | 119 | 100 |  |  |  | 3006 | if (!$done) { | 
|  |  | 100 |  |  |  |  |  | 
| 537 | 15 |  |  |  |  | 691 | $done = File::Rsync::Mirror::Recentfile::Done->new(); | 
| 538 | 15 |  |  |  |  | 199 | $done->_rfinterval ($self->interval); | 
| 539 | 15 |  |  |  |  | 408 | $self->_done ( $done ); | 
| 540 |  |  |  |  |  |  | } elsif (!blessed $done) { | 
| 541 |  |  |  |  |  |  | # when the serializer does not support blessed objects | 
| 542 | 10 |  |  |  |  | 281 | bless $done, 'File::Rsync::Mirror::Recentfile::Done'; | 
| 543 | 10 |  |  |  |  | 99 | $self->_done ( $done ); | 
| 544 |  |  |  |  |  |  | } | 
| 545 | 119 |  |  |  |  | 2118 | return $done; | 
| 546 |  |  |  |  |  |  | } | 
| 547 |  |  |  |  |  |  |  | 
| 548 |  |  |  |  |  |  | =head2 $tempfilename = $obj->get_remote_recentfile_as_tempfile () | 
| 549 |  |  |  |  |  |  |  | 
| 550 |  |  |  |  |  |  | Stores the remote I locally as a tempfile. The caller is | 
| 551 |  |  |  |  |  |  | responsible to remove the file after use. | 
| 552 |  |  |  |  |  |  |  | 
| 553 |  |  |  |  |  |  | Note: if you're intending to act as an rsync server for other slaves, | 
| 554 |  |  |  |  |  |  | then you must prefer this method to fetch that file with | 
| 555 |  |  |  |  |  |  | get_remotefile(). Otherwise downstream mirrors would expect you to | 
| 556 |  |  |  |  |  |  | already have mirrored all the files that are in the I | 
| 557 |  |  |  |  |  |  | before you have them mirrored. | 
| 558 |  |  |  |  |  |  |  | 
| 559 |  |  |  |  |  |  | =cut | 
| 560 |  |  |  |  |  |  |  | 
| 561 |  |  |  |  |  |  | sub get_remote_recentfile_as_tempfile { | 
| 562 | 74 |  |  | 74 | 1 | 280 | my($self) = @_; | 
| 563 | 74 |  |  |  |  | 559 | mkpath $self->localroot; | 
| 564 | 74 |  |  |  |  | 8329 | my $fh; | 
| 565 |  |  |  |  |  |  | my $trfilename; | 
| 566 | 74 | 100 |  |  |  | 470 | if ( $self->_use_tempfile() ) { | 
| 567 | 43 | 100 |  |  |  | 660 | if ($self->ttl_reached) { | 
| 568 | 10 |  |  |  |  | 127 | $fh = $self->_current_tempfile_fh; | 
| 569 | 10 |  |  |  |  | 172 | $trfilename = $self->rfilename; | 
| 570 |  |  |  |  |  |  | } else { | 
| 571 | 33 |  |  |  |  | 216 | return $self->_current_tempfile; | 
| 572 |  |  |  |  |  |  | } | 
| 573 |  |  |  |  |  |  | } else { | 
| 574 | 31 |  |  |  |  | 402 | $trfilename = $self->rfilename; | 
| 575 |  |  |  |  |  |  | } | 
| 576 |  |  |  |  |  |  |  | 
| 577 | 41 |  |  |  |  | 245 | my $dst; | 
| 578 | 41 | 50 |  |  |  | 300 | if ($fh) { | 
| 579 | 0 |  |  |  |  | 0 | $dst = $self->_current_tempfile; | 
| 580 |  |  |  |  |  |  | } else { | 
| 581 | 41 |  |  |  |  | 290 | $fh = $self->_get_remote_rat_provide_tempfile_object ($trfilename); | 
| 582 | 41 |  |  |  |  | 305 | $dst = $fh->filename; | 
| 583 | 41 |  |  |  |  | 610 | $self->_current_tempfile ($dst); | 
| 584 | 41 |  |  |  |  | 353 | my $rfile = eval { $self->rfile; }; # may fail (RECENT.recent has no rfile) | 
|  | 41 |  |  |  |  | 221 |  | 
| 585 | 41 | 100 | 66 |  |  | 1369 | if (defined $rfile && -e $rfile) { | 
| 586 |  |  |  |  |  |  | # saving on bandwidth. Might need to be configurable | 
| 587 |  |  |  |  |  |  | # $self->bandwidth_is_cheap? | 
| 588 | 27 | 50 |  |  |  | 626 | cp $rfile, $dst or die "Could not copy '$rfile' to '$dst': $!" | 
| 589 |  |  |  |  |  |  | } | 
| 590 |  |  |  |  |  |  | } | 
| 591 | 41 |  |  |  |  | 18615 | my $src = join ("/", | 
| 592 |  |  |  |  |  |  | $self->remoteroot, | 
| 593 |  |  |  |  |  |  | $trfilename, | 
| 594 |  |  |  |  |  |  | ); | 
| 595 | 41 | 50 |  |  |  | 496 | if ($self->verbose) { | 
| 596 | 0 | 0 |  |  |  | 0 | my $doing = -e $dst ? "Sync" : "Get"; | 
| 597 | 0 |  |  |  |  | 0 | my $display_dst = join "/", "...", basename(dirname($dst)), basename($dst); | 
| 598 | 0 |  |  |  |  | 0 | my $LFH = $self->_logfilehandle; | 
| 599 | 0 |  |  |  |  | 0 | printf $LFH | 
| 600 |  |  |  |  |  |  | ( | 
| 601 |  |  |  |  |  |  | "%-4s %d (1/1/%s) temp %s ... ", | 
| 602 |  |  |  |  |  |  | $doing, | 
| 603 |  |  |  |  |  |  | time, | 
| 604 |  |  |  |  |  |  | $self->interval, | 
| 605 |  |  |  |  |  |  | $display_dst, | 
| 606 |  |  |  |  |  |  | ); | 
| 607 |  |  |  |  |  |  | } | 
| 608 | 41 |  |  |  |  | 713 | my $gaveup = 0; | 
| 609 | 41 |  |  |  |  | 113 | my $retried = 0; | 
| 610 | 41 |  |  |  |  | 1148 | local($ENV{LANG}) = "C"; | 
| 611 | 41 |  |  |  |  | 332 | while (!$self->rsync->exec( | 
| 612 |  |  |  |  |  |  | src => $src, | 
| 613 |  |  |  |  |  |  | dst => $dst, | 
| 614 |  |  |  |  |  |  | )) { | 
| 615 | 0 |  |  |  |  | 0 | $self->register_rsync_error ($self->rsync->err); | 
| 616 | 0 | 0 |  |  |  | 0 | if (++$retried >= 3) { | 
| 617 | 0 |  |  |  |  | 0 | warn "XXX giving up"; | 
| 618 | 0 |  |  |  |  | 0 | $gaveup = 1; | 
| 619 | 0 |  |  |  |  | 0 | last; | 
| 620 |  |  |  |  |  |  | } | 
| 621 |  |  |  |  |  |  | } | 
| 622 | 41 | 50 |  |  |  | 2154324 | if ($gaveup) { | 
| 623 | 0 |  |  |  |  | 0 | my $LFH = $self->_logfilehandle; | 
| 624 | 0 |  |  |  |  | 0 | printf $LFH "Warning: gave up mirroring %s, will try again later", $self->interval; | 
| 625 |  |  |  |  |  |  | } else { | 
| 626 | 41 |  |  |  |  | 1984 | $self->_refresh_internals ($dst); | 
| 627 | 41 |  |  |  |  | 848 | $self->have_mirrored (Time::HiRes::time); | 
| 628 | 41 |  |  |  |  | 922 | $self->un_register_rsync_error (); | 
| 629 |  |  |  |  |  |  | } | 
| 630 | 41 |  |  |  |  | 672 | $self->unseed; | 
| 631 | 41 | 50 |  |  |  | 530 | if ($self->verbose) { | 
| 632 | 0 |  |  |  |  | 0 | my $LFH = $self->_logfilehandle; | 
| 633 | 0 |  |  |  |  | 0 | print $LFH "DONE\n"; | 
| 634 |  |  |  |  |  |  | } | 
| 635 | 41 |  |  |  |  | 408 | my $mode = 0644; | 
| 636 | 41 | 50 |  |  |  | 1835 | chmod $mode, $dst or die "Could not chmod $mode '$dst': $!"; | 
| 637 | 41 |  |  |  |  | 2030 | return $dst; | 
| 638 |  |  |  |  |  |  | } | 
| 639 |  |  |  |  |  |  |  | 
| 640 |  |  |  |  |  |  | sub _verified_tempdir { | 
| 641 | 41 |  |  | 41 |  | 151 | my($self) = @_; | 
| 642 | 41 |  |  |  |  | 253 | my $tempdir = $self->__verified_tempdir(); | 
| 643 | 41 | 100 |  |  |  | 566 | return $tempdir if defined $tempdir; | 
| 644 | 20 | 50 |  |  |  | 143 | unless ($tempdir = $self->tempdir) { | 
| 645 | 20 |  |  |  |  | 182 | $tempdir = $self->localroot; | 
| 646 |  |  |  |  |  |  | } | 
| 647 | 20 | 50 |  |  |  | 483 | unless (-d $tempdir) { | 
| 648 | 0 |  |  |  |  | 0 | mkpath $tempdir; | 
| 649 |  |  |  |  |  |  | } | 
| 650 | 20 |  |  |  |  | 139 | $self->__verified_tempdir($tempdir); | 
| 651 | 20 |  |  |  |  | 184 | return $tempdir; | 
| 652 |  |  |  |  |  |  | } | 
| 653 |  |  |  |  |  |  |  | 
| 654 |  |  |  |  |  |  | sub _get_remote_rat_provide_tempfile_object { | 
| 655 | 41 |  |  | 41 |  | 523 | my($self, $trfilename) = @_; | 
| 656 | 41 |  |  |  |  | 214 | my $_verified_tempdir = $self->_verified_tempdir; | 
| 657 | 41 |  |  |  |  | 558 | my $fh = File::Temp->new | 
| 658 |  |  |  |  |  |  | (TEMPLATE => sprintf(".FRMRecent-%s-XXXX", | 
| 659 |  |  |  |  |  |  | $trfilename, | 
| 660 |  |  |  |  |  |  | ), | 
| 661 |  |  |  |  |  |  | DIR => $_verified_tempdir, | 
| 662 |  |  |  |  |  |  | SUFFIX => $self->serializer_suffix, | 
| 663 |  |  |  |  |  |  | UNLINK => $self->_use_tempfile, | 
| 664 |  |  |  |  |  |  | ); | 
| 665 | 41 |  |  |  |  | 35810 | my $mode = 0644; | 
| 666 | 41 |  |  |  |  | 211 | my $dst = $fh->filename; | 
| 667 | 41 | 50 |  |  |  | 1215 | chmod $mode, $dst or die "Could not chmod $mode '$dst': $!"; | 
| 668 | 41 | 100 |  |  |  | 456 | if ($self->_use_tempfile) { | 
| 669 | 10 |  |  |  |  | 175 | $self->_current_tempfile_fh ($fh); # delay self destruction | 
| 670 |  |  |  |  |  |  | } | 
| 671 | 41 |  |  |  |  | 665 | return $fh; | 
| 672 |  |  |  |  |  |  | } | 
| 673 |  |  |  |  |  |  |  | 
| 674 |  |  |  |  |  |  | sub _logfilehandle { | 
| 675 | 0 |  |  | 0 |  | 0 | my($self) = @_; | 
| 676 | 0 |  |  |  |  | 0 | my $fh; | 
| 677 | 0 | 0 |  |  |  | 0 | if (my $vl = $self->verboselog) { | 
| 678 | 0 | 0 |  |  |  | 0 | open $fh, ">>", $vl or die "Could not open >> '$vl': $!"; | 
| 679 |  |  |  |  |  |  | } else { | 
| 680 | 0 |  |  |  |  | 0 | $fh = \*STDERR; | 
| 681 |  |  |  |  |  |  | } | 
| 682 | 0 |  |  |  |  | 0 | return $fh; | 
| 683 |  |  |  |  |  |  | } | 
| 684 |  |  |  |  |  |  |  | 
| 685 |  |  |  |  |  |  | =head2 $localpath = $obj->get_remotefile ( $relative_path ) | 
| 686 |  |  |  |  |  |  |  | 
| 687 |  |  |  |  |  |  | Rsyncs one single remote file to local filesystem. | 
| 688 |  |  |  |  |  |  |  | 
| 689 |  |  |  |  |  |  | Note: no locking is done on this file. Any number of processes may | 
| 690 |  |  |  |  |  |  | mirror this object. | 
| 691 |  |  |  |  |  |  |  | 
| 692 |  |  |  |  |  |  | Note II: do not use for recentfiles. If you are a cascading | 
| 693 |  |  |  |  |  |  | slave/server combination, it would confuse other slaves. They would | 
| 694 |  |  |  |  |  |  | expect the contents of these recentfiles to be available. Use | 
| 695 |  |  |  |  |  |  | get_remote_recentfile_as_tempfile() instead. | 
| 696 |  |  |  |  |  |  |  | 
| 697 |  |  |  |  |  |  | =cut | 
| 698 |  |  |  |  |  |  |  | 
| 699 |  |  |  |  |  |  | sub get_remotefile { | 
| 700 | 0 |  |  | 0 | 1 | 0 | my($self, $path) = @_; | 
| 701 | 0 |  |  |  |  | 0 | my $dst = File::Spec->catfile($self->localroot, $path); | 
| 702 | 0 |  |  |  |  | 0 | mkpath dirname $dst; | 
| 703 | 0 | 0 |  |  |  | 0 | if ($self->verbose) { | 
| 704 | 0 | 0 |  |  |  | 0 | my $doing = -e $dst ? "Sync" : "Get"; | 
| 705 | 0 |  |  |  |  | 0 | my $LFH = $self->_logfilehandle; | 
| 706 | 0 |  |  |  |  | 0 | printf $LFH | 
| 707 |  |  |  |  |  |  | ( | 
| 708 |  |  |  |  |  |  | "%-4s %d (1/1/%s) %s ... ", | 
| 709 |  |  |  |  |  |  | $doing, | 
| 710 |  |  |  |  |  |  | time, | 
| 711 |  |  |  |  |  |  | $self->interval, | 
| 712 |  |  |  |  |  |  | $path, | 
| 713 |  |  |  |  |  |  | ); | 
| 714 |  |  |  |  |  |  | } | 
| 715 | 0 |  |  |  |  | 0 | local($ENV{LANG}) = "C"; | 
| 716 | 0 | 0 |  |  |  | 0 | my $remoteroot = $self->remoteroot or die "Alert: missing remoteroot. Cannot continue"; | 
| 717 | 0 |  |  |  |  | 0 | while (!$self->rsync->exec( | 
| 718 |  |  |  |  |  |  | src => join("/", | 
| 719 |  |  |  |  |  |  | $remoteroot, | 
| 720 |  |  |  |  |  |  | $path), | 
| 721 |  |  |  |  |  |  | dst => $dst, | 
| 722 |  |  |  |  |  |  | )) { | 
| 723 | 0 |  |  |  |  | 0 | $self->register_rsync_error ($self->rsync->err); | 
| 724 |  |  |  |  |  |  | } | 
| 725 | 0 |  |  |  |  | 0 | $self->un_register_rsync_error (); | 
| 726 | 0 | 0 |  |  |  | 0 | if ($self->verbose) { | 
| 727 | 0 |  |  |  |  | 0 | my $LFH = $self->_logfilehandle; | 
| 728 | 0 |  |  |  |  | 0 | print $LFH "DONE\n"; | 
| 729 |  |  |  |  |  |  | } | 
| 730 | 0 |  |  |  |  | 0 | return $dst; | 
| 731 |  |  |  |  |  |  | } | 
| 732 |  |  |  |  |  |  |  | 
| 733 |  |  |  |  |  |  | =head2 $obj->interval ( $interval_spec ) | 
| 734 |  |  |  |  |  |  |  | 
| 735 |  |  |  |  |  |  | Get/set accessor. $interval_spec is a string and described below in | 
| 736 |  |  |  |  |  |  | the section INTERVAL SPEC. | 
| 737 |  |  |  |  |  |  |  | 
| 738 |  |  |  |  |  |  | =cut | 
| 739 |  |  |  |  |  |  |  | 
| 740 |  |  |  |  |  |  | sub interval { | 
| 741 | 72309 |  |  | 72309 | 1 | 134256 | my ($self, $interval) = @_; | 
| 742 | 72309 | 100 |  |  |  | 129521 | if (@_ >= 2) { | 
| 743 | 5060 |  |  |  |  | 15375 | $self->_interval($interval); | 
| 744 | 5060 |  |  |  |  | 26166 | $self->_rfile(undef); | 
| 745 |  |  |  |  |  |  | } | 
| 746 | 72309 |  |  |  |  | 147635 | $interval = $self->_interval; | 
| 747 | 72309 | 100 |  |  |  | 313546 | unless (defined $interval) { | 
| 748 |  |  |  |  |  |  | # do not ask the $self too much, it recurses! | 
| 749 | 1 |  |  |  |  | 7 | require Carp; | 
| 750 | 1 |  |  |  |  | 215 | Carp::confess("Alert: interval undefined for '".$self."'. Cannot continue."); | 
| 751 |  |  |  |  |  |  | } | 
| 752 | 72308 |  |  |  |  | 197955 | return $interval; | 
| 753 |  |  |  |  |  |  | } | 
| 754 |  |  |  |  |  |  |  | 
| 755 |  |  |  |  |  |  | =head2 $secs = $obj->interval_secs ( $interval_spec ) | 
| 756 |  |  |  |  |  |  |  | 
| 757 |  |  |  |  |  |  | $interval_spec is described below in the section INTERVAL SPEC. If | 
| 758 |  |  |  |  |  |  | empty defaults to the inherent interval for this object. | 
| 759 |  |  |  |  |  |  |  | 
| 760 |  |  |  |  |  |  | =cut | 
| 761 |  |  |  |  |  |  |  | 
| 762 |  |  |  |  |  |  | sub interval_secs { | 
| 763 | 26087 |  |  | 26087 | 1 | 190506 | my ($self, $interval) = @_; | 
| 764 | 26087 |  | 66 |  |  | 61435 | $interval ||= $self->interval; | 
| 765 | 26086 | 50 |  |  |  | 45665 | unless (defined $interval) { | 
| 766 | 0 |  |  |  |  | 0 | die "interval_secs() called without argument on an object without a declared one"; | 
| 767 |  |  |  |  |  |  | } | 
| 768 | 26086 | 100 |  |  |  | 131525 | my ($n,$t) = $interval =~ /^(\d*)([smhdWMQYZ]$)/ or | 
| 769 |  |  |  |  |  |  | die "Could not determine seconds from interval[$interval]"; | 
| 770 | 26085 | 100 | 33 |  |  | 124634 | if ($interval eq "Z") { | 
|  |  | 50 |  |  |  |  |  | 
| 771 | 961 |  |  |  |  | 3478 | return MAX_INT; | 
| 772 |  |  |  |  |  |  | } elsif (exists $seconds{$t} and $n =~ /^\d+$/) { | 
| 773 | 25124 |  |  |  |  | 89044 | return $seconds{$t}*$n; | 
| 774 |  |  |  |  |  |  | } else { | 
| 775 | 0 |  |  |  |  | 0 | die "Invalid interval specification: n[$n]t[$t]"; | 
| 776 |  |  |  |  |  |  | } | 
| 777 |  |  |  |  |  |  | } | 
| 778 |  |  |  |  |  |  |  | 
| 779 |  |  |  |  |  |  | =head2 $obj->localroot ( $localroot ) | 
| 780 |  |  |  |  |  |  |  | 
| 781 |  |  |  |  |  |  | Get/set accessor. The local root of the tree. Guaranteed without | 
| 782 |  |  |  |  |  |  | trailing slash. | 
| 783 |  |  |  |  |  |  |  | 
| 784 |  |  |  |  |  |  | =cut | 
| 785 |  |  |  |  |  |  |  | 
| 786 |  |  |  |  |  |  | sub localroot { | 
| 787 | 11543 |  |  | 11543 | 1 | 24458 | my ($self, $localroot) = @_; | 
| 788 | 11543 | 100 |  |  |  | 27971 | if (@_ >= 2) { | 
| 789 | 1663 |  |  |  |  | 9010 | $localroot =~ s|/$||; | 
| 790 | 1663 |  |  |  |  | 6535 | $self->_localroot($localroot); | 
| 791 | 1663 |  |  |  |  | 9780 | $self->_rfile(undef); | 
| 792 |  |  |  |  |  |  | } | 
| 793 | 11543 |  |  |  |  | 29154 | $localroot = $self->_localroot; | 
| 794 |  |  |  |  |  |  | } | 
| 795 |  |  |  |  |  |  |  | 
| 796 |  |  |  |  |  |  | =head2 $ret = $obj->local_path($path_found_in_recentfile) | 
| 797 |  |  |  |  |  |  |  | 
| 798 |  |  |  |  |  |  | Combines the path to our local mirror and the path of an object found | 
| 799 |  |  |  |  |  |  | in this I. In other words: the target of a mirror operation. | 
| 800 |  |  |  |  |  |  |  | 
| 801 |  |  |  |  |  |  | Implementation note: We split on slashes and then use | 
| 802 |  |  |  |  |  |  | File::Spec::catfile to adjust to the local operating system. | 
| 803 |  |  |  |  |  |  |  | 
| 804 |  |  |  |  |  |  | =cut | 
| 805 |  |  |  |  |  |  |  | 
| 806 |  |  |  |  |  |  | sub local_path { | 
| 807 | 1486 |  |  | 1486 | 1 | 4181 | my($self,$path) = @_; | 
| 808 | 1486 | 50 |  |  |  | 2970 | unless (defined $path) { | 
| 809 |  |  |  |  |  |  | # seems like a degenerated case | 
| 810 | 0 |  |  |  |  | 0 | return $self->localroot; | 
| 811 |  |  |  |  |  |  | } | 
| 812 | 1486 |  |  |  |  | 4418 | my @p = split m|/|, $path; | 
| 813 | 1486 |  |  |  |  | 3609 | File::Spec->catfile($self->localroot,@p); | 
| 814 |  |  |  |  |  |  | } | 
| 815 |  |  |  |  |  |  |  | 
| 816 |  |  |  |  |  |  | =head2 (void) $obj->lock | 
| 817 |  |  |  |  |  |  |  | 
| 818 |  |  |  |  |  |  | Locking is implemented with an C on a locking directory | 
| 819 |  |  |  |  |  |  | (C<.lock> appended to $rfile). | 
| 820 |  |  |  |  |  |  |  | 
| 821 |  |  |  |  |  |  | =cut | 
| 822 |  |  |  |  |  |  |  | 
| 823 |  |  |  |  |  |  | sub lock { | 
| 824 | 3088 |  |  | 3088 | 1 | 6129 | my ($self) = @_; | 
| 825 |  |  |  |  |  |  | # not using flock because it locks on filehandles instead of | 
| 826 |  |  |  |  |  |  | # old school ressources. | 
| 827 | 3088 | 50 |  |  |  | 8364 | my $locked = $self->_is_locked and return; | 
| 828 | 3088 |  |  |  |  | 18430 | my $rfile = $self->rfile; | 
| 829 |  |  |  |  |  |  | # XXX need a way to allow breaking the lock | 
| 830 | 3088 |  |  |  |  | 6614 | my $start = time; | 
| 831 | 3088 |  | 50 |  |  | 9015 | my $locktimeout = $self->locktimeout || 600; | 
| 832 | 3088 |  |  |  |  | 18997 | my %have_warned; | 
| 833 | 3088 |  |  |  |  | 8440 | my $lockdir = "$rfile.lock"; | 
| 834 | 3088 |  |  |  |  | 6292 | my $procfile = "$lockdir/process"; | 
| 835 | 3088 |  |  |  |  | 194679 | GETLOCK: while (not mkdir $lockdir) { | 
| 836 | 0 | 0 |  |  |  | 0 | if (open my $fh, "<", $procfile) { | 
| 837 | 0 |  |  |  |  | 0 | chomp(my $process = <$fh>); | 
| 838 | 0 | 0 |  |  |  | 0 | if (0) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 839 | 0 |  |  |  |  | 0 | } elsif ($process !~ /^\d+$/) { | 
| 840 | 0 | 0 |  |  |  | 0 | warn "Warning: unknown process holds a lock in '$lockdir', waiting..." unless $have_warned{unknown}++; | 
| 841 |  |  |  |  |  |  | } elsif ($$ == $process) { | 
| 842 | 0 |  |  |  |  | 0 | last GETLOCK; | 
| 843 |  |  |  |  |  |  | } elsif (kill 0, $process) { | 
| 844 | 0 | 0 |  |  |  | 0 | warn "Warning: process $process holds a lock in '$lockdir', waiting..." unless $have_warned{$process}++; | 
| 845 |  |  |  |  |  |  | } else { | 
| 846 | 0 |  |  |  |  | 0 | warn "Warning: breaking lock held by process $process"; | 
| 847 | 0 |  |  |  |  | 0 | sleep 1; | 
| 848 | 0 |  |  |  |  | 0 | last GETLOCK; | 
| 849 |  |  |  |  |  |  | } | 
| 850 |  |  |  |  |  |  | } else { | 
| 851 | 0 | 0 |  |  |  | 0 | warn "Warning: unknown process holds a lock in '$lockdir', waiting..." unless $have_warned{unknown}++; | 
| 852 |  |  |  |  |  |  | } | 
| 853 | 0 |  |  |  |  | 0 | Time::HiRes::sleep 0.01; | 
| 854 | 0 | 0 |  |  |  | 0 | if (time - $start > $locktimeout) { | 
| 855 | 0 |  |  |  |  | 0 | die "Could not acquire lockdirectory '$rfile.lock': $!"; | 
| 856 |  |  |  |  |  |  | } | 
| 857 |  |  |  |  |  |  | } # GETLOCK | 
| 858 | 3088 | 50 |  |  |  | 209398 | open my $fh, ">", $procfile or die "Could not open >$procfile\: $!"; | 
| 859 | 3088 |  |  |  |  | 46212 | print $fh $$, "\n"; | 
| 860 | 3088 | 50 |  |  |  | 91918 | close $fh or die "Could not close: $!"; | 
| 861 | 3088 |  |  |  |  | 20517 | $self->_is_locked (1); | 
| 862 |  |  |  |  |  |  | } | 
| 863 |  |  |  |  |  |  |  | 
| 864 |  |  |  |  |  |  | =head2 (void) $obj->merge ($other) | 
| 865 |  |  |  |  |  |  |  | 
| 866 |  |  |  |  |  |  | Bulk update of this object with another one. It's used to merge a | 
| 867 |  |  |  |  |  |  | smaller and younger $other object into the current one. If this file | 
| 868 |  |  |  |  |  |  | is a C file, then we normally do not merge in objects of type | 
| 869 |  |  |  |  |  |  | C; this can be overridden by setting | 
| 870 |  |  |  |  |  |  | keep_delete_objects_forever. But if we encounter an object of type | 
| 871 |  |  |  |  |  |  | delete we delete the corresponding C object if we have it. | 
| 872 |  |  |  |  |  |  |  | 
| 873 |  |  |  |  |  |  | If there is nothing to be merged, nothing is done. | 
| 874 |  |  |  |  |  |  |  | 
| 875 |  |  |  |  |  |  | =cut | 
| 876 |  |  |  |  |  |  |  | 
| 877 |  |  |  |  |  |  | sub merge { | 
| 878 | 721 |  |  | 721 | 1 | 1989 | my($self, $other) = @_; | 
| 879 | 721 |  |  |  |  | 2358 | $self->_merge_sanitycheck ( $other ); | 
| 880 | 721 |  |  |  |  | 2182 | $other->lock; | 
| 881 | 721 |  | 50 |  |  | 7043 | my $other_recent = $other->recent_events || []; | 
| 882 | 721 |  |  |  |  | 2764 | $self->lock; | 
| 883 | 721 |  |  |  |  | 8808 | $self->_merge_locked ( $other, $other_recent ); | 
| 884 | 721 |  |  |  |  | 10105 | $self->unlock; | 
| 885 | 721 |  |  |  |  | 5059 | $other->unlock; | 
| 886 |  |  |  |  |  |  | } | 
| 887 |  |  |  |  |  |  |  | 
| 888 |  |  |  |  |  |  | sub _merge_locked { | 
| 889 | 721 |  |  | 721 |  | 2256 | my($self, $other, $other_recent) = @_; | 
| 890 | 721 |  | 50 |  |  | 1989 | my $my_recent = $self->recent_events || []; | 
| 891 |  |  |  |  |  |  |  | 
| 892 |  |  |  |  |  |  | # calculate the target time span | 
| 893 | 721 | 100 |  |  |  | 2629 | my $myepoch = $my_recent->[0] ? $my_recent->[0]{epoch} : undef; | 
| 894 | 721 | 50 |  |  |  | 1898 | my $epoch = $other_recent->[0] ? $other_recent->[0]{epoch} : $myepoch; | 
| 895 | 721 |  |  |  |  | 1176 | my $oldest_allowed = 0; | 
| 896 | 721 |  |  |  |  | 1015 | my $something_done; | 
| 897 | 721 | 100 |  |  |  | 1841 | unless ($my_recent->[0]) { | 
| 898 |  |  |  |  |  |  | # obstetrics | 
| 899 | 75 |  |  |  |  | 110 | $something_done = 1; | 
| 900 |  |  |  |  |  |  | } | 
| 901 | 721 | 50 |  |  |  | 1663 | if ($epoch) { | 
| 902 | 721 | 100 | 50 |  |  | 1793 | if (($other->dirtymark||0) ne ($self->dirtymark||0)) { | 
|  |  | 100 | 100 |  |  |  |  | 
| 903 | 233 |  |  |  |  | 2933 | $oldest_allowed = 0; | 
| 904 | 233 |  |  |  |  | 306 | $something_done = 1; | 
| 905 |  |  |  |  |  |  | } elsif (my $merged = $self->merged) { | 
| 906 | 446 |  |  |  |  | 1147 | my $secs = $self->interval_secs(); | 
| 907 | 446 |  | 50 |  |  | 4041 | $oldest_allowed = min($epoch - $secs, $merged->{epoch}||0); | 
| 908 | 446 | 50 | 33 |  |  | 2596 | if (@$other_recent and | 
| 909 |  |  |  |  |  |  | _bigfloatlt($other_recent->[-1]{epoch}, $oldest_allowed) | 
| 910 |  |  |  |  |  |  | ) { | 
| 911 | 0 |  |  |  |  | 0 | $oldest_allowed = $other_recent->[-1]{epoch}; | 
| 912 |  |  |  |  |  |  | } | 
| 913 |  |  |  |  |  |  | } | 
| 914 | 721 |  | 100 |  |  | 3587 | while (@$my_recent && _bigfloatlt($my_recent->[-1]{epoch}, $oldest_allowed)) { | 
| 915 | 1356 |  |  |  |  | 2061 | pop @$my_recent; | 
| 916 | 1356 |  |  |  |  | 3730 | $something_done = 1; | 
| 917 |  |  |  |  |  |  | } | 
| 918 |  |  |  |  |  |  | } | 
| 919 |  |  |  |  |  |  |  | 
| 920 | 721 |  |  |  |  | 1238 | my %have_path; | 
| 921 | 721 |  |  |  |  | 1318 | my $other_recent_filtered = []; | 
| 922 | 721 |  |  |  |  | 1538 | for my $oev (@$other_recent) { | 
| 923 | 24436 |  | 50 |  |  | 48347 | my $oevepoch = $oev->{epoch} || 0; | 
| 924 | 24436 | 50 |  |  |  | 41121 | next if _bigfloatlt($oevepoch, $oldest_allowed); | 
| 925 | 24436 |  |  |  |  | 41801 | my $path = $oev->{path}; | 
| 926 | 24436 | 50 |  |  |  | 62604 | next if $have_path{$path}++; | 
| 927 | 24436 | 100 | 100 |  |  | 38942 | if (    $self->interval eq "Z" | 
|  |  |  | 66 |  |  |  |  | 
| 928 |  |  |  |  |  |  | and $oev->{type}    eq "delete" | 
| 929 |  |  |  |  |  |  | and ! $self->keep_delete_objects_forever | 
| 930 |  |  |  |  |  |  | ) { | 
| 931 |  |  |  |  |  |  | # do nothing | 
| 932 |  |  |  |  |  |  | } else { | 
| 933 | 24409 | 100 | 100 |  |  | 54146 | if (!$myepoch || _bigfloatgt($oevepoch, $myepoch)) { | 
| 934 | 4725 |  |  |  |  | 5489 | $something_done = 1; | 
| 935 |  |  |  |  |  |  | } | 
| 936 | 24409 |  |  |  |  | 103839 | push @$other_recent_filtered, { epoch => $oev->{epoch}, path => $path, type => $oev->{type} }; | 
| 937 |  |  |  |  |  |  | } | 
| 938 |  |  |  |  |  |  | } | 
| 939 | 721 | 100 |  |  |  | 3097 | if ($something_done) { | 
| 940 | 679 |  |  |  |  | 2501 | $self->_merge_something_done ($other_recent_filtered, $my_recent, $other_recent, $other, \%have_path, $epoch); | 
| 941 |  |  |  |  |  |  | } | 
| 942 |  |  |  |  |  |  | } | 
| 943 |  |  |  |  |  |  |  | 
| 944 |  |  |  |  |  |  | sub _merge_something_done { | 
| 945 | 679 |  |  | 679 |  | 2241 | my($self, $other_recent_filtered, $my_recent, $other_recent, $other, $have_path, $epoch) = @_; | 
| 946 | 679 |  |  |  |  | 1100 | my $recent = []; | 
| 947 | 679 |  |  |  |  | 1302 | my $epoch_conflict = 0; | 
| 948 | 679 |  |  |  |  | 980 | my $last_epoch; | 
| 949 | 679 |  | 100 |  |  | 1843 | ZIP: while (@$other_recent_filtered || @$my_recent) { | 
| 950 | 55206 |  |  |  |  | 57839 | my $event; | 
| 951 | 55206 | 100 | 100 |  |  | 142940 | if (!@$my_recent || | 
|  |  |  | 100 |  |  |  |  | 
| 952 |  |  |  |  |  |  | @$other_recent_filtered && _bigfloatge($other_recent_filtered->[0]{epoch},$my_recent->[0]{epoch})) { | 
| 953 | 23355 |  |  |  |  | 31361 | $event = shift @$other_recent_filtered; | 
| 954 |  |  |  |  |  |  | } else { | 
| 955 | 31851 |  |  |  |  | 39347 | $event = shift @$my_recent; | 
| 956 | 31851 | 100 |  |  |  | 110062 | next ZIP if $have_path->{$event->{path}}++; | 
| 957 |  |  |  |  |  |  | } | 
| 958 | 36654 | 100 | 100 |  |  | 101177 | $epoch_conflict=1 if defined $last_epoch && $event->{epoch} eq $last_epoch; | 
| 959 | 36654 |  |  |  |  | 45168 | $last_epoch = $event->{epoch}; | 
| 960 | 36654 |  |  |  |  | 82499 | push @$recent, $event; | 
| 961 |  |  |  |  |  |  | } | 
| 962 | 679 | 100 |  |  |  | 1474 | if ($epoch_conflict) { | 
| 963 | 10 |  |  |  |  | 15 | my %have_epoch; | 
| 964 | 10 |  |  |  |  | 45 | for (my $i = $#$recent;$i>=0;$i--) { | 
| 965 | 270 |  |  |  |  | 340 | my $epoch = $recent->[$i]{epoch}; | 
| 966 | 270 | 100 |  |  |  | 820 | if ($have_epoch{$epoch}++) { | 
| 967 | 10 |  |  |  |  | 35 | while ($have_epoch{$epoch}) { | 
| 968 | 10 |  |  |  |  | 40 | $epoch = _increase_a_bit($epoch); | 
| 969 |  |  |  |  |  |  | } | 
| 970 | 10 |  |  |  |  | 30 | $recent->[$i]{epoch} = $epoch; | 
| 971 | 10 |  |  |  |  | 35 | $have_epoch{$epoch}++; | 
| 972 |  |  |  |  |  |  | } | 
| 973 |  |  |  |  |  |  | } | 
| 974 |  |  |  |  |  |  | } | 
| 975 | 679 | 100 | 100 |  |  | 2176 | if (!$self->dirtymark || $other->dirtymark ne $self->dirtymark) { | 
| 976 | 233 |  |  |  |  | 3089 | $self->dirtymark ( $other->dirtymark ); | 
| 977 |  |  |  |  |  |  | } | 
| 978 | 679 |  |  |  |  | 8515 | $self->write_recent($recent); | 
| 979 |  |  |  |  |  |  | $other->merged({ | 
| 980 |  |  |  |  |  |  | time => Time::HiRes::time, # not used anywhere | 
| 981 |  |  |  |  |  |  | epoch => $recent->[0]{epoch}, | 
| 982 | 679 |  |  |  |  | 5530 | into_interval => $self->interval, # not used anywhere | 
| 983 |  |  |  |  |  |  | }); | 
| 984 | 679 |  |  |  |  | 1600 | $other->write_recent($other_recent); | 
| 985 |  |  |  |  |  |  | } | 
| 986 |  |  |  |  |  |  |  | 
| 987 |  |  |  |  |  |  | sub _merge_sanitycheck { | 
| 988 | 721 |  |  | 721 |  | 1416 | my($self, $other) = @_; | 
| 989 | 721 | 50 |  |  |  | 1753 | if ($self->interval_secs <= $other->interval_secs) { | 
| 990 | 0 |  |  |  |  | 0 | require Carp; | 
| 991 | 0 |  |  |  |  | 0 | Carp::confess | 
| 992 |  |  |  |  |  |  | (sprintf | 
| 993 |  |  |  |  |  |  | ( | 
| 994 |  |  |  |  |  |  | "Alert: illegal merge operation of a bigger interval[%d] into a smaller[%d]", | 
| 995 |  |  |  |  |  |  | $self->interval_secs, | 
| 996 |  |  |  |  |  |  | $other->interval_secs, | 
| 997 |  |  |  |  |  |  | )); | 
| 998 |  |  |  |  |  |  | } | 
| 999 |  |  |  |  |  |  | } | 
| 1000 |  |  |  |  |  |  |  | 
| 1001 |  |  |  |  |  |  | =head2 merged | 
| 1002 |  |  |  |  |  |  |  | 
| 1003 |  |  |  |  |  |  | Hashref denoting when this recentfile has been merged into some other | 
| 1004 |  |  |  |  |  |  | at which epoch. | 
| 1005 |  |  |  |  |  |  |  | 
| 1006 |  |  |  |  |  |  | =cut | 
| 1007 |  |  |  |  |  |  |  | 
| 1008 |  |  |  |  |  |  | sub merged { | 
| 1009 | 14503 |  |  | 14503 | 1 | 31049 | my($self, $set) = @_; | 
| 1010 | 14503 | 100 |  |  |  | 27133 | if (defined $set) { | 
| 1011 | 4542 |  |  |  |  | 10103 | $self->_merged ($set); | 
| 1012 |  |  |  |  |  |  | } | 
| 1013 | 14503 |  |  |  |  | 40140 | my $merged = $self->_merged; | 
| 1014 | 14503 |  |  |  |  | 50754 | my $into; | 
| 1015 | 14503 | 100 | 100 |  |  | 60935 | if ($merged and $into = $merged->{into_interval} and defined $self->_interval) { | 
|  |  |  | 100 |  |  |  |  | 
| 1016 |  |  |  |  |  |  | # sanity checks | 
| 1017 | 9421 | 50 |  |  |  | 53544 | if ($into eq $self->interval) { | 
|  |  | 50 |  |  |  |  |  | 
| 1018 | 0 |  |  |  |  | 0 | require Carp; | 
| 1019 | 0 |  |  |  |  | 0 | Carp::cluck(sprintf | 
| 1020 |  |  |  |  |  |  | ( | 
| 1021 |  |  |  |  |  |  | "Warning: into_interval[%s] same as own interval[%s]. Danger ahead.", | 
| 1022 |  |  |  |  |  |  | $into, | 
| 1023 |  |  |  |  |  |  | $self->interval, | 
| 1024 |  |  |  |  |  |  | )); | 
| 1025 |  |  |  |  |  |  | } elsif ($self->interval_secs($into) < $self->interval_secs) { | 
| 1026 | 0 |  |  |  |  | 0 | require Carp; | 
| 1027 | 0 |  |  |  |  | 0 | Carp::cluck(sprintf | 
| 1028 |  |  |  |  |  |  | ( | 
| 1029 |  |  |  |  |  |  | "Warning: into_interval_secs[%s] smaller than own interval_secs[%s] on interval[%s]. Danger ahead.", | 
| 1030 |  |  |  |  |  |  | $self->interval_secs($into), | 
| 1031 |  |  |  |  |  |  | $self->interval_secs, | 
| 1032 |  |  |  |  |  |  | $self->interval, | 
| 1033 |  |  |  |  |  |  | )); | 
| 1034 |  |  |  |  |  |  | } | 
| 1035 |  |  |  |  |  |  | } | 
| 1036 | 14503 |  |  |  |  | 37582 | $merged; | 
| 1037 |  |  |  |  |  |  | } | 
| 1038 |  |  |  |  |  |  |  | 
| 1039 |  |  |  |  |  |  | =head2 $hashref = $obj->meta_data | 
| 1040 |  |  |  |  |  |  |  | 
| 1041 |  |  |  |  |  |  | Returns the hashref of metadata that the server has to add to the | 
| 1042 |  |  |  |  |  |  | I. | 
| 1043 |  |  |  |  |  |  |  | 
| 1044 |  |  |  |  |  |  | =cut | 
| 1045 |  |  |  |  |  |  |  | 
| 1046 |  |  |  |  |  |  | sub meta_data { | 
| 1047 | 2712 |  |  | 2712 | 1 | 4954 | my($self) = @_; | 
| 1048 | 2712 |  |  |  |  | 5256 | my $ret = $self->{meta}; | 
| 1049 | 2712 |  |  |  |  | 6033 | for my $m ( | 
| 1050 |  |  |  |  |  |  | "aggregator", | 
| 1051 |  |  |  |  |  |  | "canonize", | 
| 1052 |  |  |  |  |  |  | "comment", | 
| 1053 |  |  |  |  |  |  | "dirtymark", | 
| 1054 |  |  |  |  |  |  | "filenameroot", | 
| 1055 |  |  |  |  |  |  | "interval", | 
| 1056 |  |  |  |  |  |  | "merged", | 
| 1057 |  |  |  |  |  |  | "minmax", | 
| 1058 |  |  |  |  |  |  | "protocol", | 
| 1059 |  |  |  |  |  |  | "serializer_suffix", | 
| 1060 |  |  |  |  |  |  | ) { | 
| 1061 | 27120 |  |  |  |  | 55872 | my $v = $self->$m; | 
| 1062 | 27120 | 100 |  |  |  | 88919 | if (defined $v) { | 
| 1063 | 23322 |  |  |  |  | 51022 | $ret->{$m} = $v; | 
| 1064 |  |  |  |  |  |  | } | 
| 1065 |  |  |  |  |  |  | } | 
| 1066 |  |  |  |  |  |  | # XXX need to reset the Producer if I am a writer, keep it when I | 
| 1067 |  |  |  |  |  |  | # am a reader | 
| 1068 |  |  |  |  |  |  | $ret->{Producers} ||= { | 
| 1069 | 2712 |  | 50 |  |  | 41294 | __PACKAGE__, "$VERSION", # stringified it looks better | 
| 1070 |  |  |  |  |  |  | '$0', $0, | 
| 1071 |  |  |  |  |  |  | 'time', Time::HiRes::time, | 
| 1072 |  |  |  |  |  |  | }; | 
| 1073 | 2712 |  | 66 |  |  | 7573 | $ret->{dirtymark} ||= Time::HiRes::time; | 
| 1074 | 2712 |  |  |  |  | 8426 | return $ret; | 
| 1075 |  |  |  |  |  |  | } | 
| 1076 |  |  |  |  |  |  |  | 
| 1077 |  |  |  |  |  |  | =head2 $success = $obj->mirror ( %options ) | 
| 1078 |  |  |  |  |  |  |  | 
| 1079 |  |  |  |  |  |  | Mirrors the files in this I as reported by | 
| 1080 |  |  |  |  |  |  | C. Options named C, C, C are passed | 
| 1081 |  |  |  |  |  |  | through to the C call. The boolean option C, | 
| 1082 |  |  |  |  |  |  | if true, causes C to only rsync C | 
| 1083 |  |  |  |  |  |  | and keep track of the rsynced files so that future calls will rsync | 
| 1084 |  |  |  |  |  |  | different files until all files are brought to sync. | 
| 1085 |  |  |  |  |  |  |  | 
| 1086 |  |  |  |  |  |  | =cut | 
| 1087 |  |  |  |  |  |  |  | 
| 1088 |  |  |  |  |  |  | sub mirror { | 
| 1089 | 32 |  |  | 32 | 1 | 6293 | my($self, %options) = @_; | 
| 1090 | 32 |  |  |  |  | 536 | my $trecentfile = $self->get_remote_recentfile_as_tempfile(); | 
| 1091 | 32 |  |  |  |  | 4908 | $self->_use_tempfile (1); | 
| 1092 |  |  |  |  |  |  | # skip-deletes is inadequat for passthrough within mirror. We | 
| 1093 |  |  |  |  |  |  | # would never reach uptodateness when a delete were on a | 
| 1094 |  |  |  |  |  |  | # borderline | 
| 1095 | 32 |  |  |  |  | 334 | my %passthrough = map { ($_ => $options{$_}) } qw(before after max); | 
|  | 96 |  |  |  |  | 1346 |  | 
| 1096 | 32 |  |  |  |  | 655 | my ($recent_events) = $self->recent_events(%passthrough); | 
| 1097 | 32 |  |  |  |  | 116 | my(@error, @dlcollector); # download-collector: array containing paths we need | 
| 1098 | 32 |  |  |  |  | 222 | my $first_item = 0; | 
| 1099 | 32 |  |  |  |  | 106 | my $last_item = $#$recent_events; | 
| 1100 | 32 |  |  |  |  | 322 | my $done = $self->done; | 
| 1101 | 32 |  |  |  |  | 174 | my $pathdb = $self->_pathdb; | 
| 1102 | 32 |  |  |  |  | 537 | ITEM: for my $i ($first_item..$last_item) { | 
| 1103 | 2732 |  |  |  |  | 4177 | my $status = +{}; | 
| 1104 | 2732 |  |  |  |  | 7590 | $self->_mirror_item | 
| 1105 |  |  |  |  |  |  | ( | 
| 1106 |  |  |  |  |  |  | $i, | 
| 1107 |  |  |  |  |  |  | $recent_events, | 
| 1108 |  |  |  |  |  |  | $last_item, | 
| 1109 |  |  |  |  |  |  | $done, | 
| 1110 |  |  |  |  |  |  | $pathdb, | 
| 1111 |  |  |  |  |  |  | \@dlcollector, | 
| 1112 |  |  |  |  |  |  | \%options, | 
| 1113 |  |  |  |  |  |  | $status, | 
| 1114 |  |  |  |  |  |  | \@error, | 
| 1115 |  |  |  |  |  |  | ); | 
| 1116 | 2732 | 100 |  |  |  | 6829 | last if $i == $last_item; | 
| 1117 | 2706 | 100 |  |  |  | 6552 | if ($status->{mustreturn}){ | 
| 1118 | 6 | 100 | 66 |  |  | 222 | if ($self->_current_tempfile && ! $self->_current_tempfile_fh) { | 
| 1119 |  |  |  |  |  |  | # looks like a bug somewhere else | 
| 1120 | 5 |  |  |  |  | 470 | my $t = $self->_current_tempfile; | 
| 1121 | 5 | 50 |  |  |  | 939 | unlink $t or die "Could not unlink '$t': $!"; | 
| 1122 | 5 |  |  |  |  | 88 | $self->_current_tempfile(undef); | 
| 1123 | 5 |  |  |  |  | 153 | $self->_use_tempfile(0); | 
| 1124 |  |  |  |  |  |  | } | 
| 1125 | 6 |  |  |  |  | 5897 | return; | 
| 1126 |  |  |  |  |  |  | } | 
| 1127 |  |  |  |  |  |  | } | 
| 1128 | 26 | 100 |  |  |  | 200 | if (@dlcollector) { | 
| 1129 | 17 |  |  |  |  | 105 | my $success = eval { $self->_mirror_dlcollector (\@dlcollector,$pathdb,$recent_events);}; | 
|  | 17 |  |  |  |  | 335 |  | 
| 1130 | 17 | 50 | 33 |  |  | 434 | if (!$success || $@) { | 
| 1131 | 0 |  |  |  |  | 0 | warn "Warning: Unknown error while mirroring: $@"; | 
| 1132 | 0 |  |  |  |  | 0 | push @error, $@; | 
| 1133 | 0 |  |  |  |  | 0 | sleep 1; | 
| 1134 |  |  |  |  |  |  | } | 
| 1135 |  |  |  |  |  |  | } | 
| 1136 | 26 | 50 |  |  |  | 361 | if ($self->verbose) { | 
| 1137 | 0 |  |  |  |  | 0 | my $LFH = $self->_logfilehandle; | 
| 1138 | 0 |  |  |  |  | 0 | print $LFH "DONE\n"; | 
| 1139 |  |  |  |  |  |  | } | 
| 1140 |  |  |  |  |  |  | # once we've gone to the end we consider ourselves free of obligations | 
| 1141 | 26 |  |  |  |  | 508 | $self->unseed; | 
| 1142 | 26 |  |  |  |  | 272 | $self->_mirror_unhide_tempfile ($trecentfile); | 
| 1143 | 26 |  |  |  |  | 543 | $self->_mirror_perform_delayed_ops(\%options); | 
| 1144 | 26 |  |  |  |  | 9805 | return !@error; | 
| 1145 |  |  |  |  |  |  | } | 
| 1146 |  |  |  |  |  |  |  | 
| 1147 |  |  |  |  |  |  | sub _mirror_item { | 
| 1148 | 2732 |  |  | 2732 |  | 5463 | my($self, | 
| 1149 |  |  |  |  |  |  | $i, | 
| 1150 |  |  |  |  |  |  | $recent_events, | 
| 1151 |  |  |  |  |  |  | $last_item, | 
| 1152 |  |  |  |  |  |  | $done, | 
| 1153 |  |  |  |  |  |  | $pathdb, | 
| 1154 |  |  |  |  |  |  | $dlcollector, | 
| 1155 |  |  |  |  |  |  | $options, | 
| 1156 |  |  |  |  |  |  | $status, | 
| 1157 |  |  |  |  |  |  | $error, | 
| 1158 |  |  |  |  |  |  | ) = @_; | 
| 1159 | 2732 |  |  |  |  | 3943 | my $recent_event = $recent_events->[$i]; | 
| 1160 | 2732 | 100 |  |  |  | 7012 | return if $done->covered ( $recent_event->{epoch} ); | 
| 1161 | 1486 | 100 |  |  |  | 3437 | if ($pathdb) { | 
| 1162 | 826 |  |  |  |  | 2462 | my $rec = $pathdb->{$recent_event->{path}}; | 
| 1163 | 826 | 50 | 66 |  |  | 2745 | if ($rec && $rec->{recentepoch}) { | 
| 1164 | 271 | 50 |  |  |  | 1076 | if (_bigfloatgt | 
| 1165 |  |  |  |  |  |  | ( $rec->{recentepoch}, $recent_event->{epoch} )){ | 
| 1166 | 0 |  |  |  |  | 0 | $done->register ($recent_events, [$i]); | 
| 1167 | 0 |  |  |  |  | 0 | return; | 
| 1168 |  |  |  |  |  |  | } | 
| 1169 |  |  |  |  |  |  | } | 
| 1170 |  |  |  |  |  |  | } | 
| 1171 | 1486 |  |  |  |  | 3449 | my $dst = $self->local_path($recent_event->{path}); | 
| 1172 | 1486 | 100 |  |  |  | 22012 | if ($recent_event->{type} eq "new"){ | 
|  |  | 50 |  |  |  |  |  | 
| 1173 | 1462 |  |  |  |  | 3539 | $self->_mirror_item_new | 
| 1174 |  |  |  |  |  |  | ( | 
| 1175 |  |  |  |  |  |  | $dst, | 
| 1176 |  |  |  |  |  |  | $i, | 
| 1177 |  |  |  |  |  |  | $last_item, | 
| 1178 |  |  |  |  |  |  | $recent_events, | 
| 1179 |  |  |  |  |  |  | $recent_event, | 
| 1180 |  |  |  |  |  |  | $dlcollector, | 
| 1181 |  |  |  |  |  |  | $pathdb, | 
| 1182 |  |  |  |  |  |  | $status, | 
| 1183 |  |  |  |  |  |  | $error, | 
| 1184 |  |  |  |  |  |  | $options, | 
| 1185 |  |  |  |  |  |  | ); | 
| 1186 |  |  |  |  |  |  | } elsif ($recent_event->{type} eq "delete") { | 
| 1187 | 24 |  |  |  |  | 160 | my $activity; | 
| 1188 | 24 | 50 |  |  |  | 202 | if ($options->{'skip-deletes'}) { | 
| 1189 | 0 |  |  |  |  | 0 | $activity = "skipped"; | 
| 1190 |  |  |  |  |  |  | } else { | 
| 1191 | 24 |  |  |  |  | 1434 | my @lstat = lstat $dst; | 
| 1192 | 24 | 100 | 33 |  |  | 980 | if (! -e _) { | 
|  |  | 50 |  |  |  |  |  | 
| 1193 | 9 |  |  |  |  | 120 | $activity = "not_found"; | 
| 1194 |  |  |  |  |  |  | } elsif (-l _ or not -d _) { | 
| 1195 | 15 |  |  |  |  | 235 | $self->delayed_operations->{unlink}{$dst}++; | 
| 1196 | 15 |  |  |  |  | 185 | $activity = "deleted"; | 
| 1197 |  |  |  |  |  |  | } else { | 
| 1198 | 0 |  |  |  |  | 0 | $self->delayed_operations->{rmdir}{$dst}++; | 
| 1199 | 0 |  |  |  |  | 0 | $activity = "deleted"; | 
| 1200 |  |  |  |  |  |  | } | 
| 1201 |  |  |  |  |  |  | } | 
| 1202 | 24 |  |  |  |  | 432 | $done->register ($recent_events, [$i]); | 
| 1203 | 24 | 100 |  |  |  | 219 | if ($pathdb) { | 
| 1204 | 9 |  |  |  |  | 116 | $self->_mirror_register_path($pathdb,[$recent_event],$activity); | 
| 1205 |  |  |  |  |  |  | } | 
| 1206 |  |  |  |  |  |  | } else { | 
| 1207 | 0 |  |  |  |  | 0 | warn "Warning: invalid upload type '$recent_event->{type}'"; | 
| 1208 |  |  |  |  |  |  | } | 
| 1209 |  |  |  |  |  |  | } | 
| 1210 |  |  |  |  |  |  |  | 
| 1211 |  |  |  |  |  |  | sub _mirror_item_new { | 
| 1212 | 1462 |  |  | 1462 |  | 3262 | my($self, | 
| 1213 |  |  |  |  |  |  | $dst, | 
| 1214 |  |  |  |  |  |  | $i, | 
| 1215 |  |  |  |  |  |  | $last_item, | 
| 1216 |  |  |  |  |  |  | $recent_events, | 
| 1217 |  |  |  |  |  |  | $recent_event, | 
| 1218 |  |  |  |  |  |  | $dlcollector, | 
| 1219 |  |  |  |  |  |  | $pathdb, | 
| 1220 |  |  |  |  |  |  | $status, | 
| 1221 |  |  |  |  |  |  | $error, | 
| 1222 |  |  |  |  |  |  | $options, | 
| 1223 |  |  |  |  |  |  | ) = @_; | 
| 1224 | 1462 | 50 |  |  |  | 3146 | if ($self->verbose) { | 
| 1225 | 0 | 0 |  |  |  | 0 | my $doing = -e $dst ? "Sync" : "Get"; | 
| 1226 | 0 |  |  |  |  | 0 | my $LFH = $self->_logfilehandle; | 
| 1227 |  |  |  |  |  |  | printf $LFH | 
| 1228 |  |  |  |  |  |  | ( | 
| 1229 |  |  |  |  |  |  | "%-4s %d (%d/%d/%s) %s ... ", | 
| 1230 |  |  |  |  |  |  | $doing, | 
| 1231 |  |  |  |  |  |  | time, | 
| 1232 |  |  |  |  |  |  | 1+$i, | 
| 1233 |  |  |  |  |  |  | 1+$last_item, | 
| 1234 |  |  |  |  |  |  | $self->interval, | 
| 1235 |  |  |  |  |  |  | $recent_event->{path}, | 
| 1236 | 0 |  |  |  |  | 0 | ); | 
| 1237 |  |  |  |  |  |  | } | 
| 1238 | 1462 |  | 50 |  |  | 7254 | my $max_files_per_connection = $self->max_files_per_connection || 42; | 
| 1239 | 1462 |  |  |  |  | 5749 | my $success; | 
| 1240 | 1462 | 50 |  |  |  | 2718 | if ($self->verbose) { | 
| 1241 | 0 |  |  |  |  | 0 | my $LFH = $self->_logfilehandle; | 
| 1242 | 0 |  |  |  |  | 0 | print $LFH "\n"; | 
| 1243 |  |  |  |  |  |  | } | 
| 1244 | 1462 |  |  |  |  | 9457 | push @$dlcollector, { rev => $recent_event, i => $i }; | 
| 1245 | 1462 | 100 |  |  |  | 3827 | if (@$dlcollector >= $max_files_per_connection) { | 
| 1246 | 11 |  |  |  |  | 35 | $success = eval {$self->_mirror_dlcollector ($dlcollector,$pathdb,$recent_events);}; | 
|  | 11 |  |  |  |  | 136 |  | 
| 1247 | 11 |  |  |  |  | 161 | my $sleep = $self->sleep_per_connection; | 
| 1248 | 11 | 50 |  |  |  | 201 | $sleep = 0.42 unless defined $sleep; | 
| 1249 | 11 |  |  |  |  | 4622636 | Time::HiRes::sleep $sleep; | 
| 1250 | 11 | 100 |  |  |  | 676 | if ($options->{piecemeal}) { | 
| 1251 | 6 |  |  |  |  | 123 | $status->{mustreturn} = 1; | 
| 1252 | 6 |  |  |  |  | 201 | return; | 
| 1253 |  |  |  |  |  |  | } | 
| 1254 |  |  |  |  |  |  | } else { | 
| 1255 | 1451 |  |  |  |  | 3184 | return; | 
| 1256 |  |  |  |  |  |  | } | 
| 1257 | 5 | 50 | 33 |  |  | 425 | if (!$success || $@) { | 
| 1258 | 0 |  |  |  |  | 0 | warn "Warning: Error while mirroring: $@"; | 
| 1259 | 0 |  |  |  |  | 0 | push @$error, $@; | 
| 1260 | 0 |  |  |  |  | 0 | sleep 1; | 
| 1261 |  |  |  |  |  |  | } | 
| 1262 | 5 | 50 |  |  |  | 220 | if ($self->verbose) { | 
| 1263 | 0 |  |  |  |  | 0 | my $LFH = $self->_logfilehandle; | 
| 1264 | 0 |  |  |  |  | 0 | print $LFH "DONE\n"; | 
| 1265 |  |  |  |  |  |  | } | 
| 1266 |  |  |  |  |  |  | } | 
| 1267 |  |  |  |  |  |  |  | 
| 1268 |  |  |  |  |  |  | sub _mirror_dlcollector { | 
| 1269 | 28 |  |  | 28 |  | 141 | my($self,$xcoll,$pathdb,$recent_events) = @_; | 
| 1270 | 28 |  |  |  |  | 123 | my $success = $self->mirror_path([map {$_->{rev}{path}} @$xcoll]); | 
|  | 1462 |  |  |  |  | 3148 |  | 
| 1271 | 28 | 100 |  |  |  | 9448 | if ($pathdb) { | 
| 1272 | 18 |  |  |  |  | 410 | $self->_mirror_register_path($pathdb,[map {$_->{rev}} @$xcoll],"rsync"); | 
|  | 817 |  |  |  |  | 4237 |  | 
| 1273 |  |  |  |  |  |  | } | 
| 1274 | 28 |  |  |  |  | 940 | $self->done->register($recent_events, [map {$_->{i}} @$xcoll]); | 
|  | 1462 |  |  |  |  | 4735 |  | 
| 1275 | 28 |  |  |  |  | 4475 | @$xcoll = (); | 
| 1276 | 28 |  |  |  |  | 319 | return $success; | 
| 1277 |  |  |  |  |  |  | } | 
| 1278 |  |  |  |  |  |  |  | 
| 1279 |  |  |  |  |  |  | sub _mirror_register_path { | 
| 1280 | 27 |  |  | 27 |  | 387 | my($self,$pathdb,$coll,$activity) = @_; | 
| 1281 | 27 |  |  |  |  | 255 | my $time = time; | 
| 1282 | 27 |  |  |  |  | 309 | for my $item (@$coll) { | 
| 1283 |  |  |  |  |  |  | $pathdb->{$item->{path}} = | 
| 1284 |  |  |  |  |  |  | { | 
| 1285 |  |  |  |  |  |  | recentepoch => $item->{epoch}, | 
| 1286 | 826 |  |  |  |  | 16869 | ($activity."_on") => $time, | 
| 1287 |  |  |  |  |  |  | }; | 
| 1288 |  |  |  |  |  |  | } | 
| 1289 |  |  |  |  |  |  | } | 
| 1290 |  |  |  |  |  |  |  | 
| 1291 |  |  |  |  |  |  | sub _mirror_unhide_tempfile { | 
| 1292 | 26 |  |  | 26 |  | 172 | my($self, $trecentfile) = @_; | 
| 1293 | 26 |  |  |  |  | 220 | my $rfile = $self->rfile; | 
| 1294 | 26 | 50 |  |  |  | 2430 | if (rename $trecentfile, $rfile) { | 
| 1295 |  |  |  |  |  |  | # warn "DEBUG: renamed '$trecentfile' to '$rfile'"; | 
| 1296 |  |  |  |  |  |  | } else { | 
| 1297 | 0 |  |  |  |  | 0 | require Carp; | 
| 1298 | 0 |  |  |  |  | 0 | Carp::confess("Could not rename '$trecentfile' to '$rfile': $!"); | 
| 1299 |  |  |  |  |  |  | } | 
| 1300 | 26 |  |  |  |  | 439 | $self->_use_tempfile (0); | 
| 1301 | 26 | 100 |  |  |  | 347 | if (my $ctfh = $self->_current_tempfile_fh) { | 
| 1302 | 10 |  |  |  |  | 412 | $ctfh->unlink_on_destroy (0); | 
| 1303 | 10 |  |  |  |  | 370 | $self->_current_tempfile_fh (undef); | 
| 1304 |  |  |  |  |  |  | } | 
| 1305 |  |  |  |  |  |  | } | 
| 1306 |  |  |  |  |  |  |  | 
| 1307 |  |  |  |  |  |  | sub _mirror_perform_delayed_ops { | 
| 1308 | 26 |  |  | 26 |  | 1523 | my($self,$options) = @_; | 
| 1309 | 26 |  |  |  |  | 318 | my $delayed = $self->delayed_operations; | 
| 1310 | 26 |  |  |  |  | 78 | for my $dst (keys %{$delayed->{unlink}}) { | 
|  | 26 |  |  |  |  | 457 |  | 
| 1311 | 30 | 100 |  |  |  | 1280 | unless (unlink $dst) { | 
| 1312 | 15 |  |  |  |  | 120 | require Carp; | 
| 1313 | 15 | 50 |  |  |  | 85 | Carp::cluck ( "Warning: Error while unlinking '$dst': $!" ) if $options->{verbose}; | 
| 1314 |  |  |  |  |  |  | } | 
| 1315 | 30 | 50 |  |  |  | 155 | if ($self->verbose) { | 
| 1316 | 0 |  |  |  |  | 0 | my $doing = "Del"; | 
| 1317 | 0 |  |  |  |  | 0 | my $LFH = $self->_logfilehandle; | 
| 1318 | 0 |  |  |  |  | 0 | printf $LFH | 
| 1319 |  |  |  |  |  |  | ( | 
| 1320 |  |  |  |  |  |  | "%-4s %d (%s) %s DONE\n", | 
| 1321 |  |  |  |  |  |  | $doing, | 
| 1322 |  |  |  |  |  |  | time, | 
| 1323 |  |  |  |  |  |  | $self->interval, | 
| 1324 |  |  |  |  |  |  | $dst, | 
| 1325 |  |  |  |  |  |  | ); | 
| 1326 | 0 |  |  |  |  | 0 | delete $delayed->{unlink}{$dst}; | 
| 1327 |  |  |  |  |  |  | } | 
| 1328 |  |  |  |  |  |  | } | 
| 1329 | 26 |  |  |  |  | 238 | for my $dst (sort {length($b) <=> length($a)} keys %{$delayed->{rmdir}}) { | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 26 |  |  |  |  | 245 |  | 
| 1330 | 0 | 0 |  |  |  | 0 | unless (rmdir $dst) { | 
| 1331 | 0 |  |  |  |  | 0 | require Carp; | 
| 1332 | 0 | 0 |  |  |  | 0 | Carp::cluck ( "Warning: Error on rmdir '$dst': $!" ) if $options->{verbose}; | 
| 1333 |  |  |  |  |  |  | } | 
| 1334 | 0 | 0 |  |  |  | 0 | if ($self->verbose) { | 
| 1335 | 0 |  |  |  |  | 0 | my $doing = "Del"; | 
| 1336 | 0 |  |  |  |  | 0 | my $LFH = $self->_logfilehandle; | 
| 1337 | 0 |  |  |  |  | 0 | printf $LFH | 
| 1338 |  |  |  |  |  |  | ( | 
| 1339 |  |  |  |  |  |  | "%-4s %d (%s) %s DONE\n", | 
| 1340 |  |  |  |  |  |  | $doing, | 
| 1341 |  |  |  |  |  |  | time, | 
| 1342 |  |  |  |  |  |  | $self->interval, | 
| 1343 |  |  |  |  |  |  | $dst, | 
| 1344 |  |  |  |  |  |  | ); | 
| 1345 | 0 |  |  |  |  | 0 | delete $delayed->{rmdir}{$dst}; | 
| 1346 |  |  |  |  |  |  | } | 
| 1347 |  |  |  |  |  |  | } | 
| 1348 |  |  |  |  |  |  | } | 
| 1349 |  |  |  |  |  |  |  | 
| 1350 |  |  |  |  |  |  | =head2 $success = $obj->mirror_path ( $arrref | $path ) | 
| 1351 |  |  |  |  |  |  |  | 
| 1352 |  |  |  |  |  |  | If the argument is a scalar it is treated as a path. The remote path | 
| 1353 |  |  |  |  |  |  | is mirrored into the local copy. $path is the path found in the | 
| 1354 |  |  |  |  |  |  | I, i.e. it is relative to the root directory of the | 
| 1355 |  |  |  |  |  |  | mirror. | 
| 1356 |  |  |  |  |  |  |  | 
| 1357 |  |  |  |  |  |  | If the argument is an array reference then all elements are treated as | 
| 1358 |  |  |  |  |  |  | a path below the current tree and all are rsynced with a single | 
| 1359 |  |  |  |  |  |  | command (and a single connection). | 
| 1360 |  |  |  |  |  |  |  | 
| 1361 |  |  |  |  |  |  | =cut | 
| 1362 |  |  |  |  |  |  |  | 
| 1363 |  |  |  |  |  |  | sub mirror_path { | 
| 1364 | 28 |  |  | 28 | 1 | 136 | my($self,$path) = @_; | 
| 1365 |  |  |  |  |  |  | # XXX simplify the two branches such that $path is treated as | 
| 1366 |  |  |  |  |  |  | # [$path] maybe even demand the argument as an arrayref to | 
| 1367 |  |  |  |  |  |  | # simplify docs and code. (rsync-over-recentfile-2.pl uses the | 
| 1368 |  |  |  |  |  |  | # interface) | 
| 1369 | 28 | 50 | 33 |  |  | 543 | if (ref $path and ref $path eq "ARRAY") { | 
| 1370 | 28 |  |  |  |  | 132 | my $dst = $self->localroot; | 
| 1371 | 28 |  |  |  |  | 4412 | mkpath dirname $dst; | 
| 1372 | 28 |  |  |  |  | 353 | my($fh) = File::Temp->new(TEMPLATE => sprintf(".%s-XXXX", | 
| 1373 |  |  |  |  |  |  | lc $self->filenameroot, | 
| 1374 |  |  |  |  |  |  | ), | 
| 1375 |  |  |  |  |  |  | TMPDIR => 1, | 
| 1376 |  |  |  |  |  |  | UNLINK => 0, | 
| 1377 |  |  |  |  |  |  | ); | 
| 1378 | 28 |  |  |  |  | 23978 | for my $p (@$path) { | 
| 1379 | 1462 |  |  |  |  | 3726 | print $fh $p, "\n"; | 
| 1380 |  |  |  |  |  |  | } | 
| 1381 | 28 |  |  |  |  | 1535 | $fh->flush; | 
| 1382 | 28 |  |  |  |  | 197 | $fh->unlink_on_destroy(1); | 
| 1383 | 28 |  |  |  |  | 418 | my $gaveup = 0; | 
| 1384 | 28 |  |  |  |  | 63 | my $retried = 0; | 
| 1385 | 28 |  |  |  |  | 495 | local($ENV{LANG}) = "C"; | 
| 1386 | 28 |  |  |  |  | 229 | while (!$self->rsync->exec | 
| 1387 |  |  |  |  |  |  | ( | 
| 1388 |  |  |  |  |  |  | src => join("/", | 
| 1389 |  |  |  |  |  |  | $self->remoteroot, | 
| 1390 |  |  |  |  |  |  | ), | 
| 1391 |  |  |  |  |  |  | dst => $dst, | 
| 1392 |  |  |  |  |  |  | 'files-from' => $fh->filename, | 
| 1393 |  |  |  |  |  |  | )) { | 
| 1394 | 0 |  |  |  |  | 0 | my(@err) = $self->rsync->err; | 
| 1395 | 0 | 0 | 0 |  |  | 0 | if ($self->_my_ignore_link_stat_errors && "@err" =~ m{^ rsync: \s link_stat }x ) { | 
| 1396 | 0 | 0 |  |  |  | 0 | if ($self->verbose) { | 
| 1397 | 0 |  |  |  |  | 0 | my $LFH = $self->_logfilehandle; | 
| 1398 | 0 |  |  |  |  | 0 | print $LFH "Info: ignoring link_stat error '@err'"; | 
| 1399 |  |  |  |  |  |  | } | 
| 1400 | 0 |  |  |  |  | 0 | return 1; | 
| 1401 |  |  |  |  |  |  | } | 
| 1402 | 0 |  |  |  |  | 0 | $self->register_rsync_error (@err); | 
| 1403 | 0 | 0 |  |  |  | 0 | if (++$retried >= 3) { | 
| 1404 | 0 |  |  |  |  | 0 | my $batchsize = @$path; | 
| 1405 | 0 |  |  |  |  | 0 | warn "The number of rsync retries now reached 3 within a batch of size $batchsize. Error was '@err'. Giving up now, will retry later, "; | 
| 1406 | 0 |  |  |  |  | 0 | $gaveup = 1; | 
| 1407 | 0 |  |  |  |  | 0 | last; | 
| 1408 |  |  |  |  |  |  | } | 
| 1409 | 0 |  |  |  |  | 0 | sleep 1; | 
| 1410 |  |  |  |  |  |  | } | 
| 1411 | 28 | 50 |  |  |  | 1797644 | unless ($gaveup) { | 
| 1412 | 28 |  |  |  |  | 1237 | $self->un_register_rsync_error (); | 
| 1413 |  |  |  |  |  |  | } | 
| 1414 |  |  |  |  |  |  | } else { | 
| 1415 | 0 |  |  |  |  | 0 | my $dst = $self->local_path($path); | 
| 1416 | 0 |  |  |  |  | 0 | mkpath dirname $dst; | 
| 1417 | 0 |  |  |  |  | 0 | local($ENV{LANG}) = "C"; | 
| 1418 | 0 |  |  |  |  | 0 | while (!$self->rsync->exec | 
| 1419 |  |  |  |  |  |  | ( | 
| 1420 |  |  |  |  |  |  | src => join("/", | 
| 1421 |  |  |  |  |  |  | $self->remoteroot, | 
| 1422 |  |  |  |  |  |  | $path | 
| 1423 |  |  |  |  |  |  | ), | 
| 1424 |  |  |  |  |  |  | dst => $dst, | 
| 1425 |  |  |  |  |  |  | )) { | 
| 1426 | 0 |  |  |  |  | 0 | my(@err) = $self->rsync->err; | 
| 1427 | 0 | 0 | 0 |  |  | 0 | if ($self->_my_ignore_link_stat_errors && "@err" =~ m{^ rsync: \s link_stat }x ) { | 
| 1428 | 0 | 0 |  |  |  | 0 | if ($self->verbose) { | 
| 1429 | 0 |  |  |  |  | 0 | my $LFH = $self->_logfilehandle; | 
| 1430 | 0 |  |  |  |  | 0 | print $LFH "Info: ignoring link_stat error '@err'"; | 
| 1431 |  |  |  |  |  |  | } | 
| 1432 | 0 |  |  |  |  | 0 | return 1; | 
| 1433 |  |  |  |  |  |  | } | 
| 1434 | 0 |  |  |  |  | 0 | $self->register_rsync_error (@err); | 
| 1435 |  |  |  |  |  |  | } | 
| 1436 | 0 |  |  |  |  | 0 | $self->un_register_rsync_error (); | 
| 1437 |  |  |  |  |  |  | } | 
| 1438 | 28 |  |  |  |  | 26014 | return 1; | 
| 1439 |  |  |  |  |  |  | } | 
| 1440 |  |  |  |  |  |  |  | 
| 1441 |  |  |  |  |  |  | sub _my_ignore_link_stat_errors { | 
| 1442 | 0 |  |  | 0 |  | 0 | my($self) = @_; | 
| 1443 | 0 |  |  |  |  | 0 | my $x = $self->ignore_link_stat_errors; | 
| 1444 | 0 | 0 |  |  |  | 0 | $x = 1 unless defined $x; | 
| 1445 | 0 |  |  |  |  | 0 | return $x; | 
| 1446 |  |  |  |  |  |  | } | 
| 1447 |  |  |  |  |  |  |  | 
| 1448 |  |  |  |  |  |  | sub _my_current_rfile { | 
| 1449 | 6619 |  |  | 6619 |  | 13489 | my($self) = @_; | 
| 1450 | 6619 |  |  |  |  | 8866 | my $rfile; | 
| 1451 | 6619 | 100 |  |  |  | 13883 | if ($self->_use_tempfile) { | 
| 1452 | 33 |  |  |  |  | 274 | $rfile = $self->_current_tempfile; | 
| 1453 |  |  |  |  |  |  | } | 
| 1454 | 6619 | 100 | 66 |  |  | 36214 | unless ($rfile && -s $rfile) { | 
| 1455 | 6586 |  |  |  |  | 13293 | $rfile = $self->rfile; | 
| 1456 |  |  |  |  |  |  | } | 
| 1457 | 6619 |  |  |  |  | 17825 | return $rfile; | 
| 1458 |  |  |  |  |  |  | } | 
| 1459 |  |  |  |  |  |  |  | 
| 1460 |  |  |  |  |  |  | =head2 $path = $obj->naive_path_normalize ($path) | 
| 1461 |  |  |  |  |  |  |  | 
| 1462 |  |  |  |  |  |  | Takes an absolute unix style path as argument and canonicalizes it to | 
| 1463 |  |  |  |  |  |  | a shorter path if possible, removing things like double slashes or | 
| 1464 |  |  |  |  |  |  | C and removes references to C<../> directories to get a shorter | 
| 1465 |  |  |  |  |  |  | unambiguos path. This is used to make the code easier that determines | 
| 1466 |  |  |  |  |  |  | if a file passed to C is indeed below our C. | 
| 1467 |  |  |  |  |  |  |  | 
| 1468 |  |  |  |  |  |  | =cut | 
| 1469 |  |  |  |  |  |  |  | 
| 1470 |  |  |  |  |  |  | sub naive_path_normalize { | 
| 1471 | 1292 |  |  | 1292 | 1 | 2836 | my($self,$path) = @_; | 
| 1472 | 1292 |  |  |  |  | 12090 | $path =~ s|/+|/|g; | 
| 1473 | 1292 |  |  |  |  | 5114 | 1 while $path =~ s|/[^/]+/\.\./|/|; | 
| 1474 | 1292 |  |  |  |  | 2335 | $path =~ s|/$||; | 
| 1475 | 1292 |  |  |  |  | 2932 | $path; | 
| 1476 |  |  |  |  |  |  | } | 
| 1477 |  |  |  |  |  |  |  | 
| 1478 |  |  |  |  |  |  | =head2 $ret = $obj->read_recent_1 ( $data ) | 
| 1479 |  |  |  |  |  |  |  | 
| 1480 |  |  |  |  |  |  | Delegate of C on protocol 1 | 
| 1481 |  |  |  |  |  |  |  | 
| 1482 |  |  |  |  |  |  | =cut | 
| 1483 |  |  |  |  |  |  |  | 
| 1484 |  |  |  |  |  |  | sub read_recent_1 { | 
| 1485 | 6487 |  |  | 6487 | 1 | 12276 | my($self, $data) = @_; | 
| 1486 | 6487 |  |  |  |  | 12089 | return $data->{recent}; | 
| 1487 |  |  |  |  |  |  | } | 
| 1488 |  |  |  |  |  |  |  | 
| 1489 |  |  |  |  |  |  | =head2 $array_ref = $obj->recent_events ( %options ) | 
| 1490 |  |  |  |  |  |  |  | 
| 1491 |  |  |  |  |  |  | Note: the code relies on the resource being written atomically. We | 
| 1492 |  |  |  |  |  |  | cannot lock because we may have no write access. If the caller has | 
| 1493 |  |  |  |  |  |  | write access (eg. aggregate() or update()), it has to care for any | 
| 1494 |  |  |  |  |  |  | necessary locking and it MUST write atomically. | 
| 1495 |  |  |  |  |  |  |  | 
| 1496 |  |  |  |  |  |  | If C<$options{after}> is specified, only file events after this | 
| 1497 |  |  |  |  |  |  | timestamp are returned. | 
| 1498 |  |  |  |  |  |  |  | 
| 1499 |  |  |  |  |  |  | If C<$options{before}> is specified, only file events before this | 
| 1500 |  |  |  |  |  |  | timestamp are returned. | 
| 1501 |  |  |  |  |  |  |  | 
| 1502 |  |  |  |  |  |  | If C<$options{max}> is specified only a maximum of this many most | 
| 1503 |  |  |  |  |  |  | recent events is returned. | 
| 1504 |  |  |  |  |  |  |  | 
| 1505 |  |  |  |  |  |  | If C<$options{'skip-deletes'}> is specified, no files-to-be-deleted | 
| 1506 |  |  |  |  |  |  | will be returned. | 
| 1507 |  |  |  |  |  |  |  | 
| 1508 |  |  |  |  |  |  | If C<$options{contains}> is specified the value must be a hash | 
| 1509 |  |  |  |  |  |  | reference containing a query. The query may contain the keys C, | 
| 1510 |  |  |  |  |  |  | C, and C. Each represents a condition that must be met. If | 
| 1511 |  |  |  |  |  |  | there is more than one such key, the conditions are ANDed. | 
| 1512 |  |  |  |  |  |  |  | 
| 1513 |  |  |  |  |  |  | If C<$options{info}> is specified, it must be a hashref. This hashref | 
| 1514 |  |  |  |  |  |  | will be filled with metadata about the unfiltered recent_events of | 
| 1515 |  |  |  |  |  |  | this object, in key C there is the first item, in key C | 
| 1516 |  |  |  |  |  |  | is the last. | 
| 1517 |  |  |  |  |  |  |  | 
| 1518 |  |  |  |  |  |  | =cut | 
| 1519 |  |  |  |  |  |  |  | 
| 1520 |  |  |  |  |  |  | sub recent_events { | 
| 1521 | 6597 |  |  | 6597 | 1 | 161572 | my ($self, %options) = @_; | 
| 1522 | 6597 |  |  |  |  | 14268 | my $info = $options{info}; | 
| 1523 | 6597 | 100 |  |  |  | 18360 | if ($self->is_slave) { | 
| 1524 |  |  |  |  |  |  | # XXX seems dubious, might produce tempfiles without removing them? | 
| 1525 | 37 |  |  |  |  | 588 | $self->get_remote_recentfile_as_tempfile; | 
| 1526 |  |  |  |  |  |  | } | 
| 1527 | 6597 | 50 |  |  |  | 36504 | my $rfile_or_tempfile = $self->_my_current_rfile or return []; | 
| 1528 | 6597 | 100 |  |  |  | 124287 | -e $rfile_or_tempfile or return []; | 
| 1529 | 6487 |  |  |  |  | 30938 | my $suffix = $self->serializer_suffix; | 
| 1530 | 6487 |  |  |  |  | 40904 | my ($data) = eval { | 
| 1531 | 6487 |  |  |  |  | 17788 | $self->_try_deserialize | 
| 1532 |  |  |  |  |  |  | ( | 
| 1533 |  |  |  |  |  |  | $suffix, | 
| 1534 |  |  |  |  |  |  | $rfile_or_tempfile, | 
| 1535 |  |  |  |  |  |  | ); | 
| 1536 |  |  |  |  |  |  | }; | 
| 1537 | 6487 |  |  |  |  | 5224750 | my $err = $@; | 
| 1538 | 6487 | 50 | 33 |  |  | 38015 | if ($err or !$data) { | 
| 1539 | 0 |  |  |  |  | 0 | return []; | 
| 1540 |  |  |  |  |  |  | } | 
| 1541 | 6487 |  |  |  |  | 11520 | my $re; | 
| 1542 | 6487 | 50 |  |  |  | 26907 | if (reftype $data eq 'ARRAY') { # protocol 0 | 
| 1543 | 0 |  |  |  |  | 0 | $re = $data; | 
| 1544 |  |  |  |  |  |  | } else { | 
| 1545 | 6487 |  |  |  |  | 19685 | $re = $self->_recent_events_protocol_x | 
| 1546 |  |  |  |  |  |  | ( | 
| 1547 |  |  |  |  |  |  | $data, | 
| 1548 |  |  |  |  |  |  | $rfile_or_tempfile, | 
| 1549 |  |  |  |  |  |  | ); | 
| 1550 |  |  |  |  |  |  | } | 
| 1551 | 6487 | 100 |  |  |  | 13666 | return $re unless grep {defined $options{$_}} qw(after before contains max skip-deletes); | 
|  | 32435 |  |  |  |  | 92811 |  | 
| 1552 | 2005 |  |  |  |  | 6410 | $self->_recent_events_handle_options ($re, \%options); | 
| 1553 |  |  |  |  |  |  | } | 
| 1554 |  |  |  |  |  |  |  | 
| 1555 |  |  |  |  |  |  | # File::Rsync::Mirror::Recentfile::_recent_events_handle_options | 
| 1556 |  |  |  |  |  |  | sub _recent_events_handle_options { | 
| 1557 | 2005 |  |  | 2005 |  | 4115 | my($self, $re, $options) = @_; | 
| 1558 | 2005 |  |  |  |  | 3515 | my $last_item = $#$re; | 
| 1559 | 2005 |  |  |  |  | 4115 | my $info = $options->{info}; | 
| 1560 | 2005 | 100 |  |  |  | 4205 | if ($info) { | 
| 1561 | 2000 |  |  |  |  | 5150 | $info->{first} = $re->[0]; | 
| 1562 | 2000 |  |  |  |  | 3915 | $info->{last} = $re->[-1]; | 
| 1563 |  |  |  |  |  |  | } | 
| 1564 | 2005 | 100 |  |  |  | 3950 | if (defined $options->{after}) { | 
| 1565 | 5 | 50 |  |  |  | 170 | if ($re->[0]{epoch} > $options->{after}) { | 
| 1566 | 5 | 50 |  |  |  | 225 | if ( | 
| 1567 |  |  |  |  |  |  | my $f = first | 
| 1568 | 125 |  |  | 125 |  | 340 | {$re->[$_]{epoch} <= $options->{after}} | 
| 1569 |  |  |  |  |  |  | 0..$#$re | 
| 1570 |  |  |  |  |  |  | ) { | 
| 1571 | 5 |  |  |  |  | 70 | $last_item = $f-1; | 
| 1572 |  |  |  |  |  |  | } | 
| 1573 |  |  |  |  |  |  | } else { | 
| 1574 | 0 |  |  |  |  | 0 | $last_item = -1; | 
| 1575 |  |  |  |  |  |  | } | 
| 1576 |  |  |  |  |  |  | } | 
| 1577 | 2005 |  |  |  |  | 2880 | my $first_item = 0; | 
| 1578 | 2005 | 100 |  |  |  | 4155 | if (defined $options->{before}) { | 
| 1579 | 2000 | 100 |  |  |  | 10000 | if ($re->[0]{epoch} > $options->{before}) { | 
| 1580 | 1855 | 100 |  |  |  | 19285 | if ( | 
| 1581 |  |  |  |  |  |  | my $f = first | 
| 1582 | 144815 |  |  | 144815 |  | 263095 | {$re->[$_]{epoch} < $options->{before}} | 
| 1583 |  |  |  |  |  |  | 0..$last_item | 
| 1584 |  |  |  |  |  |  | ) { | 
| 1585 | 570 |  |  |  |  | 1760 | $first_item = $f; | 
| 1586 |  |  |  |  |  |  | } | 
| 1587 |  |  |  |  |  |  | } else { | 
| 1588 | 145 |  |  |  |  | 605 | $first_item = 0; | 
| 1589 |  |  |  |  |  |  | } | 
| 1590 |  |  |  |  |  |  | } | 
| 1591 | 2005 | 50 | 66 |  |  | 19040 | if (0 != $first_item || -1 != $last_item) { | 
| 1592 | 2005 |  |  |  |  | 14370 | @$re = splice @$re, $first_item, 1+$last_item-$first_item; | 
| 1593 |  |  |  |  |  |  | } | 
| 1594 | 2005 | 50 |  |  |  | 5855 | if ($options->{'skip-deletes'}) { | 
| 1595 | 0 |  |  |  |  | 0 | @$re = grep { $_->{type} ne "delete" } @$re; | 
|  | 0 |  |  |  |  | 0 |  | 
| 1596 |  |  |  |  |  |  | } | 
| 1597 | 2005 | 50 |  |  |  | 4795 | if (my $contopt = $options->{contains}) { | 
| 1598 | 0 |  |  |  |  | 0 | my $seen_allowed = 0; | 
| 1599 | 0 |  |  |  |  | 0 | for my $allow (qw(epoch path type)) { | 
| 1600 | 0 | 0 |  |  |  | 0 | if (exists $contopt->{$allow}) { | 
| 1601 | 0 |  |  |  |  | 0 | $seen_allowed++; | 
| 1602 | 0 |  |  |  |  | 0 | my $v = $contopt->{$allow}; | 
| 1603 | 0 |  |  |  |  | 0 | @$re = grep { $_->{$allow} eq $v } @$re; | 
|  | 0 |  |  |  |  | 0 |  | 
| 1604 |  |  |  |  |  |  | } | 
| 1605 |  |  |  |  |  |  | } | 
| 1606 | 0 | 0 |  |  |  | 0 | if (keys %$contopt > $seen_allowed) { | 
| 1607 | 0 |  |  |  |  | 0 | require Carp; | 
| 1608 | 0 |  |  |  |  | 0 | Carp::confess | 
| 1609 |  |  |  |  |  |  | (sprintf "unknown query: %s", join ", ", %$contopt); | 
| 1610 |  |  |  |  |  |  | } | 
| 1611 |  |  |  |  |  |  | } | 
| 1612 | 2005 | 50 | 33 |  |  | 5360 | if ($options->{max} && @$re > $options->{max}) { | 
| 1613 | 0 |  |  |  |  | 0 | @$re = splice @$re, 0, $options->{max}; | 
| 1614 |  |  |  |  |  |  | } | 
| 1615 | 2005 |  |  |  |  | 15480 | $re; | 
| 1616 |  |  |  |  |  |  | } | 
| 1617 |  |  |  |  |  |  |  | 
| 1618 |  |  |  |  |  |  | sub _recent_events_protocol_x { | 
| 1619 | 6487 |  |  | 6487 |  | 13978 | my($self, | 
| 1620 |  |  |  |  |  |  | $data, | 
| 1621 |  |  |  |  |  |  | $rfile_or_tempfile, | 
| 1622 |  |  |  |  |  |  | ) = @_; | 
| 1623 | 6487 |  |  |  |  | 36229 | my $meth = sprintf "read_recent_%d", $data->{meta}{protocol}; | 
| 1624 |  |  |  |  |  |  | # we may be reading meta for the first time | 
| 1625 | 6487 |  |  |  |  | 10935 | while (my($k,$v) = each %{$data->{meta}}) { | 
|  | 68455 |  |  |  |  | 336998 |  | 
| 1626 | 61968 | 100 |  |  |  | 115161 | if ($k ne lc $k){ # "Producers" | 
| 1627 | 6487 |  |  |  |  | 19241 | $self->{ORIG}{$k} = $v; | 
| 1628 | 6487 |  |  |  |  | 14719 | next; | 
| 1629 |  |  |  |  |  |  | } | 
| 1630 | 55481 | 100 |  |  |  | 125690 | next if defined $self->$k; | 
| 1631 | 10460 |  |  |  |  | 40664 | $self->$k($v); | 
| 1632 |  |  |  |  |  |  | } | 
| 1633 | 6487 |  |  |  |  | 18428 | my $re = $self->$meth ($data); | 
| 1634 | 6487 |  |  |  |  | 8568 | my $minmax; | 
| 1635 | 6487 | 50 |  |  |  | 132351 | if (my @stat = stat $rfile_or_tempfile) { | 
| 1636 | 6487 |  |  |  |  | 31574 | $minmax = { mtime => $stat[9] }; | 
| 1637 |  |  |  |  |  |  | } else { | 
| 1638 |  |  |  |  |  |  | # defensive because ABH encountered: | 
| 1639 |  |  |  |  |  |  |  | 
| 1640 |  |  |  |  |  |  | #### Sync 1239828608 (1/1/Z) temp .../authors/.FRMRecent-RECENT-Z.yaml- | 
| 1641 |  |  |  |  |  |  | #### Ydr_.yaml ... DONE | 
| 1642 |  |  |  |  |  |  | #### Cannot stat '/mirrors/CPAN/authors/.FRMRecent-RECENT-Z.yaml- | 
| 1643 |  |  |  |  |  |  | #### Ydr_.yaml': No such file or directory at /usr/lib/perl5/site_perl/ | 
| 1644 |  |  |  |  |  |  | #### 5.8.8/File/Rsync/Mirror/Recentfile.pm line 1558. | 
| 1645 |  |  |  |  |  |  | #### unlink0: /mirrors/CPAN/authors/.FRMRecent-RECENT-Z.yaml-Ydr_.yaml is | 
| 1646 |  |  |  |  |  |  | #### gone already at cpan-pause.pl line 0 | 
| 1647 |  |  |  |  |  |  |  | 
| 1648 | 0 |  |  |  |  | 0 | my $LFH = $self->_logfilehandle; | 
| 1649 | 0 |  |  |  |  | 0 | print $LFH "Warning (maybe harmless): Cannot stat '$rfile_or_tempfile': $!" | 
| 1650 |  |  |  |  |  |  | } | 
| 1651 | 6487 | 50 |  |  |  | 16944 | if (@$re) { | 
| 1652 | 6487 |  |  |  |  | 15482 | $minmax->{min} = $re->[-1]{epoch}; | 
| 1653 | 6487 |  |  |  |  | 15694 | $minmax->{max} = $re->[0]{epoch}; | 
| 1654 |  |  |  |  |  |  | } | 
| 1655 | 6487 |  |  |  |  | 22772 | $self->minmax ( $minmax ); | 
| 1656 | 6487 |  |  |  |  | 40323 | return $re; | 
| 1657 |  |  |  |  |  |  | } | 
| 1658 |  |  |  |  |  |  |  | 
| 1659 |  |  |  |  |  |  | sub _try_deserialize { | 
| 1660 | 6487 |  |  | 6487 |  | 17229 | my($self, | 
| 1661 |  |  |  |  |  |  | $suffix, | 
| 1662 |  |  |  |  |  |  | $rfile_or_tempfile, | 
| 1663 |  |  |  |  |  |  | ) = @_; | 
| 1664 | 6487 | 50 |  |  |  | 17230 | if ($suffix eq ".yaml") { | 
|  |  | 0 |  |  |  |  |  | 
| 1665 | 6487 |  |  |  |  | 41436 | require YAML::Syck; | 
| 1666 | 6487 |  |  |  |  | 22806 | YAML::Syck::LoadFile($rfile_or_tempfile); | 
| 1667 |  |  |  |  |  |  | } elsif ($HAVE->{"Data::Serializer"}) { | 
| 1668 |  |  |  |  |  |  | my $serializer = Data::Serializer->new | 
| 1669 | 0 |  |  |  |  | 0 | ( serializer => $serializers{$suffix} ); | 
| 1670 |  |  |  |  |  |  | my $serialized = do | 
| 1671 | 0 |  |  |  |  | 0 | { | 
| 1672 | 0 | 0 |  |  |  | 0 | open my $fh, $rfile_or_tempfile or die "Could not open: $!"; | 
| 1673 | 0 |  |  |  |  | 0 | local $/; | 
| 1674 | 0 |  |  |  |  | 0 | <$fh>; | 
| 1675 |  |  |  |  |  |  | }; | 
| 1676 | 0 |  |  |  |  | 0 | $serializer->raw_deserialize($serialized); | 
| 1677 |  |  |  |  |  |  | } else { | 
| 1678 | 0 |  |  |  |  | 0 | die "Data::Serializer not installed, cannot proceed with suffix '$suffix'"; | 
| 1679 |  |  |  |  |  |  | } | 
| 1680 |  |  |  |  |  |  | } | 
| 1681 |  |  |  |  |  |  |  | 
| 1682 |  |  |  |  |  |  | sub _refresh_internals { | 
| 1683 | 41 |  |  | 41 |  | 1308 | my($self, $dst) = @_; | 
| 1684 | 41 |  |  |  |  | 1015 | my $class = ref $self; | 
| 1685 | 41 |  |  |  |  | 2858 | my $rfpeek = $class->new_from_file ($dst); | 
| 1686 | 41 |  |  |  |  | 536 | for my $acc (qw( | 
| 1687 |  |  |  |  |  |  | _merged | 
| 1688 |  |  |  |  |  |  | minmax | 
| 1689 |  |  |  |  |  |  | )) { | 
| 1690 | 82 |  |  |  |  | 1372 | $self->$acc ( $rfpeek->$acc ); | 
| 1691 |  |  |  |  |  |  | } | 
| 1692 | 41 |  |  |  |  | 727 | my $old_dirtymark = $self->dirtymark; | 
| 1693 | 41 |  |  |  |  | 687 | my $new_dirtymark = $rfpeek->dirtymark; | 
| 1694 | 41 | 100 | 66 |  |  | 1656 | if ($old_dirtymark && $new_dirtymark && $new_dirtymark ne $old_dirtymark) { | 
|  |  |  | 100 |  |  |  |  | 
| 1695 | 5 |  |  |  |  | 120 | $self->done->reset; | 
| 1696 | 5 |  |  |  |  | 73 | $self->dirtymark ( $new_dirtymark ); | 
| 1697 | 5 |  |  |  |  | 125 | $self->_uptodateness_ever_reached(0); | 
| 1698 | 5 |  |  |  |  | 151 | $self->seed; | 
| 1699 |  |  |  |  |  |  | } | 
| 1700 |  |  |  |  |  |  | } | 
| 1701 |  |  |  |  |  |  |  | 
| 1702 |  |  |  |  |  |  | =head2 $ret = $obj->rfilename | 
| 1703 |  |  |  |  |  |  |  | 
| 1704 |  |  |  |  |  |  | Just the basename of our I, composed from C, | 
| 1705 |  |  |  |  |  |  | a dash, C, and C. E.g. C | 
| 1706 |  |  |  |  |  |  |  | 
| 1707 |  |  |  |  |  |  | =cut | 
| 1708 |  |  |  |  |  |  |  | 
| 1709 |  |  |  |  |  |  | sub rfilename { | 
| 1710 | 6682 |  |  | 6682 | 1 | 31023 | my($self) = @_; | 
| 1711 | 6682 |  |  |  |  | 15744 | my $file = sprintf("%s-%s%s", | 
| 1712 |  |  |  |  |  |  | $self->filenameroot, | 
| 1713 |  |  |  |  |  |  | $self->interval, | 
| 1714 |  |  |  |  |  |  | $self->serializer_suffix, | 
| 1715 |  |  |  |  |  |  | ); | 
| 1716 | 6682 |  |  |  |  | 107592 | return $file; | 
| 1717 |  |  |  |  |  |  | } | 
| 1718 |  |  |  |  |  |  |  | 
| 1719 |  |  |  |  |  |  | =head2 $str = $self->remote_dir | 
| 1720 |  |  |  |  |  |  |  | 
| 1721 |  |  |  |  |  |  | The directory we are mirroring from. | 
| 1722 |  |  |  |  |  |  |  | 
| 1723 |  |  |  |  |  |  | =cut | 
| 1724 |  |  |  |  |  |  |  | 
| 1725 |  |  |  |  |  |  | sub remote_dir { | 
| 1726 | 15 |  |  | 15 | 1 | 90 | my($self, $set) = @_; | 
| 1727 | 15 | 100 |  |  |  | 40 | if (defined $set) { | 
| 1728 | 5 |  |  |  |  | 130 | $self->_remote_dir ($set); | 
| 1729 |  |  |  |  |  |  | } | 
| 1730 | 15 |  |  |  |  | 55 | my $x = $self->_remote_dir; | 
| 1731 | 15 |  |  |  |  | 80 | $self->is_slave (1); | 
| 1732 | 15 |  |  |  |  | 80 | return $x; | 
| 1733 |  |  |  |  |  |  | } | 
| 1734 |  |  |  |  |  |  |  | 
| 1735 |  |  |  |  |  |  | =head2 $str = $obj->remoteroot | 
| 1736 |  |  |  |  |  |  |  | 
| 1737 |  |  |  |  |  |  | =head2 (void) $obj->remoteroot ( $set ) | 
| 1738 |  |  |  |  |  |  |  | 
| 1739 |  |  |  |  |  |  | Get/Set the composed prefix needed when rsyncing from a remote module. | 
| 1740 |  |  |  |  |  |  | If remote_host, remote_module, and remote_dir are set, it is composed | 
| 1741 |  |  |  |  |  |  | from these. | 
| 1742 |  |  |  |  |  |  |  | 
| 1743 |  |  |  |  |  |  | =cut | 
| 1744 |  |  |  |  |  |  |  | 
| 1745 |  |  |  |  |  |  | sub remoteroot { | 
| 1746 | 83 |  |  | 83 | 1 | 720 | my($self, $set) = @_; | 
| 1747 | 83 | 100 |  |  |  | 819 | if (defined $set) { | 
| 1748 | 14 |  |  |  |  | 74 | $self->_remoteroot($set); | 
| 1749 |  |  |  |  |  |  | } | 
| 1750 | 83 |  |  |  |  | 864 | my $remoteroot = $self->_remoteroot; | 
| 1751 | 83 | 100 |  |  |  | 1483 | unless (defined $remoteroot) { | 
| 1752 | 5 | 50 |  |  |  | 20 | $remoteroot = sprintf | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 1753 |  |  |  |  |  |  | ( | 
| 1754 |  |  |  |  |  |  | "%s%s%s", | 
| 1755 |  |  |  |  |  |  | defined $self->remote_host   ? ($self->remote_host."::")  : "", | 
| 1756 |  |  |  |  |  |  | defined $self->remote_module ? ($self->remote_module."/") : "", | 
| 1757 |  |  |  |  |  |  | defined $self->remote_dir    ? $self->remote_dir          : "", | 
| 1758 |  |  |  |  |  |  | ); | 
| 1759 | 5 |  |  |  |  | 25 | $self->_remoteroot($remoteroot); | 
| 1760 |  |  |  |  |  |  | } | 
| 1761 | 83 |  |  |  |  | 1020 | return $remoteroot; | 
| 1762 |  |  |  |  |  |  | } | 
| 1763 |  |  |  |  |  |  |  | 
| 1764 |  |  |  |  |  |  | =head2 (void) $obj->split_rfilename ( $recentfilename ) | 
| 1765 |  |  |  |  |  |  |  | 
| 1766 |  |  |  |  |  |  | Inverse method to C. C<$recentfilename> is a plain filename | 
| 1767 |  |  |  |  |  |  | of the pattern | 
| 1768 |  |  |  |  |  |  |  | 
| 1769 |  |  |  |  |  |  | $filenameroot-$interval$serializer_suffix | 
| 1770 |  |  |  |  |  |  |  | 
| 1771 |  |  |  |  |  |  | e.g. | 
| 1772 |  |  |  |  |  |  |  | 
| 1773 |  |  |  |  |  |  | RECENT-1M.yaml | 
| 1774 |  |  |  |  |  |  |  | 
| 1775 |  |  |  |  |  |  | This filename is split into its parts and the parts are fed to the | 
| 1776 |  |  |  |  |  |  | object itself. | 
| 1777 |  |  |  |  |  |  |  | 
| 1778 |  |  |  |  |  |  | =cut | 
| 1779 |  |  |  |  |  |  |  | 
| 1780 |  |  |  |  |  |  | sub split_rfilename { | 
| 1781 | 5 |  |  | 5 | 1 | 40 | my($self, $rfname) = @_; | 
| 1782 | 5 |  |  |  |  | 75 | my($splitter) = qr(^(.+)-([^-\.]+)(\.[^\.]+)); | 
| 1783 | 5 | 50 |  |  |  | 110 | if (my($f,$i,$s) = $rfname =~ $splitter) { | 
| 1784 | 5 |  |  |  |  | 50 | $self->filenameroot      ($f); | 
| 1785 | 5 |  |  |  |  | 35 | $self->interval          ($i); | 
| 1786 | 5 |  |  |  |  | 40 | $self->serializer_suffix ($s); | 
| 1787 |  |  |  |  |  |  | } else { | 
| 1788 | 0 |  |  |  |  | 0 | die "Alert: cannot split '$rfname', doesn't match '$splitter'"; | 
| 1789 |  |  |  |  |  |  | } | 
| 1790 | 5 |  |  |  |  | 75 | return; | 
| 1791 |  |  |  |  |  |  | } | 
| 1792 |  |  |  |  |  |  |  | 
| 1793 |  |  |  |  |  |  | =head2 my $rfile = $obj->rfile | 
| 1794 |  |  |  |  |  |  |  | 
| 1795 |  |  |  |  |  |  | Returns the full path of the I | 
| 1796 |  |  |  |  |  |  |  | 
| 1797 |  |  |  |  |  |  | =cut | 
| 1798 |  |  |  |  |  |  |  | 
| 1799 |  |  |  |  |  |  | sub rfile { | 
| 1800 | 16101 |  |  | 16101 | 1 | 24705 | my($self) = @_; | 
| 1801 | 16101 |  |  |  |  | 36342 | my $rfile = $self->_rfile; | 
| 1802 | 16101 | 100 |  |  |  | 78516 | return $rfile if defined $rfile; | 
| 1803 | 4980 |  |  |  |  | 11545 | $rfile = File::Spec->catfile | 
| 1804 |  |  |  |  |  |  | ($self->localroot, | 
| 1805 |  |  |  |  |  |  | $self->rfilename, | 
| 1806 |  |  |  |  |  |  | ); | 
| 1807 | 4980 |  |  |  |  | 21358 | $self->_rfile ($rfile); | 
| 1808 | 4980 |  |  |  |  | 21496 | return $rfile; | 
| 1809 |  |  |  |  |  |  | } | 
| 1810 |  |  |  |  |  |  |  | 
| 1811 |  |  |  |  |  |  | =head2 $rsync_obj = $obj->rsync | 
| 1812 |  |  |  |  |  |  |  | 
| 1813 |  |  |  |  |  |  | The File::Rsync object that this object uses for communicating with an | 
| 1814 |  |  |  |  |  |  | upstream server. | 
| 1815 |  |  |  |  |  |  |  | 
| 1816 |  |  |  |  |  |  | =cut | 
| 1817 |  |  |  |  |  |  |  | 
| 1818 |  |  |  |  |  |  | sub rsync { | 
| 1819 | 69 |  |  | 69 | 1 | 449 | my($self) = @_; | 
| 1820 | 69 |  |  |  |  | 854 | my $rsync = $self->_rsync; | 
| 1821 | 69 | 100 |  |  |  | 1003 | unless (defined $rsync) { | 
| 1822 | 26 |  | 50 |  |  | 325 | my $rsync_options = $self->rsync_options || {}; | 
| 1823 | 26 | 50 |  |  |  | 300 | if ($HAVE->{"File::Rsync"}) { | 
| 1824 | 26 |  |  |  |  | 849 | $rsync = File::Rsync->new($rsync_options); | 
| 1825 | 26 |  |  |  |  | 79319 | $self->_rsync($rsync); | 
| 1826 |  |  |  |  |  |  | } else { | 
| 1827 | 0 |  |  |  |  | 0 | die "File::Rsync required for rsync operations. Cannot continue"; | 
| 1828 |  |  |  |  |  |  | } | 
| 1829 |  |  |  |  |  |  | } | 
| 1830 | 69 |  |  |  |  | 961 | return $rsync; | 
| 1831 |  |  |  |  |  |  | } | 
| 1832 |  |  |  |  |  |  |  | 
| 1833 |  |  |  |  |  |  | =head2 (void) $obj->register_rsync_error(@err) | 
| 1834 |  |  |  |  |  |  |  | 
| 1835 |  |  |  |  |  |  | =head2 (void) $obj->un_register_rsync_error() | 
| 1836 |  |  |  |  |  |  |  | 
| 1837 |  |  |  |  |  |  | Register_rsync_error is called whenever the File::Rsync object fails | 
| 1838 |  |  |  |  |  |  | on an exec (say, connection doesn't succeed). It issues a warning and | 
| 1839 |  |  |  |  |  |  | sleeps for an increasing amount of time. Un_register_rsync_error | 
| 1840 |  |  |  |  |  |  | resets the error count. See also accessor C. | 
| 1841 |  |  |  |  |  |  |  | 
| 1842 |  |  |  |  |  |  | =cut | 
| 1843 |  |  |  |  |  |  |  | 
| 1844 |  |  |  |  |  |  | { | 
| 1845 |  |  |  |  |  |  | my $no_success_count = 0; | 
| 1846 |  |  |  |  |  |  | my $no_success_time = 0; | 
| 1847 |  |  |  |  |  |  | sub register_rsync_error { | 
| 1848 | 0 |  |  | 0 | 1 | 0 | my($self, @err) = @_; | 
| 1849 | 0 |  |  |  |  | 0 | chomp @err; | 
| 1850 | 0 |  |  |  |  | 0 | $no_success_time = time; | 
| 1851 | 0 |  |  |  |  | 0 | $no_success_count++; | 
| 1852 | 0 |  |  |  |  | 0 | my $max_rsync_errors = $self->max_rsync_errors; | 
| 1853 | 0 | 0 |  |  |  | 0 | $max_rsync_errors = MAX_INT unless defined $max_rsync_errors; | 
| 1854 | 0 | 0 | 0 |  |  | 0 | if ($max_rsync_errors>=0 && $no_success_count >= $max_rsync_errors) { | 
| 1855 | 0 |  |  |  |  | 0 | require Carp; | 
| 1856 | 0 |  |  |  |  | 0 | Carp::confess | 
| 1857 |  |  |  |  |  |  | ( | 
| 1858 |  |  |  |  |  |  | sprintf | 
| 1859 |  |  |  |  |  |  | ( | 
| 1860 |  |  |  |  |  |  | "Alert: Error while rsyncing (%s): '%s', error count: %d, exiting now,", | 
| 1861 |  |  |  |  |  |  | $self->interval, | 
| 1862 |  |  |  |  |  |  | join(" ",@err), | 
| 1863 |  |  |  |  |  |  | $no_success_count, | 
| 1864 |  |  |  |  |  |  | )); | 
| 1865 |  |  |  |  |  |  | } | 
| 1866 | 0 |  |  |  |  | 0 | my $sleep = 12 * $no_success_count; | 
| 1867 | 0 | 0 |  |  |  | 0 | $sleep = 300 if $sleep > 300; | 
| 1868 | 0 |  |  |  |  | 0 | require Carp; | 
| 1869 | 0 |  |  |  |  | 0 | Carp::cluck | 
| 1870 |  |  |  |  |  |  | (sprintf | 
| 1871 |  |  |  |  |  |  | ( | 
| 1872 |  |  |  |  |  |  | "Warning: %s, Error while rsyncing (%s): '%s', sleeping %d", | 
| 1873 |  |  |  |  |  |  | scalar(localtime($no_success_time)), | 
| 1874 |  |  |  |  |  |  | $self->interval, | 
| 1875 |  |  |  |  |  |  | join(" ",@err), | 
| 1876 |  |  |  |  |  |  | $sleep, | 
| 1877 |  |  |  |  |  |  | )); | 
| 1878 | 0 |  |  |  |  | 0 | sleep $sleep | 
| 1879 |  |  |  |  |  |  | } | 
| 1880 |  |  |  |  |  |  | sub un_register_rsync_error { | 
| 1881 | 69 |  |  | 69 | 1 | 746 | my($self) = @_; | 
| 1882 | 69 |  |  |  |  | 688 | $no_success_time = 0; | 
| 1883 | 69 |  |  |  |  | 3566 | $no_success_count = 0; | 
| 1884 |  |  |  |  |  |  | } | 
| 1885 |  |  |  |  |  |  | } | 
| 1886 |  |  |  |  |  |  |  | 
| 1887 |  |  |  |  |  |  | =head2 $clone = $obj->_sparse_clone | 
| 1888 |  |  |  |  |  |  |  | 
| 1889 |  |  |  |  |  |  | Clones just as much from itself that it does not hurt. Experimental | 
| 1890 |  |  |  |  |  |  | method. | 
| 1891 |  |  |  |  |  |  |  | 
| 1892 |  |  |  |  |  |  | Note: what fits better: sparse or shallow? Other suggestions? | 
| 1893 |  |  |  |  |  |  |  | 
| 1894 |  |  |  |  |  |  | =cut | 
| 1895 |  |  |  |  |  |  |  | 
| 1896 |  |  |  |  |  |  | sub _sparse_clone { | 
| 1897 | 3226 |  |  | 3226 |  | 6484 | my($self) = @_; | 
| 1898 | 3226 |  |  |  |  | 13334 | my $new = bless {}, ref $self; | 
| 1899 | 3226 |  |  |  |  | 7520 | for my $m (qw( | 
| 1900 |  |  |  |  |  |  | _interval | 
| 1901 |  |  |  |  |  |  | _localroot | 
| 1902 |  |  |  |  |  |  | _remoteroot | 
| 1903 |  |  |  |  |  |  | _rfile | 
| 1904 |  |  |  |  |  |  | _use_tempfile | 
| 1905 |  |  |  |  |  |  | aggregator | 
| 1906 |  |  |  |  |  |  | filenameroot | 
| 1907 |  |  |  |  |  |  | ignore_link_stat_errors | 
| 1908 |  |  |  |  |  |  | is_slave | 
| 1909 |  |  |  |  |  |  | max_files_per_connection | 
| 1910 |  |  |  |  |  |  | protocol | 
| 1911 |  |  |  |  |  |  | rsync_options | 
| 1912 |  |  |  |  |  |  | serializer_suffix | 
| 1913 |  |  |  |  |  |  | sleep_per_connection | 
| 1914 |  |  |  |  |  |  | tempdir | 
| 1915 |  |  |  |  |  |  | verbose | 
| 1916 |  |  |  |  |  |  | )) { | 
| 1917 | 51616 |  |  |  |  | 218331 | my $o = $self->$m; | 
| 1918 | 51616 | 100 |  |  |  | 307385 | $o = Storable::dclone $o if ref $o; | 
| 1919 | 51616 |  |  |  |  | 98124 | $new->$m($o); | 
| 1920 |  |  |  |  |  |  | } | 
| 1921 | 3226 |  |  |  |  | 18080 | $new; | 
| 1922 |  |  |  |  |  |  | } | 
| 1923 |  |  |  |  |  |  |  | 
| 1924 |  |  |  |  |  |  | =head2 $boolean = OBJ->ttl_reached () | 
| 1925 |  |  |  |  |  |  |  | 
| 1926 |  |  |  |  |  |  | =cut | 
| 1927 |  |  |  |  |  |  |  | 
| 1928 |  |  |  |  |  |  | sub ttl_reached { | 
| 1929 | 43 |  |  | 43 | 1 | 175 | my($self) = @_; | 
| 1930 | 43 |  | 100 |  |  | 348 | my $have_mirrored = $self->have_mirrored || 0; | 
| 1931 | 43 |  |  |  |  | 798 | my $now = Time::HiRes::time; | 
| 1932 | 43 |  |  |  |  | 279 | my $ttl = $self->ttl; | 
| 1933 | 43 | 50 |  |  |  | 486 | $ttl = 24.2 unless defined $ttl; | 
| 1934 | 43 | 100 |  |  |  | 576 | if ($now > $have_mirrored + $ttl) { | 
| 1935 | 10 |  |  |  |  | 105 | return 1; | 
| 1936 |  |  |  |  |  |  | } | 
| 1937 | 33 |  |  |  |  | 308 | return 0; | 
| 1938 |  |  |  |  |  |  | } | 
| 1939 |  |  |  |  |  |  |  | 
| 1940 |  |  |  |  |  |  | =head2 (void) $obj->unlock() | 
| 1941 |  |  |  |  |  |  |  | 
| 1942 |  |  |  |  |  |  | Unlocking is implemented with an C on a locking directory | 
| 1943 |  |  |  |  |  |  | (C<.lock> appended to $rfile). | 
| 1944 |  |  |  |  |  |  |  | 
| 1945 |  |  |  |  |  |  | =cut | 
| 1946 |  |  |  |  |  |  |  | 
| 1947 |  |  |  |  |  |  | sub unlock { | 
| 1948 | 8286 |  |  | 8286 | 1 | 18565 | my($self) = @_; | 
| 1949 | 8286 | 100 |  |  |  | 21627 | return unless $self->_is_locked; | 
| 1950 | 3088 |  |  |  |  | 16563 | my $rfile = $self->rfile; | 
| 1951 | 3088 | 50 |  |  |  | 146287 | unlink "$rfile.lock/process" or warn "Could not unlink lockfile '$rfile.lock/process': $!"; | 
| 1952 | 3088 | 50 |  |  |  | 118055 | rmdir "$rfile.lock" or warn "Could not rmdir lockdir '$rfile.lock': $!";; | 
| 1953 | 3088 |  |  |  |  | 18186 | $self->_is_locked (0); | 
| 1954 |  |  |  |  |  |  | } | 
| 1955 |  |  |  |  |  |  |  | 
| 1956 |  |  |  |  |  |  | =head2 unseed | 
| 1957 |  |  |  |  |  |  |  | 
| 1958 |  |  |  |  |  |  | Sets this recentfile in the state of not 'seeded'. | 
| 1959 |  |  |  |  |  |  |  | 
| 1960 |  |  |  |  |  |  | =cut | 
| 1961 |  |  |  |  |  |  | sub unseed { | 
| 1962 | 67 |  |  | 67 | 1 | 434 | my($self) = @_; | 
| 1963 | 67 |  |  |  |  | 936 | $self->seeded(0); | 
| 1964 |  |  |  |  |  |  | } | 
| 1965 |  |  |  |  |  |  |  | 
| 1966 |  |  |  |  |  |  | =head2 $ret = $obj->update ($path, $type) | 
| 1967 |  |  |  |  |  |  |  | 
| 1968 |  |  |  |  |  |  | =head2 $ret = $obj->update ($path, "new", $dirty_epoch) | 
| 1969 |  |  |  |  |  |  |  | 
| 1970 |  |  |  |  |  |  | =head2 $ret = $obj->update () | 
| 1971 |  |  |  |  |  |  |  | 
| 1972 |  |  |  |  |  |  | Enter one file into the local I. $path is the (usually | 
| 1973 |  |  |  |  |  |  | absolute) path. If the path is outside I tree, then it is | 
| 1974 |  |  |  |  |  |  | ignored. | 
| 1975 |  |  |  |  |  |  |  | 
| 1976 |  |  |  |  |  |  | C<$type> is one of C or C. | 
| 1977 |  |  |  |  |  |  |  | 
| 1978 |  |  |  |  |  |  | Events of type C may set $dirty_epoch. $dirty_epoch is normally | 
| 1979 |  |  |  |  |  |  | not used and the epoch is calculated by the update() routine itself | 
| 1980 |  |  |  |  |  |  | based on current time. But if there is the demand to insert a | 
| 1981 |  |  |  |  |  |  | not-so-current file into the dataset, then the caller sets | 
| 1982 |  |  |  |  |  |  | $dirty_epoch. This causes the epoch of the registered event to become | 
| 1983 |  |  |  |  |  |  | $dirty_epoch or -- if the exact value given is already taken -- a tiny | 
| 1984 |  |  |  |  |  |  | bit more. As compensation the dirtymark of the whole dataset is set to | 
| 1985 |  |  |  |  |  |  | now or the current epoch, whichever is higher. Note: setting the | 
| 1986 |  |  |  |  |  |  | dirty_epoch to the future is prohibited as it's very unlikely to be | 
| 1987 |  |  |  |  |  |  | intended: it definitely might wreak havoc with the index files. | 
| 1988 |  |  |  |  |  |  |  | 
| 1989 |  |  |  |  |  |  | The new file event is unshifted (or, if dirty_epoch is set, inserted | 
| 1990 |  |  |  |  |  |  | at the place it belongs to, according to the rule to have a sequence | 
| 1991 |  |  |  |  |  |  | of strictly decreasing timestamps) to the array of recent_events and | 
| 1992 |  |  |  |  |  |  | the array is shortened to the length of the timespan allowed. This is | 
| 1993 |  |  |  |  |  |  | usually the timespan specified by the interval of this recentfile but | 
| 1994 |  |  |  |  |  |  | as long as this recentfile has not been merged to another one, the | 
| 1995 |  |  |  |  |  |  | timespan may grow without bounds. | 
| 1996 |  |  |  |  |  |  |  | 
| 1997 |  |  |  |  |  |  | The third form runs an update without inserting a new file. This may | 
| 1998 |  |  |  |  |  |  | be desired to truncate a recentfile. | 
| 1999 |  |  |  |  |  |  |  | 
| 2000 |  |  |  |  |  |  | =cut | 
| 2001 |  |  |  |  |  |  | sub _epoch_monotonically_increasing { | 
| 2002 | 1614 |  |  | 1614 |  | 3772 | my($self,$epoch,$recent) = @_; | 
| 2003 | 1614 | 100 |  |  |  | 3540 | return $epoch unless @$recent; # the first one goes unoffended | 
| 2004 | 1579 | 100 |  |  |  | 18003 | if (_bigfloatgt("".$epoch,$recent->[0]{epoch})) { | 
| 2005 | 1414 |  |  |  |  | 4256 | return $epoch; | 
| 2006 |  |  |  |  |  |  | } else { | 
| 2007 | 165 |  |  |  |  | 535 | return _increase_a_bit($recent->[0]{epoch}); | 
| 2008 |  |  |  |  |  |  | } | 
| 2009 |  |  |  |  |  |  | } | 
| 2010 |  |  |  |  |  |  | sub update { | 
| 2011 | 1646 |  |  | 1646 | 1 | 380517 | my($self,$path,$type,$dirty_epoch) = @_; | 
| 2012 | 1646 | 50 | 66 |  |  | 7556 | if (defined $path or defined $type or defined $dirty_epoch) { | 
|  |  |  | 66 |  |  |  |  | 
| 2013 | 1292 | 50 |  |  |  | 4622 | die "update called without path argument" unless defined $path; | 
| 2014 | 1292 | 50 |  |  |  | 3586 | die "update called without type argument" unless defined $type; | 
| 2015 | 1292 | 50 |  |  |  | 17492 | die "update called with illegal type argument: $type" unless $type =~ /(new|delete)/; | 
| 2016 |  |  |  |  |  |  | } | 
| 2017 | 1646 |  |  |  |  | 7269 | $self->lock; | 
| 2018 | 1646 |  |  |  |  | 28389 | my $ctx = $self->_locked_batch_update([{path=>$path,type=>$type,epoch=>$dirty_epoch}]); | 
| 2019 | 1646 | 100 |  |  |  | 10478 | $self->write_recent($ctx->{recent}) if $ctx->{something_done}; | 
| 2020 | 1646 |  |  |  |  | 9886 | $self->_assert_symlink; | 
| 2021 | 1646 |  |  |  |  | 6168 | $self->unlock; | 
| 2022 |  |  |  |  |  |  | } | 
| 2023 |  |  |  |  |  |  |  | 
| 2024 |  |  |  |  |  |  | =head2 $obj->batch_update($batch) | 
| 2025 |  |  |  |  |  |  |  | 
| 2026 |  |  |  |  |  |  | Like update but for many files. $batch is an arrayref containing | 
| 2027 |  |  |  |  |  |  | hashrefs with the structure | 
| 2028 |  |  |  |  |  |  |  | 
| 2029 |  |  |  |  |  |  | { | 
| 2030 |  |  |  |  |  |  | path => $path, | 
| 2031 |  |  |  |  |  |  | type => $type, | 
| 2032 |  |  |  |  |  |  | epoch => $epoch, | 
| 2033 |  |  |  |  |  |  | } | 
| 2034 |  |  |  |  |  |  |  | 
| 2035 |  |  |  |  |  |  |  | 
| 2036 |  |  |  |  |  |  |  | 
| 2037 |  |  |  |  |  |  | =cut | 
| 2038 |  |  |  |  |  |  | sub batch_update { | 
| 2039 | 0 |  |  | 0 | 1 | 0 | my($self,$batch) = @_; | 
| 2040 | 0 |  |  |  |  | 0 | $self->lock; | 
| 2041 | 0 |  |  |  |  | 0 | my $ctx = $self->_locked_batch_update($batch); | 
| 2042 | 0 | 0 |  |  |  | 0 | $self->write_recent($ctx->{recent}) if $ctx->{something_done}; | 
| 2043 | 0 |  |  |  |  | 0 | $self->_assert_symlink; | 
| 2044 | 0 |  |  |  |  | 0 | $self->unlock; | 
| 2045 |  |  |  |  |  |  | } | 
| 2046 |  |  |  |  |  |  | sub _locked_batch_update { | 
| 2047 | 1646 |  |  | 1646 |  | 5141 | my($self,$batch) = @_; | 
| 2048 | 1646 |  |  |  |  | 2935 | my $something_done = 0; | 
| 2049 | 1646 |  |  |  |  | 6182 | my $recent = $self->recent_events; | 
| 2050 | 1646 | 100 |  |  |  | 6358 | unless ($recent->[0]) { | 
| 2051 |  |  |  |  |  |  | # obstetrics | 
| 2052 | 35 |  |  |  |  | 75 | $something_done = 1; | 
| 2053 |  |  |  |  |  |  | } | 
| 2054 | 1646 |  |  |  |  | 4141 | my %paths_in_recent = map { $_->{path} => undef } @$recent; | 
|  | 61737 |  |  |  |  | 116459 |  | 
| 2055 | 1646 |  |  |  |  | 6962 | my $interval = $self->interval; | 
| 2056 | 1646 |  |  |  |  | 4121 | my $canonmeth = $self->canonize; | 
| 2057 | 1646 | 100 |  |  |  | 7785 | unless ($canonmeth) { | 
| 2058 | 390 |  |  |  |  | 670 | $canonmeth = "naive_path_normalize"; | 
| 2059 |  |  |  |  |  |  | } | 
| 2060 | 1646 |  |  |  |  | 3251 | my $oldest_allowed = 0; | 
| 2061 | 1646 |  |  |  |  | 2333 | my $setting_new_dirty_mark = 0; | 
| 2062 | 1646 |  |  |  |  | 2362 | my $console; | 
| 2063 | 1646 | 50 | 66 |  |  | 4357 | if ($self->verbose && @$batch > 1) { | 
| 2064 | 0 |  |  |  |  | 0 | eval {require Time::Progress}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 2065 | 0 | 0 |  |  |  | 0 | warn "dollarat[$@]" if $@; | 
| 2066 | 0 |  |  |  |  | 0 | $| = 1; | 
| 2067 | 0 |  |  |  |  | 0 | $console = new Time::Progress; | 
| 2068 | 0 |  |  |  |  | 0 | $console->attr( min => 1, max => scalar @$batch ); | 
| 2069 | 0 |  |  |  |  | 0 | print "\n"; | 
| 2070 |  |  |  |  |  |  | } | 
| 2071 | 1646 |  |  |  |  | 8608 | my $i = 0; | 
| 2072 | 1646 |  |  |  |  | 2596 | my $memo_splicepos; | 
| 2073 | 1646 |  | 0 |  |  | 6270 | ITEM: for my $item (sort {($b->{epoch}||0) <=> ($a->{epoch}||0)} @$batch) { | 
|  | 0 |  | 0 |  |  | 0 |  | 
| 2074 | 1646 |  |  |  |  | 2978 | $i++; | 
| 2075 | 1646 | 50 | 33 |  |  | 5041 | print $console->report( "\rdone %p elapsed: %L (%l sec), ETA %E (%e sec)", $i ) if $console and not $i % 50; | 
| 2076 | 1646 |  |  |  |  | 5966 | my $ctx = $self->_update_batch_item($item,$canonmeth,$recent,$setting_new_dirty_mark,$oldest_allowed,$something_done,\%paths_in_recent,$memo_splicepos); | 
| 2077 | 1646 |  |  |  |  | 3829 | $something_done = $ctx->{something_done}; | 
| 2078 | 1646 |  |  |  |  | 2602 | $oldest_allowed = $ctx->{oldest_allowed}; | 
| 2079 | 1646 |  |  |  |  | 2466 | $setting_new_dirty_mark = $ctx->{setting_new_dirty_mark}; | 
| 2080 | 1646 |  |  |  |  | 3636 | $recent = $ctx->{recent}; | 
| 2081 | 1646 |  |  |  |  | 5037 | $memo_splicepos = $ctx->{memo_splicepos}; | 
| 2082 |  |  |  |  |  |  | } | 
| 2083 | 1646 | 50 |  |  |  | 3639 | print "\n" if $console; | 
| 2084 | 1646 | 100 |  |  |  | 3575 | if ($setting_new_dirty_mark) { | 
| 2085 | 32 |  |  |  |  | 54 | $oldest_allowed = 0; | 
| 2086 |  |  |  |  |  |  | } | 
| 2087 | 1646 |  |  |  |  | 3651 | TRUNCATE: while (@$recent) { | 
| 2088 | 2650 | 100 |  |  |  | 7424 | if (_bigfloatlt($recent->[-1]{epoch}, $oldest_allowed)) { | 
| 2089 | 1004 |  |  |  |  | 1560 | pop @$recent; | 
| 2090 | 1004 |  |  |  |  | 2532 | $something_done = 1; | 
| 2091 |  |  |  |  |  |  | } else { | 
| 2092 | 1646 |  |  |  |  | 4032 | last TRUNCATE; | 
| 2093 |  |  |  |  |  |  | } | 
| 2094 |  |  |  |  |  |  | } | 
| 2095 | 1646 |  |  |  |  | 14232 | return {something_done=>$something_done,recent=>$recent}; | 
| 2096 |  |  |  |  |  |  | } | 
| 2097 |  |  |  |  |  |  | sub _update_batch_item { | 
| 2098 | 1646 |  |  | 1646 |  | 5616 | my($self,$item,$canonmeth,$recent,$setting_new_dirty_mark,$oldest_allowed,$something_done,$paths_in_recent,$memo_splicepos) = @_; | 
| 2099 | 1646 |  |  |  |  | 2851 | my($path,$type,$dirty_epoch) = @{$item}{qw(path type epoch)}; | 
|  | 1646 |  |  |  |  | 6251 |  | 
| 2100 | 1646 | 50 | 66 |  |  | 7467 | if (defined $path or defined $type or defined $dirty_epoch) { | 
|  |  |  | 66 |  |  |  |  | 
| 2101 | 1292 |  |  |  |  | 5962 | $path = $self->$canonmeth($path); | 
| 2102 |  |  |  |  |  |  | } | 
| 2103 |  |  |  |  |  |  | # you must calculate the time after having locked, of course | 
| 2104 | 1646 |  |  |  |  | 4851 | my $now = Time::HiRes::time; | 
| 2105 |  |  |  |  |  |  |  | 
| 2106 | 1646 |  |  |  |  | 2066 | my $epoch; | 
| 2107 | 1646 | 100 | 66 |  |  | 6284 | if (defined $dirty_epoch && _bigfloatgt($now,$dirty_epoch)) { | 
| 2108 | 32 |  |  |  |  | 79 | $epoch = $dirty_epoch; | 
| 2109 |  |  |  |  |  |  | } else { | 
| 2110 | 1614 |  |  |  |  | 4844 | $epoch = $self->_epoch_monotonically_increasing($now,$recent); | 
| 2111 |  |  |  |  |  |  | } | 
| 2112 | 1646 |  | 50 |  |  | 5307 | $recent ||= []; | 
| 2113 | 1646 |  |  |  |  | 4303 | my $merged = $self->merged; | 
| 2114 | 1646 | 100 | 66 |  |  | 7703 | if ($merged->{epoch} && !$setting_new_dirty_mark) { | 
| 2115 | 877 |  |  |  |  | 3951 | my $virtualnow = _bigfloatmax($now,$epoch); | 
| 2116 |  |  |  |  |  |  | # for the lower bound I think we need no big math, we calc already | 
| 2117 | 877 |  |  |  |  | 3269 | my $secs = $self->interval_secs(); | 
| 2118 | 877 |  |  |  |  | 7089 | $oldest_allowed = min($virtualnow - $secs, $merged->{epoch}, $epoch); | 
| 2119 |  |  |  |  |  |  | } else { | 
| 2120 |  |  |  |  |  |  | # as long as we are not merged at all, no limits! | 
| 2121 |  |  |  |  |  |  | } | 
| 2122 | 1646 |  |  |  |  | 3756 | my $lrd = $self->localroot; | 
| 2123 | 1646 | 100 | 66 |  |  | 21695 | if (defined $path && $path =~ s|^\Q$lrd\E||) { | 
| 2124 | 1292 |  |  |  |  | 4844 | $path =~ s|^/||; | 
| 2125 | 1292 |  |  |  |  | 2133 | my $splicepos; | 
| 2126 |  |  |  |  |  |  | # remove the older duplicates of this $path, irrespective of $type: | 
| 2127 | 1292 | 100 |  |  |  | 2744 | if (defined $dirty_epoch) { | 
| 2128 | 32 |  |  |  |  | 156 | my $ctx = $self->_update_with_dirty_epoch($path,$recent,$epoch,$paths_in_recent,$memo_splicepos); | 
| 2129 | 32 |  |  |  |  | 91 | $recent    = $ctx->{recent}; | 
| 2130 | 32 |  |  |  |  | 56 | $splicepos = $ctx->{splicepos}; | 
| 2131 | 32 |  |  |  |  | 89 | $epoch     = $ctx->{epoch}; | 
| 2132 | 32 |  |  |  |  | 260 | my $dirtymark = $self->dirtymark; | 
| 2133 | 32 |  |  |  |  | 167 | my $new_dm = $now; | 
| 2134 | 32 | 50 |  |  |  | 88 | if (_bigfloatgt($epoch, $now)) { # just in case we had to increase it | 
| 2135 | 0 |  |  |  |  | 0 | $new_dm = $epoch; | 
| 2136 |  |  |  |  |  |  | } | 
| 2137 | 32 |  |  |  |  | 120 | $self->dirtymark($new_dm); | 
| 2138 | 32 |  |  |  |  | 396 | $setting_new_dirty_mark = 1; | 
| 2139 | 32 | 50 | 33 |  |  | 184 | if (not defined $merged->{epoch} or _bigfloatlt($epoch,$merged->{epoch})) { | 
| 2140 | 32 |  |  |  |  | 96 | $self->merged(+{}); | 
| 2141 |  |  |  |  |  |  | } | 
| 2142 |  |  |  |  |  |  | } else { | 
| 2143 | 1260 |  |  |  |  | 2524 | $recent = [ grep { $_->{path} ne $path } @$recent ]; | 
|  | 47041 |  |  |  |  | 74676 |  | 
| 2144 | 1260 |  |  |  |  | 2318 | $splicepos = 0; | 
| 2145 |  |  |  |  |  |  | } | 
| 2146 | 1292 | 50 |  |  |  | 3155 | if (defined $splicepos) { | 
| 2147 | 1292 |  |  |  |  | 10485 | splice @$recent, $splicepos, 0, { epoch => $epoch, path => $path, type => $type }; | 
| 2148 | 1292 |  |  |  |  | 4201 | $paths_in_recent->{$path} = undef; | 
| 2149 |  |  |  |  |  |  | } | 
| 2150 | 1292 |  |  |  |  | 2318 | $memo_splicepos = $splicepos; | 
| 2151 | 1292 |  |  |  |  | 2143 | $something_done = 1; | 
| 2152 |  |  |  |  |  |  | } | 
| 2153 |  |  |  |  |  |  | return | 
| 2154 |  |  |  |  |  |  | { | 
| 2155 | 1646 |  |  |  |  | 11291 | something_done => $something_done, | 
| 2156 |  |  |  |  |  |  | oldest_allowed => $oldest_allowed, | 
| 2157 |  |  |  |  |  |  | setting_new_dirty_mark => $setting_new_dirty_mark, | 
| 2158 |  |  |  |  |  |  | recent => $recent, | 
| 2159 |  |  |  |  |  |  | memo_splicepos => $memo_splicepos, | 
| 2160 |  |  |  |  |  |  | } | 
| 2161 |  |  |  |  |  |  | } | 
| 2162 |  |  |  |  |  |  | sub _update_with_dirty_epoch { | 
| 2163 | 32 |  |  | 32 |  | 117 | my($self,$path,$recent,$epoch,$paths_in_recent,$memo_splicepos) = @_; | 
| 2164 | 32 |  |  |  |  | 59 | my $splicepos; | 
| 2165 | 32 |  |  |  |  | 86 | my $new_recent = []; | 
| 2166 | 32 | 50 |  |  |  | 128 | if (exists $paths_in_recent->{$path}) { | 
| 2167 | 0 |  |  |  |  | 0 | my $cancel = 0; | 
| 2168 | 0 |  |  |  |  | 0 | KNOWN_EVENT: for my $i (0..$#$recent) { | 
| 2169 | 0 | 0 |  |  |  | 0 | if ($recent->[$i]{path} eq $path) { | 
| 2170 | 0 | 0 |  |  |  | 0 | if ($recent->[$i]{epoch} eq $epoch) { | 
| 2171 |  |  |  |  |  |  | # nothing to do | 
| 2172 | 0 |  |  |  |  | 0 | $cancel = 1; | 
| 2173 | 0 |  |  |  |  | 0 | last KNOWN_EVENT; | 
| 2174 |  |  |  |  |  |  | } | 
| 2175 |  |  |  |  |  |  | } else { | 
| 2176 | 0 |  |  |  |  | 0 | push @$new_recent, $recent->[$i]; | 
| 2177 |  |  |  |  |  |  | } | 
| 2178 |  |  |  |  |  |  | } | 
| 2179 | 0 | 0 |  |  |  | 0 | @$recent = @$new_recent unless $cancel; | 
| 2180 |  |  |  |  |  |  | } | 
| 2181 | 32 | 50 | 33 |  |  | 214 | if (!exists $recent->[0] or _bigfloatgt($epoch,$recent->[0]{epoch})) { | 
|  |  | 50 |  |  |  |  |  | 
| 2182 | 0 |  |  |  |  | 0 | $splicepos = 0; | 
| 2183 |  |  |  |  |  |  | } elsif (_bigfloatlt($epoch,$recent->[-1]{epoch})) { | 
| 2184 | 32 |  |  |  |  | 76 | $splicepos = @$recent; | 
| 2185 |  |  |  |  |  |  | } else { | 
| 2186 | 0 |  |  |  |  | 0 | my $startingpoint; | 
| 2187 | 0 | 0 | 0 |  |  | 0 | if (_bigfloatgt($memo_splicepos<=$#$recent && $epoch, $recent->[$memo_splicepos]{epoch})) { | 
| 2188 | 0 |  |  |  |  | 0 | $startingpoint = 0; | 
| 2189 |  |  |  |  |  |  | } else { | 
| 2190 | 0 |  |  |  |  | 0 | $startingpoint = $memo_splicepos; | 
| 2191 |  |  |  |  |  |  | } | 
| 2192 | 0 |  |  |  |  | 0 | RECENT: for my $i ($startingpoint..$#$recent) { | 
| 2193 | 0 |  |  |  |  | 0 | my $ev = $recent->[$i]; | 
| 2194 | 0 | 0 |  |  |  | 0 | if ($epoch eq $recent->[$i]{epoch}) { | 
| 2195 | 0 | 0 |  |  |  | 0 | $epoch = _increase_a_bit($epoch, $i ? $recent->[$i-1]{epoch} : undef); | 
| 2196 |  |  |  |  |  |  | } | 
| 2197 | 0 | 0 |  |  |  | 0 | if (_bigfloatgt($epoch,$recent->[$i]{epoch})) { | 
| 2198 | 0 |  |  |  |  | 0 | $splicepos = $i; | 
| 2199 | 0 |  |  |  |  | 0 | last RECENT; | 
| 2200 |  |  |  |  |  |  | } | 
| 2201 |  |  |  |  |  |  | } | 
| 2202 |  |  |  |  |  |  | } | 
| 2203 |  |  |  |  |  |  | return { | 
| 2204 | 32 |  |  |  |  | 216 | recent => $recent, | 
| 2205 |  |  |  |  |  |  | splicepos => $splicepos, | 
| 2206 |  |  |  |  |  |  | epoch => $epoch, | 
| 2207 |  |  |  |  |  |  | } | 
| 2208 |  |  |  |  |  |  | } | 
| 2209 |  |  |  |  |  |  |  | 
| 2210 |  |  |  |  |  |  | =head2 seed | 
| 2211 |  |  |  |  |  |  |  | 
| 2212 |  |  |  |  |  |  | Sets this recentfile in the state of 'seeded' which means it has to | 
| 2213 |  |  |  |  |  |  | re-evaluate its uptodateness. | 
| 2214 |  |  |  |  |  |  |  | 
| 2215 |  |  |  |  |  |  | =cut | 
| 2216 |  |  |  |  |  |  | sub seed { | 
| 2217 | 28 |  |  | 28 | 1 | 204 | my($self) = @_; | 
| 2218 | 28 |  |  |  |  | 308 | $self->seeded(1); | 
| 2219 |  |  |  |  |  |  | } | 
| 2220 |  |  |  |  |  |  |  | 
| 2221 |  |  |  |  |  |  | =head2 seeded | 
| 2222 |  |  |  |  |  |  |  | 
| 2223 |  |  |  |  |  |  | Tells if the recentfile is in the state 'seeded'. | 
| 2224 |  |  |  |  |  |  |  | 
| 2225 |  |  |  |  |  |  | =cut | 
| 2226 |  |  |  |  |  |  | sub seeded { | 
| 2227 | 134 |  |  | 134 | 1 | 1018 | my($self, $set) = @_; | 
| 2228 | 134 | 100 |  |  |  | 662 | if (defined $set) { | 
| 2229 | 95 |  |  |  |  | 1160 | $self->_seeded ($set); | 
| 2230 |  |  |  |  |  |  | } | 
| 2231 | 134 |  |  |  |  | 1445 | my $x = $self->_seeded; | 
| 2232 | 134 | 100 |  |  |  | 1107 | unless (defined $x) { | 
| 2233 | 8 |  |  |  |  | 40 | $x = 0; | 
| 2234 | 8 |  |  |  |  | 45 | $self->_seeded ($x); | 
| 2235 |  |  |  |  |  |  | } | 
| 2236 | 134 |  |  |  |  | 893 | return $x; | 
| 2237 |  |  |  |  |  |  | } | 
| 2238 |  |  |  |  |  |  |  | 
| 2239 |  |  |  |  |  |  | =head2 uptodate | 
| 2240 |  |  |  |  |  |  |  | 
| 2241 |  |  |  |  |  |  | True if this object has mirrored the complete interval covered by the | 
| 2242 |  |  |  |  |  |  | current recentfile. | 
| 2243 |  |  |  |  |  |  |  | 
| 2244 |  |  |  |  |  |  | =cut | 
| 2245 |  |  |  |  |  |  | sub uptodate { | 
| 2246 | 56 |  |  | 56 | 1 | 397 | my($self) = @_; | 
| 2247 | 56 |  |  |  |  | 223 | my $uptodate; | 
| 2248 |  |  |  |  |  |  | my $why; | 
| 2249 | 56 | 100 | 66 |  |  | 588 | if ($self->_uptodateness_ever_reached and not $self->seeded) { | 
| 2250 | 19 |  |  |  |  | 197 | $why = "saturated"; | 
| 2251 | 19 |  |  |  |  | 112 | $uptodate = 1; | 
| 2252 |  |  |  |  |  |  | } | 
| 2253 |  |  |  |  |  |  | # it's too easy to misconfigure ttl and related timings and then | 
| 2254 |  |  |  |  |  |  | # never reach uptodateness, so disabled 2009-03-22 | 
| 2255 | 56 |  |  |  |  | 603 | if (0 and not defined $uptodate) { | 
| 2256 |  |  |  |  |  |  | if ($self->ttl_reached){ | 
| 2257 |  |  |  |  |  |  | $why = "ttl_reached returned true, so we are not uptodate"; | 
| 2258 |  |  |  |  |  |  | $uptodate = 0 ; | 
| 2259 |  |  |  |  |  |  | } | 
| 2260 |  |  |  |  |  |  | } | 
| 2261 | 56 | 100 |  |  |  | 441 | unless (defined $uptodate) { | 
| 2262 |  |  |  |  |  |  | # look if recentfile has unchanged timestamp | 
| 2263 | 37 |  |  |  |  | 319 | my $minmax = $self->minmax; | 
| 2264 | 37 | 100 |  |  |  | 500 | if (exists $minmax->{mtime}) { | 
| 2265 | 21 |  |  |  |  | 263 | my $rfile = $self->_my_current_rfile; | 
| 2266 | 21 |  |  |  |  | 548 | my @stat = stat $rfile; | 
| 2267 | 21 | 50 |  |  |  | 216 | if (@stat) { | 
| 2268 | 21 |  |  |  |  | 123 | my $mtime = $stat[9]; | 
| 2269 | 21 | 50 | 33 |  |  | 861 | if (defined $mtime && defined $minmax->{mtime} && $mtime > $minmax->{mtime}) { | 
|  |  |  | 33 |  |  |  |  | 
| 2270 | 0 |  |  |  |  | 0 | $why = "mtime[$mtime] of rfile[$rfile] > minmax/mtime[$minmax->{mtime}], so we are not uptodate"; | 
| 2271 | 0 |  |  |  |  | 0 | $uptodate = 0; | 
| 2272 |  |  |  |  |  |  | } else { | 
| 2273 | 21 |  |  |  |  | 196 | my $covered = $self->done->covered(@$minmax{qw(max min)}); | 
| 2274 | 21 | 50 |  |  |  | 489 | $why = sprintf "minmax covered[%s], so we return that", defined $covered ? $covered : "UNDEF"; | 
| 2275 | 21 |  |  |  |  | 199 | $uptodate = $covered; | 
| 2276 |  |  |  |  |  |  | } | 
| 2277 |  |  |  |  |  |  | } else { | 
| 2278 | 0 |  |  |  |  | 0 | require Carp; | 
| 2279 | 0 |  |  |  |  | 0 | $why = "Could not stat '$rfile': $!"; | 
| 2280 | 0 |  |  |  |  | 0 | Carp::cluck($why); | 
| 2281 | 0 |  |  |  |  | 0 | $uptodate = 0; | 
| 2282 |  |  |  |  |  |  | } | 
| 2283 |  |  |  |  |  |  | } | 
| 2284 |  |  |  |  |  |  | } | 
| 2285 | 56 | 100 |  |  |  | 348 | unless (defined $uptodate) { | 
| 2286 | 16 |  |  |  |  | 57 | $why = "fallthrough, so not uptodate"; | 
| 2287 | 16 |  |  |  |  | 28 | $uptodate = 0; | 
| 2288 |  |  |  |  |  |  | } | 
| 2289 | 56 | 100 |  |  |  | 316 | if ($uptodate) { | 
| 2290 | 34 |  |  |  |  | 175 | $self->_uptodateness_ever_reached(1); | 
| 2291 |  |  |  |  |  |  | } | 
| 2292 | 56 |  |  |  |  | 625 | my $remember = | 
| 2293 |  |  |  |  |  |  | { | 
| 2294 |  |  |  |  |  |  | uptodate => $uptodate, | 
| 2295 |  |  |  |  |  |  | why => $why, | 
| 2296 |  |  |  |  |  |  | }; | 
| 2297 | 56 |  |  |  |  | 412 | $self->_remember_last_uptodate_call($remember); | 
| 2298 | 56 |  |  |  |  | 897 | return $uptodate; | 
| 2299 |  |  |  |  |  |  | } | 
| 2300 |  |  |  |  |  |  |  | 
| 2301 |  |  |  |  |  |  | =head2 $obj->write_recent ($recent_files_arrayref) | 
| 2302 |  |  |  |  |  |  |  | 
| 2303 |  |  |  |  |  |  | Writes a I based on the current reflection of the current | 
| 2304 |  |  |  |  |  |  | state of the tree limited by the current interval. | 
| 2305 |  |  |  |  |  |  |  | 
| 2306 |  |  |  |  |  |  | =cut | 
| 2307 |  |  |  |  |  |  | sub _resort { | 
| 2308 | 0 |  |  | 0 |  | 0 | my($self) = @_; | 
| 2309 | 0 |  |  |  |  | 0 | @{$_[1]} = sort { _bigfloatcmp($b->{epoch},$a->{epoch}) } @{$_[1]}; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 2310 | 0 |  |  |  |  | 0 | return; | 
| 2311 |  |  |  |  |  |  | } | 
| 2312 |  |  |  |  |  |  | sub write_recent { | 
| 2313 | 2712 |  |  | 2712 | 1 | 6306 | my ($self,$recent) = @_; | 
| 2314 | 2712 | 50 |  |  |  | 5618 | die "write_recent called without argument" unless defined $recent; | 
| 2315 | 2712 |  |  |  |  | 3653 | my $Last_epoch; | 
| 2316 | 2712 |  |  |  |  | 8960 | SANITYCHECK: for my $i (0..$#$recent) { | 
| 2317 | 108413 | 50 | 66 |  |  | 240676 | if (defined($Last_epoch) and _bigfloatge($recent->[$i]{epoch},$Last_epoch)) { | 
| 2318 | 0 |  |  |  |  | 0 | require Carp; | 
| 2319 |  |  |  |  |  |  | Carp::confess(sprintf "Warning: disorder '%s'>='%s', re-sorting %s\n", | 
| 2320 | 0 |  |  |  |  | 0 | $recent->[$i]{epoch}, $Last_epoch, $self->interval); | 
| 2321 |  |  |  |  |  |  | # you may want to: | 
| 2322 |  |  |  |  |  |  | # $self->_resort($recent); | 
| 2323 |  |  |  |  |  |  | # last SANITYCHECK; | 
| 2324 |  |  |  |  |  |  | } | 
| 2325 | 108413 |  |  |  |  | 187700 | $Last_epoch = $recent->[$i]{epoch}; | 
| 2326 |  |  |  |  |  |  | } | 
| 2327 | 2712 |  |  |  |  | 7718 | my $minmax = $self->minmax; | 
| 2328 | 2712 | 100 | 100 |  |  | 18316 | if (!defined $minmax->{max} || _bigfloatlt($minmax->{max},$recent->[0]{epoch})) { | 
| 2329 | 1668 | 50 | 33 |  |  | 8825 | $minmax->{max} = @$recent && exists $recent->[0]{epoch} ? $recent->[0]{epoch} : undef; | 
| 2330 |  |  |  |  |  |  | } | 
| 2331 | 2712 | 100 | 100 |  |  | 11249 | if (!defined $minmax->{min} || _bigfloatlt($minmax->{min},$recent->[-1]{epoch})) { | 
| 2332 | 563 | 50 | 33 |  |  | 2900 | $minmax->{min} = @$recent && exists $recent->[-1]{epoch} ? $recent->[-1]{epoch} : undef; | 
| 2333 |  |  |  |  |  |  | } | 
| 2334 | 2712 |  |  |  |  | 8116 | $self->minmax($minmax); | 
| 2335 | 2712 |  |  |  |  | 12139 | my $meth = sprintf "write_%d", $self->protocol; | 
| 2336 | 2712 |  |  |  |  | 24667 | $self->$meth($recent); | 
| 2337 |  |  |  |  |  |  | } | 
| 2338 |  |  |  |  |  |  |  | 
| 2339 |  |  |  |  |  |  | =head2 $obj->write_0 ($recent_files_arrayref) | 
| 2340 |  |  |  |  |  |  |  | 
| 2341 |  |  |  |  |  |  | Delegate of C on protocol 0 | 
| 2342 |  |  |  |  |  |  |  | 
| 2343 |  |  |  |  |  |  | =cut | 
| 2344 |  |  |  |  |  |  |  | 
| 2345 |  |  |  |  |  |  | sub write_0 { | 
| 2346 | 0 |  |  | 0 | 1 | 0 | my ($self,$recent) = @_; | 
| 2347 | 0 |  |  |  |  | 0 | my $rfile = $self->rfile; | 
| 2348 | 0 |  |  |  |  | 0 | YAML::Syck::DumpFile("$rfile.new",$recent); | 
| 2349 | 0 | 0 |  |  |  | 0 | rename "$rfile.new", $rfile or die "Could not rename to '$rfile': $!"; | 
| 2350 |  |  |  |  |  |  | } | 
| 2351 |  |  |  |  |  |  |  | 
| 2352 |  |  |  |  |  |  | =head2 $obj->write_1 ($recent_files_arrayref) | 
| 2353 |  |  |  |  |  |  |  | 
| 2354 |  |  |  |  |  |  | Delegate of C on protocol 1 | 
| 2355 |  |  |  |  |  |  |  | 
| 2356 |  |  |  |  |  |  | =cut | 
| 2357 |  |  |  |  |  |  |  | 
| 2358 |  |  |  |  |  |  | sub write_1 { | 
| 2359 | 2712 |  |  | 2712 | 1 | 5834 | my ($self,$recent) = @_; | 
| 2360 | 2712 |  |  |  |  | 6848 | my $rfile = $self->rfile; | 
| 2361 | 2712 |  |  |  |  | 5930 | my $suffix = $self->serializer_suffix; | 
| 2362 | 2712 |  |  |  |  | 13577 | my $data = { | 
| 2363 |  |  |  |  |  |  | meta => $self->meta_data, | 
| 2364 |  |  |  |  |  |  | recent => $recent, | 
| 2365 |  |  |  |  |  |  | }; | 
| 2366 | 2712 |  |  |  |  | 4400 | my $serialized; | 
| 2367 | 2712 | 100 |  |  |  | 5756 | if ($suffix eq ".yaml") { | 
|  |  | 50 |  |  |  |  |  | 
| 2368 | 2697 |  |  |  |  | 8781 | $serialized = YAML::Syck::Dump($data); | 
| 2369 |  |  |  |  |  |  | } elsif ($HAVE->{"Data::Serializer"}) { | 
| 2370 |  |  |  |  |  |  | my $serializer = Data::Serializer->new | 
| 2371 | 15 |  |  |  |  | 105 | ( serializer => $serializers{$suffix} ); | 
| 2372 | 15 |  |  |  |  | 14640 | $serialized = $serializer->raw_serialize($data); | 
| 2373 |  |  |  |  |  |  | } else { | 
| 2374 | 0 |  |  |  |  | 0 | die "Data::Serializer not installed, cannot proceed with suffix '$suffix'"; | 
| 2375 |  |  |  |  |  |  | } | 
| 2376 | 2712 | 50 |  |  |  | 1177222 | open my $fh, ">", "$rfile.new" or die "Could not open >'$rfile.new': $!"; | 
| 2377 | 2712 |  |  |  |  | 78195 | print $fh $serialized; | 
| 2378 | 2712 | 50 |  |  |  | 87192 | close $fh or die "Could not close '$rfile.new': $!"; | 
| 2379 | 2712 | 50 |  |  |  | 341777 | rename "$rfile.new", $rfile or die "Could not rename to '$rfile': $!"; | 
| 2380 |  |  |  |  |  |  | } | 
| 2381 |  |  |  |  |  |  |  | 
| 2382 |  |  |  |  |  |  | BEGIN { | 
| 2383 | 8 |  |  | 8 |  | 99902 | my $nq = qr/[^"]+/; # non-quotes | 
| 2384 | 8 |  |  |  |  | 108 | my @pod_lines = | 
| 2385 | 8 |  |  |  |  | 34 | split /\n/, <<'=cut'; %serializers = map { my @x = /"($nq)"\s+=>\s+"($nq)"/; @x } grep {s/^=item\s+C<<\s+(.+)\s+>>$/$1/} @pod_lines; } | 
|  | 32 |  |  |  |  | 444 |  | 
|  | 32 |  |  |  |  | 1007 |  | 
|  | 136 |  |  |  |  | 387 |  | 
| 2386 |  |  |  |  |  |  |  | 
| 2387 |  |  |  |  |  |  | =head1 SERIALIZERS | 
| 2388 |  |  |  |  |  |  |  | 
| 2389 |  |  |  |  |  |  | The following suffixes are supported and trigger the use of these | 
| 2390 |  |  |  |  |  |  | serializers: | 
| 2391 |  |  |  |  |  |  |  | 
| 2392 |  |  |  |  |  |  | =over 4 | 
| 2393 |  |  |  |  |  |  |  | 
| 2394 |  |  |  |  |  |  | =item C<< ".yaml" => "YAML::Syck" >> | 
| 2395 |  |  |  |  |  |  |  | 
| 2396 |  |  |  |  |  |  | =item C<< ".json" => "JSON" >> | 
| 2397 |  |  |  |  |  |  |  | 
| 2398 |  |  |  |  |  |  | =item C<< ".sto"  => "Storable" >> | 
| 2399 |  |  |  |  |  |  |  | 
| 2400 |  |  |  |  |  |  | =item C<< ".dd"   => "Data::Dumper" >> | 
| 2401 |  |  |  |  |  |  |  | 
| 2402 |  |  |  |  |  |  | =back | 
| 2403 |  |  |  |  |  |  |  | 
| 2404 |  |  |  |  |  |  | =cut | 
| 2405 |  |  |  |  |  |  |  | 
| 2406 |  |  |  |  |  |  | BEGIN { | 
| 2407 | 8 |  |  | 8 |  | 152 | my @pod_lines = | 
| 2408 | 8 |  |  |  |  | 37 | split /\n/, <<'=cut'; %seconds = map { eval } grep {s/^=item\s+C<<(.+)>>$/$1/} @pod_lines; } | 
|  | 64 |  |  |  |  | 2557 |  | 
|  | 240 |  |  |  |  | 519 |  | 
| 2409 |  |  |  |  |  |  |  | 
| 2410 |  |  |  |  |  |  | =head1 INTERVAL SPEC | 
| 2411 |  |  |  |  |  |  |  | 
| 2412 |  |  |  |  |  |  | An interval spec is a primitive way to express time spans. Normally it | 
| 2413 |  |  |  |  |  |  | is composed from an integer and a letter. | 
| 2414 |  |  |  |  |  |  |  | 
| 2415 |  |  |  |  |  |  | As a special case, a string that consists only of the single letter | 
| 2416 |  |  |  |  |  |  | C, stands for MAX_INT seconds. | 
| 2417 |  |  |  |  |  |  |  | 
| 2418 |  |  |  |  |  |  | The following letters express the specified number of seconds: | 
| 2419 |  |  |  |  |  |  |  | 
| 2420 |  |  |  |  |  |  | =over 4 | 
| 2421 |  |  |  |  |  |  |  | 
| 2422 |  |  |  |  |  |  | =item C<< s => 1 >> | 
| 2423 |  |  |  |  |  |  |  | 
| 2424 |  |  |  |  |  |  | =item C<< m => 60 >> | 
| 2425 |  |  |  |  |  |  |  | 
| 2426 |  |  |  |  |  |  | =item C<< h => 60*60 >> | 
| 2427 |  |  |  |  |  |  |  | 
| 2428 |  |  |  |  |  |  | =item C<< d => 60*60*24 >> | 
| 2429 |  |  |  |  |  |  |  | 
| 2430 |  |  |  |  |  |  | =item C<< W => 60*60*24*7 >> | 
| 2431 |  |  |  |  |  |  |  | 
| 2432 |  |  |  |  |  |  | =item C<< M => 60*60*24*30 >> | 
| 2433 |  |  |  |  |  |  |  | 
| 2434 |  |  |  |  |  |  | =item C<< Q => 60*60*24*90 >> | 
| 2435 |  |  |  |  |  |  |  | 
| 2436 |  |  |  |  |  |  | =item C<< Y => 60*60*24*365.25 >> | 
| 2437 |  |  |  |  |  |  |  | 
| 2438 |  |  |  |  |  |  | =back | 
| 2439 |  |  |  |  |  |  |  | 
| 2440 |  |  |  |  |  |  | =cut | 
| 2441 |  |  |  |  |  |  |  | 
| 2442 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 2443 |  |  |  |  |  |  |  | 
| 2444 |  |  |  |  |  |  | L, | 
| 2445 |  |  |  |  |  |  | L, | 
| 2446 |  |  |  |  |  |  | L | 
| 2447 |  |  |  |  |  |  |  | 
| 2448 |  |  |  |  |  |  | =head1 BUGS | 
| 2449 |  |  |  |  |  |  |  | 
| 2450 |  |  |  |  |  |  | Please report any bugs or feature requests through the web interface | 
| 2451 |  |  |  |  |  |  | at | 
| 2452 |  |  |  |  |  |  | L. | 
| 2453 |  |  |  |  |  |  | I will be notified, and then you'll automatically be notified of | 
| 2454 |  |  |  |  |  |  | progress on your bug as I make changes. | 
| 2455 |  |  |  |  |  |  |  | 
| 2456 |  |  |  |  |  |  | =head1 KNOWN BUGS | 
| 2457 |  |  |  |  |  |  |  | 
| 2458 |  |  |  |  |  |  | Memory hungry: it seems all memory is allocated during the initial | 
| 2459 |  |  |  |  |  |  | rsync where a list of all files is maintained in memory. | 
| 2460 |  |  |  |  |  |  |  | 
| 2461 |  |  |  |  |  |  | =head1 SUPPORT | 
| 2462 |  |  |  |  |  |  |  | 
| 2463 |  |  |  |  |  |  | You can find documentation for this module with the perldoc command. | 
| 2464 |  |  |  |  |  |  |  | 
| 2465 |  |  |  |  |  |  | perldoc File::Rsync::Mirror::Recentfile | 
| 2466 |  |  |  |  |  |  |  | 
| 2467 |  |  |  |  |  |  | You can also look for information at: | 
| 2468 |  |  |  |  |  |  |  | 
| 2469 |  |  |  |  |  |  | =over 4 | 
| 2470 |  |  |  |  |  |  |  | 
| 2471 |  |  |  |  |  |  | =item * RT: CPAN's request tracker | 
| 2472 |  |  |  |  |  |  |  | 
| 2473 |  |  |  |  |  |  | L | 
| 2474 |  |  |  |  |  |  |  | 
| 2475 |  |  |  |  |  |  | =item * AnnoCPAN: Annotated CPAN documentation | 
| 2476 |  |  |  |  |  |  |  | 
| 2477 |  |  |  |  |  |  | L | 
| 2478 |  |  |  |  |  |  |  | 
| 2479 |  |  |  |  |  |  | =item * CPAN Ratings | 
| 2480 |  |  |  |  |  |  |  | 
| 2481 |  |  |  |  |  |  | L | 
| 2482 |  |  |  |  |  |  |  | 
| 2483 |  |  |  |  |  |  | =item * Search CPAN | 
| 2484 |  |  |  |  |  |  |  | 
| 2485 |  |  |  |  |  |  | L | 
| 2486 |  |  |  |  |  |  |  | 
| 2487 |  |  |  |  |  |  | =back | 
| 2488 |  |  |  |  |  |  |  | 
| 2489 |  |  |  |  |  |  |  | 
| 2490 |  |  |  |  |  |  | =head1 ACKNOWLEDGEMENTS | 
| 2491 |  |  |  |  |  |  |  | 
| 2492 |  |  |  |  |  |  | Thanks to RJBS for module-starter. | 
| 2493 |  |  |  |  |  |  |  | 
| 2494 |  |  |  |  |  |  | =head1 AUTHOR | 
| 2495 |  |  |  |  |  |  |  | 
| 2496 |  |  |  |  |  |  | Andreas König | 
| 2497 |  |  |  |  |  |  |  | 
| 2498 |  |  |  |  |  |  | =head1 COPYRIGHT & LICENSE | 
| 2499 |  |  |  |  |  |  |  | 
| 2500 |  |  |  |  |  |  | Copyright 2008,2009 Andreas König. | 
| 2501 |  |  |  |  |  |  |  | 
| 2502 |  |  |  |  |  |  | This program is free software; you can redistribute it and/or modify it | 
| 2503 |  |  |  |  |  |  | under the same terms as Perl itself. | 
| 2504 |  |  |  |  |  |  |  | 
| 2505 |  |  |  |  |  |  |  | 
| 2506 |  |  |  |  |  |  | =cut | 
| 2507 |  |  |  |  |  |  |  | 
| 2508 |  |  |  |  |  |  | 1; # End of File::Rsync::Mirror::Recentfile | 
| 2509 |  |  |  |  |  |  |  | 
| 2510 |  |  |  |  |  |  | # Local Variables: | 
| 2511 |  |  |  |  |  |  | # mode: cperl | 
| 2512 |  |  |  |  |  |  | # cperl-indent-level: 4 | 
| 2513 |  |  |  |  |  |  | # End: |