@ -13,10 +13,9 @@
# - If not found anywhere, remove it from 'file_locations' and send an alert.
# - If not found anywhere, remove it from 'file_locations' and send an alert.
# - If found, check the size. If it differs, recalculate the md5sum.
# - If found, check the size. If it differs, recalculate the md5sum.
# - 3. If called with '--rename --file <filename> --to <newname>', rename the file and update 'files'.
# - 3. If called with '--rename --file <filename> --to <newname>', rename the file and update 'files'.
# - 4. If called with '--delete', remove from 'file_locations' and then remove from the local storage. If
# - 4. If called with '--delete', remove from 'file_locations' and all copies on all systems. This is done by
# also used with '--everywhere', then all copies on all systems we know about will be deleted. This is
# registering a job against all known hosts. As such, if this is called and the target file doesn't exist,
# done by registering a job against all known hosts. As such, if this is called and the target file
# it just clears the job and then exits.
# doesn't exist, it just clears the job and then exits.
# - 5. If called with '--is-script=[0|1]', mark as 'script' in the 'files' table and set/remove the executable bit.
# - 5. If called with '--is-script=[0|1]', mark as 'script' in the 'files' table and set/remove the executable bit.
#
#
# Exit codes;
# Exit codes;
@ -32,7 +31,7 @@
# -
# -
#
#
# NOTE:
# NOTE:
# -
# - remove unsyncs, add syncs.
#
#
use strict;
use strict;
@ -52,25 +51,11 @@ if (($running_directory =~ /^\./) && ($ENV{PWD}))
my $anvil = Anvil::Tools->new();
my $anvil = Anvil::Tools->new();
$anvil->data->{switches}{'delete'} = "";
$anvil->Log->level({set => 2});
$anvil->data->{switches}{download} = "";
$anvil->Log->secure({set => 1});
$anvil->data->{switches}{everywhere} = "";
$anvil->data->{switches}{file} = "";
$anvil->Get->switches({list => ["delete", "download", "file", "is-script", "job-uuid", "rename", "remove", "add", "to"], man => $THIS_FILE});
$anvil->data->{switches}{'is-script'} = "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => $anvil->data->{switches}});
$anvil->data->{switches}{'job-uuid'} = "";
$anvil->data->{switches}{'rename'} = "";
$anvil->data->{switches}{to} = "";
$anvil->Get->switches;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"switches::delete" => $anvil->data->{switches}{'delete'},
"switches::download" => $anvil->data->{switches}{download},
"switches::everywhere" => $anvil->data->{switches}{everywhere},
"switches::file" => $anvil->data->{switches}{file},
"switches::is-script" => $anvil->data->{switches}{'is-script'},
"switches::job-uuid" => $anvil->data->{switches}{'job-uuid'},
"switches::rename" => $anvil->data->{switches}{'rename'},
"switches::to" => $anvil->data->{switches}{to},
}});
# Connect or die
# Connect or die
$anvil->Database->connect;
$anvil->Database->connect;
@ -171,59 +156,59 @@ sub find_missing_files
# What am I? This will impact how missing files are found.
# What am I? This will impact how missing files are found.
my $query = "
my $query = "
SELECT
SELECT
a. file_uuid,
file_uuid,
a. file_directory,
file_directory,
a. file_name,
file_name,
a. file_size,
file_size,
a. file_md5sum
file_md5sum
FROM
FROM
files a,
files
hosts b,
file_locations c
WHERE
WHERE
b.host_uuid = ".$anvil->Database->quote($anvil->data->{sys}{host_uuid})."
file_type != 'DELETED'
AND
a.file_uuid = c.file_location_file_uuid
AND
b.host_uuid = c.file_location_host_uuid
ORDER BY
ORDER BY
a.file_directory ASC,
file_directory ASC,
a.file_name ASC,
file_name ASC
b.host_name ASC
;";
;";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3 , list => { query => $query }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2 , list => { query => $query }});
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
my $count = @{$results};
my $count = @{$results};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3 , list => {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2 , list => {
results => $results,
results => $results,
count => $count,
count => $count,
}});
}});
foreach my $row (@{$results})
foreach my $row (@{$results})
{
{
my $file_uuid = $row->[0];
my $file_uuid = $row->[0];
my $file_directory = $row->[1];
my $file_directory = $row->[1];
my $file_name = $row->[2];
my $file_name = $row->[2];
my $file_size = $row->[3];
my $file_size = $row->[3];
my $file_md5sum = $row->[4];
my $file_md5sum = $row->[4];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
my $full_path = $file_directory."/".$file_name;
$full_path =~ s/\/\//\//g;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:file_uuid' => $file_uuid,
's1:file_uuid' => $file_uuid,
's2:file_directory' => $file_directory,
's2:file_directory' => $file_directory,
's3:file_name' => $file_name,
's3:file_name' => $file_name,
's4:file_size' => $file_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $file_size}).")",
's5:file_md5sum' => $file_md5sum,
's6:full_path' => $full_path,
}});
}});
my $test_file = $file_directory."/".$file_name;
# If we're a striker, we want all files.
$test_file =~ s/\/\//\//g;
my $host_type = $anvil->Get->host_type();
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { test_file => $test_file }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_type => $host_type }});
if ($host_type eq "striker")
if (not -e $test_file)
{
{
# Missing file!
if (not -e $full_path)
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0269", variables => { file => $test_file }});
{
# Missing file!
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0269", variables => { file => $full_path }});
# Find what target, if any, we'll the file from.
# Find what target, if any, we'll the file from.
my ($found) = find_file($anvil, $file_uuid, $test_file, $file_size, $file_md5sum);
my ($found) = find_file($anvil, $file_uuid, $full_path, $file_size, $file_md5sum);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { found => $found }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { found => $found }});
}
}
}
}
}
@ -232,338 +217,167 @@ ORDER BY
return(0);
return(0);
}
}
### TODO: Add sorting by preferred target
# This looks for a file on another system. The exact order of search depends on what kind of machine we are.
# This looks for a file on another system. The exact order of search depends on what kind of machine we are.
sub find_file
sub find_file
{
{
my ($anvil, $file_uuid, $full_path, $file_size, $file_md5sum) = @_;
my ($anvil, $file_uuid, $full_path, $file_size, $file_md5sum) = @_;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:file_uuid' => $file_uuid,
's2:full_path' => $full_path,
's3:file_size' => $file_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $file_size}).")",
's4:file_md5sum' => $file_md5sum,
}});
my $found = 0;
my $found = 0;
# What are my IPs?
# We want to search Striker's first, DR hosts second and nodes third.
my $local_host = $anvil->Get->short_host_name();
$anvil->Database->get_hosts ;
$anvil->Network->get_ips();
my $host_order = [] ;
foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{network}{$local_host}{interface}} )
foreach my $type ("striker", "dr", "node" )
{
{
next if not $anvil->data->{network}{$local_host}{interface}{$interface}{ip};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { type => $type }});
next if not $anvil->data->{network}{$local_host}{interface}{$interface}{subnet_mask};
foreach my $host_name (sort {$a cmp $b} keys %{$anvil->data->{sys}{hosts}{by_name}})
my $ip = $anvil->data->{network}{$local_host}{interface}{$interface}{ip};
my $subnet_mask = $anvil->data->{network}{$local_host}{interface}{$interface}{subnet_mask};
my $network = $anvil->Network->get_network({ip => $ip, subnet_mask => $subnet_mask});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
's1:interface' => $interface,
's2:ip' => $ip,
's3:subnet_mask' => $subnet_mask,
's4:network' => $network,
}});
my $type = "other";
my $sort = $interface;
if ($interface =~ /^((?:bc|s|if)n)(\d+)/)
{
{
$type = $1;
my $host_uuid = $anvil->data->{sys}{hosts}{by_name}{$host_name};
$sort = $2;
my $host_type = $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_type};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:type' => $type,
's1:host_name' => $host_name,
's2:sort' => $sort,
's2:host_type' => $host_type,
's3:host_uuid' => $host_uuid,
}});
}});
next if $host_type ne $type;
next if $host_uuid eq $anvil->Get->host_uuid;
push @{$host_order}, $host_uuid;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_uuid => $host_uuid }});
}
}
$anvil->data->{local_ip}{by_network}{$network}{type} = $type;
$anvil->data->{local_ip}{by_network}{$network}{'sort'} = $sort;
$anvil->data->{local_ip}{by_network}{$network}{interface} = $interface;
$anvil->data->{local_ip}{by_network}{$network}{ip} = $ip;
$anvil->data->{local_ip}{by_network}{$network}{subnet_mask} = $subnet_mask;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"s1:local_ip::by_network::${network}::type" => $anvil->data->{local_ip}{by_network}{$network}{type},
"s1:local_ip::by_network::${network}::sort" => $anvil->data->{local_ip}{by_network}{$network}{'sort'},
"s2:local_ip::by_network::${network}::interface" => $anvil->data->{local_ip}{by_network}{$network}{interface},
"s3:local_ip::by_network::${network}::ip" => $anvil->data->{local_ip}{by_network}{$network}{ip},
"s4:local_ip::by_network::${network}::subnet_mask" => $anvil->data->{local_ip}{by_network}{$network}{subnet_mask},
}});
}
}
# Create a hash we can sort through that are on the same subnet_mask as us.
# Now search.
my $query = "
my $file_found = 0;
SELECT
foreach my $search_host_uuid (@{$host_order})
a.host_uuid,
a.host_name,
a.host_type,
b.file_directory,
b.file_name,
b.file_size,
b.file_md5sum
FROM
hosts a,
files b,
file_locations c
WHERE
a.host_uuid = c.file_location_host_uuid
AND
b.file_uuid = c.file_location_file_uuid
AND
b.file_uuid = ".$anvil->Database->quote($file_uuid)."
AND
a.host_uuid != ".$anvil->Database->quote($anvil->data->{sys}{host_uuid})."
ORDER BY
file_mtime DESC;
;";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
my $count = @{$results};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
results => $results,
count => $count,
}});
foreach my $row (@{$results})
{
{
my $host_uuid = $row->[0];
last if $file_found;
my $host_name = $row->[1];
my $target_host = $anvil->data->{hosts}{host_uuid}{$search_host_uuid}{short_host_name};
my $host_type = $row->[2];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
my $file_directory = $row->[3];
's1:search_host_uuid' => $search_host_uuid,
my $file_name = $row->[4];
's2:target_host' => $target_host,
my $file_size = $row->[5];
my $file_md5sum = $row->[6];
my $test_file = $file_directory."/".$file_name;
$test_file =~ s/\/\//\//g;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
host_uuid => $host_uuid,
host_name => $host_name,
host_type => $host_type,
file_directory => $file_directory,
file_name => $file_name,
file_size => $file_size,
file_md5sum => $file_md5sum,
test_file => $test_file,
}});
}});
# What IP addresses are on this machine?
my $target_ip = $anvil->Network->find_target_ip({host_uuid => $search_host_uuid});
my $query = "
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { target_ip => $target_ip }});
SELECT
next if not $target_ip;
ip_address_address,
ip_address_subnet_mask
# See if the file is on the target and, if so, if it matches.
FROM
### NOTE: If we want to use md5sum again, use '--with-md5sum'
ip_addresses
my $shell_call = $anvil->data->{path}{exe}{'anvil-file-details'}." --file ".$full_path.$anvil->Log->switches;
WHERE
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
ip_address_note != 'DELETED'
AND
# Test access
ip_address_host_uuid = ".$anvil->Database->quote($host_uuid)."
my $access = $anvil->Remote->test_access({target => $target_ip});
;";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { access => $access }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
next if not $access;
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
my $count = @{$results};
my $remote_size = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
my ($output, $error, $return_code) = $anvil->Remote->call({
results => $results,
shell_call => $shell_call,
count => $count,
target => $target_ip,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
error => $error,
output => $output,
return_code => $return_code,
}});
}});
foreach my $row (@{$results})
foreach my $line (split/\n/, $output )
{
{
my $ip_address_address = $row->[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
my $ip_address_subnet_mask = $row->[1];
if ($line =~ /^size: \[(\d+)\]$/)
my $network = $anvil->Network->get_network({ip => $ip_address_address, subnet_mask => $ip_address_subnet_mask});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
ip_address_address => $ip_address_address,
ip_address_subnet_mask => $ip_address_subnet_mask,
network => $network,
}});
# Are we on the same subnet?
if (exists $anvil->data->{local_ip}{by_network}{$network})
{
{
# We're on the same subnet!
$remote_size = $1;
my $type = $anvil->data->{local_ip}{by_network}{$network}{type};
$found = 1;
my $sort = $anvil->data->{local_ip}{by_network}{$network}{'sort'};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
remote_size => $remote_size,
"s1:type" => $type,
found => $found,
"s2:sort" => $sort,
}});
# Record.
$anvil->data->{peer_ip}{$host_type}{$type}{$sort}{ip} = $ip_address_address;
$anvil->data->{peer_ip}{$host_type}{$type}{$sort}{name} = $host_name;
$anvil->data->{peer_ip}{$host_type}{$type}{$sort}{host_uuid} = $host_uuid;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"s1:peer_ip::${host_type}::${type}::${sort}::name" => $anvil->data->{peer_ip}{$host_type}{$type}{$sort}{name},
"s2:peer_ip::${host_type}::${type}::${sort}::ip" => $anvil->data->{peer_ip}{$host_type}{$type}{$sort}{ip},
"s3:peer_ip::${host_type}::${type}::${sort}::host_uuid" => $anvil->data->{peer_ip}{$host_type}{$type}{$sort}{host_uuid},
}});
}});
}
}
}
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:found' => $found,
's2:remote_size' => $remote_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $remote_size}).")",
's3:file_size' => $file_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $file_size}).")",
}});
next if not $found;
### TODO: Do this according to what we are.
if ($remote_size eq $file_size)
# Sort through what we've found.
my $file_found = 0;
my $searched_hosts = {};
foreach my $host_type (sort {$a cmp $b} keys %{$anvil->data->{peer_ip}})
{
last if $file_found;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { host_type => $host_type }});
foreach my $type (sort {$a cmp $b} keys %{$anvil->data->{peer_ip}{$host_type}})
{
{
last if $file_found;
# Pull it over!
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { type => $type }});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0276", variables => {
foreach my $sort (sort {$a cmp $b} keys %{$anvil->data->{peer_ip}{$host_type}{$type}})
file => $full_path,
{
host_name => $target_host,
last if $file_found;
ip => $target_ip,
my $ip = $anvil->data->{peer_ip}{$host_type}{$type}{$sort}{ip};
}});
my $name = $anvil->data->{peer_ip}{$host_type}{$type}{$sort}{name};
my $failed = $anvil->Storage->rsync({
my $host_uuid = $anvil->data->{peer_ip}{$host_type}{$type}{$sort}{host_uuid};
debug => 2,
my $remote_user = "admin";
destination => $full_path,
my $password = "";
source => "root\@".$target_ip.":".$full_path,
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
});
's1:host_type' => $host_type,
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { failed => $failed }});
's2:type' => $type,
's3:sort' => $sort,
's4:name' => $name,
's5:ip' => $ip,
's6:host_uuid' => $host_uuid,
's7:remote_user' => $remote_user,
's8:password' => $password,
}});
if ((exists $searched_hosts->{$name}) && ($searched_hosts->{$name}))
if (-f $full_path)
{
{
# Already searched this host, this is just a different IP. Skip it.
# Got it!
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 3, key => "log_0282", variables => {
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0277", variables => { file => $full_path }});
host_name => $name,
ip => $ip,
}});
next;
}
$searched_hosts->{$name} = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 1, list => { "searched_hosts->{$name}" => $searched_hosts->{$name} }});
### NOTE: There's a bug in Net::SSH2 on RHEL8 where passwordless SSH doesn't
### work. So for now, we'll manually pull in passwords from the
### anvil.conf using 'hosts::<host_name>::password' or
### 'hosts::<host_uuid>::password'. This will be removed when the bug
### is fixed.
if ((exists $anvil->data->{hosts}{$host_uuid}{password}{$remote_user}) && ($anvil->data->{hosts}{$host_uuid}{password}{$remote_user}))
{
$password = $anvil->data->{hosts}{$host_uuid}{password}{$remote_user};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, secure => 1, list => { password => $password }});
}
elsif ((exists $anvil->data->{hosts}{$name}{password}{$remote_user}) && ($anvil->data->{hosts}{$name}{password}{$remote_user}))
{
$password = $anvil->data->{hosts}{$host_uuid}{password};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, secure => 1, list => { password => $password }});
}
### NOTE: This might take a while for the call to return if we're md5sum'ing
# Verify the md5sum.
### a large file like an ISO.
my $local_md5sum = $anvil->Get->md5sum({file => $full_path});
### TODO: Should we ask for the size, then follow up with the md5sum for
### files that are over a certain size? Or will this get called rarely
### enough in practice that it doesn't really matter?
# If the file is found, we'll parse these out.
my $remote_size = 0;
my $remote_md5sum = "";
my ($output, $error, $return_code) = $anvil->Remote->call({
shell_call => $anvil->data->{path}{exe}{'anvil-file-details'}." --file ".$full_path." --with-md5sum".$anvil->Log->switches,
remote_user => $remote_user,
password => $password,
target => $ip,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
error => $error,
local_md5sum => $local_md5sum,
output => $output,
file_md5sum => $file_md5sum,
return_code => $return_code,
}});
}});
foreach my $line (split/\n/, $output )
if ($local_md5sum eq $file_md5sum)
{
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { line => $line }});
# Success!
if ($line =~ /^size: \[(\d+)\]$/)
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0278", variables => { file => $full_path }});
{
$remote_size = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { remote_size => $remote_size }});
}
if ($line =~ /^md5sum: \[(.*)\]$/)
{
$remote_md5sum = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { remote_md5sum => $remote_md5sum }});
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:remote_size' => $remote_size,
's2:file_size' => $file_size,
's3:remote_md5sum' => $remote_md5sum,
's4:file_md5sum' => $file_md5sum,
}});
### Do I really need to match sizes if the md5sum is the same?
$file_found = 1;
if (($remote_size eq $file_size) && ($remote_md5sum eq $file_md5sum))
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_found => $file_found }});
{
last;
# Pull it over!
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0276", variables => {
file => $full_path,
host_name => $name,
ip => $ip,
}});
my $failed = $anvil->Storage->rsync({
debug => 2,
destination => $full_path,
password => $password,
source => $remote_user."\@".$ip.":".$full_path,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { failed => $failed }});
if (-f $full_path)
{
# Got it!
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0277", variables => { file => $full_path }});
# Verify the md5sum.
my $local_md5sum = $anvil->Get->md5sum({file => $full_path});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
local_md5sum => $local_md5sum,
file_md5sum => $file_md5sum,
}});
if ($local_md5sum eq $file_md5sum)
{
# Success!
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0278", variables => { file => $full_path }});
$file_found = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_found => $file_found }});
last;
}
else
{
# Failed. :(
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0279", variables => { file => $full_path }});
unlink $full_path;
}
}
else
{
# Failed to rsync.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0280", variables => {
file => $full_path,
host_name => $name,
ip => $ip,
}});
}
}
}
else
else
{
{
# Doesn't match what we're looking for.
# Failed. :(
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0281", variables => {
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0279", variables => { file => $full_path }});
file => $full_path,
unlink $full_path;
host_name => $name,
ip => $ip,
remote_md5sum => $remote_md5sum,
file_md5sum => $file_md5sum,
file_size => $file_size,
say_file_size => $anvil->Convert->bytes_to_human_readable({'bytes' => $file_size}),
remote_size => $remote_size,
say_remote_size => $anvil->Convert->bytes_to_human_readable({'bytes' => $remote_size}),
}});
}
}
}
}
else
{
# Failed to rsync.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0280", variables => {
file => $full_path,
host_name => $target_host,
ip => $target_ip,
}});
}
}
else
{
# Doesn't match what we're looking for.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0281", variables => {
file => $full_path,
host_name => $target_host,
ip => $target_ip,
file_size => $file_size,
say_file_size => $anvil->Convert->bytes_to_human_readable({'bytes' => $file_size}),
remote_size => $remote_size,
say_remote_size => $anvil->Convert->bytes_to_human_readable({'bytes' => $remote_size}),
}});
}
}
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { found => $found }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { found => $found }});
return($found);
return($file_found);
}
}
@ -577,6 +391,7 @@ sub check_incoming
{
{
delete $anvil->data->{scan}{directories};
delete $anvil->data->{scan}{directories};
}
}
# Read any files in '/mnt/shared/incoming'.
# Read any files in '/mnt/shared/incoming'.
$anvil->Storage->scan_directory({
$anvil->Storage->scan_directory({
debug => 3,
debug => 3,
@ -600,13 +415,15 @@ sub check_incoming
}});
}});
next if $file_type ne "file";
next if $file_type ne "file";
my $file_name = $anvil->data->{scan}{directories}{$full_path}{name};
my $file_name = $anvil->data->{scan}{directories}{$full_path}{name};
my $file_directory = $anvil->data->{scan}{directories}{$full_path}{directory};
my $file_directory = $anvil->data->{scan}{directories}{$full_path}{directory};
my $file_size = $anvil->data->{scan}{directories}{$full_path}{size};
my $file_size = $anvil->data->{scan}{directories}{$full_path}{size};
my $file_mtime = $anvil->data->{scan}{directories}{$full_path}{mtime};
my $file_mtime = $anvil->data->{scan}{directories}{$full_path}{mtime};
my $file_mimetype = $anvil->data->{scan}{directories}{$full_path}{mimetype};
my $file_mimetype = $anvil->data->{scan}{directories}{$full_path}{mimetype};
my $file_executable = $anvil->data->{scan}{directories}{$full_path}{executable} = -x $full_path ? 1 : 0;
my $file_executable = $anvil->data->{scan}{directories}{$full_path}{executable} = -x $full_path ? 1 : 0;
my $say_mimetype = convert_mimetype($anvil, $file_mimetype, $full_path, $file_executable);
my $say_mimetype = convert_mimetype($anvil, $file_mimetype, $full_path, $file_executable);
my $full_path = $file_directory."/".$file_name;
$full_path =~ s/\/\//\//g;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
file_name => $file_name,
file_name => $file_name,
file_size => $file_size,
file_size => $file_size,
@ -614,6 +431,7 @@ sub check_incoming
file_mimetype => $file_mimetype,
file_mimetype => $file_mimetype,
file_executable => $file_executable,
file_executable => $file_executable,
say_mimetype => $say_mimetype,
say_mimetype => $say_mimetype,
full_path => $full_path,
}});
}});
# Do I know about this file? If so, is the file the same size? If either is no, calculate the md5sum.
# Do I know about this file? If so, is the file the same size? If either is no, calculate the md5sum.
@ -626,11 +444,22 @@ sub check_incoming
}});
}});
# Calculate the md5sum?
# Calculate the md5sum?
my $file_md5sum = $recorded_md5sum;
my $file_md5sum = $recorded_md5sum;
if ((not $file_uuid) or ($file_size != $recorded_size))
if ((not $file_uuid) or ($file_size != $recorded_size))
{
{
# Yes. But first, do we have a size mismatch? If so, see if we need to pull a newer
# It's possible the file is still uploading, so sleep for 2 seconds and see if the
# version down from elsewhere.
# size is still changing.
my $last_size = $file_size;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
last_size => $last_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $last_size}).")",
}});
$anvil->Storage->_wait_if_changing({
file => $full_path,
last_size => $file_size,
});
# Yes, caluclate md5sum, but first, do we have a size mismatch? If so, see if we need
# to pull a newer version down from elsewhere.
if (($file_uuid) && ($file_mtime <= $recorded_mtime))
if (($file_uuid) && ($file_mtime <= $recorded_mtime))
{
{
# We've got an older file, we need to update.
# We've got an older file, we need to update.
@ -675,14 +504,6 @@ sub check_incoming
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { file_uuid => $file_uuid }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { file_uuid => $file_uuid }});
next if not $file_uuid;
next if not $file_uuid;
# Make sure we know about this file on this system
my ($file_locatiom_uuid) = $anvil->Database->insert_or_update_file_locations({
debug => 3,
file_location_file_uuid => $file_uuid,
file_location_host_uuid => $anvil->data->{sys}{host_uuid},
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { file_locatiom_uuid => $file_locatiom_uuid }});
# Are we in the incoming directory? If so, move the file.
# Are we in the incoming directory? If so, move the file.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
full_path => $full_path,
full_path => $full_path,
@ -694,14 +515,21 @@ sub check_incoming
# 'path::directories::shared::definitions', otherwise we'll move it to
# 'path::directories::shared::definitions', otherwise we'll move it to
# 'path::directories::shared::files'.
# 'path::directories::shared::files'.
my $target = $say_mimetype eq "definition" ? $anvil->data->{path}{directories}{shared}{definitions} : $anvil->data->{path}{directories}{shared}{files};
my $target = $say_mimetype eq "definition" ? $anvil->data->{path}{directories}{shared}{definitions} : $anvil->data->{path}{directories}{shared}{files};
$target .= "/";
$target .= "/";
$target =~ s/\/\//\//g;
$target =~ s/\/\//\//g;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { target => $target }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { target => $target }});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0268", variables => {
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0268", variables => {
file => $full_path,
file => $full_path,
target => $target,
target => $target,
}});
}});
# Wait in case it's still being uploaded.
$anvil->Storage->_wait_if_changing({
file => $full_path,
last_size => $file_size,
});
$anvil->Storage->move_file({
$anvil->Storage->move_file({
debug => 3,
debug => 3,
source_file => $full_path,
source_file => $full_path,
@ -975,47 +803,31 @@ sub convert_mimetype
return($say_mimetype);
return($say_mimetype);
}
}
# This looks for any files with the file_type of 'DELETED' and that has an entry in file_locations for this
# This looks for any files with the file_type of 'DELETED'. Any that are found will be deleted.
# machine. Any that are found will have their file deleted and the file_locations entry deleted.
sub check_for_deletes
sub check_for_deletes
{
{
my ($anvil) = @_;
my ($anvil) = @_;
my $query = "
# Get a list of files.
SELECT
my $query = "SELECT file_uuid, file_directory || '/' || file_name AS full_path FROM files WHERE file_type = 'DELETED';";
a.file_uuid,
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
b.file_location_uuid,
a.file_directory || '/' || a.file_name AS full_path
FROM
files a,
file_locations b
WHERE
a.file_uuid = b.file_location_file_uuid
AND
a.file_type = 'DELETED'
AND
b.file_location_host_uuid = ".$anvil->Database->quote($anvil->data->{sys}{host_uuid})."
;";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
my $count = @{$results};
my $count = @{$results};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3 , list => {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
results => $results,
results => $results,
count => $count,
count => $count,
}});
}});
foreach my $row (@{$results})
foreach my $row (@{$results})
{
{
my $file_uuid = $row->[0];
my $file_uuid = $row->[0];
my $file_location_uuid = $row->[1];
my $full_path = $row->[1];
my $full_path = $row->[2];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
's1:file_uuid' => $file_uuid,
's1:file_uuid' => $file_uuid,
's2:full_path' => $full_path,
's2:file_location_uuid' => $file_location_uuid,
's3:full_path' => $full_path,
}});
}});
# Get rid of it (if it actually exists) and then remove it from file_locations .
# Get rid of it (if it actually exists).
if (-e $full_path)
if (-e $full_path)
{
{
# Delete it.
# Delete it.
@ -1038,10 +850,35 @@ AND
}
}
}
}
# Delete the entry from file_locations, if needed.
# If we're a Striker, check for any file_locations that point to this file and DELETE them.
$query = "DELETE FROM file_locations WHERE file_location_uuid = ".$anvil->Database->quote($file_location_uuid).";";
my $host_type = $anvil->Get->host_type();
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_type => $host_type }});
$anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__});
if ($host_type eq "striker")
{
my $query = "SELECT file_location_uuid FROM file_locations WHERE file_location_file_uuid = ".$anvil->Database->quote($file_uuid).";";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
my $count = @{$results};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
results => $results,
count => $count,
}});
foreach my $row (@{$results})
{
my $file_location_uuid = $row->[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_location_uuid => $file_location_uuid }});
# Delete the entry from file_locations, if needed.
my $query = "DELETE FROM history.file_locations WHERE file_location_uuid = ".$anvil->Database->quote($file_location_uuid).";";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
$anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__});
$query = "DELETE FROM file_locations WHERE file_location_uuid = ".$anvil->Database->quote($file_location_uuid).";";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
$anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__});
}
}
}
}
return(0);
return(0);
@ -1061,7 +898,7 @@ sub handle_delete
# Um...
# Um...
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0052"});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0052"});
$anvil->Job->update_progress({
$anvil->Job->update_progress({
progress => 0,
progress => 10 0,
message => "error_0052",
message => "error_0052",
job_uuid => $anvil->data->{jobs}{'job-uuid'},
job_uuid => $anvil->data->{jobs}{'job-uuid'},
});
});
@ -1072,7 +909,7 @@ sub handle_delete
# We don't do that here...
# We don't do that here...
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0053", variables => { file => $full_path }});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0053", variables => { file => $full_path }});
$anvil->Job->update_progress({
$anvil->Job->update_progress({
progress => 0,
progress => 10 0,
message => "error_0053,!!file!".$full_path."!!",
message => "error_0053,!!file!".$full_path."!!",
job_uuid => $anvil->data->{jobs}{'job-uuid'},
job_uuid => $anvil->data->{jobs}{'job-uuid'},
});
});
@ -1126,7 +963,7 @@ AND
# database. This way, if anything goes wrong below, future runs will try (again) to delete the file
# database. This way, if anything goes wrong below, future runs will try (again) to delete the file
# itself. If it's only beind deleted locally, and it fails for some reason, there will be no attempt
# itself. If it's only beind deleted locally, and it fails for some reason, there will be no attempt
# to try again.
# to try again.
if (( $file_uuid) && ($anvil->data->{switches}{everywhere}) )
if ($file_uuid)
{
{
# Yup.
# Yup.
($file_uuid) = $anvil->Database->insert_or_update_files({
($file_uuid) = $anvil->Database->insert_or_update_files({
@ -1164,18 +1001,13 @@ AND
}
}
}
}
# Now, if I have a file_uuid, delete it from file_locations if it exists.
# Now, if I have a file_uuid, delete it from file_locations if it exists. It's ok if peers in an
# Anvil! haven't deleted yet, as they'll see the file marked as 'DELETED' and purge their local
# copies.
if ($file_uuid)
if ($file_uuid)
{
{
# Delete the entry from file_locations, if needed.
# Delete the entry from file_locations, if needed.
my $query = "
my $query = "DELETE FROM file_locations WHERE file_location_file_uuid = ".$anvil->Database->quote($file_uuid).";";
DELETE FROM
file_locations
WHERE
file_location_file_uuid = ".$anvil->Database->quote($file_uuid)."
AND
file_location_host_uuid = ".$anvil->Database->quote($anvil->data->{sys}{host_uuid})."
;";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
$anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__});
$anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__});
}
}