diff --git a/Anvil/Tools.pm b/Anvil/Tools.pm index fc849b80..0eb60608 100755 --- a/Anvil/Tools.pm +++ b/Anvil/Tools.pm @@ -129,7 +129,9 @@ sub new WORDS => Anvil::Tools::Words->new(), VALIDATE => Anvil::Tools::Validate->new(), # This is to be removed before development ends. - log_file => "", + 'log' => { + main => "", + }, }, DATA => {}, ENV_VALUES => { @@ -389,10 +391,10 @@ sub nice_exit }}); # Close the log file. - if ($anvil->data->{HANDLE}{log_file}) + if ($anvil->data->{HANDLE}{'log'}{main}) { - close $anvil->data->{HANDLE}{log_file}; - $anvil->data->{HANDLE}{log_file} = ""; + close $anvil->data->{HANDLE}{'log'}{main}; + $anvil->data->{HANDLE}{'log'}{main} = ""; } exit($exit_code); @@ -800,8 +802,12 @@ sub _set_defaults host_type => "", host_uuid => "", language => "en_CA", - log_date => 1, - log_file => "/var/log/anvil.log", + 'log' => { + date => 1, + # Stores the '-v|...|-vvv' so that shell calls can be run at the same level as the + # avtive program when set by the user at the command line. + level => "", + }, manage => { firewall => 1, }, @@ -1012,6 +1018,9 @@ sub _set_paths 'lock' => { database => "/tmp/anvil-tools.database.lock", }, + 'log' => { + main => "/var/log/anvil.log", + }, proc => { uptime => "/proc/uptime", }, diff --git a/Anvil/Tools/Alert.pm b/Anvil/Tools/Alert.pm index efe94660..f3bee2ed 100755 --- a/Anvil/Tools/Alert.pm +++ b/Anvil/Tools/Alert.pm @@ -13,7 +13,7 @@ my $THIS_FILE = "Alert.pm"; ### Methods; # check_alert_sent # error -# register_alert +# register =pod @@ -282,7 +282,7 @@ WHERE return($set); } -=head2 register_alert +=head2 register This registers an alert to be sent later. @@ -318,6 +318,10 @@ Alerts at this level should not trigger alarm systems. Periodic review is suffic This is used for alerts that are almost always safe to ignore, but may be useful in testing and debugging. +=head3 clear_alert (optional, default '0') + +If set, this indicate that the alert has returned to an OK state. Alert level is still honoured for notification target delivery decisions, but some internal values are adjusted. + =head3 message (required) This is the message body of the alert. It is expected to be in the format C<< >>. If variables are to be injected into the C<< string_key >>, a comma-separated list in the format C<< !!variable_name1!value1!![,!!variable_nameN!valueN!!] >> is used. @@ -355,7 +359,7 @@ Example with a message alone; C<< foo_0001 >>. Example with two variables; C<< foo_0002,!!bar!abc!!,!!baz!123!! >>. =cut -sub register_alert +sub register { my $self = shift; my $parameter = shift; @@ -363,13 +367,15 @@ sub register_alert my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; my $alert_level = defined $parameter->{alert_level} ? $parameter->{alert_level} : 0; + my $clear_alert = defined $parameter->{clear_alert} ? $parameter->{clear_alert} : 0; my $message = defined $parameter->{message} ? $parameter->{message} : ""; my $set_by = defined $parameter->{set_by} ? $parameter->{set_by} : ""; my $show_header = defined $parameter->{show_header} ? $parameter->{show_header} : 1; my $sort_position = defined $parameter->{sort_position} ? $parameter->{sort_position} : 9999; - my $title = defined $parameter->{title} ? $parameter->{title} : "title_0003"; + my $title = defined $parameter->{title} ? $parameter->{title} : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { show_header => $show_header, + clear_alert => $clear_alert, alert_level => $alert_level, message => $message, set_by => $set_by, @@ -379,23 +385,27 @@ sub register_alert if (not $alert_level) { - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Alert->register_alert()", parameter => "alert_level" }}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Alert->register()", parameter => "alert_level" }}); return("!!error!!"); } if (not $set_by) { - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Alert->register_alert()", parameter => "set_by" }}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Alert->register()", parameter => "set_by" }}); return("!!error!!"); } if (not $message) { - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Alert->register_alert()", parameter => "message" }}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Alert->register()", parameter => "message" }}); return("!!error!!"); } if (($show_header) && (not $title)) { - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0101"}); - return("!!error!!"); + # Set it based on the alert_level. + if ($alert_level eq "1") { $title = $clear_alert ? "alert_title_0005" : "alert_title_0001"; } # Critical (or Critical Cleared) + elsif ($alert_level eq "2") { $title = $clear_alert ? "alert_title_0006" : "alert_title_0002"; } # Warning (or Warning Cleared) + elsif ($alert_level eq "3") { $title = $clear_alert ? "alert_title_0007" : "alert_title_0003"; } # Notice (or Notice Cleared) + elsif ($alert_level eq "4") { $title = $clear_alert ? "alert_title_0008" : "alert_title_0004"; } # Info (or Info Cleared) + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { title => $title }}); } # zero-pad sort numbers so that they sort properly. diff --git a/Anvil/Tools/Log.pm b/Anvil/Tools/Log.pm index 1333682d..1c7304ea 100755 --- a/Anvil/Tools/Log.pm +++ b/Anvil/Tools/Log.pm @@ -278,7 +278,7 @@ sub entry # Log the file and line, if passed. my $string = ""; my $print_string = ""; - if ($anvil->data->{sys}{log_date}) + if ($anvil->data->{sys}{'log'}{date}) { # Keep the debug level super high to avoid Get->date_and_time() going into an infinite loop. $string .= $anvil->Get->date_and_time({debug => 99}).":"; @@ -317,7 +317,7 @@ sub entry } # If the user set a log file, log to that. Otherwise, log via Log::Journald. - if ($anvil->data->{sys}{log_file}) + if ($anvil->data->{path}{'log'}{main}) { # TODO: Switch back to journald later, using a file for testing for now if ($string !~ /\n$/) @@ -326,10 +326,10 @@ sub entry } # Open the file? - if (not $anvil->data->{HANDLE}{log_file}) + if (not $anvil->data->{HANDLE}{'log'}{main}) { # If the file doesn't start with a '/', we'll put it under /var/log. - my $log_file = $anvil->data->{sys}{log_file} =~ /^\// ? $anvil->data->{sys}{log_file} : "/var/log/".$anvil->data->{sys}{log_file}; + my $log_file = $anvil->data->{path}{'log'}{main} =~ /^\// ? $anvil->data->{path}{'log'}{main} : "/var/log/".$anvil->data->{path}{'log'}{main}; my ($directory, $file) = ($log_file =~ /^(\/.*)\/(.*)$/); ### WARNING: We MUST set the debug level really high, or else we'll go into a deep @@ -342,20 +342,20 @@ sub entry # NOTE: Don't call '$anvil->Log->entry()' here, it will cause a loop! open (my $file_handle, ">>", $shell_call) or die "Failed to open: [$shell_call] for writing. The error was: $!\n"; $file_handle->autoflush(1); - $anvil->data->{HANDLE}{log_file} = $file_handle; + $anvil->data->{HANDLE}{'log'}{main} = $file_handle; # Make sure it can be written to by apache. $anvil->Storage->change_mode({debug => $debug, target => $log_file, mode => "0666"}); } - if (not $anvil->data->{HANDLE}{log_file}) + if (not $anvil->data->{HANDLE}{'log'}{main}) { # NOTE: This can't be a normal error because we can't write to the logs. die $THIS_FILE." ".__LINE__."; log file handle doesn't exist, but it should by now.\n"; } # The handle has to be wrapped in a block to make 'print' happy as it doesn't like non-scalars for file handles - print { $anvil->data->{HANDLE}{log_file} } $string; + print { $anvil->data->{HANDLE}{'log'}{main} } $string; } else { @@ -716,25 +716,31 @@ sub _adjust_log_level my $anvil = $self->parent; my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; + ### TODO: Support '--secure' and '--no-secure' if ($anvil->data->{switches}{V}) { $anvil->Log->level({set => 0}); + $anvil->data->{sys}{'log'}{level} = "-V"; } elsif ($anvil->data->{switches}{v}) { $anvil->Log->level({set => 1}); + $anvil->data->{sys}{'log'}{level} = "-v"; } elsif ($anvil->data->{switches}{vv}) { $anvil->Log->level({set => 2}); + $anvil->data->{sys}{'log'}{level} = "-vv"; } elsif ($anvil->data->{switches}{vvv}) { $anvil->Log->level({set => 3}); + $anvil->data->{sys}{'log'}{level} = "-vvv"; } elsif ($anvil->data->{switches}{vvvv}) { $anvil->Log->level({set => 4}); + $anvil->data->{sys}{'log'}{level} = "-vvvv"; } return(0); diff --git a/share/words.xml b/share/words.xml index c47ae2c1..81a9e3e6 100644 --- a/share/words.xml +++ b/share/words.xml @@ -12,14 +12,17 @@ Author: Madison Kelly - Debug - Information + Critical + Warning Notice - Warning - Critical - Warning Cleared - Critical Cleared - Important + Info + Critical Cleared! + Warning Cleared! + Notice Cleared! + Info Cleared! + + + The scan agent: [#!variable!agent_name!#] timed out! It was given: [#!variable!timeout!#] seconds to run, but it didn't return, so it was terminated. The host name: [#!variable!target!#] does not resolve to an IP address. @@ -91,7 +94,7 @@ The '-y' option prevents a confirmation prompt. This system needs to be rebooted. This system does NOT need to be rebooted. - Exiting to '--run-once' switch. + Asked to only run once, so exiting now. Previous run exited early. Restarting momentarily. No updates were found or needed. @@ -378,7 +381,7 @@ The database connection error was: [ Error ] - There is no Anvil! database user set for the local machine. Please check: [#!data!path::config::anvil.conf!#]'s DB entry: [#!variable!uuid!#]. Database user: [#!variable!user!#] password has been set/updated. - register_alert() was called but the 'title_key' parameter was not passed or it is empty and 'header' is enable (default).]]> + #!free!# I am not recording the alert with message_key: [#!variable!message_key!#] to the database because its log level was lower than any recipients. The local machine's UUID was not read properly. It should be stored in: [#!data!sys::host_uuid!#] and contain hexadecimal characters in the format: '012345-6789-abcd-ef01-23456789abcd' and usually matches the output of 'dmidecode --string system-uuid'. If this file exists and if there is a string in the file, please verify that it is structured correctly. The database with UUID: [#!variable!uuid!#] for: [#!variable!file!#] is behind. @@ -536,6 +539,8 @@ The body of the file: [#!variable!file!#] does not match the new body. The file The md5sum of: [#!variable!file!#] has changed since the daemon started. * [#!variable!old_sum!#] -> [#!variable!new_sum!#] + Reading the scan agent: [#!variable!agent_name!#]'s words file: [#!variable!file!#]. + Running the scan agent: [#!variable!agent_name!#] with a timeout of: [#!variable!timeout!#] seconds now... Test @@ -748,9 +753,9 @@ Here we will inject 't_0006', which injects 't_0001' which has a variable: [#!st read_details: [#!variable!uuid!#] is not a valid UUID.]]> read_details: [#!variable!uuid!#] was not found in the database.]]> Login failed, please try again. - #!data!sys::log_file!#] for details.]]> + #!data!path::log::file!#] for details.]]> #!variable!template!#] in the template file: [#!variable!file!#].]]> - #!variable!template!#] in the template file: [#!variable!file!#]. Details of the problem should be in: [#!data!sys::log_file!#].]]> + #!variable!template!#] in the template file: [#!variable!file!#]. Details of the problem should be in: [#!data!path::log::file!#].]]> The 'host-uuid': [#!variable!host_uuid!#] is not valid. The '#!variable!switch!#' switch is missing. The job UUID was passed via '--job-uuid' but the passed in value: [#!variable!uuid!#] is not a valid UUID. diff --git a/tools/scancore b/tools/scancore index 82140166..093b367f 100755 --- a/tools/scancore +++ b/tools/scancore @@ -47,7 +47,7 @@ wait_until_configured($anvil); # Disconnect. We'll reconnect inside the loop $anvil->Database->disconnect(); -$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "log_0203"}); +$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 3, key => "log_0203"}); # The main loop $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "log_0248"}); @@ -119,16 +119,74 @@ sub call_agents scan_directory($anvil, $anvil->data->{path}{directories}{scan_agents}); # Now loop through the agents I found and call them. + my $default_timeout = 30; + if ((exists $anvil->data->{scancore}{timing}{agent_runtime}) && ($anvil->data->{scancore}{timing}{agent_runtime} =~ /^\d+$/)) + { + $default_timeout = $anvil->data->{scancore}{timing}{agent_runtime}; + } foreach my $agent_name (sort {$a cmp $b} keys %{$anvil->data->{scancore}{agent}}) { my $agent_path = $anvil->data->{scancore}{agent}{$agent_name}; my $agent_words = $agent_path.".xml"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + agent_name => $agent_name, + agent_path => $agent_path, + agent_words => $agent_words, + }}); if ((-e $agent_words) && (-r $agent_words)) { # Read the words file so that we can generate alerts later. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0251", variables => { + agent_name => $agent_name, + file => $agent_path, + }}); $anvil->Words->read({file => $agent_words}); } + + # Now call the agent. + my $start_time = time; + my $return_code = 9999; + my $timeout = $default_timeout; + if ((exists $anvil->data->{scancore}{agent}{$agent_name}{agent_runtime}) && ($anvil->data->{scancore}{agent}{$agent_name}{agent_runtime} =~ /^\d+$/)) + { + $timeout = $anvil->data->{scancore}{agent}{$agent_name}{agent_runtime}; + } + my $shell_call = $anvil->data->{path}{exe}{timeout}." ".$timeout." ".$agent_path; + if ($anvil->data->{sys}{'log'}{level}) + { + $shell_call .= ." ".$anvil->data->{sys}{'log'}{level}; + } + $shell_call .= "; ".$anvil->data->{path}{exe}{echo}." return_code:\$?"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); + + # Tell the user this agent is about to run... + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "log_0252", variables => { + agent_name => $agent_name, + timeout => $timeout, + }}); + my $output = $anvil->System->call({shell_call => $shell_call}); + foreach my $line (split/\n/, $output) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); + + if ($line =~ /return_code:(\d+)$/) + { + $return_code = $1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { return_code => $return_code }}); + } + } + + # If the return code is '124', timeout popped. + if ($return_code eq "124") + { + # Register an alert... + $anvil->Alert->register({ + alert_level => "notice", + set_by => $THIS_FILE, + message => "alert_message_0001,!!agent_name!".$agent_name."!!,!!timeout!".$timeout."!!", + }); + } } return(0); @@ -189,7 +247,7 @@ sub scan_directory { my ($anvil, $directory) = @_; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { directory => $directory }}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { directory => $directory }}); local(*DIRECTORY); opendir(DIRECTORY, $directory); while(my $file = readdir(DIRECTORY)) @@ -197,7 +255,7 @@ sub scan_directory next if $file eq "."; next if $file eq ".."; my $full_path = $directory."/".$file; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { file => $file, full_path => $full_path, }});