* Fixed a tricky deep recursion bug in Network->is_local when the passed in host was an empty string. Also created a cache system where a host name that has been checked before is immediately returned, without needing to run through the logic in 'is_local', which gets called quite frequently.

* Updated the loop detection logic in Log->entry where processing large strings was triggering it when it shouldn't.

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 5 years ago
parent 32bcdbe6d3
commit af6e2c076d
  1. 4
      Anvil/Tools.pm
  2. 26
      Anvil/Tools/Log.pm
  3. 50
      Anvil/Tools/Network.pm
  4. 1
      Anvil/Tools/Storage.pm
  5. 2
      share/words.xml
  6. 2
      tools/anvil-daemon
  7. 21
      tools/striker-parse-oui

@ -240,7 +240,7 @@ sub new
# If the local './tools.conf' file exists, read it in. # If the local './tools.conf' file exists, read it in.
if (-r $anvil->data->{path}{configs}{'anvil.conf'}) if (-r $anvil->data->{path}{configs}{'anvil.conf'})
{ {
$anvil->Storage->read_config({debug => $debug, file => $anvil->data->{path}{configs}{'anvil.conf'}}); $anvil->Storage->read_config({debug => 3, file => $anvil->data->{path}{configs}{'anvil.conf'}});
### TODO: Should anvil.conf override parameters? ### TODO: Should anvil.conf override parameters?
# Let parameters override config file values. # Let parameters override config file values.
@ -720,7 +720,7 @@ sub _host_name
else else
{ {
# The environment variable isn't set. Call 'hostnamectl' on the command line. # The environment variable isn't set. Call 'hostnamectl' on the command line.
($host_name, my $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{hostnamectl}." --static"}); ($host_name, my $return_code) = $anvil->System->call({debug => 9999, shell_call => $anvil->data->{path}{exe}{hostnamectl}." --static"});
} }
return($host_name); return($host_name);

@ -248,11 +248,19 @@ sub entry
my $source = defined $parameter->{source} ? $parameter->{source} : ""; my $source = defined $parameter->{source} ? $parameter->{source} : "";
my $tag = defined $parameter->{tag} ? $parameter->{tag} : $anvil->data->{defaults}{'log'}{tag}; my $tag = defined $parameter->{tag} ? $parameter->{tag} : $anvil->data->{defaults}{'log'}{tag};
my $variables = defined $parameter->{variables} ? $parameter->{variables} : ""; my $variables = defined $parameter->{variables} ? $parameter->{variables} : "";
$anvil->data->{loop}{count} = 0 if not defined $anvil->data->{loop}{count}; $anvil->data->{loop}{count} = 0 if not defined $anvil->data->{loop}{count};
$anvil->data->{loop}{count}++; $anvil->data->{loop}{count}++;
print $THIS_FILE." ".__LINE__."; [ Debug ] - level: [".$level."], defaults::log::level: [".$anvil->data->{defaults}{'log'}{level}."], logging secure? [".$anvil->Log->secure."], loop::count: [".$anvil->data->{loop}{count}."]\n" if $test; print $THIS_FILE." ".__LINE__."; [ Debug ] - level: [".$level."], defaults::log::level: [".$anvil->data->{defaults}{'log'}{level}."], logging secure? [".$anvil->Log->secure."], loop::count: [".$anvil->data->{loop}{count}."], source: [".$source."], line: [".$line."], key: [".$key."], variables: [".$variables."]\n" if $test;
if (($test) && (ref($variables) eq "HASH"))
{
foreach my $key (sort {$a cmp $b} keys %{$variables})
{
print $THIS_FILE." ".__LINE__."; - key: [".$key."] -> [".$variables->{$key}."]\n";
}
}
# The counter needs to be longer than any conceivable file line count we might read. # The counter needs to be longer than any conceivable file line count we might read.
if ($anvil->data->{loop}{count} > 500000) if ($anvil->data->{loop}{count} > 5000000)
{ {
if ($anvil->environment eq "html") if ($anvil->environment eq "html")
{ {
@ -292,10 +300,12 @@ sub entry
# Exit immediately if this isn't going to be logged # Exit immediately if this isn't going to be logged
if ($level > $anvil->Log->level) if ($level > $anvil->Log->level)
{ {
$anvil->data->{loop}{count}--;
return(1); return(1);
} }
if (($secure) && (not $anvil->Log->secure)) if (($secure) && (not $anvil->Log->secure))
{ {
$anvil->data->{loop}{count}--;
return(2); return(2);
} }
@ -343,6 +353,7 @@ sub entry
$string .= "$line; "; $string .= "$line; ";
print $THIS_FILE." ".__LINE__."; string: [".$string."]\n" if $test; print $THIS_FILE." ".__LINE__."; string: [".$string."]\n" if $test;
} }
print $THIS_FILE." ".__LINE__."; loop::count: [".$anvil->data->{loop}{count}."] " if $test;
# If I have a raw string, do no more processing. # If I have a raw string, do no more processing.
print $THIS_FILE." ".__LINE__."; raw: [".$raw."], key: [".$key."]\n" if $test; print $THIS_FILE." ".__LINE__."; raw: [".$raw."], key: [".$key."]\n" if $test;
@ -386,6 +397,7 @@ sub entry
### TODO: Periodically check the log file size. If it's over a gigabyte, archive it ### TODO: Periodically check the log file size. If it's over a gigabyte, archive it
# Open the file? # Open the file?
$anvil->data->{HANDLE}{'log'}{main} = "" if not defined $anvil->data->{HANDLE}{'log'}{main};
print $THIS_FILE." ".__LINE__."; HANDLE::log::main: [".$anvil->data->{HANDLE}{'log'}{main}."]\n" if $test; print $THIS_FILE." ".__LINE__."; HANDLE::log::main: [".$anvil->data->{HANDLE}{'log'}{main}."]\n" if $test;
if (not $anvil->data->{HANDLE}{'log'}{main}) if (not $anvil->data->{HANDLE}{'log'}{main})
{ {
@ -420,7 +432,7 @@ sub entry
# The handle has to be wrapped in a block to make 'print' happy as it doesn't like non-scalars for file handles # 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'}{main} } $string; print { $anvil->data->{HANDLE}{'log'}{main} } $string;
$anvil->data->{loop}{count} = 0; $anvil->data->{loop}{count} = 0;
} }
else else
{ {
@ -432,6 +444,9 @@ sub entry
SYSLOG_FACILITY => $secure ? "authpriv" : $facility, SYSLOG_FACILITY => $secure ? "authpriv" : $facility,
SYSLOG_IDENTIFIER => $tag, SYSLOG_IDENTIFIER => $tag,
); );
# Reset the loop counter
$anvil->data->{loop}{count} = 0;
} }
if ($print) if ($print)
@ -439,6 +454,7 @@ sub entry
print $print_string."\n"; print $print_string."\n";
} }
$anvil->data->{loop}{count}--;
return(0); return(0);
} }
@ -679,7 +695,7 @@ sub variables
# Exit immediately if this isn't going to be logged # Exit immediately if this isn't going to be logged
print $THIS_FILE." ".__LINE__."; debug: [".$debug."], level: [".$level."], Log->level: [".$anvil->Log->level."]\n" if $test; print $THIS_FILE." ".__LINE__."; debug: [".$debug."], level: [".$level."], Log->level: [".$anvil->Log->level."]\n" if $test;
die if $test; #die if $test;
if (not defined $level) if (not defined $level)
{ {
die $THIS_FILE." ".__LINE__."; Log->variables() called without 'level': [".$level."] defined from: [$source : $line]\n"; die $THIS_FILE." ".__LINE__."; Log->variables() called without 'level': [".$level."] defined from: [$source : $line]\n";
@ -688,7 +704,7 @@ sub variables
{ {
die $THIS_FILE." ".__LINE__."; Log->variables() called without Log->level: [".$anvil->Log->level."] defined from: [$source : $line]\n"; die $THIS_FILE." ".__LINE__."; Log->variables() called without Log->level: [".$anvil->Log->level."] defined from: [$source : $line]\n";
} }
#print "level: [$level], logging: [".$anvil->Log->level."], secure: [$secure], logging secure: [".$anvil->Log->secure."]\n"; print "level: [$level], logging: [".$anvil->Log->level."], secure: [$secure], logging secure: [".$anvil->Log->secure."]\n" if $test;
if ($level > $anvil->Log->level) if ($level > $anvil->Log->level)
{ {
return(1); return(1);

@ -964,7 +964,8 @@ sub get_ips
my $in_iface = ""; my $in_iface = "";
my $shell_call = $anvil->data->{path}{exe}{ip}." addr list"; my $shell_call = $anvil->data->{path}{exe}{ip}." addr list";
my $output = ""; my $output = "";
if ($anvil->Network->is_local({host => $target})) my $is_local = $anvil->Network->is_local({host => $target});
if ($is_local)
{ {
# Local call. # Local call.
($output, my $return_code) = $anvil->System->call({debug => $debug, shell_call => $shell_call}); ($output, my $return_code) = $anvil->System->call({debug => $debug, shell_call => $shell_call});
@ -1058,7 +1059,7 @@ sub get_ips
# we'll read them all in. # we'll read them all in.
$shell_call = $anvil->data->{path}{exe}{ls}." ".$anvil->data->{path}{directories}{ifcfg}; $shell_call = $anvil->data->{path}{exe}{ls}." ".$anvil->data->{path}{directories}{ifcfg};
$output = ""; $output = "";
if ($anvil->Network->is_local({host => $target})) if ($is_local)
{ {
# Local call. # Local call.
($output, my $return_code) = $anvil->System->call({debug => $debug, shell_call => $shell_call}); ($output, my $return_code) = $anvil->System->call({debug => $debug, shell_call => $shell_call});
@ -1154,7 +1155,7 @@ sub get_ips
my $route_ip = ""; my $route_ip = "";
$shell_call = $anvil->data->{path}{exe}{ip}." route show"; $shell_call = $anvil->data->{path}{exe}{ip}." route show";
$output = ""; $output = "";
if ($anvil->Network->is_local({host => $target})) if ($is_local)
{ {
# Local call. # Local call.
($output, my $return_code) = $anvil->System->call({debug => $debug, shell_call => $shell_call}); ($output, my $return_code) = $anvil->System->call({debug => $debug, shell_call => $shell_call});
@ -1222,7 +1223,7 @@ sub get_ips
my $dns_hash = {}; my $dns_hash = {};
my $shell_call = $anvil->data->{path}{exe}{nmcli}." dev show"; my $shell_call = $anvil->data->{path}{exe}{nmcli}." dev show";
my $output = ""; my $output = "";
if ($anvil->Network->is_local({host => $target})) if ($is_local)
{ {
# Local call. # Local call.
($output, my $return_code) = $anvil->System->call({debug => $debug, shell_call => $shell_call}); ($output, my $return_code) = $anvil->System->call({debug => $debug, shell_call => $shell_call});
@ -1369,6 +1370,7 @@ Parameters;
This is the host name (or IP address) to check against the local system. This is the host name (or IP address) to check against the local system.
=cut =cut
### NOTE: Do not log in here, it will cause a recursive loop!
sub is_local sub is_local
{ {
my $self = shift; my $self = shift;
@ -1376,45 +1378,49 @@ sub is_local
my $anvil = $self->parent; my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
# To avoid deep recurssion, we set this on the first call so that anything below that re-calls us just gets a quick '1' and returns
$anvil->data->{env}{checking_local} = 0 if not defined $anvil->data->{env}{checking_local};
return(1) if $anvil->data->{env}{checking_local};
$anvil->data->{env}{checking_local} = 1;
my $host = $parameter->{host} ? $parameter->{host} : ""; my $host = $parameter->{host} ? $parameter->{host} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host => $host }}); return(1) if not $host;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
host => $host,
}});
# If we've checked this host before, return the cached answer
if (exists $anvil->data->{cache}{is_local}{$host})
{
return($anvil->data->{cache}{is_local}{$host});
}
my $is_local = 0; $anvil->data->{cache}{is_local}{$host} = 0;
if (($host eq $anvil->_host_name) or if (($host eq $anvil->_host_name) or
($host eq $anvil->_short_host_name) or ($host eq $anvil->_short_host_name) or
($host eq "localhost") or ($host eq "localhost") or
($host eq "127.0.0.1")) ($host eq "127.0.0.1"))
{ {
# It's local # It's local
$is_local = 1; $anvil->data->{cache}{is_local}{$host} = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { is_local => $is_local }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "cache::is_local::${host}" => $anvil->data->{cache}{is_local}{$host} }});
} }
else else
{ {
### NOTE: We use the undocumented 'is_local' parameter to avoid ->get_ips() calling us,
### causing a recursive loop.
# Get the list of current IPs and see if they match. # Get the list of current IPs and see if they match.
$anvil->Network->get_ips({debug => 3}); if (not exists $anvil->data->{network}{'local'}{interface})
{
$anvil->Network->get_ips({debug => 9999});
}
foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{network}{'local'}{interface}}) foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{network}{'local'}{interface}})
{ {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "network::local::interface::${interface}::ip" => $anvil->data->{network}{'local'}{interface}{$interface}{ip} }}); #$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "network::local::interface::${interface}::ip" => $anvil->data->{network}{'local'}{interface}{$interface}{ip} }});
if ($host eq $anvil->data->{network}{'local'}{interface}{$interface}{ip}) if ($host eq $anvil->data->{network}{'local'}{interface}{$interface}{ip})
{ {
$is_local = 1; $anvil->data->{cache}{is_local}{$host} = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { is_local => $is_local }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "cache::is_local::${host}" => $anvil->data->{cache}{is_local}{$host} }});
last; last;
} }
} }
} }
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { is_local => $is_local }}); #$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { is_local => $is_local }});
delete $anvil->data->{env}{checking_local}; return($anvil->data->{cache}{is_local}{$host});
return($is_local);
} }
# =head3 # =head3

