|
|
|
#!/usr/bin/perl
|
|
|
|
#
|
|
|
|
# This handles moving around and managing files on Anvil! nodes, DR hosts and Striker dashboards.
|
|
|
|
#
|
|
|
|
# When this is called (periodically by the daemon of after an upload / ISO generation);
|
|
|
|
# - 1. Check 'incoming/' for files. For any found, generate an md5sum and see if the file name and sum match
|
|
|
|
# anything in the database from any host;
|
|
|
|
# - If so, update/add an entry in 'file_locations'
|
|
|
|
# - If not, create a new entry in 'files' and then add the first entry in 'file_locations'
|
|
|
|
# - 2. Check 'file_locations' for any files on this system, and verify they exist still.
|
|
|
|
# - If not, check the other files for one with a matching md5sum. If found, handle as a rename.
|
|
|
|
# - If not found at all, search for the file according to the search rules and copy to here if found.
|
|
|
|
# - If not found anywhere, remove it from 'file_locations' and send an alert.
|
|
|
|
# - 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'.
|
|
|
|
# - 4. If called with '--delete', remove from 'file_locations' and then remove from the local storage. If
|
|
|
|
# also used with '--everywhere', then all copies on all systems we know about will be deleted. This is
|
|
|
|
# done by registering a job against all known hosts. As such, if this is called and the target file
|
|
|
|
# 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.
|
|
|
|
#
|
|
|
|
# Exit codes;
|
|
|
|
# 0 = Normal exit or md5sum of this program changed and it exited to reload.
|
|
|
|
# 1 = No databases available.
|
|
|
|
# 2 = Another process that is still running has picked up this job.
|
|
|
|
# 3 = No filename specified when needed by another switch.
|
|
|
|
# 4 = Request to change the file name but the new name wasn't given.
|
|
|
|
# 5 = The file specified was not found.
|
|
|
|
# 6 = The file to delete is not under '/mnt/shared/'.
|
|
|
|
#
|
|
|
|
# TODO:
|
|
|
|
# -
|
|
|
|
#
|
|
|
|
# NOTE:
|
|
|
|
# -
|
|
|
|
#
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
use warnings;
|
|
|
|
use Anvil::Tools;
|
|
|
|
use Data::Dumper;
|
|
|
|
|
|
|
|
# Disable buffering
|
|
|
|
$| = 1;
|
|
|
|
|
|
|
|
my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0];
|
|
|
|
my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0];
|
|
|
|
if (($running_directory =~ /^\./) && ($ENV{PWD}))
|
|
|
|
{
|
|
|
|
$running_directory =~ s/^\./$ENV{PWD}/;
|
|
|
|
}
|
|
|
|
|
|
|
|
my $anvil = Anvil::Tools->new();
|
|
|
|
$anvil->Log->level({set => 2});
|
|
|
|
$anvil->Log->secure({set => 1});
|
|
|
|
|
|
|
|
$anvil->data->{switches}{'delete'} = "";
|
|
|
|
$anvil->data->{switches}{download} = "";
|
|
|
|
$anvil->data->{switches}{everywhere} = "";
|
|
|
|
$anvil->data->{switches}{file} = "";
|
|
|
|
$anvil->data->{switches}{'is-script'} = "";
|
|
|
|
$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
|
|
|
|
$anvil->Database->connect;
|
|
|
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0132"});
|
|
|
|
if (not $anvil->data->{sys}{database}{connections})
|
|
|
|
{
|
|
|
|
# No databases, exit.
|
|
|
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0003"});
|
|
|
|
$anvil->nice_exit({exit_code => 1});
|
|
|
|
}
|
|
|
|
|
|
|
|
# If we have a job_uuid, pick it up.
|
|
|
|
if ($anvil->data->{switches}{'job-uuid'})
|
|
|
|
{
|
|
|
|
my $return = $anvil->Job->get_job_details({check => 1, job_uuid => $anvil->data->{switches}{'job-uuid'}});
|
|
|
|
if ($return == 1)
|
|
|
|
{
|
|
|
|
# It's not a UUID.
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { 'return' => $return }});
|
|
|
|
$anvil->nice_exit({code => 2});
|
|
|
|
}
|
|
|
|
if ($return == 2)
|
|
|
|
{
|
|
|
|
# This job is being handled by another process that is still alive.
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { 'return' => $return }});
|
|
|
|
$anvil->nice_exit({code => 3});
|
|
|
|
}
|
|
|
|
|
|
|
|
# If there's a progress, clear it.
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { 'job::job_progress' => $anvil->data->{job}{job_progress} }});
|
|
|
|
if ($anvil->data->{job}{job_progress})
|
|
|
|
{
|
|
|
|
$anvil->Job->update_progress({
|
|
|
|
progress => 1,
|
|
|
|
message => "clear",
|
|
|
|
job_uuid => $anvil->data->{jobs}{'job-uuid'},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# What are we doing?
|
|
|
|
if ($anvil->data->{switches}{'rename'})
|
|
|
|
{
|
|
|
|
handle_rename($anvil);
|
|
|
|
}
|
|
|
|
elsif ($anvil->data->{switches}{'delete'})
|
|
|
|
{
|
|
|
|
handle_delete($anvil);
|
|
|
|
}
|
|
|
|
elsif ($anvil->data->{switches}{'is-script'})
|
|
|
|
{
|
|
|
|
handle_script($anvil);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
# Check for files scheduled for deletion.
|
|
|
|
check_for_deletes($anvil);
|
|
|
|
|
|
|
|
# Check for new files
|
|
|
|
check_incoming($anvil);
|
|
|
|
|
|
|
|
# Check for files we should have but don't yet have.
|
|
|
|
find_missing_files($anvil);
|
|
|
|
}
|
|
|
|
|
|
|
|
# We're done
|
|
|
|
$anvil->nice_exit({exit_code => 0});
|
|
|
|
|
|
|
|
|
|
|
|
#############################################################################################################
|
|
|
|
# Private functions. #
|
|
|
|
#############################################################################################################
|
|
|
|
|
|
|
|
# This looks to see if there are any entries in 'file_locations' for us that, the pointed to file, doesn't
|
|
|
|
# exist on this machine. For those entries, we will search for the file on other machines. The one exception
|
|
|
|
# is server definition files. For those, we write the file out from the server's 'definition' table entry.
|
|
|
|
#
|
|
|
|
# When a missing entry is found, and an entry doesn't exist, the file will be found (if possible) and copied
|
|
|
|
# to this host. Only machines on the same subnet are searched. The search pattern is;
|
|
|
|
#
|
|
|
|
# Nodes; 1. Check for the file on the peer.
|
|
|
|
# 2. Check for the file on Strikers, in alphabetical order.
|
|
|
|
# 3. Check for the file on DR host, if available.
|
|
|
|
# 4. Check other nodes, in alphabetical order.
|
|
|
|
# 5. Check other DR hosts, in alphabetical order.
|
|
|
|
# Striker; 1. Check for the file on other Strikers, in alphabetical order.
|
|
|
|
# 2. Check for the file on DR hosts, if available
|
|
|
|
# 3. Check for the file on Anvil! nodes.
|
|
|
|
# DR Host; 1. Check for the file on Strikers, in alphabetical order.
|
|
|
|
# 2. Check for the file on Anvil! nodes.
|
|
|
|
# * If a file can't be found, it will try again every so often until it is found.
|
|
|
|
# * When a file is found, it is copied to '/mnt/shared/incoming'. Only when the file has arrived and the
|
|
|
|
# md5sum matches. At this point, it is moved into the proper directory.
|
|
|
|
sub find_missing_files
|
|
|
|
{
|
|
|
|
my ($anvil) = @_;
|
|
|
|
|
|
|
|
# What am I? This will impact how missing files are found.
|
|
|
|
my $query = "
|
|
|
|
SELECT
|
|
|
|
a.file_uuid,
|
|
|
|
a.file_directory,
|
|
|
|
a.file_name,
|
|
|
|
a.file_size,
|
|
|
|
a.file_md5sum
|
|
|
|
FROM
|
|
|
|
files a,
|
|
|
|
hosts b,
|
|
|
|
file_locations c
|
|
|
|
WHERE
|
|
|
|
b.host_uuid = ".$anvil->Database->quote($anvil->data->{sys}{host_uuid})."
|
|
|
|
AND
|
|
|
|
a.file_uuid = c.file_location_file_uuid
|
|
|
|
AND
|
|
|
|
b.host_uuid = c.file_location_host_uuid
|
|
|
|
ORDER BY
|
|
|
|
a.file_directory ASC,
|
|
|
|
a.file_name ASC,
|
|
|
|
b.host_name ASC
|
|
|
|
;";
|
|
|
|
$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 $file_uuid = $row->[0];
|
|
|
|
my $file_directory = $row->[1];
|
|
|
|
my $file_name = $row->[2];
|
|
|
|
my $file_size = $row->[3];
|
|
|
|
my $file_md5sum = $row->[4];
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
|
|
|
's1:file_uuid' => $file_uuid,
|
|
|
|
's2:file_directory' => $file_directory,
|
|
|
|
's3:file_name' => $file_name,
|
|
|
|
}});
|
|
|
|
|
|
|
|
my $test_file = $file_directory."/".$file_name;
|
|
|
|
$test_file =~ s/\/\//\//g;
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { test_file => $test_file }});
|
|
|
|
|
|
|
|
if (not -e $test_file)
|
|
|
|
{
|
|
|
|
# Missing file!
|
|
|
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0269", variables => { file => $test_file }});
|
|
|
|
|
|
|
|
# Find what target, if any, we'll the file from.
|
|
|
|
my ($found) = find_file($anvil, $file_uuid, $test_file, $file_size, $file_md5sum);
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { found => $found }});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
### TODO: Left off here.
|
|
|
|
|
|
|
|
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.
|
|
|
|
sub find_file
|
|
|
|
{
|
|
|
|
my ($anvil, $file_uuid, $full_path, $file_size, $file_md5sum) = @_;
|
|
|
|
|
|
|
|
my $found = 0;
|
|
|
|
|
|
|
|
# What are my IPs?
|
|
|
|
$anvil->Network->get_ips();
|
|
|
|
foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{network}{'local'}{interface}})
|
|
|
|
{
|
|
|
|
next if not $anvil->data->{network}{'local'}{interface}{$interface}{ip};
|
|
|
|
next if not $anvil->data->{network}{'local'}{interface}{$interface}{subnet};
|
|
|
|
my $ip = $anvil->data->{network}{'local'}{interface}{$interface}{ip};
|
|
|
|
my $subnet = $anvil->data->{network}{'local'}{interface}{$interface}{subnet};
|
|
|
|
my $network = $anvil->Network->get_network({ip => $ip, subnet => $subnet});
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
|
|
|
's1:interface' => $interface,
|
|
|
|
's2:ip' => $ip,
|
|
|
|
's3:subnet' => $subnet,
|
|
|
|
's4:network' => $network,
|
|
|
|
}});
|
|
|
|
|
|
|
|
my $type = "other";
|
|
|
|
my $sort = $interface;
|
|
|
|
if ($interface =~ /^((?:bc|s|if)n)(\d+)/)
|
|
|
|
{
|
|
|
|
$type = $1;
|
|
|
|
$sort = $2;
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
|
|
|
's1:type' => $type,
|
|
|
|
's2:sort' => $sort,
|
|
|
|
}});
|
|
|
|
}
|
|
|
|
$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} = $subnet;
|
|
|
|
$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" => $anvil->data->{local_ip}{by_network}{$network}{subnet},
|
|
|
|
}});
|
|
|
|
}
|
|
|
|
|
|
|
|
# Create a hash we can sort through that are on the same subnet as us.
|
|
|
|
my $query = "
|
|
|
|
SELECT
|
|
|
|
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];
|
|
|
|
my $host_name = $row->[1];
|
|
|
|
my $host_type = $row->[2];
|
|
|
|
my $file_directory = $row->[3];
|
|
|
|
my $file_name = $row->[4];
|
|
|
|
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 $query = "
|
|
|
|
SELECT
|
|
|
|
ip_address_address,
|
|
|
|
ip_address_subnet_mask
|
|
|
|
FROM
|
|
|
|
ip_addresses
|
|
|
|
WHERE
|
|
|
|
ip_address_on_type != 'DELETED'
|
|
|
|
AND
|
|
|
|
ip_address_host_uuid = ".$anvil->Database->quote($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 $count = @{$results};
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
|
|
|
results => $results,
|
|
|
|
count => $count,
|
|
|
|
}});
|
|
|
|
foreach my $row (@{$results})
|
|
|
|
{
|
|
|
|
my $ip_address_address = $row->[0];
|
|
|
|
my $ip_address_subnet_mask = $row->[1];
|
|
|
|
my $network = $anvil->Network->get_network({ip => $ip_address_address, subnet => $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!
|
|
|
|
my $type = $anvil->data->{local_ip}{by_network}{$network}{type};
|
|
|
|
my $sort = $anvil->data->{local_ip}{by_network}{$network}{'sort'};
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
|
|
|
"s1:type" => $type,
|
|
|
|
"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},
|
|
|
|
}});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
### TODO: Do this according to what we are.
|
|
|
|
# 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;
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { type => $type }});
|
|
|
|
foreach my $sort (sort {$a cmp $b} keys %{$anvil->data->{peer_ip}{$host_type}{$type}})
|
|
|
|
{
|
|
|
|
last if $file_found;
|
|
|
|
my $ip = $anvil->data->{peer_ip}{$host_type}{$type}{$sort}{ip};
|
|
|
|
my $name = $anvil->data->{peer_ip}{$host_type}{$type}{$sort}{name};
|
|
|
|
my $host_uuid = $anvil->data->{peer_ip}{$host_type}{$type}{$sort}{host_uuid};
|
|
|
|
my $remote_user = "admin";
|
|
|
|
my $password = "";
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
|
|
|
's1:host_type' => $host_type,
|
|
|
|
'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}))
|
|
|
|
{
|
|
|
|
# Already searched this host, this is just a different IP. Skip it.
|
|
|
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 3, key => "log_0282", variables => {
|
|
|
|
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
|
|
|
|
### a large file like an ISO.
|
|
|
|
### 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",
|
|
|
|
remote_user => $remote_user,
|
|
|
|
password => $password,
|
|
|
|
target => $ip,
|
|
|
|
});
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
|
|
|
error => $error,
|
|
|
|
output => $output,
|
|
|
|
return_code => $return_code,
|
|
|
|
}});
|
|
|
|
foreach my $line (split/\n/, $output)
|
|
|
|
{
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { line => $line }});
|
|
|
|
if ($line =~ /^size: \[(\d+)\]$/)
|
|
|
|
{
|
|
|
|
$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?
|
|
|
|
if (($remote_size eq $file_size) && ($remote_md5sum eq $file_md5sum))
|
|
|
|
{
|
|
|
|
# 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
|
|
|
|
{
|
|
|
|
# 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 => $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}),
|
|
|
|
}});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { found => $found }});
|
|
|
|
return($found);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# This handles deleting a file.
|
|
|
|
sub check_incoming
|
|
|
|
{
|
|
|
|
my ($anvil) = @_;
|
|
|
|
|
|
|
|
# This hash shouldn't exist, but lets be safe...
|
|
|
|
if (exists $anvil->data->{scan}{directories})
|
|
|
|
{
|
|
|
|
delete $anvil->data->{scan}{directories};
|
|
|
|
}
|
|
|
|
# Read any files in '/mnt/shared/incoming'.
|
|
|
|
$anvil->Storage->scan_directory({
|
|
|
|
debug => 3,
|
|
|
|
directory => $anvil->data->{path}{directories}{shared}{base},
|
|
|
|
recursive => 1,
|
|
|
|
});
|
|
|
|
|
|
|
|
my $incoming_directory = $anvil->data->{path}{directories}{shared}{incoming};
|
|
|
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0264"});
|
|
|
|
foreach my $full_path (sort {$a cmp $b} keys %{$anvil->data->{scan}{directories}})
|
|
|
|
{
|
|
|
|
# Skip this if it's under '/mnt/shared/temp' (that's used for files being downloaded, etc)
|
|
|
|
next if $full_path =~ /^\/mnt\/shared\/temp\//;
|
|
|
|
|
|
|
|
# Skip if this isn't a file.
|
|
|
|
my $file_type = $anvil->data->{scan}{directories}{$full_path}{type};
|
|
|
|
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
|
|
|
full_path => $full_path,
|
|
|
|
file_type => $file_type,
|
|
|
|
}});
|
|
|
|
next if $file_type ne "file";
|
|
|
|
|
|
|
|
my $file_name = $anvil->data->{scan}{directories}{$full_path}{name};
|
|
|
|
my $file_directory = $anvil->data->{scan}{directories}{$full_path}{directory};
|
|
|
|
my $file_size = $anvil->data->{scan}{directories}{$full_path}{size};
|
|
|
|
my $file_mtime = $anvil->data->{scan}{directories}{$full_path}{mtime};
|
|
|
|
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 $say_mimetype = convert_mimetype($anvil, $file_mimetype, $full_path, $file_executable);
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
|
|
|
file_name => $file_name,
|
|
|
|
file_size => $file_size,
|
|
|
|
file_mtime => $file_mtime,
|
|
|
|
file_mimetype => $file_mimetype,
|
|
|
|
file_executable => $file_executable,
|
|
|
|
say_mimetype => $say_mimetype,
|
|
|
|
}});
|
|
|
|
|
|
|
|
# Do I know about this file? If so, is the file the same size? If either is no, calculate the md5sum.
|
|
|
|
my ($file_uuid, $recorded_size, $recorded_mtime, $recorded_md5sum) = get_file_db_info($anvil, "", $file_name);
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
|
|
|
file_uuid => $file_uuid,
|
|
|
|
recorded_size => $recorded_size,
|
|
|
|
recorded_mtime => $recorded_mtime,
|
|
|
|
recorded_md5sum => $recorded_md5sum,
|
|
|
|
}});
|
|
|
|
|
|
|
|
# Calculate the md5sum?
|
|
|
|
my $file_md5sum = $recorded_md5sum;
|
|
|
|
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
|
|
|
|
# version down from elsewhere.
|
|
|
|
if (($file_uuid) && ($file_mtime <= $recorded_mtime))
|
|
|
|
{
|
|
|
|
# We've got an older file, we need to update.
|
|
|
|
pull_file($anvil, $file_uuid, $recorded_size, $recorded_mtime, $recorded_md5sum);
|
|
|
|
|
|
|
|
# TODO: Now see if it exists and, if it does, re-stat it. If not, loop to the
|
|
|
|
# next file and skip this one.
|
|
|
|
}
|
|
|
|
|
|
|
|
# Now generate the md5sum. If this file is over 128 MiB, warn the user that it might
|
|
|
|
# take a while.
|
|
|
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0265", variables => { file => $full_path }});
|
|
|
|
if ($file_size > (128 * (2 ** 20)))
|
|
|
|
{
|
|
|
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0266", variables => {
|
|
|
|
size => $anvil->Convert->bytes_to_human_readable({'bytes' => $file_size}),
|
|
|
|
}});
|
|
|
|
}
|
|
|
|
|
|
|
|
# Update (or get) the md5sum.
|
|
|
|
$file_md5sum = $anvil->Get->md5sum({debug => 2, file => $full_path});
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
|
|
|
say_mimetype => $say_mimetype,
|
|
|
|
file_md5sum => $file_md5sum,
|
|
|
|
}});
|
|
|
|
|
|
|
|
# Insert or update the files entry.
|
|
|
|
($file_uuid) = $anvil->Database->insert_or_update_files({
|
|
|
|
debug => 3,
|
|
|
|
file_uuid => $file_uuid,
|
|
|
|
file_name => $file_name,
|
|
|
|
file_directory => $file_directory,
|
|
|
|
file_size => $file_size,
|
|
|
|
file_md5sum => $file_md5sum,
|
|
|
|
file_mtime => $file_mtime,
|
|
|
|
file_type => $say_mimetype,
|
|
|
|
});
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { file_uuid => $file_uuid }});
|
|
|
|
}
|
|
|
|
|
|
|
|
# If we still don't have a file UUID for some reason, skip this file.
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { file_uuid => $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.
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
|
|
|
full_path => $full_path,
|
|
|
|
incoming_directory => $incoming_directory,
|
|
|
|
}});
|
|
|
|
if ($full_path =~ /^$incoming_directory/)
|
|
|
|
{
|
|
|
|
# If it's a definition file, we'll move it to
|
|
|
|
# 'path::directories::shared::definitions', otherwise we'll move it to
|
|
|
|
# 'path::directories::shared::files'.
|
|
|
|
my $target = $say_mimetype eq "definition" ? $anvil->data->{path}{directories}{shared}{definitions} : $anvil->data->{path}{directories}{shared}{files};
|
|
|
|
$target .= "/";
|
|
|
|
$target =~ s/\/\//\//g;
|
|
|
|
$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 => {
|
|
|
|
file => $full_path,
|
|
|
|
target => $target,
|
|
|
|
}});
|
|
|
|
$anvil->Storage->move_file({
|
|
|
|
debug => 3,
|
|
|
|
source_file => $full_path,
|
|
|
|
target_file => $target,
|
|
|
|
});
|
|
|
|
|
|
|
|
# Update the file_directory.
|
|
|
|
my $say_directory =~ s/\/$//;
|
|
|
|
($file_uuid) = $anvil->Database->insert_or_update_files({
|
|
|
|
debug => 3,
|
|
|
|
file_uuid => $file_uuid,
|
|
|
|
file_name => $file_name,
|
|
|
|
file_directory => $say_directory,
|
|
|
|
file_size => $file_size,
|
|
|
|
file_md5sum => $file_md5sum,
|
|
|
|
file_mtime => $file_mtime,
|
|
|
|
file_type => $say_mimetype,
|
|
|
|
});
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { file_uuid => $file_uuid }});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
# This method finds a file elsewhere on the network and pulls it to here.
|
|
|
|
sub pull_file
|
|
|
|
{
|
|
|
|
my ($anvil, $file_uuid, $recorded_size, $recorded_mtime, $recorded_md5sum) = @_;
|
|
|
|
$file_uuid = "" if not defined $file_uuid;
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
|
|
|
file_uuid => $file_uuid,
|
|
|
|
recorded_size => $recorded_size,
|
|
|
|
recorded_mtime => $recorded_mtime,
|
|
|
|
recorded_md5sum => $recorded_md5sum,
|
|
|
|
}});
|
|
|
|
|
|
|
|
# Find the hosts with this file, then connect to it to see if the size is the same as what we want.
|
|
|
|
# If so, pull it down...
|
|
|
|
### TODO
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
# This gets the file_uuid for a given file name and/or md5sum. If the file isn't found, an empty string is
|
|
|
|
# returned. If it is found, the file size as recorded in the database is returned.
|
|
|
|
sub get_file_db_info
|
|
|
|
{
|
|
|
|
my ($anvil, $file_md5sum, $file_name) = @_;
|
|
|
|
$file_md5sum = "" if not defined $file_md5sum;
|
|
|
|
$file_name = "" if not defined $file_name;
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
|
|
|
file_md5sum => $file_md5sum,
|
|
|
|
file_name => $file_name,
|
|
|
|
}});
|
|
|
|
|
|
|
|
# Get the file size and file uuid, if possible.
|
|
|
|
|
|
|
|
# If I have the md5sum, search using that. If I have the filename only, then we'll fall back to that.
|
|
|
|
my $query = "
|
|
|
|
SELECT
|
|
|
|
file_uuid,
|
|
|
|
file_size,
|
|
|
|
file_mtime,
|
|
|
|
file_md5sum
|
|
|
|
FROM
|
|
|
|
files
|
|
|
|
WHERE
|
|
|
|
";
|
|
|
|
if ($file_md5sum)
|
|
|
|
{
|
|
|
|
$query .= " file_md5sum = ".$anvil->Database->quote($file_md5sum)."\n";
|
|
|
|
}
|
|
|
|
elsif ($file_name)
|
|
|
|
{
|
|
|
|
$query .= " file_name = ".$anvil->Database->quote($file_name)."\n";
|
|
|
|
}
|
|
|
|
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,
|
|
|
|
}});
|
|
|
|
if (not $count)
|
|
|
|
{
|
|
|
|
# File wasn't found in the database
|
|
|
|
return("", 0, 0, "");
|
|
|
|
}
|
|
|
|
my $file_uuid = defined $results->[0]->[0] ? $results->[0]->[0] : "";
|
|
|
|
my $file_size = defined $results->[0]->[1] ? $results->[0]->[1] : 0;
|
|
|
|
my $file_mtime = defined $results->[0]->[2] ? $results->[0]->[2] : 0;
|
|
|
|
$file_md5sum = defined $results->[0]->[3] ? $results->[0]->[3] : "";
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
|
|
|
file_uuid => $file_uuid,
|
|
|
|
file_size => $file_size,
|
|
|
|
file_mtime => $file_mtime,
|
|
|
|
file_md5sum => $file_md5sum,
|
|
|
|
}});
|
|
|
|
|
|
|
|
return($file_uuid, $file_size, $file_mtime, $file_md5sum);
|
|
|
|
}
|
|
|
|
|
|
|
|
# This handles toggling a file to marked or unmarked as a script.
|
|
|
|
sub handle_script
|
|
|
|
{
|
|
|
|
my ($anvil) = @_;
|
|
|
|
|
|
|
|
if (not $anvil->data->{switches}{file})
|
|
|
|
{
|
|
|
|
# Um...
|
|
|
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0055"});
|
|
|
|
$anvil->Job->update_progress({
|
|
|
|
progress => 0,
|
|
|
|
message => "error_0055",
|
|
|
|
job_uuid => $anvil->data->{jobs}{'job-uuid'},
|
|
|
|
});
|
|
|
|
$anvil->nice_exit({exit_code => 3});
|
|
|
|
}
|
|
|
|
|
|
|
|
# Find the file_uuid (we don't actually care about the file size, mtime or md5sum).
|
|
|
|
my ($file_uuid, $file_size, $recorded_mtime, $file_md5sum) = get_file_db_info($anvil, "", $anvil->data->{switches}{file});
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_uuid => $file_uuid }});
|
|
|
|
|
|
|
|
# Toggle the executable bits.
|
|
|
|
my $executable = 0;
|
|
|
|
if ($anvil->data->{switches}{'is-script'})
|
|
|
|
{
|
|
|
|
# Is it already executable?
|
|
|
|
if (-x $anvil->data->{switches}{file})
|
|
|
|
{
|
|
|
|
# Switch it on
|
|
|
|
$executable = 1;
|
|
|
|
$anvil->Storage->change_mode({path => $anvil->data->{switches}{file}, mode => "a+x"});
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
# Already a script.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
# Is it executable?
|
|
|
|
if (-x $anvil->data->{switches}{file})
|
|
|
|
{
|
|
|
|
# Switch it off.
|
|
|
|
$executable = 1;
|
|
|
|
$anvil->Storage->change_mode({path => $anvil->data->{switches}{file}, mode => "a-x"});
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
# Already not a script.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# If we have a file UUID, update the 'file_type
|
|
|
|
if ($file_uuid)
|
|
|
|
{
|
|
|
|
# Load the details.
|
|
|
|
my $query = "
|
|
|
|
SELECT
|
|
|
|
file_name,
|
|
|
|
file_directory,
|
|
|
|
file_size,
|
|
|
|
file_md5sum,
|
|
|
|
file_type,
|
|
|
|
file_mtime
|
|
|
|
FROM
|
|
|
|
files
|
|
|
|
WHERE
|
|
|
|
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__});
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { results => $results }});
|
|
|
|
my $file_name = $results->[0]->[0];
|
|
|
|
my $file_directory = $results->[0]->[1];
|
|
|
|
my $file_size = $results->[0]->[2];
|
|
|
|
my $file_md5sum = $results->[0]->[3];
|
|
|
|
my $file_type = $results->[0]->[4];
|
|
|
|
my $file_mtime = $results->[0]->[5];
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
|
|
|
file_name => $file_name,
|
|
|
|
file_directory => $file_directory,
|
|
|
|
file_size => $file_size,
|
|
|
|
file_md5sum => $file_md5sum,
|
|
|
|
file_type => $file_type,
|
|
|
|
file_mtime => $file_mtime,
|
|
|
|
}});
|
|
|
|
if (($file_type eq "script") && (not $anvil->data->{switches}{'is-script'}))
|
|
|
|
{
|
|
|
|
# Figure out what the file type is and update.
|
|
|
|
my $mimetype = mimetype($file_name);
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { mimetype => $mimetype }});
|
|
|
|
|
|
|
|
my $say_mimetype = convert_mimetype($anvil, $mimetype, $file_name, 0);
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { say_mimetype => $say_mimetype }});
|
|
|
|
$anvil->Database->insert_or_update_files({
|
|
|
|
debug => 2,
|
|
|
|
file_uuid => $file_uuid,
|
|
|
|
file_name => $anvil->data->{switches}{file},
|
|
|
|
file_directory => $file_directory,
|
|
|
|
file_size => $file_size,
|
|
|
|
file_md5sum => $file_md5sum,
|
|
|
|
file_mtime => $file_mtime,
|
|
|
|
file_type => $say_mimetype,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
elsif (($file_type ne "script") && ($anvil->data->{switches}{'is-script'}))
|
|
|
|
{
|
|
|
|
# Change the file tpye to "script"
|
|
|
|
$anvil->Database->insert_or_update_files({
|
|
|
|
debug => 2,
|
|
|
|
file_uuid => $file_uuid,
|
|
|
|
file_name => $anvil->data->{switches}{file},
|
|
|
|
file_directory => $file_directory,
|
|
|
|
file_size => $file_size,
|
|
|
|
file_md5sum => $file_md5sum,
|
|
|
|
file_mtime => $file_mtime,
|
|
|
|
file_type => "script",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
# This takes the mimetype as reported by the 'mimetype' method and returns a file type we care about.
|
|
|
|
sub convert_mimetype
|
|
|
|
{
|
|
|
|
my ($anvil, $mimetype, $file, $executable) = @_;
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
|
|
|
mimetype => $mimetype,
|
|
|
|
file => $file,
|
|
|
|
executable => $executable,
|
|
|
|
}});
|
|
|
|
|
|
|
|
my $say_mimetype = "other";
|
|
|
|
if ($mimetype)
|
|
|
|
{
|
|
|
|
if ($mimetype =~ /cd-image/)
|
|
|
|
{
|
|
|
|
$say_mimetype = "iso";
|
|
|
|
}
|
|
|
|
elsif ($mimetype =~ /xml/)
|
|
|
|
{
|
|
|
|
# This might be a definition, but look inside it to be sure.
|
|
|
|
my ($is_domain, $return_code) = $anvil->System->call({debug => 3, shell_call => "if \$(".$anvil->data->{path}{exe}{'grep'}." -q '</domain>' ".$file."); then ".$anvil->data->{path}{exe}{echo}." 1; else ".$anvil->data->{path}{exe}{echo}." 0; fi" });
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { is_domain => $is_domain, return_code => $return_code }});
|
|
|
|
|
|
|
|
if ($is_domain)
|
|
|
|
{
|
|
|
|
$say_mimetype = "definition";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
elsif ($mimetype =~ /rpm$/)
|
|
|
|
{
|
|
|
|
$say_mimetype = "rpm";
|
|
|
|
}
|
|
|
|
elsif ($mimetype =~ /disk-image/)
|
|
|
|
{
|
|
|
|
$say_mimetype = "disk-image";
|
|
|
|
}
|
|
|
|
elsif ($executable)
|
|
|
|
{
|
|
|
|
$say_mimetype = "script";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { say_mimetype => $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
|
|
|
|
# machine. Any that are found will have their file deleted and the file_locations entry deleted.
|
|
|
|
sub check_for_deletes
|
|
|
|
{
|
|
|
|
my ($anvil) = @_;
|
|
|
|
|
|
|
|
my $query = "
|
|
|
|
SELECT
|
|
|
|
a.file_uuid,
|
|
|
|
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 $count = @{$results};
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
|
|
|
results => $results,
|
|
|
|
count => $count,
|
|
|
|
}});
|
|
|
|
foreach my $row (@{$results})
|
|
|
|
{
|
|
|
|
my $file_uuid = $row->[0];
|
|
|
|
my $file_location_uuid = $row->[1];
|
|
|
|
my $full_path = $row->[2];
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
|
|
|
's1:file_uuid' => $file_uuid,
|
|
|
|
'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.
|
|
|
|
if (-e $full_path)
|
|
|
|
{
|
|
|
|
# Delete it.
|
|
|
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0285", variables => { file => $full_path }});
|
|
|
|
|
|
|
|
unlink $full_path;
|
|
|
|
|
|
|
|
# Sleep and verify
|
|
|
|
sleep 1;
|
|
|
|
if (-e $full_path)
|
|
|
|
{
|
|
|
|
# Failed to delete...
|
|
|
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0284", variables => { file => $full_path }});
|
|
|
|
return("");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
# Deleted successfully.
|
|
|
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0283"});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Delete the entry from file_locations, if needed.
|
|
|
|
$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);
|
|
|
|
}
|
|
|
|
|
|
|
|
# This handles deleting a file. If the requested deletion target doesn't exist, we'll just clear the
|
|
|
|
# database. If that doesn't exist either, we still don't error. This is to handle broad requests to delete a
|
|
|
|
# file everywhere. If we're asked to delete it everywhere, then we'll register a job against all hosts.
|
|
|
|
sub handle_delete
|
|
|
|
{
|
|
|
|
my ($anvil) = @_;
|
|
|
|
|
|
|
|
my $full_path = $anvil->data->{switches}{file};
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { full_path => $full_path }});
|
|
|
|
if (not $full_path)
|
|
|
|
{
|
|
|
|
# Um...
|
|
|
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0052"});
|
|
|
|
$anvil->Job->update_progress({
|
|
|
|
progress => 0,
|
|
|
|
message => "error_0052",
|
|
|
|
job_uuid => $anvil->data->{jobs}{'job-uuid'},
|
|
|
|
});
|
|
|
|
$anvil->nice_exit({exit_code => 3});
|
|
|
|
}
|
|
|
|
elsif ($full_path !~ /^\/mnt\/shared\//)
|
|
|
|
{
|
|
|
|
# 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->Job->update_progress({
|
|
|
|
progress => 0,
|
|
|
|
message => "error_0053,!!file!".$full_path."!!",
|
|
|
|
job_uuid => $anvil->data->{jobs}{'job-uuid'},
|
|
|
|
});
|
|
|
|
$anvil->nice_exit({exit_code => 6});
|
|
|
|
}
|
|
|
|
|
|
|
|
# What's the UUID of this file? I collect all the data for the update, if appropriate.
|
|
|
|
my $file_name = ($full_path =~ /^.*\/(.*)$/)[0];
|
|
|
|
my $file_directory = ($full_path =~ /^(.*?)\/$file_name$/)[0];
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
|
|
|
full_name => $file_name,
|
|
|
|
file_directory => $file_directory,
|
|
|
|
}});
|
|
|
|
my $query = "
|
|
|
|
SELECT
|
|
|
|
file_uuid,
|
|
|
|
file_size,
|
|
|
|
file_md5sum,
|
|
|
|
file_type,
|
|
|
|
file_mtime
|
|
|
|
FROM
|
|
|
|
files
|
|
|
|
WHERE
|
|
|
|
file_name = ".$anvil->Database->quote($file_name)."
|
|
|
|
AND
|
|
|
|
file_directory = ".$anvil->Database->quote($file_directory)."
|
|
|
|
;";
|
|
|
|
$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,
|
|
|
|
}});
|
|
|
|
|
|
|
|
my $file_uuid = defined $results->[0]->[0] ? $results->[0]->[0] : "";
|
|
|
|
my $file_size = defined $results->[0]->[1] ? $results->[0]->[1] : "";
|
|
|
|
my $file_md5sum = defined $results->[0]->[2] ? $results->[0]->[2] : "";
|
|
|
|
my $file_type = defined $results->[0]->[3] ? $results->[0]->[3] : "";
|
|
|
|
my $file_mtime = defined $results->[0]->[4] ? $results->[0]->[4] : "";
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
|
|
|
file_uuid => $file_uuid,
|
|
|
|
file_size => $file_size,
|
|
|
|
file_md5sum => $file_md5sum,
|
|
|
|
file_type => $file_type,
|
|
|
|
file_mtime => $file_mtime,
|
|
|
|
}});
|
|
|
|
|
|
|
|
# If I have the file_uuid, and we've been asked to delete it everywhere, mark it as DELETEd in the
|
|
|
|
# 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
|
|
|
|
# to try again.
|
|
|
|
if (($file_uuid) && ($anvil->data->{switches}{everywhere}))
|
|
|
|
{
|
|
|
|
# Yup.
|
|
|
|
($file_uuid) = $anvil->Database->insert_or_update_files({
|
|
|
|
file_uuid => $file_uuid,
|
|
|
|
file_name => $file_name,
|
|
|
|
file_directory => $file_directory,
|
|
|
|
file_size => $file_size,
|
|
|
|
file_md5sum => $file_md5sum,
|
|
|
|
file_type => "DELETED",
|
|
|
|
file_mtime => $file_mtime,
|
|
|
|
});
|
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { file_uuid => $file_uuid }});
|
|
|
|
}
|
|
|
|
|
|
|
|
# Does the file exist?
|
|
|
|
if (-e $full_path)
|
|
|
|
{
|
|
|
|
# Delete it.
|
|
|
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0263", variables => { file => $full_path }});
|
|
|
|
|
|
|
|
unlink $full_path;
|
|
|
|
|
|
|
|
# Sleep and verify
|
|
|
|
sleep 1;
|
|
|
|
if (-e $full_path)
|
|
|
|
{
|
|
|
|
# Failed to delete...
|
|
|
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0284"});
|
|
|
|
return("");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
# Deleted successfully.
|
|
|
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0283"});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Now, if I have a file_uuid, delete it from file_locations if it exists.
|
|
|
|
if ($file_uuid)
|
|
|
|
{
|
|
|
|
# Delete the entry from file_locations, if needed.
|
|
|
|
my $query = "
|
|
|
|
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->Database->write({query => $query, source => $THIS_FILE, line => __LINE__});
|
|
|
|
}
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
# This handles renaming files.
|
|
|
|
sub handle_rename
|
|
|
|
{
|
|
|
|
my ($anvil) = @_;
|
|
|
|
|
|
|
|
# Do we have the current file name and the new one?
|
|
|
|
if (not $anvil->data->{switches}{file})
|
|
|
|
{
|
|
|
|
# Um...
|
|
|
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0049"});
|
|
|
|
$anvil->Job->update_progress({
|
|
|
|
progress => 0,
|
|
|
|
message => "error_0049",
|
|
|
|
job_uuid => $anvil->data->{jobs}{'job-uuid'},
|
|
|
|
});
|
|
|
|
$anvil->nice_exit({exit_code => 3});
|
|
|
|
}
|
|
|
|
elsif (not $anvil->data->{switches}{to})
|
|
|
|
{
|
|
|
|
# We need a target
|
|
|
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0050", variables => { file => $anvil->data->{switches}{file} }});
|
|
|
|
$anvil->Job->update_progress({
|
|
|
|
progress => 0,
|
|
|
|
message => "error_0050,!!file!".$anvil->data->{switches}{file}."!!",
|
|
|
|
job_uuid => $anvil->data->{jobs}{'job-uuid'},
|
|
|
|
});
|
|
|
|
$anvil->nice_exit({exit_code => 4});
|
|
|
|
}
|
|
|
|
elsif (not -f $anvil->data->{switches}{file})
|
|
|
|
{
|
|
|
|
# The file to rename doesn't exist.
|
|
|
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0051", variables => { file => $anvil->data->{switches}{file} }});
|
|
|
|
$anvil->Job->update_progress({
|
|
|
|
progress => 0,
|
|
|
|
message => "error_0051,!!file!".$anvil->data->{switches}{file}."!!",
|
|
|
|
job_uuid => $anvil->data->{jobs}{'job-uuid'},
|
|
|
|
});
|
|
|
|
$anvil->nice_exit({exit_code => 5});
|
|
|
|
}
|
|
|
|
elsif (-e $anvil->data->{switches}{to})
|
|
|
|
{
|
|
|
|
# There's already a file (or directory or something) with that name.
|
|
|
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0056", variables => { file => $anvil->data->{switches}{file}, to => $anvil->data->{switches}{to} }});
|
|
|
|
$anvil->Job->update_progress({
|
|
|
|
progress => 0,
|
|
|
|
message => "error_0056,!!file!".$anvil->data->{switches}{file}."!!,!!to!".$anvil->data->{switches}{to}."!!",
|
|
|
|
job_uuid => $anvil->data->{jobs}{'job-uuid'},
|
|
|
|
});
|
|
|
|
$anvil->nice_exit({exit_code => 6});
|
|
|
|
}
|
|
|
|
|
|
|
|
### TODO: Left off here
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
}
|