#!/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 --to ', 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->data->{switches}{'job-uuid'} = ""; $anvil->data->{switches}{'rename'} = ""; $anvil->data->{switches}{'is-script'} = ""; $anvil->data->{switches}{file} = ""; $anvil->data->{switches}{to} = ""; $anvil->data->{switches}{'delete'} = ""; $anvil->data->{switches}{everywhere} = ""; $anvil->Get->switches; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "switches::job-uuid" => $anvil->data->{switches}{'job-uuid'}, "switches::rename" => $anvil->data->{switches}{'rename'}, "switches::is-script" => $anvil->data->{switches}{'is-script'}, "switches::file" => $anvil->data->{switches}{file}, "switches::to" => $anvil->data->{switches}{to}, "switches::delete" => $anvil->data->{switches}{'delete'}, "switches::everywhere" => $anvil->data->{switches}{everywhere}, }}); # 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); } # We're done $anvil->nice_exit({exit_code => 0}); ############################################################################################################# # Private functions. # ############################################################################################################# # This gets the file_uuid for a given file name and/or md5sum. If the file isn't found, an empty string is # returned. sub get_file_uuid { 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 => 2, list => { file_md5sum => $file_md5sum, file_name => $file_name, }}); ### TODO: At some point, we'll need to deal with the possibility that the same file name with ### different md5sums might exist in the database. # 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 FROM files WHERE "; if ($file_md5sum) { $query .= " file_md5sum = ".$anvil->data->{sys}{database}{use_handle}->quote($file_md5sum)."\n"; } elsif ($file_name) { $query .= " file_name = ".$anvil->data->{sys}{database}{use_handle}->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 => 2, list => { results => $results, count => $count, }}); if (not $count) { # File wasn't found in the database return(""); } my $file_uuid = $results->[0]->[0]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_uuid => $file_uuid, }}); return($file_uuid); } # 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"}); if ($anvil->data->{jobs}{'job-uuid'}) $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. my ($file_uuid) = get_file_uuid($anvil, "", $anvil->data->{switches}{file}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_uuid => $file_uuid }}); # Toggle the executable bits. if ($anvil->data->{switches}{'is-script'}) { # Is it already executable? if (-x $anvil->data->{switches}{file}) { # Switch it on $anvil->Storage->change_mode({target => $anvil->data->{switches}{file}, mode => "a+x"}); } else { # Already a script. } } else { # Is it executable? if (-x $anvil->data->{switches}{file}) { # Switch it off. $anvil->Storage->change_mode({target => $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_size, file_md5sum, file_type FROM files WHERE file_uuid = ".$anvil->data->{sys}{database}{use_handle}->quote($file_uuid)." ;"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); my $results = $anvil->Database->query({query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { results => $results }}); my $file_name = $results->[0]->[0]; my $file_size = $results->[0]->[1]; my $file_md5sum = $results->[0]->[2]; my $file_type = $results->[0]->[3]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_name => $file_name, file_size => $file_size, file_md5sum => $file_md5sum, file_type => $file_type, }}); if (($file_type eq "script") && (not $anvil->data->{switches}{'is-script'})) { # Figure out what the file type is and update. my $mimetype = mimetype($full_path); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { mimetype => $mimetype }}); my $say_mimetype = "other"; if ($mimetype =~ /cd-image/) { $say_mimetype = "iso"; } elsif ($mimetype =~ /rpm$/) { $say_mimetype = "rpm"; } elsif ($mimetype =~ /disk-image/) { $say_mimetype = "disk-image"; } $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { say_mimetype => $say_mimetype }}); $anvil->Database->insert_or_update_files({ file_uuid => $file_uuid, file_name => $anvil->data->{switches}{file}, file_size => $file_size, file_md5sum => $file_md5sum, 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({ file_uuid => $file_uuid, file_name => $anvil->data->{switches}{file}, file_size => $file_size, file_md5sum => $file_md5sum, file_type => "script", }); } } 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) = @_; if (not $anvil->data->{switches}{file}) { # Um... $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0052"}); if ($anvil->data->{jobs}{'job-uuid'}) $anvil->Job->update_progress({ progress => 0, message => "error_0052", job_uuid => $anvil->data->{jobs}{'job-uuid'}, }); $anvil->nice_exit({exit_code => 3}); } elsif ($anvil->data->{switches}{file} !~ /^\/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 => $anvil->data->{switches}{file} }}); $anvil->Job->update_progress({ progress => 0, message => "error_0053,!!file!".$anvil->data->{switches}{file}."!!", job_uuid => $anvil->data->{jobs}{'job-uuid'}, }); $anvil->nice_exit({exit_code => 6}); } # Does the file exist? if (-e $anvil->data->{switches}{file}) { # Delete it. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0263", variables => { file => $anvil->data->{switches}{file} }}); unlink $anvil->data->{switches}{file}; # Sleep and verify sleep 1; if (-e $anvil->data->{switches}{file}) { # Failed to delete... # TODO: } else { # Deleted successfully. Remove from 'file_locations' ### TODO: Find the file_uuid, then if found, see if we have and entry in file_location and then delete it. } } 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_0052", variables => { file => $anvil->data->{switches}{file}, to => $anvil->data->{switches}{to} }}); $anvil->Job->update_progress({ progress => 0, message => "error_0052,!!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); }