@ -1651,7 +1651,6 @@ sub read_file
return("!!error!!"); return("!!error!!");
} }
### NOTE: This is called by 'is_local', so it pre-sets 'is_local' to avoid a deep recursion.
# Reading locally or remote? # Reading locally or remote?
if ($anvil->Network->is_local({host => $target})) if ($anvil->Network->is_local({host => $target}))
{ {

@ -781,7 +781,7 @@ Failed to promote the DRBD resource: [#!variable!resource!#] primary. Expected a
<key name="log_0447">About to download: [#!variable!url!#] and save it to: [#!variable!file!#].</key> <key name="log_0447">About to download: [#!variable!url!#] and save it to: [#!variable!file!#].</key>
<key name="log_0448">Ready to parse: [#!variable!file!#].</key> <key name="log_0448">Ready to parse: [#!variable!file!#].</key>
<key name="log_0449">Parsed: [#!variable!records!#], adding/updating them to the database now.</key> <key name="log_0449">Parsed: [#!variable!records!#], adding/updating them to the database now.</key>
<key name="log_0450">Skipping the network scan. The next scheduled scan will be done in: [#!variable!next_scan!#] second(s). Override with '--force'.</key> <key name="log_0450">Skipping the network scan. The next scheduled scan will be done in: [#!variable!next_scan!#]. Override with '--force'.</key>
<!-- Test words. Do NOT change unless you update 't/Words.t' or tests will needlessly fail. --> <!-- Test words. Do NOT change unless you update 't/Words.t' or tests will needlessly fail. -->
<key name="t_0000">Test</key> <key name="t_0000">Test</key>

@ -1085,7 +1085,7 @@ sub prep_database
if ($host_type eq "dashboard") if ($host_type eq "dashboard")
{ {
my ($database_output, $return_code) = $anvil->System->call({ my ($database_output, $return_code) = $anvil->System->call({
debug => 2, debug => 3,
shell_call => $anvil->data->{path}{exe}{'striker-prep-database'}, shell_call => $anvil->data->{path}{exe}{'striker-prep-database'},
source => $THIS_FILE, source => $THIS_FILE,
line => __LINE__, line => __LINE__,

@ -53,16 +53,21 @@ my $process = 0;
if (-e $oui_file) if (-e $oui_file)
{ {
# How long ago did we download it? # How long ago did we download it?
my $mtime = (stat($oui_file))[9]; my $refresh_time = 259200;
my $size = (stat($oui_file))[7]; my $modified_time = (stat($oui_file))[9];
my $age = time - $mtime; my $size = (stat($oui_file))[7];
my $age = time - $modified_time;
my $download_after = $refresh_time - $age;
$download_after = 0 if $download_after < 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:oui_file' => $oui_file, 's1:oui_file' => $oui_file,
's2:mtime' => $mtime." (".$anvil->Get->date_and_time({use_time => $mtime}).")", 's2:modified_time' => $modified_time." (".$anvil->Get->date_and_time({use_time => $modified_time}).")",
's3:age' => $anvil->Convert->add_commas({number => $age})." (".$anvil->Convert->time({'time' => $age, translate => 1}).")", 's3:age' => $anvil->Convert->add_commas({number => $age})." (".$anvil->Convert->time({'time' => $age, translate => 1}).")",
's4:size' => $anvil->Convert->add_commas({number => $size})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $size}).")", 's4:refresh_time' => $anvil->Convert->add_commas({number => $refresh_time})." (".$anvil->Convert->time({'time' => $refresh_time, translate => 1}).")",
's5:download_after' => $anvil->Convert->add_commas({number => $download_after})." (".$anvil->Convert->time({'time' => $download_after, translate => 1}).")",
's6:size' => $anvil->Convert->add_commas({number => $size})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $size}).")",
}}); }});
if (($age < 259200) && ($size > 0)) if (($download_after) && ($size > 0))
{ {
# It's less than three days old, don't download. Do parse though (for now at least) # It's less than three days old, don't download. Do parse though (for now at least)
$download = 0; $download = 0;

Loading…
Cancel
Save