Line 1... |
Line 1... |
1 |
#! /usr/bin/env perl
|
1 |
#! /usr/bin/env perl
|
2 |
|
2 |
|
3 |
# archiveDirectories.pl
|
3 |
# archiveDirectories.pl
|
4 |
# Author: R. W. Rodolico
|
4 |
# Author: R. W. Rodolico
|
5 |
# Date: 20180603
|
5 |
# Date: 20180603
|
- |
|
6 |
|
6 |
# Copyright: 2018, Vanduzen Enterprises, Dallas TX
|
7 |
# Copyright (c) 2018, Daily Data, Inc
|
- |
|
8 |
# All rights reserved.
|
- |
|
9 |
#
|
- |
|
10 |
# Redistribution and use in source and binary forms, with or without
|
- |
|
11 |
# modification, are permitted provided that the following conditions are met:
|
- |
|
12 |
#
|
- |
|
13 |
# 1. Redistributions of source code must retain the above copyright notice, this
|
- |
|
14 |
# list of conditions and the following disclaimer.
|
- |
|
15 |
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
- |
|
16 |
# this list of conditions and the following disclaimer in the documentation
|
- |
|
17 |
# and/or other materials provided with the distribution.
|
- |
|
18 |
#
|
- |
|
19 |
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
- |
|
20 |
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
- |
|
21 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
- |
|
22 |
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
- |
|
23 |
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
- |
|
24 |
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
- |
|
25 |
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
- |
|
26 |
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
- |
|
27 |
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
- |
|
28 |
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
- |
|
29 |
#
|
- |
|
30 |
# The views and conclusions contained in the software and documentation are those
|
- |
|
31 |
# of the authors and should not be interpreted as representing official policies,
|
- |
|
32 |
# either expressed or implied, of the <project name> project.
|
7 |
|
33 |
|
8 |
# Script designed to be run from a cron job, which checks if any directories
|
34 |
# Script designed to be run from a cron job, which checks if any directories
|
9 |
# are ready to be archived. A directory is defined as a directory under
|
35 |
# are ready to be archived. A directory is defined as a directory under
|
10 |
# the root of $config{'local root dir'}.
|
36 |
# the root of $config{'local root dir'}.
|
11 |
|
37 |
|
Line 19... |
Line 45... |
19 |
# $config{'target final directory'}.
|
45 |
# $config{'target final directory'}.
|
20 |
# After the copy and move, the directory and its MD5 sum file are moved
|
46 |
# After the copy and move, the directory and its MD5 sum file are moved
|
21 |
# to the $config{'local trash dir'} (which is cleaned on the next invocation of
|
47 |
# to the $config{'local trash dir'} (which is cleaned on the next invocation of
|
22 |
# the script).
|
48 |
# the script).
|
23 |
|
49 |
|
24 |
# Script does NOT handle the situation where directories are being moved
|
- |
|
25 |
# while the script is running, so the script should be run at a time
|
- |
|
26 |
# when there is no other activity on the server.
|
- |
|
27 |
#
|
50 |
#
|
28 |
# Version: 1.0
|
51 |
# Version: 1.0
|
29 |
|
52 |
|
30 |
use warnings;
|
53 |
use warnings;
|
31 |
use strict;
|
54 |
use strict;
|
32 |
use Cwd qw();
|
55 |
use Cwd qw();
|
33 |
use File::Copy qw(move);
|
56 |
use File::Copy qw(move);
|
34 |
use File::Basename;
|
57 |
use File::Basename;
|
35 |
use File::stat;
|
58 |
use File::stat;
|
36 |
|
59 |
|
- |
|
60 |
our $VERSION = '1.0';
|
- |
|
61 |
|
37 |
my $DEBUG = 5;
|
62 |
my $DEBUG = 0;
|
38 |
|
63 |
|
39 |
my %config;
|
64 |
my %config;
|
40 |
|
65 |
|
41 |
my @DirectoriesToMove;
|
66 |
my @DirectoriesToMove;
|
42 |
|
67 |
|
Line 54... |
Line 79... |
54 |
} else {
|
79 |
} else {
|
55 |
die "Could not locate config file $configFileName\n";
|
80 |
die "Could not locate config file $configFileName\n";
|
56 |
} # if..else
|
81 |
} # if..else
|
57 |
} #loadConfig
|
82 |
} #loadConfig
|
58 |
|
83 |
|
59 |
# simply read the entire fiel into a string
|
84 |
# simply read the entire file into a string
|
60 |
sub slurpFile {
|
85 |
sub slurpFile {
|
61 |
my $filename = shift;
|
86 |
my $filename = shift;
|
62 |
return '' unless -e $filename;
|
87 |
return '' unless -e $filename;
|
63 |
open TEMP, "<$filename" or die "could not read $filename: $!\n";
|
88 |
open TEMP, "<$filename" or die "could not read $filename: $!\n";
|
64 |
my @contents = <TEMP>;
|
89 |
my @contents = <TEMP>;
|
Line 72... |
Line 97... |
72 |
open TEMP, ">$filename" or die "could not write to $filename: $!\n";
|
97 |
open TEMP, ">$filename" or die "could not write to $filename: $!\n";
|
73 |
print TEMP join( '', @_ );
|
98 |
print TEMP join( '', @_ );
|
74 |
close TEMP;
|
99 |
close TEMP;
|
75 |
}
|
100 |
}
|
76 |
|
101 |
|
- |
|
102 |
# returns how many seconds ago a file was created
|
- |
|
103 |
sub fileAge {
|
- |
|
104 |
my $filename = shift;
|
- |
|
105 |
my $age = stat( $filename );
|
- |
|
106 |
$age = $$age[9];
|
- |
|
107 |
print "$age\t$filename" if $DEBUG > 3;
|
- |
|
108 |
return time - $age;
|
- |
|
109 |
}
|
- |
|
110 |
|
- |
|
111 |
|
77 |
# look in the directories to move directory and see if there is anything
|
112 |
# look in the directories to move directory and see if there is anything
|
78 |
# new in there. If so, check MD5 Sum file (create if necessary) and ensure
|
113 |
# new in there. If so, check MD5 Sum file (create if necessary) and ensure
|
79 |
# we have waited long enough and the sums match
|
114 |
# we have waited long enough and the sums match
|
80 |
sub getDirectories {
|
115 |
sub getDirectories {
|
81 |
my $rootDir = shift;
|
116 |
my $rootDir = shift;
|
Line 91... |
Line 126... |
91 |
print "\tFound Dir $fullyQualified with MD5 of $md5\n" if $DEBUG > 2;
|
126 |
print "\tFound Dir $fullyQualified with MD5 of $md5\n" if $DEBUG > 2;
|
92 |
# let's look for the md5 checksum file and compare if it exist
|
127 |
# let's look for the md5 checksum file and compare if it exist
|
93 |
my $md5Name = "$fullyQualified.$config{'md5 suffix'}";
|
128 |
my $md5Name = "$fullyQualified.$config{'md5 suffix'}";
|
94 |
if ( -e $md5Name ) {
|
129 |
if ( -e $md5Name ) {
|
95 |
# find out when it was last written to
|
130 |
# find out when it was last written to
|
96 |
my $lastModification = stat( $md5Name );
|
- |
|
97 |
$lastModification = $$lastModification[9];
|
- |
|
98 |
my $howOld = time - $lastModification;
|
- |
|
99 |
print "\tFound existing MD5 file $md5Name written to at $lastModification, or $howOld seconds ago\n" if $DEBUG > 3;
|
131 |
print "\tFound existing MD5 file $md5Name\n" if $DEBUG > 3;
|
100 |
# and blow it off if it is too recent
|
132 |
# and blow it off if it is too recent
|
101 |
if ( $howOld < $config{'quiesent seconds'} ) {
|
133 |
if ( &fileAge( $md5Name) < $config{'quiesent seconds'} ) {
|
102 |
print "\t\tBlowing it off because $howOld is less than $config{'quiesent seconds'}\n" if $DEBUG > 4;
|
134 |
print "\t\tBlowing it off because it is less than $config{'quiesent seconds'} seconds old\n" if $DEBUG > 4;
|
103 |
next;
|
135 |
next;
|
104 |
}
|
136 |
}
|
105 |
my $oldMD5 = &slurpFile( $md5Name );
|
137 |
my $oldMD5 = &slurpFile( $md5Name );
|
106 |
if ( $md5 eq $oldMD5 ) {
|
138 |
if ( $md5 eq $oldMD5 ) {
|
107 |
print "\t\tAdding, md5 not changed, $md5 same as $oldMD5\n" if $DEBUG > 4;
|
139 |
print "\t\tAdding, md5 not changed, $md5 same as $oldMD5\n" if $DEBUG > 4;
|
Line 202... |
Line 234... |
202 |
}
|
234 |
}
|
203 |
return ('', 0);
|
235 |
return ('', 0);
|
204 |
}
|
236 |
}
|
205 |
|
237 |
|
206 |
|
238 |
|
207 |
# simply remove everything from the trash directory
|
- |
|
208 |
sub cleanTrash {
|
- |
|
209 |
my ( $trashDir, $age ) = @_;
|
- |
|
210 |
`mkdir -p $trashDir` unless -d $trashDir;
|
- |
|
211 |
`rm -fR $trashDir/*`;
|
- |
|
212 |
}
|
- |
|
213 |
|
- |
|
214 |
sub copyToRemote {
|
239 |
sub copyToRemote {
|
215 |
my ( $path, $dirname, $remoteServer, $remotePath ) = @_;
|
240 |
my ( $path, $dirname, $remoteServer, $remotePath ) = @_;
|
216 |
# first, copy the file
|
241 |
# first, copy the file
|
217 |
#print "rsync -a $path/$dirname $remoteServer:$remotePath > /tmp/lastrsync.log";
|
242 |
#print "rsync -a $path/$dirname $remoteServer:$remotePath > /tmp/lastrsync.log";
|
218 |
#die;
|
243 |
#die;
|
219 |
qx"rsync -a $path/$dirname $remoteServer:$remotePath > /tmp/lastrsync.log";
|
244 |
qx"rsync -a $path/$dirname $remoteServer:$remotePath > /tmp/lastrsync.log";
|
220 |
return 'rsync failed with error :' . $? & 127 if $? & 127;
|
245 |
return 'rsync failed with error :' . $? & 127 if $? & 127;
|
221 |
return '';
|
246 |
return '';
|
222 |
}
|
247 |
}
|
223 |
|
248 |
|
- |
|
249 |
# simply remove everything from the trash directory over $age seconds old
|
- |
|
250 |
sub cleanTrash {
|
- |
|
251 |
my ( $trashDir, $age ) = @_;
|
- |
|
252 |
my $md5Suffix = $config{'md5 suffix'};
|
- |
|
253 |
my @toRemove = ();
|
- |
|
254 |
if ( opendir( my $dh, $trashDir ) ) {
|
- |
|
255 |
# get all the md5sum files which are older than $age seconds old
|
- |
|
256 |
@toRemove = grep { &fileAge( "$_" ) > $age } map{ "$trashDir/$_" } grep{ /$md5Suffix$/ } readdir( $dh);
|
- |
|
257 |
closedir( $dh );
|
- |
|
258 |
}
|
- |
|
259 |
print "You should remove the following files\n" if $DEBUG > 1;
|
- |
|
260 |
foreach my $thisDir ( @toRemove ) {
|
- |
|
261 |
$thisDir =~ m/(.*)\.$md5Suffix/;
|
- |
|
262 |
$thisDir = $1;
|
- |
|
263 |
qx/rm -fR $thisDir $thisDir.$md5Suffix/;
|
- |
|
264 |
}
|
- |
|
265 |
}
|
- |
|
266 |
|
- |
|
267 |
|
- |
|
268 |
|
224 |
###############################################################################
|
269 |
###############################################################################
|
225 |
# Main
|
270 |
# Main
|
226 |
###############################################################################
|
271 |
###############################################################################
|
227 |
|
272 |
|
228 |
&loadConfig();
|
273 |
&loadConfig();
|
Line 234... |
Line 279... |
234 |
`mkdir -p $config{'local root dir'}`;
|
279 |
`mkdir -p $config{'local root dir'}`;
|
235 |
`chmod 777 $config{'local root dir'}`;
|
280 |
`chmod 777 $config{'local root dir'}`;
|
236 |
}
|
281 |
}
|
237 |
# clean the trash if $config{ 'trash cleanup' } is non-zero
|
282 |
# clean the trash if $config{ 'trash cleanup' } is non-zero
|
238 |
&cleanTrash( $config{'local trash dir'}, $config{ 'trash cleanup' } ) if $config{ 'trash cleanup' };
|
283 |
&cleanTrash( $config{'local trash dir'}, $config{ 'trash cleanup' } ) if $config{ 'trash cleanup' };
|
239 |
|
284 |
|
240 |
# Check if we have any directories which are ready to be moved.
|
285 |
# Check if we have any directories which are ready to be moved.
|
241 |
@DirectoriesToMove = &getDirectories( $config{'local root dir'} );
|
286 |
@DirectoriesToMove = &getDirectories( $config{'local root dir'} );
|
242 |
|
287 |
|
243 |
print "Processing\n\t" . join( "\n\t", @DirectoriesToMove ) . "\n";
|
288 |
print "Processing\n\t" . join( "\n\t", @DirectoriesToMove ) . "\n" if $DEBUG > 1;
|
244 |
|
289 |
|
245 |
foreach my $directory ( @DirectoriesToMove ) {
|
290 |
foreach my $directory ( @DirectoriesToMove ) {
|
246 |
my $fullPath = $config{'local root dir'} . "/$directory";
|
291 |
my $fullPath = $config{'local root dir'} . "/$directory";
|
247 |
my $logFile = "$fullPath.$config{'log suffix'}";
|
292 |
my $logFile = "$fullPath.$config{'log suffix'}";
|
248 |
my $errorFile = "$fullPath.$config{'error suffix'}";
|
293 |
my $errorFile = "$fullPath.$config{'error suffix'}";
|
Line 269... |
Line 314... |
269 |
opendir( my $dh, $config{'local staging area'} ) or die "Could not read $config{'local staging area'}: $!\n";
|
314 |
opendir( my $dh, $config{'local staging area'} ) or die "Could not read $config{'local staging area'}: $!\n";
|
270 |
my @directories;
|
315 |
my @directories;
|
271 |
# get all the .md5 files
|
316 |
# get all the .md5 files
|
272 |
my @toMove = grep { /$config{'md5 suffix'}$/ } readdir( $dh );
|
317 |
my @toMove = grep { /$config{'md5 suffix'}$/ } readdir( $dh );
|
273 |
my $targetPath = "$config{'target server'}:$config{'target staging area'}/";
|
318 |
my $targetPath = "$config{'target server'}:$config{'target staging area'}/";
|
274 |
print "Copying the following to $targetPath\n\t" . join ("\n\t", @toMove ) . "\n";
|
319 |
print "Copying the following to $targetPath\n\t" . join ("\n\t", @toMove ) . "\n" if $DEBUG > 1;
|
275 |
# create the target directory on the server if it doesn't exist
|
320 |
# create the target directory on the server if it doesn't exist
|
276 |
&runRemoteCommand( $config{'target server'},
|
321 |
&runRemoteCommand( $config{'target server'},
|
277 |
"[ ! -d $config{'target staging area'} ] && mkdir -p $config{'target staging area'}",
|
322 |
"[ ! -d $config{'target staging area'} ] && mkdir -p $config{'target staging area'}",
|
278 |
"[ ! -d $config{'target final directory'} ] && mkdir -p $config{'target final directory'}"
|
323 |
"[ ! -d $config{'target final directory'} ] && mkdir -p $config{'target final directory'}"
|
279 |
);
|
324 |
);
|
Line 299... |
Line 344... |
299 |
&logit( $dirname, $config{'log suffix'}, $rsync );
|
344 |
&logit( $dirname, $config{'log suffix'}, $rsync );
|
300 |
if ( system ( $rsync ) == 0 ) { # we succeeded
|
345 |
if ( system ( $rsync ) == 0 ) { # we succeeded
|
301 |
if ( &validateTarget( $config{'target server'}, $config{'target staging area'}, $config{'target final directory'}, $dirname, $md5sum ) ) {
|
346 |
if ( &validateTarget( $config{'target server'}, $config{'target staging area'}, $config{'target final directory'}, $dirname, $md5sum ) ) {
|
302 |
`mkdir -p $config{'local trash dir'}` unless -d $config{'local trash dir'};
|
347 |
`mkdir -p $config{'local trash dir'}` unless -d $config{'local trash dir'};
|
303 |
move( "$config{'local staging area'}/$dirname", "$config{'local trash dir'}/$dirname" );
|
348 |
move( "$config{'local staging area'}/$dirname", "$config{'local trash dir'}/$dirname" );
|
304 |
$dirname .= $config{'md5 suffix'};
|
349 |
my $md5File = $dirname . '.' . $config{'md5 suffix'};
|
305 |
move( "$config{'local staging area'}/$dirname", "$config{'local trash dir'}/$dirname" );
|
350 |
move( "$config{'local staging area'}/$md5File", "$config{'local trash dir'}/$md5File" );
|
306 |
&logit( $dirname, $config{'log suffix'}, "Successfully moved directory $dirname to $config{'target server'}" );
|
351 |
&logit( $dirname, $config{'log suffix'}, "Successfully moved directory $dirname to $config{'target server'}" );
|
307 |
} else {
|
352 |
} else {
|
308 |
&logit( $dirname, $config{'error suffix'}, "Unable to validate target for $dirname" );
|
353 |
&logit( $dirname, $config{'error suffix'}, "Unable to validate target for $dirname" );
|
309 |
}
|
354 |
}
|
310 |
} else {
|
355 |
} else {
|