From 4e6f492c4f07ea893623b33ebbf1cd803ae706c6 Mon Sep 17 00:00:00 2001 From: Digimer Date: Mon, 21 May 2018 22:02:45 -0400 Subject: [PATCH] * Updated Log->variables to add dots when printing a variable/value pair per line so that variables line up vertically. * Renamed tools/anvil-configure-network to tools/anvil-configure-striker given that it will also now update system passwords. * Started working on tools/anvil-update-states to properly handle a Striker with already-configured networking. * Cleaned up tools/anvil-change-password. * Fixed a bug in Storage->update_config to set the ownership of anvil.conf to 'apache:apache' so that the web server can read it. Signed-off-by: Digimer --- Anvil/Tools/Log.pm | 29 +- Anvil/Tools/Storage.pm | 4 +- Anvil/Tools/System.pm | 279 +----------------- cgi-bin/home | 2 +- share/words.xml | 12 +- tools/anvil-change-password | 69 +++-- ...figure-network => anvil-configure-striker} | 14 +- tools/anvil-update-states | 171 ++++++++--- 8 files changed, 229 insertions(+), 351 deletions(-) rename tools/{anvil-configure-network => anvil-configure-striker} (98%) diff --git a/Anvil/Tools/Log.pm b/Anvil/Tools/Log.pm index 58eafb88..d78f5270 100755 --- a/Anvil/Tools/Log.pm +++ b/Anvil/Tools/Log.pm @@ -608,20 +608,43 @@ sub variables } else { - # Put all the entries on their own line. + # Put all the entries on their own line. We'll loop twice; the first time to get the + # longest variable, and the second loop to print them (with dots to make the values + # all line up). + my $length = 0; + foreach my $key (sort {$a cmp $b} keys %{$list}) + { + if (length($key) > $length) + { + $length = length($key); + } + } + # We add '1' to account for the colon we append. + $length++; + $raw .= $anvil->Words->string({key => "log_0019"})."\n"; foreach my $key (sort {$a cmp $b} keys %{$list}) { # Strip a leading 'sX:' in case the user is sorting the output. my $say_key = $key; $say_key =~ s/^s(\d+)://; + $say_key .= ":"; + my $difference = $length - length($say_key); + if ($difference) + { + $say_key .= " "; + for (2..$difference) + { + $say_key .= "."; + } + } if ($entry ne $entries) { - $raw .= "|- $say_key: [".$list->{$key}."]\n"; + $raw .= "|- $say_key [".$list->{$key}."]\n"; } else { - $raw .= "\\- $say_key: [".$list->{$key}."]\n"; + $raw .= "\\- $say_key [".$list->{$key}."]\n"; } $entry++; } diff --git a/Anvil/Tools/Storage.pm b/Anvil/Tools/Storage.pm index 0dc990ae..8e0421a4 100755 --- a/Anvil/Tools/Storage.pm +++ b/Anvil/Tools/Storage.pm @@ -1949,11 +1949,11 @@ sub update_config body => $new_file, debug => $debug, file => $anvil->data->{path}{configs}{'anvil.conf'}, - group => "admin", + group => "apache", mode => "0640", overwrite => 1, secure => 1, - user => "admin", + user => "apache", password => $password, port => $port, target => $target, diff --git a/Anvil/Tools/System.pm b/Anvil/Tools/System.pm index cc73e0e7..26ff6b8c 100755 --- a/Anvil/Tools/System.pm +++ b/Anvil/Tools/System.pm @@ -15,7 +15,6 @@ my $THIS_FILE = "System.pm"; ### Methods; # call -# (disabled) change_apache_password # change_shell_user_password # check_daemon # check_memory @@ -197,262 +196,6 @@ sub call return($output); } -# =head2 change_apache_password -# -# NOTE: Likely to be removed. -# -# This changes the password used to connet to Striker's web interface. If the C<< .htpasswd >> file isn't found, this method will effectively enable the password feature. -# -# The return code will be C<< 255 >> on internal error. Otherwise, it will be the code returned from the C<< passwd >> call. -# -# Parameters; -# -# =head3 new_password (required) -# -# This is the new password to set. The user should be encouraged to select a good (long) password. -# -# =head3 password (optional) -# -# If you are changing the apache password on a remote machine, this is the password used to connect to that machine. If not passed, an attempt to connect with passwordless SSH will be made (but this won't be the case in most instances). Ignored if C<< target >> is not given. -# -# =head3 port (optional, default 22) -# -# This is the TCP port number to use if connecting to a remote machine over SSH. Ignored if C<< target >> is not given. -# -# =head3 remote_user (optional, default root) -# -# If C<< target >> is set and we're changing the password for a remote user, this is the user we B<< log into >> the remote machine as, B<< not >> the user whose password we will change. -# -# =head3 target (optional) -# -# This is the IP address or (resolvable) host name of the target machine whose user account you want to change the password -# -# =head3 user (optional, default 'sys::apache::user' or 'admin') -# -# This is the apache user name to use. If another name existed before in C<< .htpasswd >>, that old user name will be removed. -# -# =cut -# sub change_apache_password -# { -# my $self = shift; -# my $parameter = shift; -# my $anvil = $self->parent; -# my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; -# -# my $new_password = defined $parameter->{new_password} ? $parameter->{new_password} : ""; -# my $password = defined $parameter->{password} ? $parameter->{password} : ""; -# my $port = defined $parameter->{port} ? $parameter->{port} : ""; -# my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : ""; -# my $target = defined $parameter->{target} ? $parameter->{target} : ""; -# my $user = defined $parameter->{user} ? $parameter->{user} : $anvil->data->{sys}{apache}{user}; -# my $return_code = 255; -# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { -# user => $user, -# target => $target, -# port => $port, -# remote_user => $remote_user, -# new_password => $anvil->Log->secure ? $new_password : "--", -# password => $anvil->Log->secure ? $password : "--", -# }}); -# -# # Set the user to 'admin' if it's not set. -# if (not $user) -# { -# $user = "admin"; -# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { user => $user }}); -# } -# -# # OK, what about a password? -# if (not $new_password) -# { -# # Um... -# $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Systeme->change_apache_password()", parameter => "new_password" }}); -# return($return_code); -# } -# -# # Only the root user can do this! -# # $< == real UID, $> == effective UID -# if (($< != 0) && ($> != 0)) -# { -# # Not root -# $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0156", variables => { method => "Systeme->change_apache_password()" }}); -# return($return_code); -# } -# -# # Read httpd.conf and make sure apache is configured for .htpasswd. -# my $httpd_conf = $anvil->Storage->read_file({ -# file => $anvil->data->{path}{configs}{'httpd.conf'}, -# debug => $debug, -# target => $target, -# port => $port, -# remote_user => $remote_user, -# password => $password, -# }); -# if ($httpd_conf eq "!!error!!") -# { -# # We're done. -# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { httpd_conf => $httpd_conf }}); -# return($return_code); -# } -# my $rewrite = 0; -# my $new_file = ""; -# my $in_html_directory = 0; -# my $in_cgi_directory = 0; -# my $cgi_first_line = 0; -# foreach my $line (split/\n/, $httpd_conf) -# { -# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { line => $line, in_cgi_directory => $in_cgi_directory }}); -# -# if ($in_html_directory) -# { -# if ($line =~ /^(\s+)AllowOverride None/i) -# { -# # We need to update. -# my $space = $1; -# $rewrite = 1; -# $new_file .= $space."AllowOverride AuthConfig\n"; -# next; -# } -# elsif ($line eq "") -# { -# $in_html_directory = 0; -# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { in_html_directory => $in_html_directory }}); -# } -# } -# elsif ($line eq '') -# { -# $in_html_directory = 1; -# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { in_html_directory => $in_html_directory }}); -# } -# -# if ($in_cgi_directory) -# { -# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { cgi_first_line => $cgi_first_line, line => $line }}); -# if ((not $cgi_first_line) && ($line =~ /^(\s+)AllowOverride None/i)) -# { -# # We need to update. -# my $space = $1; -# $rewrite = 1; -# $new_file .= $space."# Password login\n"; -# $new_file .= $space."AuthType Basic\n"; -# $new_file .= $space."AuthName \"Striker - The Anvil! Dashboard\"\n"; -# $new_file .= $space."AuthUserFile ".$anvil->data->{path}{data}{'.htpasswd'}."\n"; -# $new_file .= $space."Require valid-user \n"; -# -# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { space => $space, rewrite => $rewrite }}); -# } -# elsif ($line eq "") -# { -# $in_cgi_directory = 0; -# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { in_cgi_directory => $in_cgi_directory }}); -# } -# elsif ($line =~ /Require all granted/) -# { -# # We don't want this line. -# $rewrite = 1; -# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { rewrite => $rewrite }}); -# next; -# } -# $cgi_first_line = 1; -# } -# elsif ($line eq '') -# { -# $in_cgi_directory = 1; -# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { in_cgi_directory => $in_cgi_directory }}); -# } -# -# $new_file .= $line."\n"; -# } -# -# # Update the file if needed. -# if ($rewrite) -# { -# # Back it up first. -# my $backup_file = $anvil->Storage->backup({ -# file => $anvil->data->{path}{configs}{'httpd.conf'}, -# debug => $debug, -# target => $target, -# port => $port, -# remote_user => $remote_user, -# password => $password, -# }); -# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { backup_file => $backup_file }}); -# -# if ($backup_file) -# { -# # Proceed. -# $anvil->Storage->write_file({ -# body => $new_file, -# debug => $debug, -# file => $anvil->data->{path}{configs}{'httpd.conf'}, -# overwrite => 1, -# secure => 0, -# target => $target, -# port => $port, -# remote_user => $remote_user, -# password => $password, -# }); -# } -# } -# -# # Generate the htpasswd md5 (special flavour) hash string. -# # See: https://httpd.apache.org/docs/2.4/misc/password_encryptions.html -# -# # :$apr1$$ -# #admin:$apr1$Upbg/ujf$SJNCufHfq76uo2t0rZTZI/ -# -# # my $salt = $anvil->System->call({debug => $debug, shell_call => $anvil->data->{path}{exe}{openssl}." rand 1000 | ".$anvil->data->{path}{exe}{strings}." | ".$anvil->data->{path}{exe}{'grep'}." -io [0-9A-Za-z\.\/] | ".$anvil->data->{path}{exe}{head}." -n 16 | ".$anvil->data->{path}{exe}{'tr'}." -d '\n'" }); -# # my $new_hash = crypt($new_password,"\$6\$".$salt."\$"); -# # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { -# # salt => $salt, -# # new_hash => $new_hash, -# # }}); -# -# # htpasswd -cb /etc/httpd/.htpasswd admin Initial2 -# # chown apache:apache /etc/httpd/.htpasswd -# # chmod 0660 /etc/httpd/.htpasswd -# -# -# # (re)write the htpasswd file. -# ### TODO: Temporary! -# $new_password =~ s/"/\"/g; -# my $shell_call = $anvil->data->{path}{exe}{htpasswd}." -nb ".$user." \"".$new_password."\""; -# my $output = $anvil->System->call({debug => $debug, secure => 1, shell_call => $shell_call}); -# my $hash_string = ""; -# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { output => $output }}); -# foreach my $line (split/\n/, $output) -# { -# $hash_string = $line; -# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { hash_string => $hash_string }}); -# last; -# } -# -# # Write out the file. -# $return_code = $anvil->Storage->write_file({ -# body => $hash_string."\n\n", -# debug => $debug, -# file => $anvil->data->{path}{data}{'.htpasswd'}, -# group => "apache", -# mode => "0660", -# overwrite => 1, -# secure => 0, -# target => $target, -# user => "apache", -# port => $port, -# remote_user => $remote_user, -# password => $password, -# }); -# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { return_code => $return_code }}); -# -# if (not $return_code) -# { -# # Restart apache -# $anvil->System->reload_daemon({debug => $debug, daemon => "httpd"}); -# } -# -# return($return_code); -# } - =head2 change_shell_user_password @@ -793,9 +536,9 @@ sub get_ips $in_iface = $1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { in_iface => $in_iface }}); - $anvil->data->{sys}{networks}{$in_iface}{ip} = "" if not defined $anvil->data->{sys}{networks}{$in_iface}{ip}; - $anvil->data->{sys}{networks}{$in_iface}{subnet} = "" if not defined $anvil->data->{sys}{networks}{$in_iface}{subnet}; - $anvil->data->{sys}{networks}{$in_iface}{mac} = "" if not defined $anvil->data->{sys}{networks}{$in_iface}{mac}; + $anvil->data->{sys}{network}{interface}{$in_iface}{ip} = "" if not defined $anvil->data->{sys}{network}{interface}{$in_iface}{ip}; + $anvil->data->{sys}{network}{interface}{$in_iface}{subnet} = "" if not defined $anvil->data->{sys}{network}{interface}{$in_iface}{subnet}; + $anvil->data->{sys}{network}{interface}{$in_iface}{mac} = "" if not defined $anvil->data->{sys}{network}{interface}{$in_iface}{mac}; } next if not $in_iface; next if $in_iface eq "lo"; @@ -813,21 +556,21 @@ sub get_ips $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { subnet => $subnet }}); } - $anvil->data->{sys}{networks}{$in_iface}{ip} = $ip; - $anvil->data->{sys}{networks}{$in_iface}{subnet} = $subnet; + $anvil->data->{sys}{network}{interface}{$in_iface}{ip} = $ip; + $anvil->data->{sys}{network}{interface}{$in_iface}{subnet} = $subnet; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "s1:sys::networks::${in_iface}::ip" => $anvil->data->{sys}{networks}{$in_iface}{ip}, - "s2:sys::networks::${in_iface}::subnet" => $anvil->data->{sys}{networks}{$in_iface}{subnet}, + "s1:sys::network::interface::${in_iface}::ip" => $anvil->data->{sys}{network}{interface}{$in_iface}{ip}, + "s2:sys::network::interface::${in_iface}::subnet" => $anvil->data->{sys}{network}{interface}{$in_iface}{subnet}, }}); } if ($line =~ /ether ([0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}) /i) { my $mac = $1; - $anvil->data->{sys}{networks}{$in_iface}{mac} = $mac; + $anvil->data->{sys}{network}{interface}{$in_iface}{mac} = $mac; $anvil->data->{sys}{mac}{$mac}{iface} = $in_iface; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { - "sys::networks::${in_iface}::mac" => $anvil->data->{sys}{networks}{$in_iface}{mac}, - "sys::mac::${mac}::iface" => $anvil->data->{sys}{mac}{$mac}{iface}, + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "sys::network::interface::${in_iface}::mac" => $anvil->data->{sys}{network}{interface}{$in_iface}{mac}, + "sys::mac::${mac}::iface" => $anvil->data->{sys}{mac}{$mac}{iface}, }}); } } diff --git a/cgi-bin/home b/cgi-bin/home index 03ec9d04..3fef0463 100755 --- a/cgi-bin/home +++ b/cgi-bin/home @@ -206,7 +206,7 @@ sub save_job debug => 2, file => $THIS_FILE, line => __LINE__, - job_command => "anvil-configure-network", + job_command => "anvil-configure-striker", job_data => "form::config_step2", job_name => "configure::network", job_title => "job_0001", diff --git a/share/words.xml b/share/words.xml index d2e8f28f..8c7b1583 100644 --- a/share/words.xml +++ b/share/words.xml @@ -45,6 +45,16 @@ Author: Madison Kelly Proceed? [y/N] Aborting. Auto-approved by command line switch, proceeding. + Updating the Striker user: [#!variable!user!#] password... + Done. + Updating the database user: [#!variable!user!#] password... + Updating the local config file: [#!variable!file!#] database password... + Updating the shell user: [#!variable!user!#] password... + Finished! + +NOTE: You must update the password of any other system using this host's + database manually! + Starting: [#!variable!program!#]. @@ -352,7 +362,7 @@ Here we will inject 't_0006', which injects 't_0001' which has a variable: [#!st This program must run with 'root' level privileges. No password was given, exiting. The passwords don't match, exiting. - Failed to read the file: [#!variable!file!#]. Please see the logs for details + Failed to read the file: [#!variable!file!#]. It doesn't appear to exist. Failed to add the target: [#!variable!target!#]:[#!variable!port!#]'s RSA fingerprint to: [#!variable!user!#]'s list of known hosts. There was a problem adding the local machine to the: [#!data!path::configs::anvil.conf!#] file. Please see the log for details. diff --git a/tools/anvil-change-password b/tools/anvil-change-password index 200e65a5..72ea53c0 100755 --- a/tools/anvil-change-password +++ b/tools/anvil-change-password @@ -75,7 +75,7 @@ if ($anvil->data->{switches}{'password-file'}) else { # The file doesn't exist. - print $anvil->Words->string({key => "error_0008", variables => { file => $anvil->data->{switches}{'password-file'} }}); + print $anvil->Words->string({key => "error_0008", variables => { file => $anvil->data->{switches}{'password-file'} }})."\n"; $anvil->nice_exit({exit_code => 4}); } } @@ -136,40 +136,6 @@ else { print $anvil->Words->string({key => "message_0023"})."\n"; update_local_passwords($anvil); - - # Update the user password. - my $user = "admin"; - my $user_uuid = $anvil->Database->insert_or_update_users({ - debug => 2, - user_name => $user, - user_password_hash => $anvil->data->{switches}{'new-password'}, - user_is_admin => 1, - user_is_experienced => 1, - user_is_trusted => 1, - }); - - # Validate - my $valid = $anvil->Account->validate_password({ - debug => 2, - user => $user, - password => $anvil->data->{switches}{'new-password'}, - }); - - # Update the database password. -# foreach my $user ("postgres", $database_user) -# { -# my $update_output = $anvil->System->call({secure => 1, shell_call => $anvil->data->{path}{exe}{su}." - postgres -c \"".$anvil->data->{path}{exe}{psql}." template1 -c \\\"ALTER ROLE $user WITH PASSWORD '".$anvil->data->{database}{$local_uuid}{password}."';\\\"\"", source => $THIS_FILE, line => __LINE__}); -# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, secure => 1, list => { update_output => $update_output }}); -# foreach my $line (split/\n/, $user_list) -# { -# if ($line =~ /ALTER ROLE/) -# { -# # Password set -# $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0100", variables => { user => $user }}); -# } -# } -# } - } else { @@ -201,7 +167,29 @@ sub update_local_passwords { my ($anvil) = @_; + # Update the 'admin' user password in the database. + my $user = "admin"; + print $anvil->Words->string({key => "message_0024", variables => { user => $user }}); + + my $user_uuid = $anvil->Database->insert_or_update_users({ + debug => 2, + user_name => $user, + user_password_hash => $anvil->data->{switches}{'new-password'}, + user_is_admin => 1, + user_is_experienced => 1, + user_is_trusted => 1, + }); + print $anvil->Words->string({key => "message_0025"})."\n"; + # Validate + my $valid = $anvil->Account->validate_password({ + debug => 2, + user => $user, + password => $anvil->data->{switches}{'new-password'}, + }); + + ### NOTE: We directly connect to the local 'template1' database as + # Update the database passwords my $host_uuid = $anvil->data->{sys}{host_uuid}; my $old_password = $anvil->data->{database}{$host_uuid}{password}; my $dbh = DBI->connect("DBI:Pg:dbname=template1;host=localhost;port=5432", "postgres", $old_password, { @@ -229,30 +217,39 @@ sub update_local_passwords my $owner_name = $results->[0]->[1]; foreach my $user ("postgres", $owner_name) { + print $anvil->Words->string({key => "message_0026", variables => { user => $user }}); my $query = "ALTER ROLE ".$user." WITH PASSWORD ".$dbh->quote($anvil->data->{switches}{'new-password'}); $dbh->do($query) or $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0090", variables => { query => $anvil->Log->secure ? $query : "--", server => "localhost", db_error => $DBI::errstr, }}); + print $anvil->Words->string({key => "message_0025"})."\n"; } # Update our database password in anvil.conf + print $anvil->Words->string({key => "message_0027", variables => { file => $anvil->data->{switches}{'new-password'} }}); $anvil->Storage->update_config({ debug => 2, secure => 1, variable => "database::${host_uuid}::password", value => $anvil->data->{switches}{'new-password'}, }); + print $anvil->Words->string({key => "message_0025"})."\n"; + ### TODO: Loop through any other dashboards and nodes we know about and call the above with 'target' ### (and password, port and remote_user) set. # Update the local users. foreach my $user ("admin", "root") { - print "Updating: [$user] with password: [".$anvil->data->{switches}{'new-password'}."]\n"; + print $anvil->Words->string({key => "message_0028", variables => { user => $user }}); $anvil->System->change_shell_user_password({debug => 2, user => $user, new_password => $anvil->data->{switches}{'new-password'}}); + print $anvil->Words->string({key => "message_0025"})."\n"; } + # All done! + print $anvil->Words->string({key => "message_0029"})."\n"; + return(0); } diff --git a/tools/anvil-configure-network b/tools/anvil-configure-striker similarity index 98% rename from tools/anvil-configure-network rename to tools/anvil-configure-striker index 160c0b0d..5a9da8ea 100755 --- a/tools/anvil-configure-network +++ b/tools/anvil-configure-striker @@ -1,6 +1,6 @@ #!/usr/bin/perl # -# This is called when the local network needs to be reconfigured. +# This is called when striker needs to configure the local network and user accounts. # # Exit codes; # 0 = Normal exit. @@ -85,6 +85,18 @@ sub update_passwords { my ($anvil) = @_; + # Write the password into a temporary file. + my $error = $anvil->Storage->write_file({ + body => , + debug => $debug, + file => $temp_file, + group => $group, + mode => $mode, + overwrite => 1, + secure => $secure, + user => $user, + }); + return(0); } diff --git a/tools/anvil-update-states b/tools/anvil-update-states index 632ab726..0a33d5c0 100755 --- a/tools/anvil-update-states +++ b/tools/anvil-update-states @@ -48,11 +48,17 @@ sub report_network { my ($anvil) = @_; - # Write out the data in json format. + # Run 'ip addr' to see what IPs are in use. The results will be stored in: + $anvil->System->get_ips(); + + # We'll read through '/sys/class/net' looking for network interfaces. + # * 'sys::network::interface::::ip' - If an IP address is set + # * 'sys::network::interface::::subnet' - If an IP is set my $directory = $anvil->data->{path}{sysfs}{network_interfaces}; - #print $THIS_FILE." ".__LINE__."; directory: [".$directory."]\n"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { directory => $directory }}); + local(*DIRECTORY); - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0018", variables => { directory => $directory }}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0018", variables => { directory => $directory }}); opendir(DIRECTORY, $directory); while(my $file = readdir(DIRECTORY)) { @@ -61,7 +67,7 @@ sub report_network next if $file eq "lo"; next if $file =~ /virbr\d/; my $full_path = "$directory/$file"; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { full_path => $full_path }}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { full_path => $full_path }}); if (-d $full_path) { # Pull out the data I want. Note that some of these don't exist with virtio-net interfaces. @@ -71,48 +77,96 @@ sub report_network my $mtu = -e $full_path."/mtu" ? $anvil->Storage->read_file({file => $full_path."/mtu"}) : 0; my $duplex = -e $full_path."/duplex" ? $anvil->Storage->read_file({file => $full_path."/duplex"}) : "unknown"; # full or half? my $operational = -e $full_path."/operstate" ? $anvil->Storage->read_file({file => $full_path."/operstate"}) : "unknown"; # up or down + my $ip_address = ""; + my $subnet_mask = ""; my $speed = $link_state ? $anvil->Storage->read_file({file => $full_path."/speed", debug => 2}) : 0; # Mbps (ie: 1000 = Gbps), gives a very high number for unplugged link + my $media = "unknown"; + my $is_bond = 0; + my $is_slave = 0; + + if (exists $anvil->data->{sys}{network}{interface}{$interface}) + { + $ip_address = $anvil->data->{sys}{network}{interface}{$interface}{ip} ? $anvil->data->{sys}{network}{interface}{$interface}{ip} : ""; + $subnet_mask = $anvil->data->{sys}{network}{interface}{$interface}{subnet} ? $anvil->data->{sys}{network}{interface}{$interface}{subnet} : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + ip_address => $ip_address, + subnet_mask => $subnet_mask, + }}); + } + + # If this interface is already a bond slave, the real mac address will be in a + # sub-directory. + my $mac_bond_file = $directory."/".$file."/bonding_slave/perm_hwaddr"; + if (-e $mac_bond_file) + { + # It's a slave. + $is_slave = 1; + $mac_address = $anvil->Storage->read_file({file => $mac_bond_file}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + is_slave => $is_slave, + mac_address => $mac_address, + }}); + } + + # If this is a virtual interface, set some fake values that don't actually exist on + # the system for the sake of a cleaner display. if ($mac_address =~ /^52:54:00/) { ### Set some fake values. # Speed is "as fast as possible", so we'll record 100 Gbps, but that is really kind of arbitrary. $speed = 100000 if not $speed; $duplex = "full" if not $duplex; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + speed => $speed, + duplex => $duplex, + }}); } # If the state is 'down', set the speed to '0'. if (not $link_state) { $speed = 0; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { speed => $speed }}); + } + + # Is this a bond interface? + if (-e "/proc/net/bonding/".$interface) + { + # Yup, we'll neet to dig into the bond proc files to get the proper slaved + # interface MAC addresses. + $is_bond = 1; } $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + duplex => $duplex, interface => $interface, - mac_address => $mac_address, + ip_address => $ip_address, link_state => $link_state, + mac_address => $mac_address, mtu => $mtu, - duplex => $duplex, operational => $operational, speed => $speed, + subnet_mask => $subnet_mask, }}); # If the MAC address starts with '52:54:00', we've got a virtio NIC. - - die $THIS_FILE." ".__LINE__."; No speed for: [".$full_path."/speed]\n" if ((not defined $speed) or ($speed eq "")); - die $THIS_FILE." ".__LINE__."; Speed: [$speed] isn't numeric for: [".$full_path."/speed]\n" if $speed =~ /\D/; - # - # 100000 + if ((not defined $speed) or ($speed eq "")) + { + die $THIS_FILE." ".__LINE__."; No speed for: [".$full_path."/speed]\n"; + } + if ($speed =~ /\D/) + { + die $THIS_FILE." ".__LINE__."; Speed: [$speed] isn't numeric for: [".$full_path."/speed]\n"; + } if ($speed > 100000) { # NOTE: This is probably 0 now... Though someday >100 Gbps will be reasonable # and we'll need to change this. $speed = 0; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { speed => $speed }}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { speed => $speed }}); } # Find the media, if possible. - my $media = "unknown"; my $shell_call = $anvil->data->{path}{exe}{ethtool}." $interface"; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { shell_call => $shell_call }}); my $ethtool = $anvil->System->call({shell_call => $shell_call}); foreach my $line (split/\n/, $ethtool) { @@ -120,44 +174,83 @@ sub report_network if ($line =~ /Supported ports: \[ (.*?) \]/i) { $media = lc($1); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { media => $media }}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { media => $media }}); last; } } # Log - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { - interface => $interface, - mac_address => $mac_address, + $anvil->data->{network}{interfaces}{by_name}{$interface} = { + duplex => $duplex, + ip_address => $ip_address, + is_bond => $is_bond, + is_slave => $is_slave, link_state => $link_state, + mac_address => $mac_address, + media => $media, mtu => $mtu, - duplex => $duplex, operational => $operational, speed => $speed, - media => $media, + subnet_mask => $subnet_mask, + }; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "network::interfaces::by_name::${interface}::duplex" => $duplex, + "network::interfaces::by_name::${interface}::ip_address" => $ip_address, + "network::interfaces::by_name::${interface}::is_bond" => $is_bond, + "network::interfaces::by_name::${interface}::is_slave" => $is_slave, + "network::interfaces::by_name::${interface}::link_state" => $link_state, + "network::interfaces::by_name::${interface}::mac_address" => $mac_address, + "network::interfaces::by_name::${interface}::media" => $media, + "network::interfaces::by_name::${interface}::mtu" => $mtu, + "network::interfaces::by_name::${interface}::operational" => $operational, + "network::interfaces::by_name::${interface}::speed" => $speed, + "network::interfaces::by_name::${interface}::subnet_mask" => $subnet_mask, }}); - - $anvil->Database->insert_or_update_network_interfaces({ - file => $THIS_FILE, - line => __LINE__, - network_interface_name => $interface, - network_interface_duplex => $duplex, - network_interface_link_state => $link_state, - network_interface_operational => $operational, - network_interface_mac_address => $mac_address, - network_interface_medium => $media, - network_interface_mtu => $mtu, - network_interface_speed => $speed, - }); - } } closedir(DIRECTORY); - - ### TODO: Create $anvil "ip" table and record IPs on this system, linking back to $anvil interface, bond or - ### bridge. - # Run 'ip addr' to see what IPs are in use. - $anvil->System->get_ips; + + foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{network}{interfaces}{by_name}}) + { + my $duplex = $anvil->data->{network}{interfaces}{by_name}{$interface}{duplex}; + my $ip_address = $anvil->data->{network}{interfaces}{by_name}{$interface}{ip_address}; + my $is_bond = $anvil->data->{network}{interfaces}{by_name}{$interface}{is_bond}; + my $is_slave = $anvil->data->{network}{interfaces}{by_name}{$interface}{is_slave}; + my $link_state = $anvil->data->{network}{interfaces}{by_name}{$interface}{link_state}; + my $mac_address = $anvil->data->{network}{interfaces}{by_name}{$interface}{mac_address}; + my $media = $anvil->data->{network}{interfaces}{by_name}{$interface}{media}; + my $mtu = $anvil->data->{network}{interfaces}{by_name}{$interface}{mtu}; + my $operational = $anvil->data->{network}{interfaces}{by_name}{$interface}{operational}; + my $speed = $anvil->data->{network}{interfaces}{by_name}{$interface}{speed}; + my $subnet_mask = $anvil->data->{network}{interfaces}{by_name}{$interface}{subnet_mask}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + duplex => $duplex, + interface => $interface, + ip_address => $ip_address, + is_bond => $is_bond, + is_slave => $is_slave, + link_state => $link_state, + mac_address => $mac_address, + media => $media, + mtu => $mtu, + operational => $operational, + speed => $speed, + subnet_mask => $subnet_mask, + }}); + +# $anvil->Database->insert_or_update_network_interfaces({ +# file => $THIS_FILE, +# line => __LINE__, +# network_interface_name => $interface, +# network_interface_duplex => $duplex, +# network_interface_link_state => $link_state, +# network_interface_operational => $operational, +# network_interface_mac_address => $mac_address, +# network_interface_medium => $media, +# network_interface_mtu => $mtu, +# network_interface_speed => $speed, +# }); + } # Write out the XML file and JSON file. my $order = 1;